summaryrefslogtreecommitdiff
path: root/src/ToolBox
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/ToolBox
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/ToolBox')
-rw-r--r--src/ToolBox/.gitmirror1
-rw-r--r--src/ToolBox/CMakeLists.txt2
-rw-r--r--src/ToolBox/PdbTypeMatch/.gitmirror1
-rw-r--r--src/ToolBox/PdbTypeMatch/PdbTypeMatch.cpp2023
-rw-r--r--src/ToolBox/PdbTypeMatch/PdbTypeMatch.h69
-rw-r--r--src/ToolBox/PdbTypeMatch/PdbTypeMatch.nativeproj59
-rw-r--r--src/ToolBox/PdbTypeMatch/PrintSymbol.cpp2254
-rw-r--r--src/ToolBox/PdbTypeMatch/PrintSymbol.h66
-rw-r--r--src/ToolBox/PdbTypeMatch/callback.h96
-rw-r--r--src/ToolBox/PdbTypeMatch/include/.gitmirror1
-rw-r--r--src/ToolBox/PdbTypeMatch/include/cvconst.h3181
-rw-r--r--src/ToolBox/PdbTypeMatch/include/dia2.h7854
-rw-r--r--src/ToolBox/PdbTypeMatch/include/diacreate.h40
-rw-r--r--src/ToolBox/PdbTypeMatch/native.rc8
-rw-r--r--src/ToolBox/PdbTypeMatch/regs.cpp1708
-rw-r--r--src/ToolBox/PdbTypeMatch/regs.h23
-rw-r--r--src/ToolBox/PdbTypeMatch/stdafx.cpp9
-rw-r--r--src/ToolBox/PdbTypeMatch/stdafx.h25
-rw-r--r--src/ToolBox/SOS/.gitmirror1
-rw-r--r--src/ToolBox/SOS/CMakeLists.txt3
-rw-r--r--src/ToolBox/SOS/DacTableGen/.gitmirror1
-rw-r--r--src/ToolBox/SOS/DacTableGen/CMakeLists.txt19
-rw-r--r--src/ToolBox/SOS/DacTableGen/DacTableGen.csproj55
-rw-r--r--src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs503
-rw-r--r--src/ToolBox/SOS/DacTableGen/cvconst.cs2037
-rw-r--r--src/ToolBox/SOS/DacTableGen/diautil.cs809
-rw-r--r--src/ToolBox/SOS/DacTableGen/main.cs656
-rw-r--r--src/ToolBox/SOS/NETCore/.gitmirror1
-rw-r--r--src/ToolBox/SOS/NETCore/SOS.NETCore.csproj61
-rw-r--r--src/ToolBox/SOS/NETCore/SOS.NETCore.sln22
-rw-r--r--src/ToolBox/SOS/NETCore/SymbolReader.cs694
-rw-r--r--src/ToolBox/SOS/NETCore/project.json20
-rw-r--r--src/ToolBox/SOS/SOSAndICorDebug.md81
-rw-r--r--src/ToolBox/SOS/Strike/.gitmirror1
-rw-r--r--src/ToolBox/SOS/Strike/ApolloNative.rc10
-rw-r--r--src/ToolBox/SOS/Strike/CMakeLists.txt201
-rw-r--r--src/ToolBox/SOS/Strike/EventCallbacks.cpp160
-rw-r--r--src/ToolBox/SOS/Strike/EventCallbacks.h68
-rw-r--r--src/ToolBox/SOS/Strike/ExpressionNode.cpp2178
-rw-r--r--src/ToolBox/SOS/Strike/ExpressionNode.h307
-rw-r--r--src/ToolBox/SOS/Strike/Native.rc10
-rw-r--r--src/ToolBox/SOS/Strike/SOS.nativeproj7
-rw-r--r--src/ToolBox/SOS/Strike/SOS.sln76
-rw-r--r--src/ToolBox/SOS/Strike/SOS.vcproj303
-rw-r--r--src/ToolBox/SOS/Strike/UtilCode.h11
-rw-r--r--src/ToolBox/SOS/Strike/WatchCmd.cpp331
-rw-r--r--src/ToolBox/SOS/Strike/WatchCmd.h110
-rw-r--r--src/ToolBox/SOS/Strike/apollososdocs.txt2727
-rw-r--r--src/ToolBox/SOS/Strike/data.h51
-rw-r--r--src/ToolBox/SOS/Strike/datatarget.cpp215
-rw-r--r--src/ToolBox/SOS/Strike/datatarget.h90
-rw-r--r--src/ToolBox/SOS/Strike/dirs.proj20
-rw-r--r--src/ToolBox/SOS/Strike/disasm.cpp1142
-rw-r--r--src/ToolBox/SOS/Strike/disasm.h453
-rw-r--r--src/ToolBox/SOS/Strike/disasmARM.cpp626
-rw-r--r--src/ToolBox/SOS/Strike/disasmARM64.cpp392
-rw-r--r--src/ToolBox/SOS/Strike/disasmX86.cpp1707
-rw-r--r--src/ToolBox/SOS/Strike/dllsext.cpp278
-rw-r--r--src/ToolBox/SOS/Strike/eeheap.cpp1913
-rw-r--r--src/ToolBox/SOS/Strike/exts.cpp435
-rw-r--r--src/ToolBox/SOS/Strike/exts.h513
-rw-r--r--src/ToolBox/SOS/Strike/gchist.cpp636
-rw-r--r--src/ToolBox/SOS/Strike/gcroot.cpp2503
-rw-r--r--src/ToolBox/SOS/Strike/inc/.gitmirror1
-rw-r--r--src/ToolBox/SOS/Strike/inc/dbgeng.h16122
-rw-r--r--src/ToolBox/SOS/Strike/inc/dbghelp.h4540
-rw-r--r--src/ToolBox/SOS/Strike/inc/wdbgexts.h2807
-rw-r--r--src/ToolBox/SOS/Strike/metadata.cpp1041
-rw-r--r--src/ToolBox/SOS/Strike/ntinfo.h193
-rw-r--r--src/ToolBox/SOS/Strike/platformspecific.h195
-rw-r--r--src/ToolBox/SOS/Strike/sildasm.cpp1090
-rw-r--r--src/ToolBox/SOS/Strike/sos.cpp888
-rw-r--r--src/ToolBox/SOS/Strike/sos.def231
-rw-r--r--src/ToolBox/SOS/Strike/sos.h792
-rw-r--r--src/ToolBox/SOS/Strike/sos.targets166
-rw-r--r--src/ToolBox/SOS/Strike/sos_md.h926
-rw-r--r--src/ToolBox/SOS/Strike/sos_stacktrace.h174
-rw-r--r--src/ToolBox/SOS/Strike/sos_unixexports.src54
-rw-r--r--src/ToolBox/SOS/Strike/sosdocs.txt2572
-rw-r--r--src/ToolBox/SOS/Strike/sosdocsunix.txt1713
-rw-r--r--src/ToolBox/SOS/Strike/stressLogDump.cpp549
-rw-r--r--src/ToolBox/SOS/Strike/strike.cpp14462
-rw-r--r--src/ToolBox/SOS/Strike/strike.h144
-rw-r--r--src/ToolBox/SOS/Strike/util.cpp6975
-rw-r--r--src/ToolBox/SOS/Strike/util.h3292
-rw-r--r--src/ToolBox/SOS/Strike/vm.cpp732
-rw-r--r--src/ToolBox/SOS/Strike/xplat/.gitmirror1
-rw-r--r--src/ToolBox/SOS/Strike/xplat/dbgeng.h485
-rw-r--r--src/ToolBox/SOS/Strike/xplat/dbghelp.h17
-rw-r--r--src/ToolBox/SOS/Strike/xplat/wdbgexts.h28
-rw-r--r--src/ToolBox/SOS/diasdk/.gitmirror1
-rw-r--r--src/ToolBox/SOS/diasdk/CMakeLists.txt16
-rw-r--r--src/ToolBox/SOS/diasdk/diasdk.nativeproj43
-rw-r--r--src/ToolBox/SOS/diasdk/native.rc7
-rw-r--r--src/ToolBox/SOS/dirs.proj32
-rw-r--r--src/ToolBox/SOS/lldbplugin/.gitmirrorall1
-rw-r--r--src/ToolBox/SOS/lldbplugin/CMakeLists.txt115
-rw-r--r--src/ToolBox/SOS/lldbplugin/coreruncommand.cpp47
-rw-r--r--src/ToolBox/SOS/lldbplugin/inc/lldbservices.h565
-rw-r--r--src/ToolBox/SOS/lldbplugin/mstypes.h106
-rw-r--r--src/ToolBox/SOS/lldbplugin/services.cpp1692
-rw-r--r--src/ToolBox/SOS/lldbplugin/services.h274
-rw-r--r--src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp53
-rw-r--r--src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp64
-rw-r--r--src/ToolBox/SOS/lldbplugin/soscommand.cpp152
-rw-r--r--src/ToolBox/SOS/lldbplugin/sosplugin.cpp21
-rw-r--r--src/ToolBox/SOS/lldbplugin/sosplugin.h28
-rw-r--r--src/ToolBox/SOS/tests/.gitmirrorall1
-rw-r--r--src/ToolBox/SOS/tests/OnCrash.do2
-rw-r--r--src/ToolBox/SOS/tests/README.md33
-rw-r--r--src/ToolBox/SOS/tests/dumpil.py26
-rw-r--r--src/ToolBox/SOS/tests/dumpmodule.py26
-rw-r--r--src/ToolBox/SOS/tests/runprocess.py34
-rw-r--r--src/ToolBox/SOS/tests/test_libsosplugin.py84
-rw-r--r--src/ToolBox/SOS/tests/testutils.py40
-rw-r--r--src/ToolBox/dirs.proj100
-rw-r--r--src/ToolBox/superpmi/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/CMakeLists.txt5
-rw-r--r--src/ToolBox/superpmi/mcs/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/mcs/CMakeLists.txt76
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.cpp581
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.h76
-rw-r--r--src/ToolBox/superpmi/mcs/mcs.cpp108
-rw-r--r--src/ToolBox/superpmi/mcs/mcs.h9
-rw-r--r--src/ToolBox/superpmi/mcs/verbasmdump.cpp68
-rw-r--r--src/ToolBox/superpmi/mcs/verbasmdump.h19
-rw-r--r--src/ToolBox/superpmi/mcs/verbconcat.cpp99
-rw-r--r--src/ToolBox/superpmi/mcs/verbconcat.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdump.cpp36
-rw-r--r--src/ToolBox/superpmi/mcs/verbdump.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumpmap.cpp64
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumpmap.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumptoc.cpp32
-rw-r--r--src/ToolBox/superpmi/mcs/verbdumptoc.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbfracture.cpp74
-rw-r--r--src/ToolBox/superpmi/mcs/verbfracture.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbildump.cpp625
-rw-r--r--src/ToolBox/superpmi/mcs/verbildump.h24
-rw-r--r--src/ToolBox/superpmi/mcs/verbinteg.cpp37
-rw-r--r--src/ToolBox/superpmi/mcs/verbinteg.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbmerge.cpp470
-rw-r--r--src/ToolBox/superpmi/mcs/verbmerge.h29
-rw-r--r--src/ToolBox/superpmi/mcs/verbremovedup.cpp145
-rw-r--r--src/ToolBox/superpmi/mcs/verbremovedup.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.cpp96
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.h22
-rw-r--r--src/ToolBox/superpmi/mcs/verbstat.cpp74
-rw-r--r--src/ToolBox/superpmi/mcs/verbstat.h17
-rw-r--r--src/ToolBox/superpmi/mcs/verbstrip.cpp150
-rw-r--r--src/ToolBox/superpmi/mcs/verbstrip.h18
-rw-r--r--src/ToolBox/superpmi/mcs/verbtoc.cpp108
-rw-r--r--src/ToolBox/superpmi/mcs/verbtoc.h17
-rw-r--r--src/ToolBox/superpmi/readme.txt86
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/asmdumper.cpp91
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/asmdumper.h18
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/callutils.cpp416
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/callutils.h44
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/compileresult.cpp1080
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/compileresult.h276
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/crlwmlist.h46
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp151
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/errorhandling.h88
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h70
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h55
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h1316
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/lightweightmap.h726
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/logging.cpp342
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/logging.h108
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/lwmlist.h149
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/mclist.cpp258
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/mclist.h35
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp6509
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.h1179
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp127
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h104
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp469
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h123
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/registertablexarch.h124
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/runtimedetails.h43
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp56
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/simpletimer.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp109
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/spmiutil.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/standardpch.cpp7
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/standardpch.h106
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/tocfile.cpp79
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/tocfile.h78
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/typeutils.cpp169
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/typeutils.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp62
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h19
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp120
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp2284
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h30
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h43
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp154
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h70
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp64
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/jithost.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp309
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def5
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h17
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt73
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp62
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h19
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp68
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.h26
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp1901
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.h26
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h107
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp157
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h149
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp49
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/jithost.h25
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp140
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h23
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp231
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def5
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h13
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp58
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.h20
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp59
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h24
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp1726
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h24
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp72
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h108
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp158
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h150
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp40
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/jithost.h22
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp217
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def5
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h13
-rw-r--r--src/ToolBox/superpmi/superpmi/.gitmirror1
-rw-r--r--src/ToolBox/superpmi/superpmi/CMakeLists.txt76
-rw-r--r--src/ToolBox/superpmi/superpmi/commandline.cpp543
-rw-r--r--src/ToolBox/superpmi/superpmi/commandline.h74
-rw-r--r--src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp69
-rw-r--r--src/ToolBox/superpmi/superpmi/coreclrcallbacks.h18
-rw-r--r--src/ToolBox/superpmi/superpmi/cycletimer.cpp60
-rw-r--r--src/ToolBox/superpmi/superpmi/cycletimer.h28
-rw-r--r--src/ToolBox/superpmi/superpmi/filecache.cpp150
-rw-r--r--src/ToolBox/superpmi/superpmi/filecache.h78
-rw-r--r--src/ToolBox/superpmi/superpmi/icorjitinfo.cpp2044
-rw-r--r--src/ToolBox/superpmi/superpmi/icorjitinfo.h26
-rw-r--r--src/ToolBox/superpmi/superpmi/ieememorymanager.cpp110
-rw-r--r--src/ToolBox/superpmi/superpmi/ieememorymanager.h113
-rw-r--r--src/ToolBox/superpmi/superpmi/iexecutionengine.cpp213
-rw-r--r--src/ToolBox/superpmi/superpmi/iexecutionengine.h151
-rw-r--r--src/ToolBox/superpmi/superpmi/jitdebugger.cpp459
-rw-r--r--src/ToolBox/superpmi/superpmi/jitdebugger.h33
-rw-r--r--src/ToolBox/superpmi/superpmi/jithost.cpp120
-rw-r--r--src/ToolBox/superpmi/superpmi/jithost.h20
-rw-r--r--src/ToolBox/superpmi/superpmi/jitinstance.cpp436
-rw-r--r--src/ToolBox/superpmi/superpmi/jitinstance.h57
-rw-r--r--src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp126
-rw-r--r--src/ToolBox/superpmi/superpmi/methodstatsemitter.h29
-rw-r--r--src/ToolBox/superpmi/superpmi/neardiffer.cpp1031
-rw-r--r--src/ToolBox/superpmi/superpmi/neardiffer.h86
-rw-r--r--src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp587
-rw-r--r--src/ToolBox/superpmi/superpmi/superpmi.cpp564
-rw-r--r--src/ToolBox/superpmi/superpmi/superpmi.h27
272 files changed, 138140 insertions, 0 deletions
diff --git a/src/ToolBox/.gitmirror b/src/ToolBox/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/CMakeLists.txt b/src/ToolBox/CMakeLists.txt
new file mode 100644
index 0000000000..4e7f436868
--- /dev/null
+++ b/src/ToolBox/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(SOS)
+add_subdirectory(superpmi)
diff --git a/src/ToolBox/PdbTypeMatch/.gitmirror b/src/ToolBox/PdbTypeMatch/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/PdbTypeMatch/PdbTypeMatch.cpp b/src/ToolBox/PdbTypeMatch/PdbTypeMatch.cpp
new file mode 100644
index 0000000000..052e9e0d1d
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/PdbTypeMatch.cpp
@@ -0,0 +1,2023 @@
+// 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 "stdafx.h"
+#include "PdbTypeMatch.h"
+#include "PrintSymbol.h"
+
+#include "Callback.h"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <sys/stat.h>
+#include <algorithm>
+#include "Shlwapi.h"
+
+#define DEBUG_VERBOSE 0
+
+#pragma warning (disable : 4100)
+
+const wchar_t *g_szFilename1, *g_szFilename2;
+IDiaDataSource *g_pDiaDataSource1, *g_pDiaDataSource2;
+IDiaSession *g_pDiaSession1, *g_pDiaSession2;
+IDiaSymbol *g_pGlobalSymbol1, *g_pGlobalSymbol2;
+DWORD g_dwMachineType = CV_CFL_80386;
+IDiaSymbolSet g_excludedTypes;
+IDiaSymbolSet g_excludedTypePatterns;
+
+////////////////////////////////////////////////////////////
+//
+int __cdecl wmain(int argc, wchar_t *argv[])
+{
+ FILE *pFile;
+
+ if (argc < 3) {
+ PrintHelpOptions();
+ return -1;
+ }
+
+ if (!_wcsicmp(argv[1], L"-type")) {
+
+ // -type <symbolname> <pdbfilename>: dump this type in detail
+
+ if (argc < 4) {
+ PrintHelpOptions();
+ return -1;
+ }
+
+ if ((argc > 1) && (*argv[2] != L'-')) {
+
+ if (_wfopen_s(&pFile, argv[3], L"r") || !pFile) {
+ // invalid file name or file does not exist
+
+ PrintHelpOptions();
+ return -1;
+ }
+ fclose(pFile);
+ // CoCreate() and initialize COM objects
+ if (!InitDiaSource(&g_pDiaDataSource1))
+ {
+ return -1;
+ }
+ if (!LoadDataFromPdb(argv[3], g_pDiaDataSource1, &g_pDiaSession1, &g_pGlobalSymbol1)) {
+ return -1;
+ }
+
+ DumpType(g_pGlobalSymbol1, argv[2]);
+
+ // release COM objects and CoUninitialize()
+ Cleanup();
+
+ return 0;
+ }
+ }
+
+ if (argc < 3) {
+ PrintHelpOptions();
+ return -1;
+ }
+
+ if (_wfopen_s(&pFile, argv[1], L"r") || !pFile) {
+ // invalid file name or file does not exist
+
+ PrintHelpOptions();
+ return -1;
+ }
+
+ fclose(pFile);
+
+ if (_wfopen_s(&pFile, argv[2], L"r") || !pFile) {
+ // invalid file name or file does not exist
+
+ PrintHelpOptions();
+ return -1;
+ }
+
+ fclose(pFile);
+
+ g_szFilename1 = argv[1];
+
+ // CoCreate() and initialize COM objects
+ if (!InitDiaSource(&g_pDiaDataSource1))
+ {
+ return -1;
+ }
+ if (!LoadDataFromPdb(g_szFilename1, g_pDiaDataSource1, &g_pDiaSession1, &g_pGlobalSymbol1)) {
+ return -1;
+ }
+
+ g_szFilename2 = argv[2];
+
+ InitDiaSource(&g_pDiaDataSource2);
+ if (!LoadDataFromPdb(g_szFilename2, g_pDiaDataSource2, &g_pDiaSession2, &g_pGlobalSymbol2)) {
+ return -1;
+ }
+
+ // Read exclusion list.
+ struct stat fileStatus;
+ if (stat(UnicodeToAnsi(argv[3]), &fileStatus) != 0)
+ {
+ wprintf(L"Could not open type_exclusion_list file!\n");
+ return -1;
+ }
+
+ char linec[2048];
+ FILE *file = fopen(UnicodeToAnsi(argv[3]), "r");
+ while (fgets (linec, sizeof(linec), file) != NULL)
+ {
+ std::string line(linec);
+ line.erase(std::remove_if(line.begin(), line.end(), isspace), line.end());
+ if (line.empty() || line.length() <= 1)
+ {
+ continue;
+ }
+ if (line.front() == '#') continue;
+ int len;
+ int slength = (int)line.length() + 1;
+ len = MultiByteToWideChar(CP_ACP, 0, line.c_str(), slength, 0, 0);
+ wchar_t* buf = new wchar_t[len];
+ MultiByteToWideChar(CP_ACP, 0, line.c_str(), slength, buf, len);
+ std::wstring wLine(buf);
+ delete[] buf;
+
+ /// Add *str in the patterns list.
+ if (line.front() == '*')
+ {
+ g_excludedTypePatterns.insert((std::wstring)(wLine.substr(1, wLine.size()-1)));
+ }
+ else
+ {
+ g_excludedTypes.insert((std::wstring)(wLine));
+ }
+ }
+ fclose(file);
+
+ IDiaSymbolSet types1;
+ IDiaSymbolSet types2;
+ if (!EnumTypesInPdb(&types1, g_pDiaSession1, g_pGlobalSymbol1))
+ {
+ return -1;
+ }
+
+ if (!EnumTypesInPdb(&types2, g_pDiaSession2, g_pGlobalSymbol2))
+ {
+ return -1;
+ }
+
+ IDiaSymbolSet commonTypes;
+
+ // Intersect types
+ for (IDiaSymbolSet::iterator i = types1.begin(); i != types1.end(); i++)
+ {
+ std::wstring typeName = *i;
+
+ /// Skip excluded types
+ if (g_excludedTypes.find(typeName) != g_excludedTypes.end())
+ {
+ continue;
+ }
+ bool skipType = false;
+ /// Skip if includes one pattern string.
+ for (IDiaSymbolSet::iterator j = g_excludedTypePatterns.begin(); j != g_excludedTypePatterns.end(); j++)
+ {
+ std::wstring patternStr = *j;
+ if (wcsstr(typeName.c_str(), patternStr.c_str()) != NULL)
+ {
+ skipType = true;
+ break;
+ }
+ }
+
+ if (skipType) continue;
+
+ if (types2.find(typeName) != types2.end())
+ {
+ commonTypes.insert(typeName);
+ }
+ }
+
+ bool matchedSymbols = true;
+ ULONG failuresNb = 0;
+
+
+ // Compare layout for common types
+ for (IDiaSymbolSet::iterator i = commonTypes.begin(); i != commonTypes.end(); i++)
+ {
+ std::wstring typeName = *i;
+
+ IDiaEnumSymbols *pEnumSymbols1;
+ BSTR pwstrTypeName = SysAllocString(typeName.c_str());
+ if (FAILED(g_pGlobalSymbol1->findChildren(SymTagUDT, pwstrTypeName, nsNone, &pEnumSymbols1))) {
+ SysFreeString(pwstrTypeName);
+ return false;
+ }
+
+ IDiaEnumSymbols *pEnumSymbols2;
+ if (FAILED(g_pGlobalSymbol2->findChildren(SymTagUDT, pwstrTypeName, nsNone, &pEnumSymbols2))) {
+ SysFreeString(pwstrTypeName);
+ return false;
+ }
+
+ IDiaSymbol *pSymbol1;
+ IDiaSymbol *pSymbol2;
+ ULONG celt = 0;
+
+
+ while (SUCCEEDED(pEnumSymbols1->Next(1, &pSymbol1, &celt)) && (celt == 1)) {
+ if (SUCCEEDED(pEnumSymbols2->Next(1, &pSymbol2, &celt)) && (celt == 1))
+ {
+
+ BSTR bstrSymbol1Name;
+ if (pSymbol1->get_name(&bstrSymbol1Name) != S_OK)
+ {
+ bstrSymbol1Name = NULL;
+ pSymbol2->Release();
+ continue;
+ }
+ BSTR bstrSymbol2Name;
+ if (pSymbol2->get_name(&bstrSymbol2Name) != S_OK)
+ {
+ bstrSymbol2Name = NULL;
+ pSymbol2->Release();
+ continue;
+ }
+ if (_wcsicmp(bstrSymbol1Name, bstrSymbol2Name) != 0)
+ {
+ pSymbol2->Release();
+ continue;
+ }
+ ULONGLONG sym1Size;
+ ULONGLONG sym2Size;
+ if (pSymbol1->get_length(&sym1Size) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's length\n");
+ pSymbol2->Release();
+ continue;
+ }
+ //wprintf(L"sym1Size = %x\n", sym1Size);
+ if (pSymbol2->get_length(&sym2Size) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's length\n");
+ pSymbol2->Release();
+ continue;
+ }
+ //wprintf(L"sym2Size = %x\n", sym2Size);
+ if (sym1Size == 0 || sym2Size == 2)
+ {
+ pSymbol2->Release();
+ continue;
+ }
+
+ if (!LayoutMatches(pSymbol1, pSymbol2))
+ {
+ wprintf(L"Type \"%s\" is not matching in %s and %s\n", pwstrTypeName, g_szFilename1, g_szFilename2);
+ pSymbol2->Release();
+
+ matchedSymbols = false;
+ failuresNb++;
+ // Continue to compare and report all inconsistencies.
+ continue;
+ }
+ else
+ {
+#if DEBUG_VERBOSE
+ wprintf(L"Matched type: %s\n", pwstrTypeName);
+#endif
+ }
+
+ pSymbol2->Release();
+ }
+
+ pSymbol1->Release();
+ }
+
+ SysFreeString(pwstrTypeName);
+ pEnumSymbols1->Release();
+ pEnumSymbols2->Release();
+ }
+
+ // release COM objects and CoUninitialize()
+ Cleanup();
+
+ if (matchedSymbols)
+ {
+ wprintf(L"OK: All %d common types of %s and %s match!\n", commonTypes.size(), g_szFilename1, g_szFilename2);
+ return 0;
+ }
+ else
+ {
+ wprintf(L"FAIL: Failed to match %d common types of %s and %s!\n", failuresNb, g_szFilename1, g_szFilename2);
+ wprintf(L"Matched %d common types!\n", commonTypes.size() - failuresNb);
+ return -1;
+ }
+}
+
+LPSTR UnicodeToAnsi(LPCWSTR s)
+{
+ if (s==NULL) return NULL;
+ int cw=lstrlenW(s);
+ if (cw==0)
+ {
+ CHAR *psz=new CHAR[1];*psz='\0';return psz;
+ }
+ int cc=WideCharToMultiByte(CP_ACP,0,s,cw,NULL,0,NULL,NULL);
+ if (cc==0) return NULL;
+ CHAR *psz=new CHAR[cc+1];
+ cc=WideCharToMultiByte(CP_ACP,0,s,cw,psz,cc,NULL,NULL);
+ if (cc==0) {delete[] psz;return NULL;}
+ psz[cc]='\0';
+ return psz;
+}
+
+
+bool InitDiaSource(IDiaDataSource **ppSource)
+{
+ HRESULT hr = CoInitialize(NULL);
+
+ // Obtain access to the provider
+
+ hr = CoCreateInstance(__uuidof(DiaSource),
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ __uuidof(IDiaDataSource),
+ (void **) ppSource);
+
+ if (FAILED(hr)) {
+ ACTCTX actCtx;
+ memset((void*)&actCtx, 0, sizeof(ACTCTX));
+ actCtx.cbSize = sizeof(ACTCTX);
+ CHAR dllPath[MAX_PATH*2];
+ GetModuleFileName(NULL, dllPath, _countof(dllPath));
+ PathRemoveFileSpec(dllPath);
+ strcat(dllPath, "\\msdia100.sxs.manifest");
+ actCtx.lpSource = dllPath;
+
+ HANDLE hCtx = ::CreateActCtx(&actCtx);
+ if (hCtx == INVALID_HANDLE_VALUE)
+ wprintf(L"CreateActCtx returned: INVALID_HANDLE_VALUE\n");
+ else
+ {
+ ULONG_PTR cookie;
+ if (::ActivateActCtx(hCtx, &cookie))
+ {
+ hr = CoCreateInstance(__uuidof(DiaSource),
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ __uuidof(IDiaDataSource),
+ (void **) ppSource);
+ ::DeactivateActCtx(0, cookie);
+ if (FAILED(hr)) {
+ wprintf(L"CoCreateInstance failed - HRESULT = %08X\n", hr);
+ return false;
+ }
+ }
+ }
+ }
+ if (FAILED(hr)) {
+ wprintf(L"CoCreateInstance failed - HRESULT = %08X\n", hr);
+
+ return false;
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Create an IDiaData source and open a PDB file
+//
+bool LoadDataFromPdb(
+ const wchar_t *szFilename,
+ IDiaDataSource *ppSource,
+ IDiaSession **ppSession,
+ IDiaSymbol **ppGlobal)
+{
+ wchar_t wszExt[MAX_PATH];
+ wchar_t *wszSearchPath = L"SRV**\\\\symbols\\symbols"; // Alternate path to search for debug data
+ DWORD dwMachType = 0;
+ HRESULT hr;
+
+ _wsplitpath_s(szFilename, NULL, 0, NULL, 0, NULL, 0, wszExt, MAX_PATH);
+
+ if (!_wcsicmp(wszExt, L".pdb")) {
+ // Open and prepare a program database (.pdb) file as a debug data source
+
+ hr = (ppSource)->loadDataFromPdb(szFilename);
+
+ if (FAILED(hr)) {
+ wprintf(L"loadDataFromPdb failed - HRESULT = %08X\n", hr);
+
+ return false;
+ }
+ }
+
+ else {
+ CCallback callback; // Receives callbacks from the DIA symbol locating procedure,
+ // thus enabling a user interface to report on the progress of
+ // the location attempt. The client application may optionally
+ // provide a reference to its own implementation of this
+ // virtual base class to the IDiaDataSource::loadDataForExe method.
+ callback.AddRef();
+
+ // Open and prepare the debug data associated with the executable
+
+ hr = (ppSource)->loadDataForExe(szFilename, wszSearchPath, &callback);
+
+ if (FAILED(hr)) {
+ wprintf(L"loadDataForExe failed - HRESULT = %08X\n", hr);
+
+ return false;
+ }
+ }
+
+ // Open a session for querying symbols
+
+ hr = (ppSource)->openSession(ppSession);
+
+ if (FAILED(hr)) {
+ wprintf(L"openSession failed - HRESULT = %08X\n", hr);
+
+ return false;
+ }
+
+ // Retrieve a reference to the global scope
+
+ hr = (*ppSession)->get_globalScope(ppGlobal);
+
+ if (hr != S_OK) {
+ wprintf(L"get_globalScope failed\n");
+
+ return false;
+ }
+
+ // Set Machine type for getting correct register names
+
+ if ((*ppGlobal)->get_machineType(&dwMachType) == S_OK) {
+ switch (dwMachType) {
+ case IMAGE_FILE_MACHINE_I386 : g_dwMachineType = CV_CFL_80386; break;
+ case IMAGE_FILE_MACHINE_IA64 : g_dwMachineType = CV_CFL_IA64; break;
+ case IMAGE_FILE_MACHINE_AMD64 : g_dwMachineType = CV_CFL_AMD64; break;
+ case IMAGE_FILE_MACHINE_ARMNT : g_dwMachineType = CV_CFL_ARM7; break;
+ }
+ }
+
+ return true;
+}
+
+bool LayoutMatches(IDiaSymbol* pSymbol1, IDiaSymbol* pSymbol2)
+{
+ DWORD dwTag1, dwTag2;
+ DWORD dwLocType1, dwLocType2;
+ LONG lOffset1, lOffset2;
+
+ if (pSymbol1->get_symTag(&dwTag1) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's SymTag\n");
+ return false;
+ }
+ if (pSymbol2->get_symTag(&dwTag2) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's SymTag\n");
+ return false;
+ }
+
+ if (dwTag1 == SymTagUDT)
+ {
+ if (dwTag2 != SymTagUDT)
+ {
+
+ wprintf(L"ERROR - symbols don't match\n");
+ wprintf(L"Symbol 1:\n");
+ PrintTypeInDetail(pSymbol1, 0);
+ wprintf(L"Symbol 2:\n");
+ PrintTypeInDetail(pSymbol2, 0);
+ return false;
+ }
+
+ // First check that types size match
+ ULONGLONG sym1Size;
+ ULONGLONG sym2Size;
+ if (pSymbol1->get_length(&sym1Size) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's length\n");
+ return false;
+ }
+ if (pSymbol2->get_length(&sym2Size) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's length\n");
+ return false;
+ }
+ if (sym1Size == 0 || sym2Size == 0)
+ {
+ return true;
+ }
+ if (sym1Size != sym2Size)
+ {
+ wprintf(L"Failed to match type size: (sizeof(sym1)=%x) != (sizeof(sym2)=%x)\n", sym1Size, sym2Size);
+ return false;
+ }
+ IDiaEnumSymbols *pEnumChildren1, *pEnumChildren2;
+ IDiaSymbol *pChild1, *pChild2;
+ ULONG celt = 0;
+ BSTR bstrName1, bstrName2;
+ if (SUCCEEDED(pSymbol1->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren1)))
+ {
+ while (SUCCEEDED(pEnumChildren1->Next(1, &pChild1, &celt)) && (celt == 1))
+ {
+ if (pChild1->get_symTag(&dwTag1) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's SymTag\n");
+ pChild1->Release();
+ return false;
+ }
+ if (dwTag1 != SymTagData) { pChild1->Release(); continue; }
+ if (pChild1->get_locationType(&dwLocType1) != S_OK)
+ {
+ wprintf(L"symbol in optmized code");
+ pChild1->Release();
+ return false;
+ }
+ if (dwLocType1 != LocIsThisRel) { pChild1->Release(); continue; }
+ if (pChild1->get_offset(&lOffset1) != S_OK)
+ {
+ wprintf(L"ERROR - geting field offset\n");
+ pChild1->Release();
+ return false;
+ }
+ if (pChild1->get_name(&bstrName1) != S_OK)
+ {
+ bstrName1 = NULL;
+ }
+ /// Search in the second symbol the field at lOffset1
+ bool fieldMatched = false;
+ if (SUCCEEDED(pSymbol2->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren2)))
+ {
+ while (SUCCEEDED(pEnumChildren2->Next(1, &pChild2, &celt)) && (celt == 1))
+ {
+ if (pChild2->get_symTag(&dwTag2) != S_OK)
+ {
+ wprintf(L"ERROR - can't retrieve the symbol's SymTag\n");
+ pChild2->Release();
+ return false;
+ }
+ if (dwTag2 != SymTagData) { pChild2->Release(); continue; }
+ if (pChild2->get_locationType(&dwLocType2) != S_OK)
+ {
+ wprintf(L"symbol in optmized code");
+ pChild2->Release();
+ return false;
+ }
+ if (dwLocType2 != LocIsThisRel) { pChild2->Release(); continue; }
+ if (pChild2->get_offset(&lOffset2) != S_OK)
+ {
+ wprintf(L"ERROR - geting field offset\n");
+ pChild2->Release();
+ return false;
+ }
+ if (pChild2->get_name(&bstrName2) != S_OK)
+ {
+ bstrName2 = NULL;
+ }
+ if (lOffset2 == lOffset1)
+ {
+ if (_wcsicmp(bstrName1, bstrName2) == 0
+ || wcsstr(bstrName1, bstrName2) == bstrName1
+ || wcsstr(bstrName2, bstrName1) == bstrName2)
+ {
+ //wprintf(L"Matched field %s at offset %x\n", bstrName1, lOffset2);
+ fieldMatched = true;
+ pChild2->Release();
+ break;
+ }
+ }
+ pChild2->Release();
+ }
+ pEnumChildren2->Release();
+ }
+ if (!fieldMatched)
+ {
+ BSTR bstrSymbol1Name;
+ if (pSymbol1->get_name(&bstrSymbol1Name) != S_OK)
+ {
+ bstrSymbol1Name = NULL;
+ }
+ wprintf(L"Failed to match %s field %s at offset %x\n", bstrSymbol1Name, bstrName1, lOffset1);
+ pChild1->Release();
+ return false;
+ }
+ pChild1->Release();
+ }
+
+ pEnumChildren1->Release();
+ }
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Release DIA objects and CoUninitialize
+//
+void Cleanup()
+{
+ if (g_pGlobalSymbol1) {
+ g_pGlobalSymbol1->Release();
+ g_pGlobalSymbol1 = NULL;
+ }
+
+ if (g_pGlobalSymbol2) {
+ g_pGlobalSymbol2->Release();
+ g_pGlobalSymbol2 = NULL;
+ }
+
+ if (g_pDiaSession1) {
+ g_pDiaSession1->Release();
+ g_pDiaSession1 = NULL;
+ }
+
+ if (g_pDiaSession2) {
+ g_pDiaSession2->Release();
+ g_pDiaSession2 = NULL;
+ }
+
+ CoUninitialize();
+}
+
+
+////////////////////////////////////////////////////////////
+// Display the usage
+//
+void PrintHelpOptions()
+{
+ static const wchar_t * const helpString = L"usage: PdbTypeMatch.exe <pdb_filename_1> <pdb_filename_2> <type_exclusion_list_file> : compare all common types by size and fields\n"
+ L" PdbTypeMatch.exe -type <symbolname> <pdb_filename_1>: dump this type in detail\n";
+
+ wprintf(helpString);
+}
+
+bool EnumTypesInPdb(IDiaSymbolSet* types, IDiaSession *pSession, IDiaSymbol *pGlobal)
+{
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagUDT, NULL, nsNone, &pEnumSymbols)))
+ {
+ wprintf(L"ERROR - EnumTypesInPdb() returned no symbols\n");
+
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1))
+ {
+ std::wstring typeName;
+ GetSymbolName(typeName, pSymbol);
+ types->insert(std::wstring(typeName));
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the data stored in a PDB
+//
+void DumpAllPdbInfo(IDiaSession *pSession, IDiaSymbol *pGlobal)
+{
+ DumpAllMods(pGlobal);
+ DumpAllPublics(pGlobal);
+ DumpAllSymbols(pGlobal);
+ DumpAllGlobals(pGlobal);
+ DumpAllTypes(pGlobal);
+ DumpAllFiles(pSession, pGlobal);
+ DumpAllLines(pSession, pGlobal);
+ DumpAllSecContribs(pSession);
+ DumpAllDebugStreams(pSession);
+ DumpAllInjectedSources(pSession);
+ DumpAllFPO(pSession);
+ DumpAllOEMs(pGlobal);
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the modules information
+//
+bool DumpAllMods(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** MODULES\n\n");
+
+ // Retrieve all the compiland symbols
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagCompiland, NULL, nsNone, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pCompiland;
+ ULONG celt = 0;
+ ULONG iMod = 1;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) {
+ BSTR bstrName;
+
+ if (pCompiland->get_name(&bstrName) != S_OK) {
+ wprintf(L"ERROR - Failed to get the compiland's name\n");
+
+ pCompiland->Release();
+ pEnumSymbols->Release();
+
+ return false;
+ }
+
+ wprintf(L"%04X %s\n", iMod++, bstrName);
+
+ // Deallocate the string allocated previously by the call to get_name
+
+ SysFreeString(bstrName);
+
+ pCompiland->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the public symbols - SymTagPublicSymbol
+//
+bool DumpAllPublics(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** PUBLICS\n\n");
+
+ // Retrieve all the public symbols
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagPublicSymbol, NULL, nsNone, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintPublicSymbol(pSymbol);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the symbol information stored in the compilands
+//
+bool DumpAllSymbols(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** SYMBOLS\n\n\n");
+
+ // Retrieve the compilands first
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagCompiland, NULL, nsNone, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pCompiland;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) {
+ wprintf(L"\n** Module: ");
+
+ // Retrieve the name of the module
+
+ BSTR bstrName;
+
+ if (pCompiland->get_name(&bstrName) != S_OK) {
+ wprintf(L"(???)\n\n");
+ }
+
+ else {
+ wprintf(L"%s\n\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ // Find all the symbols defined in this compiland and print their info
+
+ IDiaEnumSymbols *pEnumChildren;
+
+ if (SUCCEEDED(pCompiland->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren))) {
+ IDiaSymbol *pSymbol;
+ ULONG celtChildren = 0;
+
+ while (SUCCEEDED(pEnumChildren->Next(1, &pSymbol, &celtChildren)) && (celtChildren == 1)) {
+ PrintSymbol(pSymbol, 0);
+ pSymbol->Release();
+ }
+
+ pEnumChildren->Release();
+ }
+
+ pCompiland->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the global symbols - SymTagFunction,
+// SymTagThunk and SymTagData
+//
+bool DumpAllGlobals(IDiaSymbol *pGlobal)
+{
+ IDiaEnumSymbols *pEnumSymbols;
+ IDiaSymbol *pSymbol;
+ enum SymTagEnum dwSymTags[] = { SymTagFunction, SymTagThunk, SymTagData };
+ ULONG celt = 0;
+
+ wprintf(L"\n\n*** GLOBALS\n\n");
+
+ for (size_t i = 0; i < _countof(dwSymTags); i++, pEnumSymbols = NULL) {
+ if (SUCCEEDED(pGlobal->findChildren(dwSymTags[i], NULL, nsNone, &pEnumSymbols))) {
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintGlobalSymbol(pSymbol);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+ }
+
+ else {
+ wprintf(L"ERROR - DumpAllGlobals() returned no symbols\n");
+
+ return false;
+ }
+ }
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the types information
+// (type symbols can be UDTs, enums or typedefs)
+//
+bool DumpAllTypes(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** TYPES\n");
+
+ return DumpAllUDTs(pGlobal) || DumpAllEnums(pGlobal) || DumpAllTypedefs(pGlobal);
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the user defined types (UDT)
+//
+bool DumpAllUDTs(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n** User Defined Types\n\n");
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagUDT, NULL, nsNone, &pEnumSymbols))) {
+ wprintf(L"ERROR - DumpAllUDTs() returned no symbols\n");
+
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintTypeInDetail(pSymbol, 0);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the enum types from the pdb
+//
+bool DumpAllEnums(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n** ENUMS\n\n");
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagEnum, NULL, nsNone, &pEnumSymbols))) {
+ wprintf(L"ERROR - DumpAllEnums() returned no symbols\n");
+
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintTypeInDetail(pSymbol, 0);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the typedef types from the pdb
+//
+bool DumpAllTypedefs(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n** TYPEDEFS\n\n");
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagTypedef, NULL, nsNone, &pEnumSymbols))) {
+ wprintf(L"ERROR - DumpAllTypedefs() returned no symbols\n");
+
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintTypeInDetail(pSymbol, 0);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump OEM specific types
+//
+bool DumpAllOEMs(IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** OEM Specific types\n\n");
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagCustomType, NULL, nsNone, &pEnumSymbols))) {
+ wprintf(L"ERROR - DumpAllOEMs() returned no symbols\n");
+
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintTypeInDetail(pSymbol, 0);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// For each compiland in the PDB dump all the source files
+//
+bool DumpAllFiles(IDiaSession *pSession, IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** FILES\n\n");
+
+ // In order to find the source files, we have to look at the image's compilands/modules
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagCompiland, NULL, nsNone, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pCompiland;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) {
+ BSTR bstrName;
+
+ if (pCompiland->get_name(&bstrName) == S_OK) {
+ wprintf(L"\nCompiland = %s\n\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ // Every compiland could contain multiple references to the source files which were used to build it
+ // Retrieve all source files by compiland by passing NULL for the name of the source file
+
+ IDiaEnumSourceFiles *pEnumSourceFiles;
+
+ if (SUCCEEDED(pSession->findFile(pCompiland, NULL, nsNone, &pEnumSourceFiles))) {
+ IDiaSourceFile *pSourceFile;
+
+ while (SUCCEEDED(pEnumSourceFiles->Next(1, &pSourceFile, &celt)) && (celt == 1)) {
+ PrintSourceFile(pSourceFile);
+ putwchar(L'\n');
+
+ pSourceFile->Release();
+ }
+
+ pEnumSourceFiles->Release();
+ }
+
+ putwchar(L'\n');
+
+ pCompiland->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the line numbering information contained in the PDB
+// Only function symbols have corresponding line numbering information
+bool DumpAllLines(IDiaSession *pSession, IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** LINES\n\n");
+
+ // First retrieve the compilands/modules
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagCompiland, NULL, nsNone, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pCompiland;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) {
+ IDiaEnumSymbols *pEnumFunction;
+
+ // For every function symbol defined in the compiland, retrieve and print the line numbering info
+
+ if (SUCCEEDED(pCompiland->findChildren(SymTagFunction, NULL, nsNone, &pEnumFunction))) {
+ IDiaSymbol *pFunction;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumFunction->Next(1, &pFunction, &celt)) && (celt == 1)) {
+ PrintLines(pSession, pFunction);
+
+ pFunction->Release();
+ }
+
+ pEnumFunction->Release();
+ }
+
+ pCompiland->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the line numbering information for a given RVA
+// and a given range
+//
+bool DumpAllLines(IDiaSession *pSession, DWORD dwRVA, DWORD dwRange)
+{
+ // Retrieve and print the lines that corresponds to a specified RVA
+
+ IDiaEnumLineNumbers *pLines;
+
+ if (FAILED(pSession->findLinesByRVA(dwRVA, dwRange, &pLines))) {
+ return false;
+ }
+
+ PrintLines(pLines);
+
+ pLines->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the section contributions from the PDB
+//
+// Section contributions are stored in a table which will
+// be retrieved via IDiaSession->getEnumTables through
+// QueryInterface()using the REFIID of the IDiaEnumSectionContribs
+//
+bool DumpAllSecContribs(IDiaSession *pSession)
+{
+ wprintf(L"\n\n*** SECTION CONTRIBUTION\n\n");
+
+ IDiaEnumSectionContribs *pEnumSecContribs;
+
+ if (FAILED(GetTable(pSession, __uuidof(IDiaEnumSectionContribs), (void **) &pEnumSecContribs))) {
+ return false;
+ }
+
+ wprintf(L" RVA Address Size Module\n");
+
+ IDiaSectionContrib *pSecContrib;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSecContribs->Next(1, &pSecContrib, &celt)) && (celt == 1)) {
+ PrintSecContribs(pSecContrib);
+
+ pSecContrib->Release();
+ }
+
+ pEnumSecContribs->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all debug data streams contained in the PDB
+//
+bool DumpAllDebugStreams(IDiaSession *pSession)
+{
+ IDiaEnumDebugStreams *pEnumStreams;
+
+ wprintf(L"\n\n*** DEBUG STREAMS\n\n");
+
+ // Retrieve an enumerated sequence of debug data streams
+
+ if (FAILED(pSession->getEnumDebugStreams(&pEnumStreams))) {
+ return false;
+ }
+
+ IDiaEnumDebugStreamData *pStream;
+ ULONG celt = 0;
+
+ for (; SUCCEEDED(pEnumStreams->Next(1, &pStream, &celt)) && (celt == 1); pStream = NULL) {
+ PrintStreamData(pStream);
+
+ pStream->Release();
+ }
+
+ pEnumStreams->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the injected source from the PDB
+//
+// Injected sources data is stored in a table which will
+// be retrieved via IDiaSession->getEnumTables through
+// QueryInterface()using the REFIID of the IDiaEnumSectionContribs
+//
+bool DumpAllInjectedSources(IDiaSession *pSession)
+{
+ wprintf(L"\n\n*** INJECTED SOURCES TABLE\n\n");
+
+ IDiaEnumInjectedSources *pEnumInjSources;
+
+ if (SUCCEEDED(GetTable(pSession, __uuidof(IDiaEnumInjectedSources), (void **) &pEnumInjSources))) {
+ return false;
+ }
+
+ IDiaInjectedSource *pInjSource;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumInjSources->Next(1, &pInjSource, &celt)) && (celt == 1)) {
+ PrintGeneric(pInjSource);
+
+ pInjSource->Release();
+ }
+
+ pEnumInjSources->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump info corresponing to a given injected source filename
+//
+bool DumpInjectedSource(IDiaSession *pSession, const wchar_t *szName)
+{
+ // Retrieve a source that has been placed into the symbol store by attribute providers or
+ // other components of the compilation process
+
+ IDiaEnumInjectedSources *pEnumInjSources;
+
+ if (FAILED(pSession->findInjectedSource(szName, &pEnumInjSources))) {
+ wprintf(L"ERROR - DumpInjectedSources() could not find %s\n", szName);
+
+ return false;
+ }
+
+ IDiaInjectedSource *pInjSource;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumInjSources->Next(1, &pInjSource, &celt)) && (celt == 1)) {
+ PrintGeneric(pInjSource);
+
+ pInjSource->Release();
+ }
+
+ pEnumInjSources->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the source file information stored in the PDB
+// We have to go through every compiland in order to retrieve
+// all the information otherwise checksums for instance
+// will not be available
+// Compilands can have multiple source files with the same
+// name but different content which produces diffrent
+// checksums
+//
+bool DumpAllSourceFiles(IDiaSession *pSession, IDiaSymbol *pGlobal)
+{
+ wprintf(L"\n\n*** SOURCE FILES\n\n");
+
+ // To get the complete source file info we must go through the compiland first
+ // by passing NULL instead all the source file names only will be retrieved
+
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagCompiland, NULL, nsNone, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pCompiland;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) {
+ BSTR bstrName;
+
+ if (pCompiland->get_name(&bstrName) == S_OK) {
+ wprintf(L"\nCompiland = %s\n\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ // Every compiland could contain multiple references to the source files which were used to build it
+ // Retrieve all source files by compiland by passing NULL for the name of the source file
+
+ IDiaEnumSourceFiles *pEnumSourceFiles;
+
+ if (SUCCEEDED(pSession->findFile(pCompiland, NULL, nsNone, &pEnumSourceFiles))) {
+ IDiaSourceFile *pSourceFile;
+
+ while (SUCCEEDED(pEnumSourceFiles->Next(1, &pSourceFile, &celt)) && (celt == 1)) {
+ PrintSourceFile(pSourceFile);
+ putwchar(L'\n');
+
+ pSourceFile->Release();
+ }
+
+ pEnumSourceFiles->Release();
+ }
+
+ putwchar(L'\n');
+
+ pCompiland->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the FPO info
+//
+// FPO data stored in a table which will be retrieved via
+// IDiaSession->getEnumTables through QueryInterface()
+// using the REFIID of the IDiaEnumFrameData
+//
+bool DumpAllFPO(IDiaSession *pSession)
+{
+ IDiaEnumFrameData *pEnumFrameData;
+
+ wprintf(L"\n\n*** FPO\n\n");
+
+ if (FAILED(GetTable(pSession, __uuidof(IDiaEnumFrameData), (void **) &pEnumFrameData))) {
+ return false;
+ }
+
+ IDiaFrameData *pFrameData;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumFrameData->Next(1, &pFrameData, &celt)) && (celt == 1)) {
+ PrintFrameData(pFrameData);
+
+ pFrameData->Release();
+ }
+
+ pEnumFrameData->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump FPO info for a function at the specified RVA
+//
+bool DumpFPO(IDiaSession *pSession, DWORD dwRVA)
+{
+ IDiaEnumFrameData *pEnumFrameData;
+
+ // Retrieve first the table holding all the FPO info
+
+ if ((dwRVA != 0) && SUCCEEDED(GetTable(pSession, __uuidof(IDiaEnumFrameData), (void **) &pEnumFrameData))) {
+ IDiaFrameData *pFrameData;
+
+ // Retrieve the frame data corresponding to the given RVA
+
+ if (SUCCEEDED(pEnumFrameData->frameByRVA(dwRVA, &pFrameData))) {
+ PrintGeneric(pFrameData);
+
+ pFrameData->Release();
+ }
+
+ else {
+ // Some function might not have FPO data available (see ASM funcs like strcpy)
+
+ wprintf(L"ERROR - DumpFPO() frameByRVA invalid RVA: 0x%08X\n", dwRVA);
+
+ pEnumFrameData->Release();
+
+ return false;
+ }
+
+ pEnumFrameData->Release();
+ }
+
+ else {
+ wprintf(L"ERROR - DumpFPO() GetTable\n");
+
+ return false;
+ }
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump FPO info for a specified function symbol using its
+// name (a regular expression string is used for the search)
+//
+bool DumpFPO(IDiaSession *pSession, IDiaSymbol *pGlobal, const wchar_t *szSymbolName)
+{
+ IDiaEnumSymbols *pEnumSymbols;
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+ DWORD dwRVA;
+
+ // Find first all the function symbols that their names matches the search criteria
+
+ if (FAILED(pGlobal->findChildren(SymTagFunction, szSymbolName, nsRegularExpression, &pEnumSymbols))) {
+ wprintf(L"ERROR - DumpFPO() findChildren could not find symol %s\n", szSymbolName);
+
+ return false;
+ }
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ if (pSymbol->get_relativeVirtualAddress(&dwRVA) == S_OK) {
+ PrintPublicSymbol(pSymbol);
+
+ DumpFPO(pSession, dwRVA);
+ }
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ putwchar(L'\n');
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump a specified compiland and all the symbols defined in it
+//
+bool DumpCompiland(IDiaSymbol *pGlobal, const wchar_t *szCompName)
+{
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagCompiland, szCompName, nsCaseInsensitive, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pCompiland;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) {
+ wprintf(L"\n** Module: ");
+
+ // Retrieve the name of the module
+
+ BSTR bstrName;
+
+ if (pCompiland->get_name(&bstrName) != S_OK) {
+ wprintf(L"(???)\n\n");
+ }
+
+ else {
+ wprintf(L"%s\n\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ IDiaEnumSymbols *pEnumChildren;
+
+ if (SUCCEEDED(pCompiland->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren))) {
+ IDiaSymbol *pSymbol;
+ ULONG celt_ = 0;
+
+ while (SUCCEEDED(pEnumChildren->Next(1, &pSymbol, &celt_)) && (celt_ == 1)) {
+ PrintSymbol(pSymbol, 0);
+
+ pSymbol->Release();
+ }
+
+ pEnumChildren->Release();
+ }
+
+ pCompiland->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump the line numbering information for a specified RVA
+//
+bool DumpLines(IDiaSession *pSession, DWORD dwRVA)
+{
+ IDiaEnumLineNumbers *pLines;
+
+ if (FAILED(pSession->findLinesByRVA(dwRVA, MAX_RVA_LINES_BYTES_RANGE, &pLines))) {
+ return false;
+ }
+
+ PrintLines(pLines);
+
+ pLines->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump the all line numbering information for a specified
+// function symbol name (as a regular expression string)
+//
+bool DumpLines(IDiaSession *pSession, IDiaSymbol *pGlobal, const wchar_t *szFuncName)
+{
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagFunction, szFuncName, nsRegularExpression, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pFunction;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pFunction, &celt)) && (celt == 1)) {
+ PrintLines(pSession, pFunction);
+
+ pFunction->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump the symbol information corresponding to a specified RVA
+//
+bool DumpSymbolWithRVA(IDiaSession *pSession, DWORD dwRVA, const wchar_t *szChildname)
+{
+ IDiaSymbol *pSymbol;
+ LONG lDisplacement;
+
+ if (FAILED(pSession->findSymbolByRVAEx(dwRVA, SymTagNull, &pSymbol, &lDisplacement))) {
+ return false;
+ }
+
+ wprintf(L"Displacement = 0x%X\n", lDisplacement);
+
+ PrintGeneric(pSymbol);
+
+ bool bReturn = DumpSymbolWithChildren(pSymbol, szChildname);
+
+ while (pSymbol != NULL) {
+ IDiaSymbol *pParent;
+
+ if ((pSymbol->get_lexicalParent(&pParent) == S_OK) && pParent) {
+ wprintf(L"\nParent\n");
+
+ PrintSymbol(pParent, 0);
+
+ pSymbol->Release();
+
+ pSymbol = pParent;
+ }
+
+ else {
+ pSymbol->Release();
+ break;
+ }
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump the symbols information where their names matches a
+// specified regular expression string
+//
+bool DumpSymbolsWithRegEx(IDiaSymbol *pGlobal, const wchar_t *szRegEx, const wchar_t *szChildname)
+{
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagNull, szRegEx, nsRegularExpression, &pEnumSymbols))) {
+ return false;
+ }
+
+ bool bReturn = true;
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintGeneric(pSymbol);
+
+ bReturn = DumpSymbolWithChildren(pSymbol, szChildname);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ return bReturn;
+}
+
+////////////////////////////////////////////////////////////
+// Dump the information corresponding to a symbol name which
+// is a children of the specified parent symbol
+//
+bool DumpSymbolWithChildren(IDiaSymbol *pSymbol, const wchar_t *szChildname)
+{
+ if (szChildname != NULL) {
+ IDiaEnumSymbols *pEnumSyms;
+
+ if (FAILED(pSymbol->findChildren(SymTagNull, szChildname, nsRegularExpression, &pEnumSyms))) {
+ return false;
+ }
+
+ IDiaSymbol *pChild;
+ DWORD celt = 1;
+
+ while (SUCCEEDED(pEnumSyms->Next(celt, &pChild, &celt)) && (celt == 1)) {
+ PrintGeneric(pChild);
+ PrintSymbol(pChild, 0);
+
+ pChild->Release();
+ }
+
+ pEnumSyms->Release();
+ }
+
+ else {
+ // If the specified name is NULL then only the parent symbol data is displayed
+
+ DWORD dwSymTag;
+
+ if ((pSymbol->get_symTag(&dwSymTag) == S_OK) && (dwSymTag == SymTagPublicSymbol)) {
+ PrintPublicSymbol(pSymbol);
+ }
+
+ else {
+ PrintSymbol(pSymbol, 0);
+ }
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump all the type symbols information that matches their
+// names to a specified regular expression string
+//
+bool DumpType(IDiaSymbol *pGlobal, const wchar_t *szRegEx)
+{
+ IDiaEnumSymbols *pEnumSymbols;
+
+ if (FAILED(pGlobal->findChildren(SymTagUDT, szRegEx, nsRegularExpression, &pEnumSymbols))) {
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSymbols->Next(1, &pSymbol, &celt)) && (celt == 1)) {
+ PrintTypeInDetail(pSymbol, 0);
+
+ pSymbol->Release();
+ }
+
+ pEnumSymbols->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump line numbering information for a given file name and
+// an optional line number
+//
+bool DumpLinesForSourceFile(IDiaSession *pSession, const wchar_t *szFileName, DWORD dwLine)
+{
+ IDiaEnumSourceFiles *pEnumSrcFiles;
+
+ if (FAILED(pSession->findFile(NULL, szFileName, nsFNameExt, &pEnumSrcFiles))) {
+ return false;
+ }
+
+ IDiaSourceFile *pSrcFile;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumSrcFiles->Next(1, &pSrcFile, &celt)) && (celt == 1)) {
+ IDiaEnumSymbols *pEnumCompilands;
+
+ if (pSrcFile->get_compilands(&pEnumCompilands) == S_OK) {
+ IDiaSymbol *pCompiland;
+
+ celt = 0;
+ while (SUCCEEDED(pEnumCompilands->Next(1, &pCompiland, &celt)) && (celt == 1)) {
+ BSTR bstrName;
+
+ if (pCompiland->get_name(&bstrName) == S_OK) {
+ wprintf(L"Compiland = %s\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ else {
+ wprintf(L"Compiland = (???)\n");
+ }
+
+ IDiaEnumLineNumbers *pLines;
+
+ if (dwLine != 0) {
+ if (SUCCEEDED(pSession->findLinesByLinenum(pCompiland, pSrcFile, dwLine, 0, &pLines))) {
+ PrintLines(pLines);
+
+ pLines->Release();
+ }
+ }
+
+ else {
+ if (SUCCEEDED(pSession->findLines(pCompiland, pSrcFile, &pLines))) {
+ PrintLines(pLines);
+
+ pLines->Release();
+ }
+ }
+
+ pCompiland->Release();
+ }
+
+ pEnumCompilands->Release();
+ }
+
+ pSrcFile->Release();
+ }
+
+ pEnumSrcFiles->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump public symbol information for a given number of
+// symbols around a given RVA address
+//
+bool DumpPublicSymbolsSorted(IDiaSession *pSession, DWORD dwRVA, DWORD dwRange, bool bReverse)
+{
+ IDiaEnumSymbolsByAddr *pEnumSymsByAddr;
+
+ if (FAILED(pSession->getSymbolsByAddr(&pEnumSymsByAddr))) {
+ return false;
+ }
+
+ IDiaSymbol *pSymbol;
+
+ if (SUCCEEDED(pEnumSymsByAddr->symbolByRVA(dwRVA, &pSymbol))) {
+ if (dwRange == 0) {
+ PrintPublicSymbol(pSymbol);
+ }
+
+ ULONG celt;
+ ULONG i;
+
+ if (bReverse) {
+ pSymbol->Release();
+
+ i = 0;
+
+ for (pSymbol = NULL; (i < dwRange) && SUCCEEDED(pEnumSymsByAddr->Next(1, &pSymbol, &celt)) && (celt == 1); i++) {
+ PrintPublicSymbol(pSymbol);
+
+ pSymbol->Release();
+ }
+ }
+
+ else {
+ PrintPublicSymbol(pSymbol);
+
+ pSymbol->Release();
+
+ i = 1;
+
+ for (pSymbol = NULL; (i < dwRange) && SUCCEEDED(pEnumSymsByAddr->Prev(1, &pSymbol, &celt)) && (celt == 1); i++) {
+ PrintPublicSymbol(pSymbol);
+ }
+ }
+ }
+
+ pEnumSymsByAddr->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump label symbol information at a given RVA
+//
+bool DumpLabel(IDiaSession *pSession, DWORD dwRVA)
+{
+ IDiaSymbol *pSymbol;
+ LONG lDisplacement;
+
+ if (FAILED(pSession->findSymbolByRVAEx(dwRVA, SymTagLabel, &pSymbol, &lDisplacement)) || (pSymbol == NULL)) {
+ return false;
+ }
+
+ wprintf(L"Displacement = 0x%X\n", lDisplacement);
+
+ PrintGeneric(pSymbol);
+
+ pSymbol->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Dump annotation symbol information at a given RVA
+//
+bool DumpAnnotations(IDiaSession *pSession, DWORD dwRVA)
+{
+ IDiaSymbol *pSymbol;
+ LONG lDisplacement;
+
+ if (FAILED(pSession->findSymbolByRVAEx(dwRVA, SymTagAnnotation, &pSymbol, &lDisplacement)) || (pSymbol == NULL)) {
+ return false;
+ }
+
+ wprintf(L"Displacement = 0x%X\n", lDisplacement);
+
+ PrintGeneric(pSymbol);
+
+ pSymbol->Release();
+
+ return true;
+}
+
+struct OMAP_DATA
+{
+ DWORD dwRVA;
+ DWORD dwRVATo;
+};
+
+////////////////////////////////////////////////////////////
+//
+bool DumpMapToSrc(IDiaSession *pSession, DWORD dwRVA)
+{
+ IDiaEnumDebugStreams *pEnumStreams;
+ IDiaEnumDebugStreamData *pStream;
+ ULONG celt;
+
+ if (FAILED(pSession->getEnumDebugStreams(&pEnumStreams))) {
+ return false;
+ }
+
+ celt = 0;
+
+ for (; SUCCEEDED(pEnumStreams->Next(1, &pStream, &celt)) && (celt == 1); pStream = NULL) {
+ BSTR bstrName;
+
+ if (pStream->get_name(&bstrName) != S_OK) {
+ bstrName = NULL;
+ }
+
+ if (bstrName && wcscmp(bstrName, L"OMAPTO") == 0) {
+ OMAP_DATA data, datasav;
+ DWORD cbData, celt;
+ DWORD dwRVATo = 0;
+ unsigned int i;
+
+ datasav.dwRVATo = 0;
+ datasav.dwRVA = 0;
+
+ while (SUCCEEDED(pStream->Next(1, sizeof(data), &cbData, (BYTE*) &data, &celt)) && (celt == 1)) {
+ if (dwRVA > data.dwRVA) {
+ datasav = data;
+ continue;
+ }
+
+ else if (dwRVA == data.dwRVA) {
+ dwRVATo = data.dwRVATo;
+ }
+
+ else if (datasav.dwRVATo) {
+ dwRVATo = datasav.dwRVATo + (dwRVA - datasav.dwRVA);
+ }
+ break;
+ }
+
+ wprintf(L"image rva = %08X ==> source rva = %08X\n\nRelated OMAP entries:\n", dwRVA, dwRVATo);
+ wprintf(L"image rva ==> source rva\n");
+ wprintf(L"%08X ==> %08X\n", datasav.dwRVA, datasav.dwRVATo);
+
+ i = 0;
+
+ do {
+ wprintf(L"%08X ==> %08X\n", data.dwRVA, data.dwRVATo);
+ }
+ while ((++i) < 5 && SUCCEEDED(pStream->Next(1, sizeof(data), &cbData, (BYTE*) &data, &celt)) && (celt == 1));
+ }
+
+ if (bstrName != NULL) {
+ SysFreeString(bstrName);
+ }
+
+ pStream->Release();
+ }
+
+ pEnumStreams->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+//
+bool DumpMapFromSrc(IDiaSession *pSession, DWORD dwRVA)
+{
+ IDiaEnumDebugStreams *pEnumStreams;
+
+ if (FAILED(pSession->getEnumDebugStreams(&pEnumStreams))) {
+ return false;
+ }
+
+ IDiaEnumDebugStreamData *pStream;
+ ULONG celt = 0;
+
+ for (; SUCCEEDED(pEnumStreams->Next(1, &pStream, &celt)) && (celt == 1); pStream = NULL) {
+ BSTR bstrName;
+
+ if (pStream->get_name(&bstrName) != S_OK) {
+ bstrName = NULL;
+ }
+
+ if (bstrName && wcscmp(bstrName, L"OMAPFROM") == 0) {
+ OMAP_DATA data;
+ OMAP_DATA datasav;
+ DWORD cbData;
+ DWORD celt;
+ DWORD dwRVATo = 0;
+ unsigned int i;
+
+ datasav.dwRVATo = 0;
+ datasav.dwRVA = 0;
+
+ while (SUCCEEDED(pStream->Next(1, sizeof(data), &cbData, (BYTE*) &data, &celt)) && (celt == 1)) {
+ if (dwRVA > data.dwRVA) {
+ datasav = data;
+ continue;
+ }
+
+ else if (dwRVA == data.dwRVA) {
+ dwRVATo = data.dwRVATo;
+ }
+
+ else if (datasav.dwRVATo) {
+ dwRVATo = datasav.dwRVATo + (dwRVA - datasav.dwRVA);
+ }
+ break;
+ }
+
+ wprintf(L"source rva = %08X ==> image rva = %08X\n\nRelated OMAP entries:\n", dwRVA, dwRVATo);
+ wprintf(L"source rva ==> image rva\n");
+ wprintf(L"%08X ==> %08X\n", datasav.dwRVA, datasav.dwRVATo);
+
+ i = 0;
+
+ do {
+ wprintf(L"%08X ==> %08X\n", data.dwRVA, data.dwRVATo);
+ }
+ while ((++i) < 5 && SUCCEEDED(pStream->Next(1, sizeof(data), &cbData, (BYTE*) &data, &celt)) && (celt == 1));
+ }
+
+ if (bstrName != NULL) {
+ SysFreeString(bstrName);
+ }
+
+ pStream->Release();
+ }
+
+ pEnumStreams->Release();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Retreive the table that matches the given iid
+//
+// A PDB table could store the section contributions, the frame data,
+// the injected sources
+//
+HRESULT GetTable(IDiaSession *pSession, REFIID iid, void **ppUnk)
+{
+ IDiaEnumTables *pEnumTables;
+
+ if (FAILED(pSession->getEnumTables(&pEnumTables))) {
+ wprintf(L"ERROR - GetTable() getEnumTables\n");
+
+ return E_FAIL;
+ }
+
+ IDiaTable *pTable;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumTables->Next(1, &pTable, &celt)) && (celt == 1)) {
+ // There's only one table that matches the given IID
+
+ if (SUCCEEDED(pTable->QueryInterface(iid, (void **) ppUnk))) {
+ pTable->Release();
+ pEnumTables->Release();
+
+ return S_OK;
+ }
+
+ pTable->Release();
+ }
+
+ pEnumTables->Release();
+
+ return E_FAIL;
+}
diff --git a/src/ToolBox/PdbTypeMatch/PdbTypeMatch.h b/src/ToolBox/PdbTypeMatch/PdbTypeMatch.h
new file mode 100644
index 0000000000..2da38cf2b5
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/PdbTypeMatch.h
@@ -0,0 +1,69 @@
+// 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 "dia2.h"
+#include <set>
+
+extern const wchar_t *g_szFilename;
+extern IDiaDataSource *g_pDiaDataSource;
+extern IDiaSession *g_pDiaSession1, *g_pDiaSession2;
+extern IDiaSymbol *g_pGlobalSymbol1, *g_pGlobalSymbol2;
+extern DWORD g_dwMachineType;
+
+typedef std::set<std::wstring> IDiaSymbolSet;
+
+
+void PrintHelpOptions();
+bool ParseArg(int , wchar_t *[]);
+
+bool InitDiaSource(IDiaDataSource **ppSource);
+void Cleanup();
+bool LoadDataFromPdb(const wchar_t *, IDiaDataSource *, IDiaSession **, IDiaSymbol **);
+
+
+bool EnumTypesInPdb(IDiaSymbolSet* types, IDiaSession *pSession, IDiaSymbol *pGlobal);
+bool LayoutMatches(IDiaSymbol* pSymbol1, IDiaSymbol* pSymbol2);
+
+LPSTR UnicodeToAnsi(LPCWSTR s);
+void DumpAllPdbInfo(IDiaSession *, IDiaSymbol *);
+bool DumpAllMods(IDiaSymbol *);
+bool DumpAllPublics(IDiaSymbol *);
+bool DumpCompiland(IDiaSymbol *, const wchar_t *);
+bool DumpAllSymbols(IDiaSymbol *);
+bool DumpAllGlobals(IDiaSymbol *);
+bool DumpAllTypes(IDiaSymbol *);
+bool DumpAllUDTs(IDiaSymbol *);
+bool DumpAllEnums(IDiaSymbol *);
+bool DumpAllTypedefs(IDiaSymbol *);
+bool DumpAllOEMs(IDiaSymbol *);
+bool DumpAllFiles(IDiaSession *, IDiaSymbol *);
+bool DumpAllLines(IDiaSession *, IDiaSymbol *);
+bool DumpAllLines(IDiaSession *, DWORD, DWORD);
+bool DumpAllSecContribs(IDiaSession *);
+bool DumpAllDebugStreams(IDiaSession *);
+bool DumpAllInjectedSources(IDiaSession *);
+bool DumpInjectedSource(IDiaSession *, const wchar_t *);
+bool DumpAllSourceFiles(IDiaSession *, IDiaSymbol *);
+bool DumpAllFPO(IDiaSession *);
+bool DumpFPO(IDiaSession *, DWORD);
+bool DumpFPO(IDiaSession *, IDiaSymbol *, const wchar_t *);
+bool DumpSymbolWithRVA(IDiaSession *, DWORD, const wchar_t *);
+bool DumpSymbolsWithRegEx(IDiaSymbol *, const wchar_t *, const wchar_t *);
+bool DumpSymbolWithChildren(IDiaSymbol *, const wchar_t *);
+bool DumpLines(IDiaSession *, DWORD);
+bool DumpLines(IDiaSession *, IDiaSymbol *, const wchar_t *);
+bool DumpType(IDiaSymbol *, const wchar_t *);
+bool DumpLinesForSourceFile(IDiaSession *, const wchar_t *, DWORD);
+bool DumpPublicSymbolsSorted(IDiaSession *, DWORD, DWORD, bool);
+bool DumpLabel(IDiaSession *, DWORD);
+bool DumpAnnotations(IDiaSession *, DWORD);
+bool DumpMapToSrc(IDiaSession *, DWORD);
+bool DumpMapFromSrc(IDiaSession *, DWORD);
+
+HRESULT GetTable(IDiaSession *, REFIID, void **);
+
+///////////////////////////////////////////////////////////////////
+// Functions defined in regs.cpp
+const wchar_t *SzNameC7Reg(USHORT, DWORD);
+const wchar_t *SzNameC7Reg(USHORT);
diff --git a/src/ToolBox/PdbTypeMatch/PdbTypeMatch.nativeproj b/src/ToolBox/PdbTypeMatch/PdbTypeMatch.nativeproj
new file mode 100644
index 0000000000..fb2f71b36c
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/PdbTypeMatch.nativeproj
@@ -0,0 +1,59 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <PropertyGroup>
+ <ClCallingConvention>cdecl</ClCallingConvention>
+ <OutputName>pdbtypematch</OutputName>
+ <OutputPath>$(IntermediateOutputPath)</OutputPath>
+ <TargetType>PROGRAM</TargetType>
+ <LinkSubsystem>console</LinkSubsystem>
+ <EntryPoint>wmain</EntryPoint>
+ <UseStl>true</UseStl>
+ <PCHHeader>stdafx.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders>
+ <PCHCompile>stdafx.cpp</PCHCompile>
+ <PCHOutput>pdbtypematch.pch</PCHOutput>
+ <LinkAdditionalOptions>$(LinkAdditionalOptions) /DYNAMICBASE /NXCOMPAT /ALLOWISOLATION</LinkAdditionalOptions>
+ <CharacterSet>MultiByte</CharacterSet>
+ <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <OutputPath>$(IntermediateOutputPath)</OutputPath>
+ <UserIncludes>
+ .;
+ .\include;
+ $(UserIncludes);
+ </UserIncludes>
+ <LinkUseCMT>true</LinkUseCMT>
+ <UseMsvcrt>false</UseMsvcrt>
+ <AlwaysUseReleaseLibCMT>true</AlwaysUseReleaseLibCMT>
+ <!--
+ This would build (cl.exe) with /MT switch instead of default /MTd. However it works either way, so we do not need it now.
+ It might be handy if we ever use some debug-only CRT features by accident.
+ <ClRuntimeLibrary>MultiThreaded</ClRuntimeLibrary>
+ -->
+
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <TargetLib Include="$(SdkLibPath)\ole32.lib" />
+ <TargetLib Include="$(SdkLibPath)\oleaut32.lib" />
+ <TargetLib Include="$(SdkLibPath)\Shlwapi.lib" />
+ <TargetLib Include="$(CrtLibPath)\comsupp.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="DiaLib">
+ <ProjectReference>$(ClrSrcDirectory)toolbox\sos\diasdk\diasdk.nativeproj</ProjectReference>
+ <HintPath>$(ClrIntraLibPath)\DiaLib.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <CppCompile Include="PrintSymbol.cpp" />
+ <CppCompile Include="regs.cpp" />
+ <CppCompile Include="PdbTypeMatch.cpp" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <RCResourceFile Include="native.rc" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project> \ No newline at end of file
diff --git a/src/ToolBox/PdbTypeMatch/PrintSymbol.cpp b/src/ToolBox/PdbTypeMatch/PrintSymbol.cpp
new file mode 100644
index 0000000000..41e3d4cf0e
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/PrintSymbol.cpp
@@ -0,0 +1,2254 @@
+// 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.
+
+// PrintSymbol.cpp : Defines the printing procedures for the symbols
+//
+// This is a part of the Debug Interface Access SDK
+//
+// This source code is only intended as a supplement to the
+// Debug Interface Access SDK and related electronic documentation
+// provided with the library.
+// See these sources for detailed information regarding the
+// Debug Interface Access SDK API.
+//
+
+#include "stdafx.h"
+
+#include <malloc.h>
+
+#include "dia2.h"
+#include "regs.h"
+#include "PrintSymbol.h"
+#include <comdef.h>
+
+
+// Basic types
+const wchar_t * const rgBaseType[] =
+{
+ L"<NoType>", // btNoType = 0,
+ L"void", // btVoid = 1,
+ L"char", // btChar = 2,
+ L"wchar_t", // btWChar = 3,
+ L"signed char",
+ L"unsigned char",
+ L"int", // btInt = 6,
+ L"unsigned int", // btUInt = 7,
+ L"float", // btFloat = 8,
+ L"<BCD>", // btBCD = 9,
+ L"bool", // btBool = 10,
+ L"short",
+ L"unsigned short",
+ L"long", // btLong = 13,
+ L"unsigned long", // btULong = 14,
+ L"__int8",
+ L"__int16",
+ L"__int32",
+ L"__int64",
+ L"__int128",
+ L"unsigned __int8",
+ L"unsigned __int16",
+ L"unsigned __int32",
+ L"unsigned __int64",
+ L"unsigned __int128",
+ L"<currency>", // btCurrency = 25,
+ L"<date>", // btDate = 26,
+ L"VARIANT", // btVariant = 27,
+ L"<complex>", // btComplex = 28,
+ L"<bit>", // btBit = 29,
+ L"BSTR", // btBSTR = 30,
+ L"HRESULT" // btHresult = 31
+};
+
+// Tags returned by Dia
+const wchar_t * const rgTags[] =
+{
+ L"(SymTagNull)", // SymTagNull
+ L"Executable (Global)", // SymTagExe
+ L"Compiland", // SymTagCompiland
+ L"CompilandDetails", // SymTagCompilandDetails
+ L"CompilandEnv", // SymTagCompilandEnv
+ L"Function", // SymTagFunction
+ L"Block", // SymTagBlock
+ L"Data", // SymTagData
+ L"Annotation", // SymTagAnnotation
+ L"Label", // SymTagLabel
+ L"PublicSymbol", // SymTagPublicSymbol
+ L"UserDefinedType", // SymTagUDT
+ L"Enum", // SymTagEnum
+ L"FunctionType", // SymTagFunctionType
+ L"PointerType", // SymTagPointerType
+ L"ArrayType", // SymTagArrayType
+ L"BaseType", // SymTagBaseType
+ L"Typedef", // SymTagTypedef
+ L"BaseClass", // SymTagBaseClass
+ L"Friend", // SymTagFriend
+ L"FunctionArgType", // SymTagFunctionArgType
+ L"FuncDebugStart", // SymTagFuncDebugStart
+ L"FuncDebugEnd", // SymTagFuncDebugEnd
+ L"UsingNamespace", // SymTagUsingNamespace
+ L"VTableShape", // SymTagVTableShape
+ L"VTable", // SymTagVTable
+ L"Custom", // SymTagCustom
+ L"Thunk", // SymTagThunk
+ L"CustomType", // SymTagCustomType
+ L"ManagedType", // SymTagManagedType
+ L"Dimension", // SymTagDimension
+ L"CallSite", // SymTagCallSite
+};
+
+
+// Processors
+const wchar_t * const rgFloatPackageStrings[] =
+{
+ L"hardware processor (80x87 for Intel processors)", // CV_CFL_NDP
+ L"emulator", // CV_CFL_EMU
+ L"altmath", // CV_CFL_ALT
+ L"???"
+};
+
+const wchar_t * const rgProcessorStrings[] =
+{
+ L"8080", // CV_CFL_8080
+ L"8086", // CV_CFL_8086
+ L"80286", // CV_CFL_80286
+ L"80386", // CV_CFL_80386
+ L"80486", // CV_CFL_80486
+ L"Pentium", // CV_CFL_PENTIUM
+ L"Pentium Pro/Pentium II", // CV_CFL_PENTIUMII/CV_CFL_PENTIUMPRO
+ L"Pentium III", // CV_CFL_PENTIUMIII
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"MIPS (Generic)", // CV_CFL_MIPSR4000
+ L"MIPS16", // CV_CFL_MIPS16
+ L"MIPS32", // CV_CFL_MIPS32
+ L"MIPS64", // CV_CFL_MIPS64
+ L"MIPS I", // CV_CFL_MIPSI
+ L"MIPS II", // CV_CFL_MIPSII
+ L"MIPS III", // CV_CFL_MIPSIII
+ L"MIPS IV", // CV_CFL_MIPSIV
+ L"MIPS V", // CV_CFL_MIPSV
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"M68000", // CV_CFL_M68000
+ L"M68010", // CV_CFL_M68010
+ L"M68020", // CV_CFL_M68020
+ L"M68030", // CV_CFL_M68030
+ L"M68040", // CV_CFL_M68040
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"Alpha 21064", // CV_CFL_ALPHA, CV_CFL_ALPHA_21064
+ L"Alpha 21164", // CV_CFL_ALPHA_21164
+ L"Alpha 21164A", // CV_CFL_ALPHA_21164A
+ L"Alpha 21264", // CV_CFL_ALPHA_21264
+ L"Alpha 21364", // CV_CFL_ALPHA_21364
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"PPC 601", // CV_CFL_PPC601
+ L"PPC 603", // CV_CFL_PPC603
+ L"PPC 604", // CV_CFL_PPC604
+ L"PPC 620", // CV_CFL_PPC620
+ L"PPC w/FP", // CV_CFL_PPCFP
+ L"PPC (Big Endian)", // CV_CFL_PPCBE
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"SH3", // CV_CFL_SH3
+ L"SH3E", // CV_CFL_SH3E
+ L"SH3DSP", // CV_CFL_SH3DSP
+ L"SH4", // CV_CFL_SH4
+ L"SHmedia", // CV_CFL_SHMEDIA
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"ARM3", // CV_CFL_ARM3
+ L"ARM4", // CV_CFL_ARM4
+ L"ARM4T", // CV_CFL_ARM4T
+ L"ARM5", // CV_CFL_ARM5
+ L"ARM5T", // CV_CFL_ARM5T
+ L"ARM6", // CV_CFL_ARM6
+ L"ARM (XMAC)", // CV_CFL_ARM_XMAC
+ L"ARM (WMMX)", // CV_CFL_ARM_WMMX
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"Omni", // CV_CFL_OMNI
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"Itanium", // CV_CFL_IA64, CV_CFL_IA64_1
+ L"Itanium (McKinley)", // CV_CFL_IA64_2
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"CEE", // CV_CFL_CEE
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"AM33", // CV_CFL_AM33
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"M32R", // CV_CFL_M32R
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"TriCore", // CV_CFL_TRICORE
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"x64", // CV_CFL_X64
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"EBC", // CV_CFL_EBC
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"???",
+ L"Thumb", // CV_CFL_THUMB
+};
+
+const wchar_t * const rgDataKind[] =
+{
+ L"Unknown",
+ L"Local",
+ L"Static Local",
+ L"Param",
+ L"Object Ptr",
+ L"File Static",
+ L"Global",
+ L"Member",
+ L"Static Member",
+ L"Constant",
+};
+
+const wchar_t * const rgUdtKind[] =
+{
+ L"struct",
+ L"class",
+ L"union",
+ L"enum",
+};
+
+const wchar_t * const rgAccess[] =
+{
+ L"", // No access specifier
+ L"private",
+ L"protected",
+ L"public"
+};
+
+const wchar_t * const rgCallingConvention[] =
+{
+ L"CV_CALL_NEAR_C ",
+ L"CV_CALL_FAR_C ",
+ L"CV_CALL_NEAR_PASCAL ",
+ L"CV_CALL_FAR_PASCAL ",
+ L"CV_CALL_NEAR_FAST ",
+ L"CV_CALL_FAR_FAST ",
+ L"CV_CALL_SKIPPED ",
+ L"CV_CALL_NEAR_STD ",
+ L"CV_CALL_FAR_STD ",
+ L"CV_CALL_NEAR_SYS ",
+ L"CV_CALL_FAR_SYS ",
+ L"CV_CALL_THISCALL ",
+ L"CV_CALL_MIPSCALL ",
+ L"CV_CALL_GENERIC ",
+ L"CV_CALL_ALPHACALL ",
+ L"CV_CALL_PPCCALL ",
+ L"CV_CALL_SHCALL ",
+ L"CV_CALL_ARMCALL ",
+ L"CV_CALL_AM33CALL ",
+ L"CV_CALL_TRICALL ",
+ L"CV_CALL_SH5CALL ",
+ L"CV_CALL_M32RCALL ",
+ L"CV_CALL_RESERVED "
+};
+
+const wchar_t * const rgLanguage[] =
+{
+ L"C", // CV_CFL_C
+ L"C++", // CV_CFL_CXX
+ L"FORTRAN", // CV_CFL_FORTRAN
+ L"MASM", // CV_CFL_MASM
+ L"Pascal", // CV_CFL_PASCAL
+ L"Basic", // CV_CFL_BASIC
+ L"COBOL", // CV_CFL_COBOL
+ L"LINK", // CV_CFL_LINK
+ L"CVTRES", // CV_CFL_CVTRES
+ L"CVTPGD", // CV_CFL_CVTPGD
+ L"C#", // CV_CFL_CSHARP
+ L"Visual Basic", // CV_CFL_VB
+ L"ILASM", // CV_CFL_ILASM
+ L"Java", // CV_CFL_JAVA
+ L"JScript", // CV_CFL_JSCRIPT
+ L"MSIL", // CV_CFL_MSIL
+};
+
+const wchar_t * const rgLocationTypeString[] =
+{
+ L"NULL",
+ L"static",
+ L"TLS",
+ L"RegRel",
+ L"ThisRel",
+ L"Enregistered",
+ L"BitField",
+ L"Slot",
+ L"IL Relative",
+ L"In MetaData",
+ L"Constant"
+};
+
+
+////////////////////////////////////////////////////////////
+// Print a public symbol info: name, VA, RVA, SEG:OFF
+//
+void PrintPublicSymbol(IDiaSymbol *pSymbol)
+{
+ DWORD dwSymTag;
+ DWORD dwRVA;
+ DWORD dwSeg;
+ DWORD dwOff;
+ BSTR bstrName;
+
+ if (pSymbol->get_symTag(&dwSymTag) != S_OK) {
+ return;
+ }
+
+ if (pSymbol->get_relativeVirtualAddress(&dwRVA) != S_OK) {
+ dwRVA = 0xFFFFFFFF;
+ }
+
+ pSymbol->get_addressSection(&dwSeg);
+ pSymbol->get_addressOffset(&dwOff);
+
+ wprintf(L"%s: [%08X][%04X:%08X] ", rgTags[dwSymTag], dwRVA, dwSeg, dwOff);
+
+ if (dwSymTag == SymTagThunk) {
+ if (pSymbol->get_name(&bstrName) == S_OK) {
+ wprintf(L"%s\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ else {
+ if (pSymbol->get_targetRelativeVirtualAddress(&dwRVA) != S_OK) {
+ dwRVA = 0xFFFFFFFF;
+ }
+
+ pSymbol->get_targetSection(&dwSeg);
+ pSymbol->get_targetOffset(&dwOff);
+
+ wprintf(L"target -> [%08X][%04X:%08X]\n", dwRVA, dwSeg, dwOff);
+ }
+ }
+
+ else {
+ // must be a function or a data symbol
+
+ BSTR bstrUndname;
+
+ if (pSymbol->get_name(&bstrName) == S_OK) {
+ if (pSymbol->get_undecoratedName(&bstrUndname) == S_OK) {
+ wprintf(L"%s(%s)\n", bstrName, bstrUndname);
+
+ SysFreeString(bstrUndname);
+ }
+
+ else {
+ wprintf(L"%s\n", bstrName);
+ }
+
+ SysFreeString(bstrName);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print a global symbol info: name, VA, RVA, SEG:OFF
+//
+void PrintGlobalSymbol(IDiaSymbol *pSymbol)
+{
+ DWORD dwSymTag;
+ DWORD dwRVA;
+ DWORD dwSeg;
+ DWORD dwOff;
+
+ if (pSymbol->get_symTag(&dwSymTag) != S_OK) {
+ return;
+ }
+
+ if (pSymbol->get_relativeVirtualAddress(&dwRVA) != S_OK) {
+ dwRVA = 0xFFFFFFFF;
+ }
+
+ pSymbol->get_addressSection(&dwSeg);
+ pSymbol->get_addressOffset(&dwOff);
+
+ wprintf(L"%s: [%08X][%04X:%08X] ", rgTags[dwSymTag], dwRVA, dwSeg, dwOff);
+
+ if (dwSymTag == SymTagThunk) {
+ BSTR bstrName;
+
+ if (pSymbol->get_name(&bstrName) == S_OK) {
+ wprintf(L"%s\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ else {
+ if (pSymbol->get_targetRelativeVirtualAddress(&dwRVA) != S_OK) {
+ dwRVA = 0xFFFFFFFF;
+ }
+
+ pSymbol->get_targetSection(&dwSeg);
+ pSymbol->get_targetOffset(&dwOff);
+ wprintf(L"target -> [%08X][%04X:%08X]\n", dwRVA, dwSeg, dwOff);
+ }
+ }
+
+ else {
+ BSTR bstrName;
+ BSTR bstrUndname;
+
+ if (pSymbol->get_name(&bstrName) == S_OK) {
+ if (pSymbol->get_undecoratedName(&bstrUndname) == S_OK) {
+ wprintf(L"%s(%s)\n", bstrName, bstrUndname);
+
+ SysFreeString(bstrUndname);
+ }
+
+ else {
+ wprintf(L"%s\n", bstrName);
+ }
+
+ SysFreeString(bstrName);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print a callsite symbol info: SEG:OFF, RVA, type
+//
+void PrintCallSiteInfo(IDiaSymbol *pSymbol)
+{
+ DWORD dwISect, dwOffset;
+ if (pSymbol->get_addressSection(&dwISect) == S_OK &&
+ pSymbol->get_addressOffset(&dwOffset) == S_OK) {
+ wprintf(L"[0x%04x:0x%08x] ", dwISect, dwOffset);
+ }
+
+ DWORD rva;
+ if (pSymbol->get_relativeVirtualAddress(&rva) == S_OK) {
+ wprintf(L"0x%08X ", rva);
+ }
+
+ IDiaSymbol *pFuncType;
+ if (pSymbol->get_type(&pFuncType) == S_OK) {
+ DWORD tag;
+ if (pFuncType->get_symTag(&tag) == S_OK) {
+ switch(tag)
+ {
+ case SymTagFunctionType:
+ PrintFunctionType(pSymbol);
+ break;
+ case SymTagPointerType:
+ PrintFunctionType(pFuncType);
+ break;
+ default:
+ wprintf(L"???\n");
+ break;
+ }
+ }
+ pFuncType->Release();
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print a symbol info: name, type etc.
+//
+void PrintSymbol(IDiaSymbol *pSymbol, DWORD dwIndent)
+{
+ IDiaSymbol *pType;
+ DWORD dwSymTag;
+ ULONGLONG ulLen;
+
+ if (pSymbol->get_symTag(&dwSymTag) != S_OK) {
+ wprintf(L"ERROR - PrintSymbol get_symTag() failed\n");
+ return;
+ }
+
+ if (dwSymTag == SymTagFunction) {
+ putwchar(L'\n');
+ }
+
+ PrintSymTag(dwSymTag);
+
+ for (DWORD i = 0; i < dwIndent; i++) {
+ putwchar(L' ');
+ }
+
+ switch (dwSymTag) {
+ case SymTagCompilandDetails:
+ PrintCompilandDetails(pSymbol);
+ break;
+
+ case SymTagCompilandEnv:
+ PrintCompilandEnv(pSymbol);
+ break;
+
+ case SymTagData:
+ PrintData(pSymbol, dwIndent + 2);
+ break;
+
+ case SymTagFunction:
+ case SymTagBlock:
+ PrintLocation(pSymbol);
+
+ if (pSymbol->get_length(&ulLen) == S_OK) {
+ wprintf(L", len = %08X, ", ulLen);
+ }
+
+ if (dwSymTag == SymTagFunction) {
+ DWORD dwCall;
+
+ if (pSymbol->get_callingConvention(&dwCall) == S_OK) {
+ wprintf(L", %s", SafeDRef(rgCallingConvention, dwCall));
+ }
+ }
+
+ PrintUndName(pSymbol);
+ putwchar(L'\n');
+
+ if (dwSymTag == SymTagFunction)
+ {
+ BOOL f;
+
+ for (DWORD i = 0; i < dwIndent; i++) {
+ putwchar(L' ');
+ }
+ wprintf(L" Function attribute:");
+
+ if ((pSymbol->get_isCxxReturnUdt(&f) == S_OK) && f) {
+ wprintf(L" return user defined type (C++ style)");
+ }
+ if ((pSymbol->get_constructor(&f) == S_OK) && f) {
+ wprintf(L" instance constructor");
+ }
+ if ((pSymbol->get_isConstructorVirtualBase(&f) == S_OK) && f) {
+ wprintf(L" instance constructor of a class with virtual base");
+ }
+ putwchar (L'\n');
+
+ for (DWORD i = 0; i < dwIndent; i++) {
+ putwchar(L' ');
+ }
+ wprintf(L" Function info:");
+
+ if ((pSymbol->get_hasAlloca(&f) == S_OK) && f) {
+ wprintf(L" alloca");
+ }
+
+ if ((pSymbol->get_hasSetJump(&f) == S_OK) && f) {
+ wprintf(L" setjmp");
+ }
+
+ if ((pSymbol->get_hasLongJump(&f) == S_OK) && f) {
+ wprintf(L" longjmp");
+ }
+
+ if ((pSymbol->get_hasInlAsm(&f) == S_OK) && f) {
+ wprintf(L" inlasm");
+ }
+
+ if ((pSymbol->get_hasEH(&f) == S_OK) && f) {
+ wprintf(L" eh");
+ }
+
+ if ((pSymbol->get_inlSpec(&f) == S_OK) && f) {
+ wprintf(L" inl_specified");
+ }
+
+ if ((pSymbol->get_hasSEH(&f) == S_OK) && f) {
+ wprintf(L" seh");
+ }
+
+ if ((pSymbol->get_isNaked(&f) == S_OK) && f) {
+ wprintf(L" naked");
+ }
+
+ if ((pSymbol->get_hasSecurityChecks(&f) == S_OK) && f) {
+ wprintf(L" gschecks");
+ }
+
+ if ((pSymbol->get_isSafeBuffers(&f) == S_OK) && f) {
+ wprintf(L" safebuffers");
+ }
+
+ if ((pSymbol->get_hasEHa(&f) == S_OK) && f) {
+ wprintf(L" asyncheh");
+ }
+
+ if ((pSymbol->get_noStackOrdering(&f) == S_OK) && f) {
+ wprintf(L" gsnostackordering");
+ }
+
+ if ((pSymbol->get_wasInlined(&f) == S_OK) && f) {
+ wprintf(L" wasinlined");
+ }
+
+ if ((pSymbol->get_strictGSCheck(&f) == S_OK) && f) {
+ wprintf(L" strict_gs_check");
+ }
+
+ putwchar(L'\n');
+ }
+
+ IDiaEnumSymbols *pEnumChildren;
+
+ if (SUCCEEDED(pSymbol->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren))) {
+ IDiaSymbol *pChild;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumChildren->Next(1, &pChild, &celt)) && (celt == 1)) {
+ PrintSymbol(pChild, dwIndent + 2);
+ pChild->Release();
+ }
+
+ pEnumChildren->Release();
+ }
+ return;
+
+ case SymTagAnnotation:
+ PrintLocation(pSymbol);
+ putwchar(L'\n');
+ break;
+
+ case SymTagLabel:
+ PrintLocation(pSymbol);
+ wprintf(L", ");
+ PrintName(pSymbol);
+ break;
+
+ case SymTagEnum:
+ case SymTagTypedef:
+ case SymTagUDT:
+ case SymTagBaseClass:
+ PrintUDT(pSymbol);
+ break;
+
+ case SymTagFuncDebugStart:
+ case SymTagFuncDebugEnd:
+ PrintLocation(pSymbol);
+ break;
+
+ case SymTagFunctionArgType:
+ case SymTagFunctionType:
+ case SymTagPointerType:
+ case SymTagArrayType:
+ case SymTagBaseType:
+ if (pSymbol->get_type(&pType) == S_OK) {
+ PrintType(pType);
+ pType->Release();
+ }
+
+ putwchar(L'\n');
+ break;
+
+ case SymTagThunk:
+ PrintThunk(pSymbol);
+ break;
+
+ case SymTagCallSite:
+ PrintCallSiteInfo(pSymbol);
+ break;
+
+ default:
+ PrintName(pSymbol);
+
+ IDiaSymbol *pType;
+
+ if (pSymbol->get_type(&pType) == S_OK) {
+ wprintf(L" has type ");
+ PrintType(pType);
+ pType->Release();
+ }
+ }
+
+ if ((dwSymTag == SymTagUDT) || (dwSymTag == SymTagAnnotation)) {
+ IDiaEnumSymbols *pEnumChildren;
+
+ putwchar(L'\n');
+
+ if (SUCCEEDED(pSymbol->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren))) {
+ IDiaSymbol *pChild;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pEnumChildren->Next(1, &pChild, &celt)) && (celt == 1)) {
+ PrintSymbol(pChild, dwIndent + 2);
+ pChild->Release();
+ }
+
+ pEnumChildren->Release();
+ }
+ }
+ putwchar(L'\n');
+}
+
+////////////////////////////////////////////////////////////
+// Print the string coresponding to the symbol's tag property
+//
+void PrintSymTag(DWORD dwSymTag)
+{
+ wprintf(L"%-15s: ", SafeDRef(rgTags, dwSymTag));
+}
+
+////////////////////////////////////////////////////////////
+// Print the name of the symbol
+//
+void PrintName(IDiaSymbol *pSymbol)
+{
+ BSTR bstrName;
+ BSTR bstrUndName;
+
+ if (pSymbol->get_name(&bstrName) != S_OK) {
+ wprintf(L"(none)");
+ return;
+ }
+
+ if (pSymbol->get_undecoratedName(&bstrUndName) == S_OK) {
+ if (wcscmp(bstrName, bstrUndName) == 0) {
+ wprintf(L"%s", bstrName);
+ }
+
+ else {
+ wprintf(L"%s(%s)", bstrUndName, bstrName);
+ }
+
+ SysFreeString(bstrUndName);
+ }
+
+ else {
+ wprintf(L"%s", bstrName);
+ }
+
+ SysFreeString(bstrName);
+}
+
+void GetSymbolName(std::wstring& symbolName, IDiaSymbol *pSymbol)
+{
+ BSTR bstrName;
+ BSTR bstrUndName;
+
+ if (pSymbol->get_name(&bstrName) != S_OK) {
+ symbolName.clear();
+ return;
+ }
+
+ if (pSymbol->get_undecoratedName(&bstrUndName) == S_OK) {
+ if (wcscmp(bstrName, bstrUndName) == 0)
+ {
+ symbolName= _bstr_t(bstrName);
+ }
+ else
+ {
+ symbolName= _bstr_t(bstrUndName);
+ }
+
+ SysFreeString(bstrUndName);
+ }
+
+ else
+ {
+ symbolName= _bstr_t(bstrName);
+ }
+
+ SysFreeString(bstrName);
+}
+
+////////////////////////////////////////////////////////////
+// Print the undecorated name of the symbol
+// - only SymTagFunction, SymTagData and SymTagPublicSymbol
+// can have this property set
+//
+void PrintUndName(IDiaSymbol *pSymbol)
+{
+ BSTR bstrName;
+
+ if (pSymbol->get_undecoratedName(&bstrName) != S_OK) {
+ if (pSymbol->get_name(&bstrName) == S_OK) {
+ // Print the name of the symbol instead
+
+ wprintf(L"%s", (bstrName[0] != L'\0') ? bstrName : L"(none)");
+
+ SysFreeString(bstrName);
+ }
+
+ else {
+ wprintf(L"(none)");
+ }
+
+ return;
+ }
+
+ if (bstrName[0] != L'\0') {
+ wprintf(L"%s", bstrName);
+ }
+
+ SysFreeString(bstrName);
+}
+
+////////////////////////////////////////////////////////////
+// Print a SymTagThunk symbol's info
+//
+void PrintThunk(IDiaSymbol *pSymbol)
+{
+ DWORD dwRVA;
+ DWORD dwISect;
+ DWORD dwOffset;
+
+ if ((pSymbol->get_relativeVirtualAddress(&dwRVA) == S_OK) &&
+ (pSymbol->get_addressSection(&dwISect) == S_OK) &&
+ (pSymbol->get_addressOffset(&dwOffset) == S_OK)) {
+ wprintf(L"[%08X][%04X:%08X]", dwRVA, dwISect, dwOffset);
+ }
+
+ if ((pSymbol->get_targetSection(&dwISect) == S_OK) &&
+ (pSymbol->get_targetOffset(&dwOffset) == S_OK) &&
+ (pSymbol->get_targetRelativeVirtualAddress(&dwRVA) == S_OK)) {
+ wprintf(L", target [%08X][%04X:%08X] ", dwRVA, dwISect, dwOffset);
+ }
+
+ else {
+ wprintf(L", target ");
+
+ PrintName(pSymbol);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print the compiland/module details: language, platform...
+//
+void PrintCompilandDetails(IDiaSymbol *pSymbol)
+{
+ DWORD dwLanguage;
+
+ if (pSymbol->get_language(&dwLanguage) == S_OK) {
+ wprintf(L"\n\tLanguage: %s\n", SafeDRef(rgLanguage, dwLanguage));
+ }
+
+ DWORD dwPlatform;
+
+ if (pSymbol->get_platform(&dwPlatform) == S_OK) {
+ wprintf(L"\tTarget processor: %s\n", SafeDRef(rgProcessorStrings, dwPlatform));
+ }
+
+ BOOL fEC;
+
+ if (pSymbol->get_editAndContinueEnabled(&fEC) == S_OK) {
+ if (fEC) {
+ wprintf(L"\tCompiled for edit and continue: yes\n");
+ }
+
+ else {
+ wprintf(L"\tCompiled for edit and continue: no\n");
+ }
+ }
+
+ BOOL fDbgInfo;
+
+ if (pSymbol->get_hasDebugInfo(&fDbgInfo) == S_OK) {
+ if (fDbgInfo) {
+ wprintf(L"\tCompiled without debugging info: no\n");
+ }
+
+ else {
+ wprintf(L"\tCompiled without debugging info: yes\n");
+ }
+ }
+
+ BOOL fLTCG;
+
+ if (pSymbol->get_isLTCG(&fLTCG) == S_OK) {
+ if (fLTCG) {
+ wprintf(L"\tCompiled with LTCG: yes\n");
+ }
+
+ else {
+ wprintf(L"\tCompiled with LTCG: no\n");
+ }
+ }
+
+ BOOL fDataAlign;
+
+ if (pSymbol->get_isDataAligned(&fDataAlign) == S_OK) {
+ if (fDataAlign) {
+ wprintf(L"\tCompiled with /bzalign: no\n");
+ }
+
+ else {
+ wprintf(L"\tCompiled with /bzalign: yes\n");
+ }
+ }
+
+ BOOL fManagedPresent;
+
+ if (pSymbol->get_hasManagedCode(&fManagedPresent) == S_OK) {
+ if (fManagedPresent) {
+ wprintf(L"\tManaged code present: yes\n");
+ }
+
+ else {
+ wprintf(L"\tManaged code present: no\n");
+ }
+ }
+
+ BOOL fSecurityChecks;
+
+ if (pSymbol->get_hasSecurityChecks(&fSecurityChecks) == S_OK) {
+ if (fSecurityChecks) {
+ wprintf(L"\tCompiled with /GS: yes\n");
+ }
+
+ else {
+ wprintf(L"\tCompiled with /GS: no\n");
+ }
+ }
+
+ BOOL fHotPatch;
+
+ if (pSymbol->get_isHotpatchable(&fHotPatch) == S_OK) {
+ if (fHotPatch) {
+ wprintf(L"\tCompiled with /hotpatch: yes\n");
+ }
+
+ else {
+ wprintf(L"\tCompiled with /hotpatch: no\n");
+ }
+ }
+
+ BOOL fCVTCIL;
+
+ if (pSymbol->get_isCVTCIL(&fCVTCIL) == S_OK) {
+ if (fCVTCIL) {
+ wprintf(L"\tConverted by CVTCIL: yes\n");
+ }
+
+ else {
+ wprintf(L"\tConverted by CVTCIL: no\n");
+ }
+ }
+
+ BOOL fMSILModule;
+
+ if (pSymbol->get_isMSILNetmodule(&fMSILModule) == S_OK) {
+ if (fMSILModule) {
+ wprintf(L"\tMSIL module: yes\n");
+ }
+
+ else {
+ wprintf(L"\tMSIL module: no\n");
+ }
+ }
+
+ DWORD dwVerMajor;
+ DWORD dwVerMinor;
+ DWORD dwVerBuild;
+ DWORD dwVerQFE;
+
+ if ((pSymbol->get_frontEndMajor(&dwVerMajor) == S_OK) &&
+ (pSymbol->get_frontEndMinor(&dwVerMinor) == S_OK) &&
+ (pSymbol->get_frontEndBuild(&dwVerBuild) == S_OK)) {
+ wprintf(L"\tFrontend Version: Major = %u, Minor = %u, Build = %u",
+ dwVerMajor,
+ dwVerMinor,
+ dwVerBuild);
+
+ if (pSymbol->get_frontEndQFE(&dwVerQFE) == S_OK) {
+ wprintf(L", QFE = %u", dwVerQFE);
+ }
+
+ putwchar(L'\n');
+ }
+
+ if ((pSymbol->get_backEndMajor(&dwVerMajor) == S_OK) &&
+ (pSymbol->get_backEndMinor(&dwVerMinor) == S_OK) &&
+ (pSymbol->get_backEndBuild(&dwVerBuild) == S_OK)) {
+ wprintf(L"\tBackend Version: Major = %u, Minor = %u, Build = %u",
+ dwVerMajor,
+ dwVerMinor,
+ dwVerBuild);
+
+ if (pSymbol->get_backEndQFE(&dwVerQFE) == S_OK) {
+ wprintf(L", QFE = %u", dwVerQFE);
+ }
+
+ putwchar(L'\n');
+ }
+
+ BSTR bstrCompilerName;
+
+ if (pSymbol->get_compilerName(&bstrCompilerName) == S_OK) {
+ if (bstrCompilerName != NULL) {
+ wprintf(L"\tVersion string: %s", bstrCompilerName);
+
+ SysFreeString(bstrCompilerName);
+ }
+ }
+
+ putwchar(L'\n');
+}
+
+////////////////////////////////////////////////////////////
+// Print the compilan/module env
+//
+void PrintCompilandEnv(IDiaSymbol *pSymbol)
+{
+ PrintName(pSymbol);
+ wprintf(L" =");
+
+ VARIANT vt = { VT_EMPTY };
+
+ if (pSymbol->get_value(&vt) == S_OK) {
+ PrintVariant(vt);
+ VariantClear((VARIANTARG *) &vt);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print a string corespondig to a location type
+//
+void PrintLocation(IDiaSymbol *pSymbol)
+{
+ DWORD dwLocType;
+ DWORD dwRVA, dwSect, dwOff, dwReg, dwBitPos, dwSlot;
+ LONG lOffset;
+ ULONGLONG ulLen;
+ VARIANT vt = { VT_EMPTY };
+
+ if (pSymbol->get_locationType(&dwLocType) != S_OK) {
+ // It must be a symbol in optimized code
+
+ wprintf(L"symbol in optmized code");
+ return;
+ }
+
+ switch (dwLocType) {
+ case LocIsStatic:
+ if ((pSymbol->get_relativeVirtualAddress(&dwRVA) == S_OK) &&
+ (pSymbol->get_addressSection(&dwSect) == S_OK) &&
+ (pSymbol->get_addressOffset(&dwOff) == S_OK)) {
+ wprintf(L"%s, [%08X][%04X:%08X]", SafeDRef(rgLocationTypeString, dwLocType), dwRVA, dwSect, dwOff);
+ }
+ break;
+
+ case LocIsTLS:
+ case LocInMetaData:
+ case LocIsIlRel:
+ if ((pSymbol->get_relativeVirtualAddress(&dwRVA) == S_OK) &&
+ (pSymbol->get_addressSection(&dwSect) == S_OK) &&
+ (pSymbol->get_addressOffset(&dwOff) == S_OK)) {
+ wprintf(L"%s, [%08X][%04X:%08X]", SafeDRef(rgLocationTypeString, dwLocType), dwRVA, dwSect, dwOff);
+ }
+ break;
+
+ case LocIsRegRel:
+ if ((pSymbol->get_registerId(&dwReg) == S_OK) &&
+ (pSymbol->get_offset(&lOffset) == S_OK)) {
+ wprintf(L"%s Relative, [%08X]", SzNameC7Reg((USHORT) dwReg), lOffset);
+ }
+ break;
+
+ case LocIsThisRel:
+ if (pSymbol->get_offset(&lOffset) == S_OK) {
+ wprintf(L"this+0x%X", lOffset);
+ }
+ break;
+
+ case LocIsBitField:
+ if ((pSymbol->get_offset(&lOffset) == S_OK) &&
+ (pSymbol->get_bitPosition(&dwBitPos) == S_OK) &&
+ (pSymbol->get_length(&ulLen) == S_OK)) {
+ wprintf(L"this(bf)+0x%X:0x%X len(0x%X)", lOffset, dwBitPos, ulLen);
+ }
+ break;
+
+ case LocIsEnregistered:
+ if (pSymbol->get_registerId(&dwReg) == S_OK) {
+ wprintf(L"enregistered %s", SzNameC7Reg((USHORT) dwReg));
+ }
+ break;
+
+ case LocIsSlot:
+ if (pSymbol->get_slot(&dwSlot) == S_OK) {
+ wprintf(L"%s, [%08X]", SafeDRef(rgLocationTypeString, dwLocType), dwSlot);
+ }
+ break;
+
+ case LocIsConstant:
+ wprintf(L"constant");
+
+ if (pSymbol->get_value(&vt) == S_OK) {
+ PrintVariant(vt);
+ VariantClear((VARIANTARG *) &vt);
+ }
+ break;
+
+ case LocIsNull:
+ break;
+
+ default :
+ wprintf(L"Error - invalid location type: 0x%X", dwLocType);
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print the type, value and the name of a const symbol
+//
+void PrintConst(IDiaSymbol *pSymbol)
+{
+ PrintSymbolType(pSymbol);
+
+ VARIANT vt = { VT_EMPTY };
+
+ if (pSymbol->get_value(&vt) == S_OK) {
+ PrintVariant(vt);
+ VariantClear((VARIANTARG *) &vt);
+ }
+
+ PrintName(pSymbol);
+}
+
+////////////////////////////////////////////////////////////
+// Print the name and the type of an user defined type
+//
+void PrintUDT(IDiaSymbol *pSymbol)
+{
+ PrintName(pSymbol);
+ PrintSymbolType(pSymbol);
+}
+
+////////////////////////////////////////////////////////////
+// Print a string representing the type of a symbol
+//
+void PrintSymbolType(IDiaSymbol *pSymbol)
+{
+ IDiaSymbol *pType;
+
+ if (pSymbol->get_type(&pType) == S_OK) {
+ wprintf(L", Type: ");
+ PrintType(pType);
+ pType->Release();
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print the information details for a type symbol
+//
+void PrintType(IDiaSymbol *pSymbol)
+{
+ IDiaSymbol *pBaseType;
+ IDiaEnumSymbols *pEnumSym;
+ IDiaSymbol *pSym;
+ DWORD dwTag;
+ BSTR bstrName;
+ DWORD dwInfo;
+ BOOL bSet;
+ DWORD dwRank;
+ LONG lCount = 0;
+ ULONG celt = 1;
+
+ if (pSymbol->get_symTag(&dwTag) != S_OK) {
+ wprintf(L"ERROR - can't retrieve the symbol's SymTag\n");
+ return;
+ }
+
+ if (pSymbol->get_name(&bstrName) != S_OK) {
+ bstrName = NULL;
+ }
+
+ if (dwTag != SymTagPointerType) {
+ if ((pSymbol->get_constType(&bSet) == S_OK) && bSet) {
+ wprintf(L"const ");
+ }
+
+ if ((pSymbol->get_volatileType(&bSet) == S_OK) && bSet) {
+ wprintf(L"volatile ");
+ }
+
+ if ((pSymbol->get_unalignedType(&bSet) == S_OK) && bSet) {
+ wprintf(L"__unaligned ");
+ }
+ }
+
+ ULONGLONG ulLen;
+
+ pSymbol->get_length(&ulLen);
+
+ switch (dwTag) {
+ case SymTagUDT:
+ PrintUdtKind(pSymbol);
+ PrintName(pSymbol);
+ break;
+
+ case SymTagEnum:
+ wprintf(L"enum ");
+ PrintName(pSymbol);
+ break;
+
+ case SymTagFunctionType:
+ wprintf(L"function ");
+ break;
+
+ case SymTagPointerType:
+ if (pSymbol->get_type(&pBaseType) != S_OK) {
+ wprintf(L"ERROR - SymTagPointerType get_type");
+ if (bstrName != NULL) {
+ SysFreeString(bstrName);
+ }
+ return;
+ }
+
+ PrintType(pBaseType);
+ pBaseType->Release();
+
+ if ((pSymbol->get_reference(&bSet) == S_OK) && bSet) {
+ wprintf(L" &");
+ }
+
+ else {
+ wprintf(L" *");
+ }
+
+ if ((pSymbol->get_constType(&bSet) == S_OK) && bSet) {
+ wprintf(L" const");
+ }
+
+ if ((pSymbol->get_volatileType(&bSet) == S_OK) && bSet) {
+ wprintf(L" volatile");
+ }
+
+ if ((pSymbol->get_unalignedType(&bSet) == S_OK) && bSet) {
+ wprintf(L" __unaligned");
+ }
+ break;
+
+ case SymTagArrayType:
+ if (pSymbol->get_type(&pBaseType) == S_OK) {
+ PrintType(pBaseType);
+
+ if (pSymbol->get_rank(&dwRank) == S_OK) {
+ if (SUCCEEDED(pSymbol->findChildren(SymTagDimension, NULL, nsNone, &pEnumSym)) && (pEnumSym != NULL)) {
+ while (SUCCEEDED(pEnumSym->Next(1, &pSym, &celt)) && (celt == 1)) {
+ IDiaSymbol *pBound;
+
+ wprintf(L"[");
+
+ if (pSym->get_lowerBound(&pBound) == S_OK) {
+ PrintBound(pBound);
+
+ wprintf(L"..");
+
+ pBound->Release();
+ }
+
+ pBound = NULL;
+
+ if (pSym->get_upperBound(&pBound) == S_OK) {
+ PrintBound(pBound);
+
+ pBound->Release();
+ }
+
+ pSym->Release();
+ pSym = NULL;
+
+ wprintf(L"]");
+ }
+
+ pEnumSym->Release();
+ }
+ }
+
+ else if (SUCCEEDED(pSymbol->findChildren(SymTagCustomType, NULL, nsNone, &pEnumSym)) &&
+ (pEnumSym != NULL) &&
+ (pEnumSym->get_Count(&lCount) == S_OK) &&
+ (lCount > 0)) {
+ while (SUCCEEDED(pEnumSym->Next(1, &pSym, &celt)) && (celt == 1)) {
+ wprintf(L"[");
+ PrintType(pSym);
+ wprintf(L"]");
+
+ pSym->Release();
+ }
+
+ pEnumSym->Release();
+ }
+
+ else {
+ DWORD dwCountElems;
+ ULONGLONG ulLenArray;
+ ULONGLONG ulLenElem;
+
+ if (pSymbol->get_count(&dwCountElems) == S_OK) {
+ wprintf(L"[0x%X]", dwCountElems);
+ }
+
+ else if ((pSymbol->get_length(&ulLenArray) == S_OK) &&
+ (pBaseType->get_length(&ulLenElem) == S_OK)) {
+ if (ulLenElem == 0) {
+ wprintf(L"[0x%lX]", ulLenArray);
+ }
+
+ else {
+ wprintf(L"[0x%lX]", ulLenArray/ulLenElem);
+ }
+ }
+ }
+
+ pBaseType->Release();
+ }
+
+ else {
+ wprintf(L"ERROR - SymTagArrayType get_type\n");
+ if (bstrName != NULL) {
+ SysFreeString(bstrName);
+ }
+ return;
+ }
+ break;
+
+ case SymTagBaseType:
+ if (pSymbol->get_baseType(&dwInfo) != S_OK) {
+ wprintf(L"SymTagBaseType get_baseType\n");
+ if (bstrName != NULL) {
+ SysFreeString(bstrName);
+ }
+ return;
+ }
+
+ switch (dwInfo) {
+ case btUInt :
+ wprintf(L"unsigned ");
+
+ // Fall through
+
+ case btInt :
+ switch (ulLen) {
+ case 1:
+ if (dwInfo == btInt) {
+ wprintf(L"signed ");
+ }
+
+ wprintf(L"char");
+ break;
+
+ case 2:
+ wprintf(L"short");
+ break;
+
+ case 4:
+ wprintf(L"int");
+ break;
+
+ case 8:
+ wprintf(L"__int64");
+ break;
+ }
+
+ dwInfo = 0xFFFFFFFF;
+ break;
+
+ case btFloat :
+ switch (ulLen) {
+ case 4:
+ wprintf(L"float");
+ break;
+
+ case 8:
+ wprintf(L"double");
+ break;
+ }
+
+ dwInfo = 0xFFFFFFFF;
+ break;
+ }
+
+ if (dwInfo == 0xFFFFFFFF) {
+ break;
+ }
+
+ wprintf(L"%s", rgBaseType[dwInfo]);
+ break;
+
+ case SymTagTypedef:
+ PrintName(pSymbol);
+ break;
+
+ case SymTagCustomType:
+ {
+ DWORD idOEM, idOEMSym;
+ DWORD cbData = 0;
+ DWORD count;
+
+ if (pSymbol->get_oemId(&idOEM) == S_OK) {
+ wprintf(L"OEMId = %X, ", idOEM);
+ }
+
+ if (pSymbol->get_oemSymbolId(&idOEMSym) == S_OK) {
+ wprintf(L"SymbolId = %X, ", idOEMSym);
+ }
+
+ if (pSymbol->get_types(0, &count, NULL) == S_OK) {
+ IDiaSymbol** rgpDiaSymbols = (IDiaSymbol**) _alloca(sizeof(IDiaSymbol *) * count);
+
+ if (pSymbol->get_types(count, &count, rgpDiaSymbols) == S_OK) {
+ for (ULONG i = 0; i < count; i++) {
+ PrintType(rgpDiaSymbols[i]);
+ rgpDiaSymbols[i]->Release();
+ }
+ }
+ }
+
+ // print custom data
+
+ if ((pSymbol->get_dataBytes(cbData, &cbData, NULL) == S_OK) && (cbData != 0)) {
+ wprintf(L", Data: ");
+
+ BYTE *pbData = new BYTE[cbData];
+
+ pSymbol->get_dataBytes(cbData, &cbData, pbData);
+
+ for (ULONG i = 0; i < cbData; i++) {
+ wprintf(L"0x%02X ", pbData[i]);
+ }
+
+ delete [] pbData;
+ }
+ }
+ break;
+
+ case SymTagData: // This really is member data, just print its location
+ PrintLocation(pSymbol);
+ break;
+ }
+
+ if (bstrName != NULL) {
+ SysFreeString(bstrName);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print bound information
+//
+void PrintBound(IDiaSymbol *pSymbol)
+{
+ DWORD dwTag = 0;
+ DWORD dwKind;
+
+ if (pSymbol->get_symTag(&dwTag) != S_OK) {
+ wprintf(L"ERROR - PrintBound() get_symTag");
+ return;
+ }
+
+ if (pSymbol->get_locationType(&dwKind) != S_OK) {
+ wprintf(L"ERROR - PrintBound() get_locationType");
+ return;
+ }
+
+ if (dwTag == SymTagData && dwKind == LocIsConstant) {
+ VARIANT v;
+
+ if (pSymbol->get_value(&v) == S_OK) {
+ PrintVariant(v);
+ VariantClear((VARIANTARG *) &v);
+ }
+ }
+
+ else {
+ PrintName(pSymbol);
+ }
+}
+
+////////////////////////////////////////////////////////////
+//
+void PrintData(IDiaSymbol *pSymbol, DWORD dwIndent)
+{
+ PrintLocation(pSymbol);
+
+ DWORD dwDataKind;
+ if (pSymbol->get_dataKind(&dwDataKind) != S_OK) {
+ wprintf(L"ERROR - PrintData() get_dataKind");
+ return;
+ }
+
+ wprintf(L", %s", SafeDRef(rgDataKind, dwDataKind));
+ PrintSymbolType(pSymbol);
+
+ wprintf(L", ");
+ PrintName(pSymbol);
+}
+
+////////////////////////////////////////////////////////////
+// Print a VARIANT
+//
+void PrintVariant(VARIANT var)
+{
+ switch (var.vt) {
+ case VT_UI1:
+ case VT_I1:
+ wprintf(L" 0x%X", var.bVal);
+ break;
+
+ case VT_I2:
+ case VT_UI2:
+ case VT_BOOL:
+ wprintf(L" 0x%X", var.iVal);
+ break;
+
+ case VT_I4:
+ case VT_UI4:
+ case VT_INT:
+ case VT_UINT:
+ case VT_ERROR:
+ wprintf(L" 0x%X", var.lVal);
+ break;
+
+ case VT_R4:
+ wprintf(L" %g", var.fltVal);
+ break;
+
+ case VT_R8:
+ wprintf(L" %g", var.dblVal);
+ break;
+
+ case VT_BSTR:
+ wprintf(L" \"%s\"", var.bstrVal);
+ break;
+
+ default:
+ wprintf(L" ??");
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print a string corresponding to a UDT kind
+//
+void PrintUdtKind(IDiaSymbol *pSymbol)
+{
+ DWORD dwKind = 0;
+
+ if (pSymbol->get_udtKind(&dwKind) == S_OK) {
+ wprintf(L"%s ", rgUdtKind[dwKind]);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print type informations is details
+//
+void PrintTypeInDetail(IDiaSymbol *pSymbol, DWORD dwIndent)
+{
+ IDiaEnumSymbols *pEnumChildren;
+ IDiaSymbol *pType;
+ IDiaSymbol *pChild;
+ DWORD dwSymTag;
+ DWORD dwSymTagType;
+ ULONG celt = 0;
+ BOOL bFlag;
+
+ if (dwIndent > MAX_TYPE_IN_DETAIL) {
+ return;
+ }
+
+ if (pSymbol->get_symTag(&dwSymTag) != S_OK) {
+ wprintf(L"ERROR - PrintTypeInDetail() get_symTag\n");
+ return;
+ }
+
+ PrintSymTag(dwSymTag);
+
+ for (DWORD i = 0;i < dwIndent; i++) {
+ putwchar(L' ');
+ }
+
+ switch (dwSymTag) {
+ case SymTagData:
+ PrintData(pSymbol, dwIndent);
+
+ if (pSymbol->get_type(&pType) == S_OK) {
+ if (pType->get_symTag(&dwSymTagType) == S_OK) {
+ if (dwSymTagType == SymTagUDT) {
+ putwchar(L'\n');
+ PrintTypeInDetail(pType, dwIndent + 2);
+ }
+ }
+ pType->Release();
+ }
+ break;
+
+ case SymTagTypedef:
+ case SymTagVTable:
+ PrintSymbolType(pSymbol);
+ break;
+
+ case SymTagEnum:
+ case SymTagUDT:
+ PrintUDT(pSymbol);
+ putwchar(L'\n');
+
+ if (SUCCEEDED(pSymbol->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren))) {
+ while (SUCCEEDED(pEnumChildren->Next(1, &pChild, &celt)) && (celt == 1)) {
+ PrintTypeInDetail(pChild, dwIndent + 2);
+
+ pChild->Release();
+ }
+
+ pEnumChildren->Release();
+ }
+ return;
+ break;
+
+ case SymTagFunction:
+ PrintFunctionType(pSymbol);
+ return;
+ break;
+
+ case SymTagPointerType:
+ PrintName(pSymbol);
+ wprintf(L" has type ");
+ PrintType(pSymbol);
+ break;
+
+ case SymTagArrayType:
+ case SymTagBaseType:
+ case SymTagFunctionArgType:
+ case SymTagUsingNamespace:
+ case SymTagCustom:
+ case SymTagFriend:
+ PrintName(pSymbol);
+ PrintSymbolType(pSymbol);
+ break;
+
+ case SymTagVTableShape:
+ case SymTagBaseClass:
+ PrintName(pSymbol);
+
+ if ((pSymbol->get_virtualBaseClass(&bFlag) == S_OK) && bFlag) {
+ IDiaSymbol *pVBTableType;
+ LONG ptrOffset;
+ DWORD dispIndex;
+
+ if ((pSymbol->get_virtualBaseDispIndex(&dispIndex) == S_OK) &&
+ (pSymbol->get_virtualBasePointerOffset(&ptrOffset) == S_OK)) {
+ wprintf(L" virtual, offset = 0x%X, pointer offset = %ld, virtual base pointer type = ", dispIndex, ptrOffset);
+
+ if (pSymbol->get_virtualBaseTableType(&pVBTableType) == S_OK) {
+ PrintType(pVBTableType);
+ pVBTableType->Release();
+ }
+
+ else {
+ wprintf(L"(unknown)");
+ }
+ }
+ }
+
+ else {
+ LONG offset;
+
+ if (pSymbol->get_offset(&offset) == S_OK) {
+ wprintf(L", offset = 0x%X", offset);
+ }
+ }
+
+ putwchar(L'\n');
+
+ if (SUCCEEDED(pSymbol->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren))) {
+ while (SUCCEEDED(pEnumChildren->Next(1, &pChild, &celt)) && (celt == 1)) {
+ PrintTypeInDetail(pChild, dwIndent + 2);
+ pChild->Release();
+ }
+
+ pEnumChildren->Release();
+ }
+ break;
+
+ case SymTagFunctionType:
+ if (pSymbol->get_type(&pType) == S_OK) {
+ PrintType(pType);
+ }
+ break;
+
+ case SymTagThunk:
+ // Happens for functions which only have S_PROCREF
+ PrintThunk(pSymbol);
+ break;
+
+ default:
+ wprintf(L"ERROR - PrintTypeInDetail() invalid SymTag\n");
+ }
+
+ putwchar(L'\n');
+}
+
+////////////////////////////////////////////////////////////
+// Print a function type
+//
+void PrintFunctionType(IDiaSymbol *pSymbol)
+{
+ DWORD dwAccess = 0;
+
+ if (pSymbol->get_access(&dwAccess) == S_OK) {
+ wprintf(L"%s ", SafeDRef(rgAccess, dwAccess));
+ }
+
+ BOOL bIsStatic = FALSE;
+
+ if ((pSymbol->get_isStatic(&bIsStatic) == S_OK) && bIsStatic) {
+ wprintf(L"static ");
+ }
+
+ IDiaSymbol *pFuncType;
+
+ if (pSymbol->get_type(&pFuncType) == S_OK) {
+ IDiaSymbol *pReturnType;
+
+ if (pFuncType->get_type(&pReturnType) == S_OK) {
+ PrintType(pReturnType);
+ putwchar(L' ');
+
+ BSTR bstrName;
+
+ if (pSymbol->get_name(&bstrName) == S_OK) {
+ wprintf(L"%s", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ IDiaEnumSymbols *pEnumChildren;
+
+ if (SUCCEEDED(pFuncType->findChildren(SymTagNull, NULL, nsNone, &pEnumChildren))) {
+ IDiaSymbol *pChild;
+ ULONG celt = 0;
+ ULONG nParam = 0;
+
+ wprintf(L"(");
+
+ while (SUCCEEDED(pEnumChildren->Next(1, &pChild, &celt)) && (celt == 1)) {
+ IDiaSymbol *pType;
+
+ if (pChild->get_type(&pType) == S_OK) {
+ if (nParam++) {
+ wprintf(L", ");
+ }
+
+ PrintType(pType);
+ pType->Release();
+ }
+
+ pChild->Release();
+ }
+
+ pEnumChildren->Release();
+
+ wprintf(L")\n");
+ }
+
+ pReturnType->Release();
+ }
+
+ pFuncType->Release();
+ }
+}
+
+////////////////////////////////////////////////////////////
+//
+void PrintSourceFile(IDiaSourceFile *pSource)
+{
+ BSTR bstrSourceName;
+
+ if (pSource->get_fileName(&bstrSourceName) == S_OK) {
+ wprintf(L"\t%s", bstrSourceName);
+
+ SysFreeString(bstrSourceName);
+ }
+
+ else {
+ wprintf(L"ERROR - PrintSourceFile() get_fileName");
+ return;
+ }
+
+ BYTE checksum[256];
+ DWORD cbChecksum = sizeof(checksum);
+
+ if (pSource->get_checksum(cbChecksum, &cbChecksum, checksum) == S_OK) {
+ wprintf(L" (");
+
+ DWORD checksumType;
+
+ if (pSource->get_checksumType(&checksumType) == S_OK) {
+ switch (checksumType) {
+ case CHKSUM_TYPE_NONE :
+ wprintf(L"None");
+ break;
+
+ case CHKSUM_TYPE_MD5 :
+ wprintf(L"MD5");
+ break;
+
+ case CHKSUM_TYPE_SHA1 :
+ wprintf(L"SHA1");
+ break;
+
+ default :
+ wprintf(L"0x%X", checksumType);
+ break;
+ }
+
+ if (cbChecksum != 0) {
+ wprintf(L": ");
+ }
+ }
+
+ for (DWORD ib = 0; ib < cbChecksum; ib++) {
+ wprintf(L"%02X", checksum[ib]);
+ }
+
+ wprintf(L")");
+ }
+}
+
+////////////////////////////////////////////////////////////
+//
+void PrintLines(IDiaSession *pSession, IDiaSymbol *pFunction)
+{
+ DWORD dwSymTag;
+
+ if ((pFunction->get_symTag(&dwSymTag) != S_OK) || (dwSymTag != SymTagFunction)) {
+ wprintf(L"ERROR - PrintLines() dwSymTag != SymTagFunction");
+ return;
+ }
+
+ BSTR bstrName;
+
+ if (pFunction->get_name(&bstrName) == S_OK) {
+ wprintf(L"\n** %s\n\n", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ ULONGLONG ulLength;
+
+ if (pFunction->get_length(&ulLength) != S_OK) {
+ wprintf(L"ERROR - PrintLines() get_length");
+ return;
+ }
+
+ DWORD dwRVA;
+ IDiaEnumLineNumbers *pLines;
+
+ if (pFunction->get_relativeVirtualAddress(&dwRVA) == S_OK) {
+ if (SUCCEEDED(pSession->findLinesByRVA(dwRVA, static_cast<DWORD>(ulLength), &pLines))) {
+ PrintLines(pLines);
+ pLines->Release();
+ }
+ }
+
+ else {
+ DWORD dwSect;
+ DWORD dwOffset;
+
+ if ((pFunction->get_addressSection(&dwSect) == S_OK) &&
+ (pFunction->get_addressOffset(&dwOffset) == S_OK)) {
+ if (SUCCEEDED(pSession->findLinesByAddr(dwSect, dwOffset, static_cast<DWORD>(ulLength), &pLines))) {
+ PrintLines(pLines);
+ pLines->Release();
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////
+//
+void PrintLines(IDiaEnumLineNumbers *pLines)
+{
+ IDiaLineNumber *pLine;
+ DWORD celt;
+ DWORD dwRVA;
+ DWORD dwSeg;
+ DWORD dwOffset;
+ DWORD dwLinenum;
+ DWORD dwSrcId;
+ DWORD dwLength;
+
+ DWORD dwSrcIdLast = (DWORD)(-1);
+
+ while (SUCCEEDED(pLines->Next(1, &pLine, &celt)) && (celt == 1)) {
+ if ((pLine->get_relativeVirtualAddress(&dwRVA) == S_OK) &&
+ (pLine->get_addressSection(&dwSeg) == S_OK) &&
+ (pLine->get_addressOffset(&dwOffset) == S_OK) &&
+ (pLine->get_lineNumber(&dwLinenum) == S_OK) &&
+ (pLine->get_sourceFileId(&dwSrcId) == S_OK) &&
+ (pLine->get_length(&dwLength) == S_OK)) {
+ wprintf(L"\tline %u at [%08X][%04X:%08X], len = 0x%X", dwLinenum, dwRVA, dwSeg, dwOffset, dwLength);
+
+ if (dwSrcId != dwSrcIdLast) {
+ IDiaSourceFile *pSource;
+
+ if (pLine->get_sourceFile(&pSource) == S_OK) {
+ PrintSourceFile(pSource);
+
+ dwSrcIdLast = dwSrcId;
+
+ pSource->Release();
+ }
+ }
+
+ pLine->Release();
+
+ putwchar(L'\n');
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print the section contribution data: name, Sec::Off, length
+void PrintSecContribs(IDiaSectionContrib *pSegment)
+{
+ DWORD dwRVA;
+ DWORD dwSect;
+ DWORD dwOffset;
+ DWORD dwLen;
+ IDiaSymbol *pCompiland;
+ BSTR bstrName;
+
+ if ((pSegment->get_relativeVirtualAddress(&dwRVA) == S_OK) &&
+ (pSegment->get_addressSection(&dwSect) == S_OK) &&
+ (pSegment->get_addressOffset(&dwOffset) == S_OK) &&
+ (pSegment->get_length(&dwLen) == S_OK) &&
+ (pSegment->get_compiland(&pCompiland) == S_OK) &&
+ (pCompiland->get_name(&bstrName) == S_OK)) {
+ wprintf(L" %08X %04X:%08X %08X %s\n", dwRVA, dwSect, dwOffset, dwLen, bstrName);
+
+ pCompiland->Release();
+
+ SysFreeString(bstrName);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print a debug stream data
+//
+void PrintStreamData(IDiaEnumDebugStreamData *pStream)
+{
+ BSTR bstrName;
+
+ if (pStream->get_name(&bstrName) != S_OK) {
+ wprintf(L"ERROR - PrintStreamData() get_name\n");
+ }
+
+ else {
+ wprintf(L"Stream: %s", bstrName);
+
+ SysFreeString(bstrName);
+ }
+
+ LONG dwElem;
+
+ if (pStream->get_Count(&dwElem) != S_OK) {
+ wprintf(L"ERROR - PrintStreamData() get_Count\n");
+ }
+
+ else {
+ wprintf(L"(%u)\n", dwElem);
+ }
+
+ DWORD cbTotal = 0;
+
+ BYTE data[1024];
+ DWORD cbData;
+ ULONG celt = 0;
+
+ while (SUCCEEDED(pStream->Next(1, sizeof(data), &cbData, (BYTE *) &data, &celt)) && (celt == 1)) {
+ DWORD i;
+
+ for (i = 0; i < cbData; i++) {
+ wprintf(L"%02X ", data[i]);
+
+ if (i && (i % 8 == 7) && (i+1 < cbData)) {
+ wprintf(L"- ");
+ }
+ }
+
+ wprintf(L"| ");
+
+ for (i = 0; i < cbData; i++) {
+ wprintf(L"%c", iswprint(data[i]) ? data[i] : '.');
+ }
+
+ putwchar(L'\n');
+
+ cbTotal += cbData;
+ }
+
+ wprintf(L"Summary :\n\tNo of Elems = %u\n", dwElem);
+ if (dwElem != 0) {
+ wprintf(L"\tSizeof(Elem) = %u\n", cbTotal / dwElem);
+ }
+ putwchar(L'\n');
+}
+
+////////////////////////////////////////////////////////////
+// Print the FPO info for a given symbol;
+//
+void PrintFrameData(IDiaFrameData *pFrameData)
+{
+ DWORD dwSect;
+ DWORD dwOffset;
+ DWORD cbBlock;
+ DWORD cbLocals; // Number of bytes reserved for the function locals
+ DWORD cbParams; // Number of bytes reserved for the function arguments
+ DWORD cbMaxStack;
+ DWORD cbProlog;
+ DWORD cbSavedRegs;
+ BOOL bSEH;
+ BOOL bEH;
+ BOOL bStart;
+
+ if ((pFrameData->get_addressSection(&dwSect) == S_OK) &&
+ (pFrameData->get_addressOffset(&dwOffset) == S_OK) &&
+ (pFrameData->get_lengthBlock(&cbBlock) == S_OK) &&
+ (pFrameData->get_lengthLocals(&cbLocals) == S_OK) &&
+ (pFrameData->get_lengthParams(&cbParams) == S_OK) &&
+ (pFrameData->get_maxStack(&cbMaxStack) == S_OK) &&
+ (pFrameData->get_lengthProlog(&cbProlog) == S_OK) &&
+ (pFrameData->get_lengthSavedRegisters(&cbSavedRegs) == S_OK) &&
+ (pFrameData->get_systemExceptionHandling(&bSEH) == S_OK) &&
+ (pFrameData->get_cplusplusExceptionHandling(&bEH) == S_OK) &&
+ (pFrameData->get_functionStart(&bStart) == S_OK)) {
+ wprintf(L"%04X:%08X %8X %8X %8X %8X %8X %8X %c %c %c",
+ dwSect, dwOffset, cbBlock, cbLocals, cbParams, cbMaxStack, cbProlog, cbSavedRegs,
+ bSEH ? L'Y' : L'N',
+ bEH ? L'Y' : L'N',
+ bStart ? L'Y' : L'N');
+
+ BSTR bstrProgram;
+
+ if (pFrameData->get_program(&bstrProgram) == S_OK) {
+ wprintf(L" %s", bstrProgram);
+
+ SysFreeString(bstrProgram);
+ }
+
+ putwchar(L'\n');
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Print all the valid properties associated to a symbol
+//
+void PrintPropertyStorage(IDiaPropertyStorage *pPropertyStorage)
+{
+ IEnumSTATPROPSTG *pEnumProps;
+
+ if (SUCCEEDED(pPropertyStorage->Enum(&pEnumProps))) {
+ STATPROPSTG prop;
+ DWORD celt = 1;
+
+ while (SUCCEEDED(pEnumProps->Next(celt, &prop, &celt)) && (celt == 1)) {
+ PROPSPEC pspec = { PRSPEC_PROPID, prop.propid };
+ PROPVARIANT vt = { VT_EMPTY };
+
+ if (SUCCEEDED(pPropertyStorage->ReadMultiple(1, &pspec, &vt))) {
+ switch (vt.vt) {
+ case VT_BOOL:
+ wprintf(L"%32s:\t %s\n", prop.lpwstrName, vt.bVal ? L"true" : L"false");
+ break;
+
+ case VT_I2:
+ wprintf(L"%32s:\t %d\n", prop.lpwstrName, vt.iVal);
+ break;
+
+ case VT_UI2:
+ wprintf(L"%32s:\t %u\n", prop.lpwstrName, vt.uiVal);
+ break;
+
+ case VT_I4:
+ wprintf(L"%32s:\t %d\n", prop.lpwstrName, vt.intVal);
+ break;
+
+ case VT_UI4:
+ wprintf(L"%32s:\t 0x%0X\n", prop.lpwstrName, vt.uintVal);
+ break;
+
+ case VT_UI8:
+ wprintf(L"%32s:\t 0x%X\n", prop.lpwstrName, vt.uhVal.QuadPart);
+ break;
+
+ case VT_BSTR:
+ wprintf(L"%32s:\t %s\n", prop.lpwstrName, vt.bstrVal);
+ break;
+
+ case VT_UNKNOWN:
+ wprintf(L"%32s:\t %p\n", prop.lpwstrName, vt.punkVal);
+ break;
+
+ case VT_SAFEARRAY:
+ break;
+ }
+
+ VariantClear((VARIANTARG *) &vt);
+ }
+
+ SysFreeString( prop.lpwstrName );
+ }
+
+ pEnumProps->Release();
+ }
+}
diff --git a/src/ToolBox/PdbTypeMatch/PrintSymbol.h b/src/ToolBox/PdbTypeMatch/PrintSymbol.h
new file mode 100644
index 0000000000..5a39460f3f
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/PrintSymbol.h
@@ -0,0 +1,66 @@
+// 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 <string>
+
+inline int myDebugBreak( int ){
+ DebugBreak();
+ return 0;
+}
+#define MAXELEMS(x) (sizeof(x)/sizeof(x[0]))
+#define SafeDRef(a, i) ((i < MAXELEMS(a)) ? a[i] : a[myDebugBreak(i)])
+
+#define MAX_TYPE_IN_DETAIL 5
+#define MAX_RVA_LINES_BYTES_RANGE 0x100
+
+extern const wchar_t * const rgBaseType[];
+extern const wchar_t * const rgTags[];
+extern const wchar_t * const rgFloatPackageStrings[];
+extern const wchar_t * const rgProcessorStrings[];
+extern const wchar_t * const rgDataKind[];
+extern const wchar_t * const rgUdtKind[];
+extern const wchar_t * const rgAccess[];
+extern const wchar_t * const rgCallingConvention[];
+extern const wchar_t * const rgLanguage[];
+extern const wchar_t * const rgLocationTypeString[];
+
+void PrintPublicSymbol( IDiaSymbol* );
+void PrintGlobalSymbol( IDiaSymbol* );
+void PrintSymbol( IDiaSymbol* , DWORD );
+void GetSymbolName(std::wstring& symbolName, IDiaSymbol *pSymbol);
+void PrintSymTag( DWORD );
+void PrintName( IDiaSymbol* );
+void PrintUndName( IDiaSymbol* );
+void PrintThunk( IDiaSymbol* );
+void PrintCompilandDetails( IDiaSymbol* );
+void PrintCompilandEnv( IDiaSymbol* );
+void PrintLocation( IDiaSymbol* );
+void PrintConst( IDiaSymbol* );
+void PrintUDT( IDiaSymbol* );
+void PrintSymbolType( IDiaSymbol* );
+void PrintType( IDiaSymbol* );
+void PrintBound( IDiaSymbol* );
+void PrintData( IDiaSymbol* , DWORD );
+void PrintVariant( VARIANT );
+void PrintUdtKind( IDiaSymbol* );
+void PrintTypeInDetail( IDiaSymbol* , DWORD );
+void PrintFunctionType( IDiaSymbol* );
+void PrintSourceFile( IDiaSourceFile* );
+void PrintLines( IDiaSession* , IDiaSymbol* );
+void PrintLines( IDiaEnumLineNumbers* );
+void PrintSource( IDiaSourceFile* );
+void PrintSecContribs( IDiaSectionContrib* );
+void PrintStreamData( IDiaEnumDebugStreamData* );
+void PrintFrameData( IDiaFrameData* );
+
+void PrintPropertyStorage( IDiaPropertyStorage* );
+
+template<class T> void PrintGeneric( T t ){
+ IDiaPropertyStorage* pPropertyStorage;
+
+ if(t->QueryInterface( __uuidof(IDiaPropertyStorage), (void **)&pPropertyStorage ) == S_OK){
+ PrintPropertyStorage(pPropertyStorage);
+ pPropertyStorage->Release();
+ }
+}
diff --git a/src/ToolBox/PdbTypeMatch/callback.h b/src/ToolBox/PdbTypeMatch/callback.h
new file mode 100644
index 0000000000..34ea3d80c0
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/callback.h
@@ -0,0 +1,96 @@
+// 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 "dia2.h"
+
+#pragma warning ( disable : 4100)
+
+class CCallback : public IDiaLoadCallback2{
+ int m_nRefCount;
+public:
+ CCallback() { m_nRefCount = 0; }
+
+ //IUnknown
+ ULONG STDMETHODCALLTYPE AddRef() {
+ m_nRefCount++;
+ return m_nRefCount;
+ }
+ ULONG STDMETHODCALLTYPE Release() {
+ ULONG newRefCount = --m_nRefCount;
+ if ( newRefCount == 0 )
+ delete this;
+ return newRefCount;
+ }
+ HRESULT STDMETHODCALLTYPE QueryInterface( REFIID rid, void **ppUnk ) {
+ if ( ppUnk == NULL ) {
+ return E_POINTER;
+ }
+ if (rid == __uuidof( IDiaLoadCallback2 ) )
+ *ppUnk = (IDiaLoadCallback2 *)this;
+ else if (rid == __uuidof( IDiaLoadCallback ) )
+ *ppUnk = (IDiaLoadCallback *)this;
+ else if (rid == __uuidof( IUnknown ) )
+ *ppUnk = (IUnknown *)this;
+ else
+ *ppUnk = NULL;
+ if ( *ppUnk != NULL ) {
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ HRESULT STDMETHODCALLTYPE NotifyDebugDir(
+ BOOL fExecutable,
+ DWORD cbData,
+ BYTE data[]) // really a const struct _IMAGE_DEBUG_DIRECTORY *
+ {
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE NotifyOpenDBG(
+ LPCOLESTR dbgPath,
+ HRESULT resultCode)
+ {
+ // wprintf(L"opening %s...\n", dbgPath);
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE NotifyOpenPDB(
+ LPCOLESTR pdbPath,
+ HRESULT resultCode)
+ {
+ // wprintf(L"opening %s...\n", pdbPath);
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE RestrictRegistryAccess()
+ {
+ // return hr != S_OK to prevent querying the registry for symbol search paths
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE RestrictSymbolServerAccess()
+ {
+ // return hr != S_OK to prevent accessing a symbol server
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE RestrictOriginalPathAccess()
+ {
+ // return hr != S_OK to prevent querying the registry for symbol search paths
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE RestrictReferencePathAccess()
+ {
+ // return hr != S_OK to prevent accessing a symbol server
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE RestrictDBGAccess()
+ {
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE RestrictSystemRootAccess()
+ {
+ return S_OK;
+ }
+};
+
+#pragma warning ( default : 4100 )
diff --git a/src/ToolBox/PdbTypeMatch/include/.gitmirror b/src/ToolBox/PdbTypeMatch/include/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/include/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/PdbTypeMatch/include/cvconst.h b/src/ToolBox/PdbTypeMatch/include/cvconst.h
new file mode 100644
index 0000000000..6356197c81
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/include/cvconst.h
@@ -0,0 +1,3181 @@
+// 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.
+// cvconst.h - codeview constant definitions
+
+#ifndef _CVCONST_H_
+#define _CVCONST_H_
+
+
+
+// Enumeration for function call type
+
+
+typedef enum CV_call_e {
+ CV_CALL_NEAR_C = 0x00, // near right to left push, caller pops stack
+ CV_CALL_FAR_C = 0x01, // far right to left push, caller pops stack
+ CV_CALL_NEAR_PASCAL = 0x02, // near left to right push, callee pops stack
+ CV_CALL_FAR_PASCAL = 0x03, // far left to right push, callee pops stack
+ CV_CALL_NEAR_FAST = 0x04, // near left to right push with regs, callee pops stack
+ CV_CALL_FAR_FAST = 0x05, // far left to right push with regs, callee pops stack
+ CV_CALL_SKIPPED = 0x06, // skipped (unused) call index
+ CV_CALL_NEAR_STD = 0x07, // near standard call
+ CV_CALL_FAR_STD = 0x08, // far standard call
+ CV_CALL_NEAR_SYS = 0x09, // near sys call
+ CV_CALL_FAR_SYS = 0x0a, // far sys call
+ CV_CALL_THISCALL = 0x0b, // this call (this passed in register)
+ CV_CALL_MIPSCALL = 0x0c, // Mips call
+ CV_CALL_GENERIC = 0x0d, // Generic call sequence
+ CV_CALL_ALPHACALL = 0x0e, // Alpha call
+ CV_CALL_PPCCALL = 0x0f, // PPC call
+ CV_CALL_SHCALL = 0x10, // Hitachi SuperH call
+ CV_CALL_ARMCALL = 0x11, // ARM call
+ CV_CALL_AM33CALL = 0x12, // AM33 call
+ CV_CALL_TRICALL = 0x13, // TriCore Call
+ CV_CALL_SH5CALL = 0x14, // Hitachi SuperH-5 call
+ CV_CALL_M32RCALL = 0x15, // M32R Call
+ CV_CALL_CLRCALL = 0x16, // clr call
+ CV_CALL_RESERVED = 0x17 // first unused call enumeration
+
+ // Do NOT add any more machine specific conventions. This is to be used for
+ // calling conventions in the source only (e.g. __cdecl, __stdcall).
+} CV_call_e;
+
+
+// Values for the access protection of class attributes
+
+
+typedef enum CV_access_e {
+ CV_private = 1,
+ CV_protected = 2,
+ CV_public = 3
+} CV_access_e;
+
+typedef enum THUNK_ORDINAL {
+ THUNK_ORDINAL_NOTYPE, // standard thunk
+ THUNK_ORDINAL_ADJUSTOR, // "this" adjustor thunk
+ THUNK_ORDINAL_VCALL, // virtual call thunk
+ THUNK_ORDINAL_PCODE, // pcode thunk
+ THUNK_ORDINAL_LOAD, // thunk which loads the address to jump to
+ // via unknown means...
+
+ // trampoline thunk ordinals - only for use in Trampoline thunk symbols
+ THUNK_ORDINAL_TRAMP_INCREMENTAL,
+ THUNK_ORDINAL_TRAMP_BRANCHISLAND,
+
+} THUNK_ORDINAL;
+
+
+enum CV_SourceChksum_t {
+ CHKSUM_TYPE_NONE = 0, // indicates no checksum is available
+ CHKSUM_TYPE_MD5,
+ CHKSUM_TYPE_SHA1
+};
+
+//
+// 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,
+ SymTagCallSite,
+ SymTagMax
+};
+
+enum LocationType
+{
+ LocIsNull,
+ LocIsStatic,
+ LocIsTLS,
+ LocIsRegRel,
+ LocIsThisRel,
+ LocIsEnregistered,
+ LocIsBitField,
+ LocIsSlot,
+ LocIsIlRel,
+ LocInMetaData,
+ LocIsConstant,
+ LocTypeMax
+};
+
+enum DataKind
+{
+ DataIsUnknown,
+ DataIsLocal,
+ DataIsStaticLocal,
+ DataIsParam,
+ DataIsObjectPtr,
+ DataIsFileStatic,
+ DataIsGlobal,
+ DataIsMember,
+ DataIsStaticMember,
+ DataIsConstant
+};
+
+enum UdtKind
+{
+ UdtStruct,
+ UdtClass,
+ UdtUnion
+};
+
+enum BasicType
+{
+ btNoType = 0,
+ btVoid = 1,
+ btChar = 2,
+ btWChar = 3,
+ btInt = 6,
+ btUInt = 7,
+ btFloat = 8,
+ btBCD = 9,
+ btBool = 10,
+ btLong = 13,
+ btULong = 14,
+ btCurrency = 25,
+ btDate = 26,
+ btVariant = 27,
+ btComplex = 28,
+ btBit = 29,
+ btBSTR = 30,
+ btHresult = 31
+};
+
+
+// enum describing the compile flag source language
+
+
+typedef enum CV_CFL_LANG {
+ CV_CFL_C = 0x00,
+ CV_CFL_CXX = 0x01,
+ CV_CFL_FORTRAN = 0x02,
+ CV_CFL_MASM = 0x03,
+ CV_CFL_PASCAL = 0x04,
+ CV_CFL_BASIC = 0x05,
+ CV_CFL_COBOL = 0x06,
+ CV_CFL_LINK = 0x07,
+ CV_CFL_CVTRES = 0x08,
+ CV_CFL_CVTPGD = 0x09,
+ CV_CFL_CSHARP = 0x0A, // C#
+ CV_CFL_VB = 0x0B, // Visual Basic
+ CV_CFL_ILASM = 0x0C, // IL (as in CLR) ASM
+ CV_CFL_JAVA = 0x0D,
+ CV_CFL_JSCRIPT = 0x0E,
+ CV_CFL_MSIL = 0x0F, // Unknown MSIL (LTCG of .NETMODULE)
+} CV_CFL_LANG;
+
+
+// enum describing target processor
+
+
+typedef enum CV_CPU_TYPE_e {
+ CV_CFL_8080 = 0x00,
+ CV_CFL_8086 = 0x01,
+ CV_CFL_80286 = 0x02,
+ CV_CFL_80386 = 0x03,
+ CV_CFL_80486 = 0x04,
+ CV_CFL_PENTIUM = 0x05,
+ CV_CFL_PENTIUMII = 0x06,
+ CV_CFL_PENTIUMPRO = CV_CFL_PENTIUMII,
+ CV_CFL_PENTIUMIII = 0x07,
+ CV_CFL_MIPS = 0x10,
+ CV_CFL_MIPSR4000 = CV_CFL_MIPS, // don't break current code
+ CV_CFL_MIPS16 = 0x11,
+ CV_CFL_MIPS32 = 0x12,
+ CV_CFL_MIPS64 = 0x13,
+ CV_CFL_MIPSI = 0x14,
+ CV_CFL_MIPSII = 0x15,
+ CV_CFL_MIPSIII = 0x16,
+ CV_CFL_MIPSIV = 0x17,
+ CV_CFL_MIPSV = 0x18,
+ CV_CFL_M68000 = 0x20,
+ CV_CFL_M68010 = 0x21,
+ CV_CFL_M68020 = 0x22,
+ CV_CFL_M68030 = 0x23,
+ CV_CFL_M68040 = 0x24,
+ CV_CFL_ALPHA = 0x30,
+ CV_CFL_ALPHA_21064 = 0x30,
+ CV_CFL_ALPHA_21164 = 0x31,
+ CV_CFL_ALPHA_21164A = 0x32,
+ CV_CFL_ALPHA_21264 = 0x33,
+ CV_CFL_ALPHA_21364 = 0x34,
+ CV_CFL_PPC601 = 0x40,
+ CV_CFL_PPC603 = 0x41,
+ CV_CFL_PPC604 = 0x42,
+ CV_CFL_PPC620 = 0x43,
+ CV_CFL_PPCFP = 0x44,
+ CV_CFL_PPCBE = 0x45,
+ CV_CFL_SH3 = 0x50,
+ CV_CFL_SH3E = 0x51,
+ CV_CFL_SH3DSP = 0x52,
+ CV_CFL_SH4 = 0x53,
+ CV_CFL_SHMEDIA = 0x54,
+ CV_CFL_ARM3 = 0x60,
+ CV_CFL_ARM4 = 0x61,
+ CV_CFL_ARM4T = 0x62,
+ CV_CFL_ARM5 = 0x63,
+ CV_CFL_ARM5T = 0x64,
+ CV_CFL_ARM6 = 0x65,
+ CV_CFL_ARM_XMAC = 0x66,
+ CV_CFL_ARM_WMMX = 0x67,
+ CV_CFL_ARM7 = 0x68,
+ CV_CFL_OMNI = 0x70,
+ CV_CFL_IA64 = 0x80,
+ CV_CFL_IA64_1 = 0x80,
+ CV_CFL_IA64_2 = 0x81,
+ CV_CFL_CEE = 0x90,
+ CV_CFL_AM33 = 0xA0,
+ CV_CFL_M32R = 0xB0,
+ CV_CFL_TRICORE = 0xC0,
+ CV_CFL_X64 = 0xD0,
+ CV_CFL_AMD64 = CV_CFL_X64,
+ CV_CFL_EBC = 0xE0,
+ CV_CFL_THUMB = 0xF0,
+} CV_CPU_TYPE_e;
+
+typedef enum CV_HREG_e {
+ // Register subset shared by all processor types,
+ // must not overlap with any of the ranges below, hence the high values
+
+ CV_ALLREG_ERR = 30000,
+ CV_ALLREG_TEB = 30001,
+ CV_ALLREG_TIMER = 30002,
+ CV_ALLREG_EFAD1 = 30003,
+ CV_ALLREG_EFAD2 = 30004,
+ CV_ALLREG_EFAD3 = 30005,
+ CV_ALLREG_VFRAME= 30006,
+ CV_ALLREG_HANDLE= 30007,
+ CV_ALLREG_PARAMS= 30008,
+ CV_ALLREG_LOCALS= 30009,
+ CV_ALLREG_TID = 30010,
+ CV_ALLREG_ENV = 30011,
+ CV_ALLREG_CMDLN = 30012,
+
+
+ // Register set for the Intel 80x86 and ix86 processor series
+ // (plus PCODE registers)
+
+ CV_REG_NONE = 0,
+ CV_REG_AL = 1,
+ CV_REG_CL = 2,
+ CV_REG_DL = 3,
+ CV_REG_BL = 4,
+ CV_REG_AH = 5,
+ CV_REG_CH = 6,
+ CV_REG_DH = 7,
+ CV_REG_BH = 8,
+ CV_REG_AX = 9,
+ CV_REG_CX = 10,
+ CV_REG_DX = 11,
+ CV_REG_BX = 12,
+ CV_REG_SP = 13,
+ CV_REG_BP = 14,
+ CV_REG_SI = 15,
+ CV_REG_DI = 16,
+ CV_REG_EAX = 17,
+ CV_REG_ECX = 18,
+ CV_REG_EDX = 19,
+ CV_REG_EBX = 20,
+ CV_REG_ESP = 21,
+ CV_REG_EBP = 22,
+ CV_REG_ESI = 23,
+ CV_REG_EDI = 24,
+ CV_REG_ES = 25,
+ CV_REG_CS = 26,
+ CV_REG_SS = 27,
+ CV_REG_DS = 28,
+ CV_REG_FS = 29,
+ CV_REG_GS = 30,
+ CV_REG_IP = 31,
+ CV_REG_FLAGS = 32,
+ CV_REG_EIP = 33,
+ CV_REG_EFLAGS = 34,
+ CV_REG_TEMP = 40, // PCODE Temp
+ CV_REG_TEMPH = 41, // PCODE TempH
+ CV_REG_QUOTE = 42, // PCODE Quote
+ CV_REG_PCDR3 = 43, // PCODE reserved
+ CV_REG_PCDR4 = 44, // PCODE reserved
+ CV_REG_PCDR5 = 45, // PCODE reserved
+ CV_REG_PCDR6 = 46, // PCODE reserved
+ CV_REG_PCDR7 = 47, // PCODE reserved
+ CV_REG_CR0 = 80, // CR0 -- control registers
+ CV_REG_CR1 = 81,
+ CV_REG_CR2 = 82,
+ CV_REG_CR3 = 83,
+ CV_REG_CR4 = 84, // Pentium
+ CV_REG_DR0 = 90, // Debug register
+ CV_REG_DR1 = 91,
+ CV_REG_DR2 = 92,
+ CV_REG_DR3 = 93,
+ CV_REG_DR4 = 94,
+ CV_REG_DR5 = 95,
+ CV_REG_DR6 = 96,
+ CV_REG_DR7 = 97,
+ CV_REG_GDTR = 110,
+ CV_REG_GDTL = 111,
+ CV_REG_IDTR = 112,
+ CV_REG_IDTL = 113,
+ CV_REG_LDTR = 114,
+ CV_REG_TR = 115,
+
+ CV_REG_PSEUDO1 = 116,
+ CV_REG_PSEUDO2 = 117,
+ CV_REG_PSEUDO3 = 118,
+ CV_REG_PSEUDO4 = 119,
+ CV_REG_PSEUDO5 = 120,
+ CV_REG_PSEUDO6 = 121,
+ CV_REG_PSEUDO7 = 122,
+ CV_REG_PSEUDO8 = 123,
+ CV_REG_PSEUDO9 = 124,
+
+ CV_REG_ST0 = 128,
+ CV_REG_ST1 = 129,
+ CV_REG_ST2 = 130,
+ CV_REG_ST3 = 131,
+ CV_REG_ST4 = 132,
+ CV_REG_ST5 = 133,
+ CV_REG_ST6 = 134,
+ CV_REG_ST7 = 135,
+ CV_REG_CTRL = 136,
+ CV_REG_STAT = 137,
+ CV_REG_TAG = 138,
+ CV_REG_FPIP = 139,
+ CV_REG_FPCS = 140,
+ CV_REG_FPDO = 141,
+ CV_REG_FPDS = 142,
+ CV_REG_ISEM = 143,
+ CV_REG_FPEIP = 144,
+ CV_REG_FPEDO = 145,
+
+ CV_REG_MM0 = 146,
+ CV_REG_MM1 = 147,
+ CV_REG_MM2 = 148,
+ CV_REG_MM3 = 149,
+ CV_REG_MM4 = 150,
+ CV_REG_MM5 = 151,
+ CV_REG_MM6 = 152,
+ CV_REG_MM7 = 153,
+
+ CV_REG_XMM0 = 154, // KATMAI registers
+ CV_REG_XMM1 = 155,
+ CV_REG_XMM2 = 156,
+ CV_REG_XMM3 = 157,
+ CV_REG_XMM4 = 158,
+ CV_REG_XMM5 = 159,
+ CV_REG_XMM6 = 160,
+ CV_REG_XMM7 = 161,
+
+ CV_REG_XMM00 = 162, // KATMAI sub-registers
+ CV_REG_XMM01 = 163,
+ CV_REG_XMM02 = 164,
+ CV_REG_XMM03 = 165,
+ CV_REG_XMM10 = 166,
+ CV_REG_XMM11 = 167,
+ CV_REG_XMM12 = 168,
+ CV_REG_XMM13 = 169,
+ CV_REG_XMM20 = 170,
+ CV_REG_XMM21 = 171,
+ CV_REG_XMM22 = 172,
+ CV_REG_XMM23 = 173,
+ CV_REG_XMM30 = 174,
+ CV_REG_XMM31 = 175,
+ CV_REG_XMM32 = 176,
+ CV_REG_XMM33 = 177,
+ CV_REG_XMM40 = 178,
+ CV_REG_XMM41 = 179,
+ CV_REG_XMM42 = 180,
+ CV_REG_XMM43 = 181,
+ CV_REG_XMM50 = 182,
+ CV_REG_XMM51 = 183,
+ CV_REG_XMM52 = 184,
+ CV_REG_XMM53 = 185,
+ CV_REG_XMM60 = 186,
+ CV_REG_XMM61 = 187,
+ CV_REG_XMM62 = 188,
+ CV_REG_XMM63 = 189,
+ CV_REG_XMM70 = 190,
+ CV_REG_XMM71 = 191,
+ CV_REG_XMM72 = 192,
+ CV_REG_XMM73 = 193,
+
+ CV_REG_XMM0L = 194,
+ CV_REG_XMM1L = 195,
+ CV_REG_XMM2L = 196,
+ CV_REG_XMM3L = 197,
+ CV_REG_XMM4L = 198,
+ CV_REG_XMM5L = 199,
+ CV_REG_XMM6L = 200,
+ CV_REG_XMM7L = 201,
+
+ CV_REG_XMM0H = 202,
+ CV_REG_XMM1H = 203,
+ CV_REG_XMM2H = 204,
+ CV_REG_XMM3H = 205,
+ CV_REG_XMM4H = 206,
+ CV_REG_XMM5H = 207,
+ CV_REG_XMM6H = 208,
+ CV_REG_XMM7H = 209,
+
+ CV_REG_MXCSR = 211, // XMM status register
+
+ CV_REG_EDXEAX = 212, // EDX:EAX pair
+
+ CV_REG_EMM0L = 220, // XMM sub-registers (WNI integer)
+ CV_REG_EMM1L = 221,
+ CV_REG_EMM2L = 222,
+ CV_REG_EMM3L = 223,
+ CV_REG_EMM4L = 224,
+ CV_REG_EMM5L = 225,
+ CV_REG_EMM6L = 226,
+ CV_REG_EMM7L = 227,
+
+ CV_REG_EMM0H = 228,
+ CV_REG_EMM1H = 229,
+ CV_REG_EMM2H = 230,
+ CV_REG_EMM3H = 231,
+ CV_REG_EMM4H = 232,
+ CV_REG_EMM5H = 233,
+ CV_REG_EMM6H = 234,
+ CV_REG_EMM7H = 235,
+
+ // do not change the order of these regs, first one must be even too
+ CV_REG_MM00 = 236,
+ CV_REG_MM01 = 237,
+ CV_REG_MM10 = 238,
+ CV_REG_MM11 = 239,
+ CV_REG_MM20 = 240,
+ CV_REG_MM21 = 241,
+ CV_REG_MM30 = 242,
+ CV_REG_MM31 = 243,
+ CV_REG_MM40 = 244,
+ CV_REG_MM41 = 245,
+ CV_REG_MM50 = 246,
+ CV_REG_MM51 = 247,
+ CV_REG_MM60 = 248,
+ CV_REG_MM61 = 249,
+ CV_REG_MM70 = 250,
+ CV_REG_MM71 = 251,
+
+ CV_REG_YMM0 = 252, // AVX registers
+ CV_REG_YMM1 = 253,
+ CV_REG_YMM2 = 254,
+ CV_REG_YMM3 = 255,
+ CV_REG_YMM4 = 256,
+ CV_REG_YMM5 = 257,
+ CV_REG_YMM6 = 258,
+ CV_REG_YMM7 = 259,
+
+ CV_REG_YMM0H = 260,
+ CV_REG_YMM1H = 261,
+ CV_REG_YMM2H = 262,
+ CV_REG_YMM3H = 263,
+ CV_REG_YMM4H = 264,
+ CV_REG_YMM5H = 265,
+ CV_REG_YMM6H = 266,
+ CV_REG_YMM7H = 267,
+
+ CV_REG_YMM0I0 = 268, // AVX integer registers
+ CV_REG_YMM0I1 = 269,
+ CV_REG_YMM0I2 = 270,
+ CV_REG_YMM0I3 = 271,
+ CV_REG_YMM1I0 = 272,
+ CV_REG_YMM1I1 = 273,
+ CV_REG_YMM1I2 = 274,
+ CV_REG_YMM1I3 = 275,
+ CV_REG_YMM2I0 = 276,
+ CV_REG_YMM2I1 = 277,
+ CV_REG_YMM2I2 = 278,
+ CV_REG_YMM2I3 = 279,
+ CV_REG_YMM3I0 = 280,
+ CV_REG_YMM3I1 = 281,
+ CV_REG_YMM3I2 = 282,
+ CV_REG_YMM3I3 = 283,
+ CV_REG_YMM4I0 = 284,
+ CV_REG_YMM4I1 = 285,
+ CV_REG_YMM4I2 = 286,
+ CV_REG_YMM4I3 = 287,
+ CV_REG_YMM5I0 = 288,
+ CV_REG_YMM5I1 = 289,
+ CV_REG_YMM5I2 = 290,
+ CV_REG_YMM5I3 = 291,
+ CV_REG_YMM6I0 = 292,
+ CV_REG_YMM6I1 = 293,
+ CV_REG_YMM6I2 = 294,
+ CV_REG_YMM6I3 = 295,
+ CV_REG_YMM7I0 = 296,
+ CV_REG_YMM7I1 = 297,
+ CV_REG_YMM7I2 = 298,
+ CV_REG_YMM7I3 = 299,
+
+ CV_REG_YMM0F0 = 300, // AVX floating-point single precise registers
+ CV_REG_YMM0F1 = 301,
+ CV_REG_YMM0F2 = 302,
+ CV_REG_YMM0F3 = 303,
+ CV_REG_YMM0F4 = 304,
+ CV_REG_YMM0F5 = 305,
+ CV_REG_YMM0F6 = 306,
+ CV_REG_YMM0F7 = 307,
+ CV_REG_YMM1F0 = 308,
+ CV_REG_YMM1F1 = 309,
+ CV_REG_YMM1F2 = 310,
+ CV_REG_YMM1F3 = 311,
+ CV_REG_YMM1F4 = 312,
+ CV_REG_YMM1F5 = 313,
+ CV_REG_YMM1F6 = 314,
+ CV_REG_YMM1F7 = 315,
+ CV_REG_YMM2F0 = 316,
+ CV_REG_YMM2F1 = 317,
+ CV_REG_YMM2F2 = 318,
+ CV_REG_YMM2F3 = 319,
+ CV_REG_YMM2F4 = 320,
+ CV_REG_YMM2F5 = 321,
+ CV_REG_YMM2F6 = 322,
+ CV_REG_YMM2F7 = 323,
+ CV_REG_YMM3F0 = 324,
+ CV_REG_YMM3F1 = 325,
+ CV_REG_YMM3F2 = 326,
+ CV_REG_YMM3F3 = 327,
+ CV_REG_YMM3F4 = 328,
+ CV_REG_YMM3F5 = 329,
+ CV_REG_YMM3F6 = 330,
+ CV_REG_YMM3F7 = 331,
+ CV_REG_YMM4F0 = 332,
+ CV_REG_YMM4F1 = 333,
+ CV_REG_YMM4F2 = 334,
+ CV_REG_YMM4F3 = 335,
+ CV_REG_YMM4F4 = 336,
+ CV_REG_YMM4F5 = 337,
+ CV_REG_YMM4F6 = 338,
+ CV_REG_YMM4F7 = 339,
+ CV_REG_YMM5F0 = 340,
+ CV_REG_YMM5F1 = 341,
+ CV_REG_YMM5F2 = 342,
+ CV_REG_YMM5F3 = 343,
+ CV_REG_YMM5F4 = 344,
+ CV_REG_YMM5F5 = 345,
+ CV_REG_YMM5F6 = 346,
+ CV_REG_YMM5F7 = 347,
+ CV_REG_YMM6F0 = 348,
+ CV_REG_YMM6F1 = 349,
+ CV_REG_YMM6F2 = 350,
+ CV_REG_YMM6F3 = 351,
+ CV_REG_YMM6F4 = 352,
+ CV_REG_YMM6F5 = 353,
+ CV_REG_YMM6F6 = 354,
+ CV_REG_YMM6F7 = 355,
+ CV_REG_YMM7F0 = 356,
+ CV_REG_YMM7F1 = 357,
+ CV_REG_YMM7F2 = 358,
+ CV_REG_YMM7F3 = 359,
+ CV_REG_YMM7F4 = 360,
+ CV_REG_YMM7F5 = 361,
+ CV_REG_YMM7F6 = 362,
+ CV_REG_YMM7F7 = 363,
+
+ CV_REG_YMM0D0 = 364, // AVX floating-point double precise registers
+ CV_REG_YMM0D1 = 365,
+ CV_REG_YMM0D2 = 366,
+ CV_REG_YMM0D3 = 367,
+ CV_REG_YMM1D0 = 368,
+ CV_REG_YMM1D1 = 369,
+ CV_REG_YMM1D2 = 370,
+ CV_REG_YMM1D3 = 371,
+ CV_REG_YMM2D0 = 372,
+ CV_REG_YMM2D1 = 373,
+ CV_REG_YMM2D2 = 374,
+ CV_REG_YMM2D3 = 375,
+ CV_REG_YMM3D0 = 376,
+ CV_REG_YMM3D1 = 377,
+ CV_REG_YMM3D2 = 378,
+ CV_REG_YMM3D3 = 379,
+ CV_REG_YMM4D0 = 380,
+ CV_REG_YMM4D1 = 381,
+ CV_REG_YMM4D2 = 382,
+ CV_REG_YMM4D3 = 383,
+ CV_REG_YMM5D0 = 384,
+ CV_REG_YMM5D1 = 385,
+ CV_REG_YMM5D2 = 386,
+ CV_REG_YMM5D3 = 387,
+ CV_REG_YMM6D0 = 388,
+ CV_REG_YMM6D1 = 389,
+ CV_REG_YMM6D2 = 390,
+ CV_REG_YMM6D3 = 391,
+ CV_REG_YMM7D0 = 392,
+ CV_REG_YMM7D1 = 393,
+ CV_REG_YMM7D2 = 394,
+ CV_REG_YMM7D3 = 395,
+
+ // registers for the 68K processors
+
+ CV_R68_D0 = 0,
+ CV_R68_D1 = 1,
+ CV_R68_D2 = 2,
+ CV_R68_D3 = 3,
+ CV_R68_D4 = 4,
+ CV_R68_D5 = 5,
+ CV_R68_D6 = 6,
+ CV_R68_D7 = 7,
+ CV_R68_A0 = 8,
+ CV_R68_A1 = 9,
+ CV_R68_A2 = 10,
+ CV_R68_A3 = 11,
+ CV_R68_A4 = 12,
+ CV_R68_A5 = 13,
+ CV_R68_A6 = 14,
+ CV_R68_A7 = 15,
+ CV_R68_CCR = 16,
+ CV_R68_SR = 17,
+ CV_R68_USP = 18,
+ CV_R68_MSP = 19,
+ CV_R68_SFC = 20,
+ CV_R68_DFC = 21,
+ CV_R68_CACR = 22,
+ CV_R68_VBR = 23,
+ CV_R68_CAAR = 24,
+ CV_R68_ISP = 25,
+ CV_R68_PC = 26,
+ //reserved 27
+ CV_R68_FPCR = 28,
+ CV_R68_FPSR = 29,
+ CV_R68_FPIAR = 30,
+ //reserved 31
+ CV_R68_FP0 = 32,
+ CV_R68_FP1 = 33,
+ CV_R68_FP2 = 34,
+ CV_R68_FP3 = 35,
+ CV_R68_FP4 = 36,
+ CV_R68_FP5 = 37,
+ CV_R68_FP6 = 38,
+ CV_R68_FP7 = 39,
+ //reserved 40
+ CV_R68_MMUSR030 = 41,
+ CV_R68_MMUSR = 42,
+ CV_R68_URP = 43,
+ CV_R68_DTT0 = 44,
+ CV_R68_DTT1 = 45,
+ CV_R68_ITT0 = 46,
+ CV_R68_ITT1 = 47,
+ //reserved 50
+ CV_R68_PSR = 51,
+ CV_R68_PCSR = 52,
+ CV_R68_VAL = 53,
+ CV_R68_CRP = 54,
+ CV_R68_SRP = 55,
+ CV_R68_DRP = 56,
+ CV_R68_TC = 57,
+ CV_R68_AC = 58,
+ CV_R68_SCC = 59,
+ CV_R68_CAL = 60,
+ CV_R68_TT0 = 61,
+ CV_R68_TT1 = 62,
+ //reserved 63
+ CV_R68_BAD0 = 64,
+ CV_R68_BAD1 = 65,
+ CV_R68_BAD2 = 66,
+ CV_R68_BAD3 = 67,
+ CV_R68_BAD4 = 68,
+ CV_R68_BAD5 = 69,
+ CV_R68_BAD6 = 70,
+ CV_R68_BAD7 = 71,
+ CV_R68_BAC0 = 72,
+ CV_R68_BAC1 = 73,
+ CV_R68_BAC2 = 74,
+ CV_R68_BAC3 = 75,
+ CV_R68_BAC4 = 76,
+ CV_R68_BAC5 = 77,
+ CV_R68_BAC6 = 78,
+ CV_R68_BAC7 = 79,
+
+ // Register set for the MIPS 4000
+
+ CV_M4_NOREG = CV_REG_NONE,
+
+ CV_M4_IntZERO = 10, /* CPU REGISTER */
+ CV_M4_IntAT = 11,
+ CV_M4_IntV0 = 12,
+ CV_M4_IntV1 = 13,
+ CV_M4_IntA0 = 14,
+ CV_M4_IntA1 = 15,
+ CV_M4_IntA2 = 16,
+ CV_M4_IntA3 = 17,
+ CV_M4_IntT0 = 18,
+ CV_M4_IntT1 = 19,
+ CV_M4_IntT2 = 20,
+ CV_M4_IntT3 = 21,
+ CV_M4_IntT4 = 22,
+ CV_M4_IntT5 = 23,
+ CV_M4_IntT6 = 24,
+ CV_M4_IntT7 = 25,
+ CV_M4_IntS0 = 26,
+ CV_M4_IntS1 = 27,
+ CV_M4_IntS2 = 28,
+ CV_M4_IntS3 = 29,
+ CV_M4_IntS4 = 30,
+ CV_M4_IntS5 = 31,
+ CV_M4_IntS6 = 32,
+ CV_M4_IntS7 = 33,
+ CV_M4_IntT8 = 34,
+ CV_M4_IntT9 = 35,
+ CV_M4_IntKT0 = 36,
+ CV_M4_IntKT1 = 37,
+ CV_M4_IntGP = 38,
+ CV_M4_IntSP = 39,
+ CV_M4_IntS8 = 40,
+ CV_M4_IntRA = 41,
+ CV_M4_IntLO = 42,
+ CV_M4_IntHI = 43,
+
+ CV_M4_Fir = 50,
+ CV_M4_Psr = 51,
+
+ CV_M4_FltF0 = 60, /* Floating point registers */
+ CV_M4_FltF1 = 61,
+ CV_M4_FltF2 = 62,
+ CV_M4_FltF3 = 63,
+ CV_M4_FltF4 = 64,
+ CV_M4_FltF5 = 65,
+ CV_M4_FltF6 = 66,
+ CV_M4_FltF7 = 67,
+ CV_M4_FltF8 = 68,
+ CV_M4_FltF9 = 69,
+ CV_M4_FltF10 = 70,
+ CV_M4_FltF11 = 71,
+ CV_M4_FltF12 = 72,
+ CV_M4_FltF13 = 73,
+ CV_M4_FltF14 = 74,
+ CV_M4_FltF15 = 75,
+ CV_M4_FltF16 = 76,
+ CV_M4_FltF17 = 77,
+ CV_M4_FltF18 = 78,
+ CV_M4_FltF19 = 79,
+ CV_M4_FltF20 = 80,
+ CV_M4_FltF21 = 81,
+ CV_M4_FltF22 = 82,
+ CV_M4_FltF23 = 83,
+ CV_M4_FltF24 = 84,
+ CV_M4_FltF25 = 85,
+ CV_M4_FltF26 = 86,
+ CV_M4_FltF27 = 87,
+ CV_M4_FltF28 = 88,
+ CV_M4_FltF29 = 89,
+ CV_M4_FltF30 = 90,
+ CV_M4_FltF31 = 91,
+ CV_M4_FltFsr = 92,
+
+
+ // Register set for the ALPHA AXP
+
+ CV_ALPHA_NOREG = CV_REG_NONE,
+
+ CV_ALPHA_FltF0 = 10, // Floating point registers
+ CV_ALPHA_FltF1 = 11,
+ CV_ALPHA_FltF2 = 12,
+ CV_ALPHA_FltF3 = 13,
+ CV_ALPHA_FltF4 = 14,
+ CV_ALPHA_FltF5 = 15,
+ CV_ALPHA_FltF6 = 16,
+ CV_ALPHA_FltF7 = 17,
+ CV_ALPHA_FltF8 = 18,
+ CV_ALPHA_FltF9 = 19,
+ CV_ALPHA_FltF10 = 20,
+ CV_ALPHA_FltF11 = 21,
+ CV_ALPHA_FltF12 = 22,
+ CV_ALPHA_FltF13 = 23,
+ CV_ALPHA_FltF14 = 24,
+ CV_ALPHA_FltF15 = 25,
+ CV_ALPHA_FltF16 = 26,
+ CV_ALPHA_FltF17 = 27,
+ CV_ALPHA_FltF18 = 28,
+ CV_ALPHA_FltF19 = 29,
+ CV_ALPHA_FltF20 = 30,
+ CV_ALPHA_FltF21 = 31,
+ CV_ALPHA_FltF22 = 32,
+ CV_ALPHA_FltF23 = 33,
+ CV_ALPHA_FltF24 = 34,
+ CV_ALPHA_FltF25 = 35,
+ CV_ALPHA_FltF26 = 36,
+ CV_ALPHA_FltF27 = 37,
+ CV_ALPHA_FltF28 = 38,
+ CV_ALPHA_FltF29 = 39,
+ CV_ALPHA_FltF30 = 40,
+ CV_ALPHA_FltF31 = 41,
+
+ CV_ALPHA_IntV0 = 42, // Integer registers
+ CV_ALPHA_IntT0 = 43,
+ CV_ALPHA_IntT1 = 44,
+ CV_ALPHA_IntT2 = 45,
+ CV_ALPHA_IntT3 = 46,
+ CV_ALPHA_IntT4 = 47,
+ CV_ALPHA_IntT5 = 48,
+ CV_ALPHA_IntT6 = 49,
+ CV_ALPHA_IntT7 = 50,
+ CV_ALPHA_IntS0 = 51,
+ CV_ALPHA_IntS1 = 52,
+ CV_ALPHA_IntS2 = 53,
+ CV_ALPHA_IntS3 = 54,
+ CV_ALPHA_IntS4 = 55,
+ CV_ALPHA_IntS5 = 56,
+ CV_ALPHA_IntFP = 57,
+ CV_ALPHA_IntA0 = 58,
+ CV_ALPHA_IntA1 = 59,
+ CV_ALPHA_IntA2 = 60,
+ CV_ALPHA_IntA3 = 61,
+ CV_ALPHA_IntA4 = 62,
+ CV_ALPHA_IntA5 = 63,
+ CV_ALPHA_IntT8 = 64,
+ CV_ALPHA_IntT9 = 65,
+ CV_ALPHA_IntT10 = 66,
+ CV_ALPHA_IntT11 = 67,
+ CV_ALPHA_IntRA = 68,
+ CV_ALPHA_IntT12 = 69,
+ CV_ALPHA_IntAT = 70,
+ CV_ALPHA_IntGP = 71,
+ CV_ALPHA_IntSP = 72,
+ CV_ALPHA_IntZERO = 73,
+
+
+ CV_ALPHA_Fpcr = 74, // Control registers
+ CV_ALPHA_Fir = 75,
+ CV_ALPHA_Psr = 76,
+ CV_ALPHA_FltFsr = 77,
+ CV_ALPHA_SoftFpcr = 78,
+
+ // Register Set for Motorola/IBM PowerPC
+
+ /*
+ ** PowerPC General Registers ( User Level )
+ */
+ CV_PPC_GPR0 = 1,
+ CV_PPC_GPR1 = 2,
+ CV_PPC_GPR2 = 3,
+ CV_PPC_GPR3 = 4,
+ CV_PPC_GPR4 = 5,
+ CV_PPC_GPR5 = 6,
+ CV_PPC_GPR6 = 7,
+ CV_PPC_GPR7 = 8,
+ CV_PPC_GPR8 = 9,
+ CV_PPC_GPR9 = 10,
+ CV_PPC_GPR10 = 11,
+ CV_PPC_GPR11 = 12,
+ CV_PPC_GPR12 = 13,
+ CV_PPC_GPR13 = 14,
+ CV_PPC_GPR14 = 15,
+ CV_PPC_GPR15 = 16,
+ CV_PPC_GPR16 = 17,
+ CV_PPC_GPR17 = 18,
+ CV_PPC_GPR18 = 19,
+ CV_PPC_GPR19 = 20,
+ CV_PPC_GPR20 = 21,
+ CV_PPC_GPR21 = 22,
+ CV_PPC_GPR22 = 23,
+ CV_PPC_GPR23 = 24,
+ CV_PPC_GPR24 = 25,
+ CV_PPC_GPR25 = 26,
+ CV_PPC_GPR26 = 27,
+ CV_PPC_GPR27 = 28,
+ CV_PPC_GPR28 = 29,
+ CV_PPC_GPR29 = 30,
+ CV_PPC_GPR30 = 31,
+ CV_PPC_GPR31 = 32,
+
+ /*
+ ** PowerPC Condition Register ( User Level )
+ */
+ CV_PPC_CR = 33,
+ CV_PPC_CR0 = 34,
+ CV_PPC_CR1 = 35,
+ CV_PPC_CR2 = 36,
+ CV_PPC_CR3 = 37,
+ CV_PPC_CR4 = 38,
+ CV_PPC_CR5 = 39,
+ CV_PPC_CR6 = 40,
+ CV_PPC_CR7 = 41,
+
+ /*
+ ** PowerPC Floating Point Registers ( User Level )
+ */
+ CV_PPC_FPR0 = 42,
+ CV_PPC_FPR1 = 43,
+ CV_PPC_FPR2 = 44,
+ CV_PPC_FPR3 = 45,
+ CV_PPC_FPR4 = 46,
+ CV_PPC_FPR5 = 47,
+ CV_PPC_FPR6 = 48,
+ CV_PPC_FPR7 = 49,
+ CV_PPC_FPR8 = 50,
+ CV_PPC_FPR9 = 51,
+ CV_PPC_FPR10 = 52,
+ CV_PPC_FPR11 = 53,
+ CV_PPC_FPR12 = 54,
+ CV_PPC_FPR13 = 55,
+ CV_PPC_FPR14 = 56,
+ CV_PPC_FPR15 = 57,
+ CV_PPC_FPR16 = 58,
+ CV_PPC_FPR17 = 59,
+ CV_PPC_FPR18 = 60,
+ CV_PPC_FPR19 = 61,
+ CV_PPC_FPR20 = 62,
+ CV_PPC_FPR21 = 63,
+ CV_PPC_FPR22 = 64,
+ CV_PPC_FPR23 = 65,
+ CV_PPC_FPR24 = 66,
+ CV_PPC_FPR25 = 67,
+ CV_PPC_FPR26 = 68,
+ CV_PPC_FPR27 = 69,
+ CV_PPC_FPR28 = 70,
+ CV_PPC_FPR29 = 71,
+ CV_PPC_FPR30 = 72,
+ CV_PPC_FPR31 = 73,
+
+ /*
+ ** PowerPC Floating Point Status and Control Register ( User Level )
+ */
+ CV_PPC_FPSCR = 74,
+
+ /*
+ ** PowerPC Machine State Register ( Supervisor Level )
+ */
+ CV_PPC_MSR = 75,
+
+ /*
+ ** PowerPC Segment Registers ( Supervisor Level )
+ */
+ CV_PPC_SR0 = 76,
+ CV_PPC_SR1 = 77,
+ CV_PPC_SR2 = 78,
+ CV_PPC_SR3 = 79,
+ CV_PPC_SR4 = 80,
+ CV_PPC_SR5 = 81,
+ CV_PPC_SR6 = 82,
+ CV_PPC_SR7 = 83,
+ CV_PPC_SR8 = 84,
+ CV_PPC_SR9 = 85,
+ CV_PPC_SR10 = 86,
+ CV_PPC_SR11 = 87,
+ CV_PPC_SR12 = 88,
+ CV_PPC_SR13 = 89,
+ CV_PPC_SR14 = 90,
+ CV_PPC_SR15 = 91,
+
+ /*
+ ** For all of the special purpose registers add 100 to the SPR# that the
+ ** Motorola/IBM documentation gives with the exception of any imaginary
+ ** registers.
+ */
+
+ /*
+ ** PowerPC Special Purpose Registers ( User Level )
+ */
+ CV_PPC_PC = 99, // PC (imaginary register)
+
+ CV_PPC_MQ = 100, // MPC601
+ CV_PPC_XER = 101,
+ CV_PPC_RTCU = 104, // MPC601
+ CV_PPC_RTCL = 105, // MPC601
+ CV_PPC_LR = 108,
+ CV_PPC_CTR = 109,
+
+ CV_PPC_COMPARE = 110, // part of XER (internal to the debugger only)
+ CV_PPC_COUNT = 111, // part of XER (internal to the debugger only)
+
+ /*
+ ** PowerPC Special Purpose Registers ( Supervisor Level )
+ */
+ CV_PPC_DSISR = 118,
+ CV_PPC_DAR = 119,
+ CV_PPC_DEC = 122,
+ CV_PPC_SDR1 = 125,
+ CV_PPC_SRR0 = 126,
+ CV_PPC_SRR1 = 127,
+ CV_PPC_SPRG0 = 372,
+ CV_PPC_SPRG1 = 373,
+ CV_PPC_SPRG2 = 374,
+ CV_PPC_SPRG3 = 375,
+ CV_PPC_ASR = 280, // 64-bit implementations only
+ CV_PPC_EAR = 382,
+ CV_PPC_PVR = 287,
+ CV_PPC_BAT0U = 628,
+ CV_PPC_BAT0L = 629,
+ CV_PPC_BAT1U = 630,
+ CV_PPC_BAT1L = 631,
+ CV_PPC_BAT2U = 632,
+ CV_PPC_BAT2L = 633,
+ CV_PPC_BAT3U = 634,
+ CV_PPC_BAT3L = 635,
+ CV_PPC_DBAT0U = 636,
+ CV_PPC_DBAT0L = 637,
+ CV_PPC_DBAT1U = 638,
+ CV_PPC_DBAT1L = 639,
+ CV_PPC_DBAT2U = 640,
+ CV_PPC_DBAT2L = 641,
+ CV_PPC_DBAT3U = 642,
+ CV_PPC_DBAT3L = 643,
+
+ /*
+ ** PowerPC Special Purpose Registers Implementation Dependent ( Supervisor Level )
+ */
+
+ /*
+ ** Doesn't appear that IBM/Motorola has finished defining these.
+ */
+
+ CV_PPC_PMR0 = 1044, // MPC620,
+ CV_PPC_PMR1 = 1045, // MPC620,
+ CV_PPC_PMR2 = 1046, // MPC620,
+ CV_PPC_PMR3 = 1047, // MPC620,
+ CV_PPC_PMR4 = 1048, // MPC620,
+ CV_PPC_PMR5 = 1049, // MPC620,
+ CV_PPC_PMR6 = 1050, // MPC620,
+ CV_PPC_PMR7 = 1051, // MPC620,
+ CV_PPC_PMR8 = 1052, // MPC620,
+ CV_PPC_PMR9 = 1053, // MPC620,
+ CV_PPC_PMR10 = 1054, // MPC620,
+ CV_PPC_PMR11 = 1055, // MPC620,
+ CV_PPC_PMR12 = 1056, // MPC620,
+ CV_PPC_PMR13 = 1057, // MPC620,
+ CV_PPC_PMR14 = 1058, // MPC620,
+ CV_PPC_PMR15 = 1059, // MPC620,
+
+ CV_PPC_DMISS = 1076, // MPC603
+ CV_PPC_DCMP = 1077, // MPC603
+ CV_PPC_HASH1 = 1078, // MPC603
+ CV_PPC_HASH2 = 1079, // MPC603
+ CV_PPC_IMISS = 1080, // MPC603
+ CV_PPC_ICMP = 1081, // MPC603
+ CV_PPC_RPA = 1082, // MPC603
+
+ CV_PPC_HID0 = 1108, // MPC601, MPC603, MPC620
+ CV_PPC_HID1 = 1109, // MPC601
+ CV_PPC_HID2 = 1110, // MPC601, MPC603, MPC620 ( IABR )
+ CV_PPC_HID3 = 1111, // Not Defined
+ CV_PPC_HID4 = 1112, // Not Defined
+ CV_PPC_HID5 = 1113, // MPC601, MPC604, MPC620 ( DABR )
+ CV_PPC_HID6 = 1114, // Not Defined
+ CV_PPC_HID7 = 1115, // Not Defined
+ CV_PPC_HID8 = 1116, // MPC620 ( BUSCSR )
+ CV_PPC_HID9 = 1117, // MPC620 ( L2CSR )
+ CV_PPC_HID10 = 1118, // Not Defined
+ CV_PPC_HID11 = 1119, // Not Defined
+ CV_PPC_HID12 = 1120, // Not Defined
+ CV_PPC_HID13 = 1121, // MPC604 ( HCR )
+ CV_PPC_HID14 = 1122, // Not Defined
+ CV_PPC_HID15 = 1123, // MPC601, MPC604, MPC620 ( PIR )
+
+ //
+ // JAVA VM registers
+ //
+
+ CV_JAVA_PC = 1,
+
+ //
+ // Register set for the Hitachi SH3
+ //
+
+ CV_SH3_NOREG = CV_REG_NONE,
+
+ CV_SH3_IntR0 = 10, // CPU REGISTER
+ CV_SH3_IntR1 = 11,
+ CV_SH3_IntR2 = 12,
+ CV_SH3_IntR3 = 13,
+ CV_SH3_IntR4 = 14,
+ CV_SH3_IntR5 = 15,
+ CV_SH3_IntR6 = 16,
+ CV_SH3_IntR7 = 17,
+ CV_SH3_IntR8 = 18,
+ CV_SH3_IntR9 = 19,
+ CV_SH3_IntR10 = 20,
+ CV_SH3_IntR11 = 21,
+ CV_SH3_IntR12 = 22,
+ CV_SH3_IntR13 = 23,
+ CV_SH3_IntFp = 24,
+ CV_SH3_IntSp = 25,
+ CV_SH3_Gbr = 38,
+ CV_SH3_Pr = 39,
+ CV_SH3_Mach = 40,
+ CV_SH3_Macl = 41,
+
+ CV_SH3_Pc = 50,
+ CV_SH3_Sr = 51,
+
+ CV_SH3_BarA = 60,
+ CV_SH3_BasrA = 61,
+ CV_SH3_BamrA = 62,
+ CV_SH3_BbrA = 63,
+ CV_SH3_BarB = 64,
+ CV_SH3_BasrB = 65,
+ CV_SH3_BamrB = 66,
+ CV_SH3_BbrB = 67,
+ CV_SH3_BdrB = 68,
+ CV_SH3_BdmrB = 69,
+ CV_SH3_Brcr = 70,
+
+ //
+ // Additional registers for Hitachi SH processors
+ //
+
+ CV_SH_Fpscr = 75, // floating point status/control register
+ CV_SH_Fpul = 76, // floating point communication register
+
+ CV_SH_FpR0 = 80, // Floating point registers
+ CV_SH_FpR1 = 81,
+ CV_SH_FpR2 = 82,
+ CV_SH_FpR3 = 83,
+ CV_SH_FpR4 = 84,
+ CV_SH_FpR5 = 85,
+ CV_SH_FpR6 = 86,
+ CV_SH_FpR7 = 87,
+ CV_SH_FpR8 = 88,
+ CV_SH_FpR9 = 89,
+ CV_SH_FpR10 = 90,
+ CV_SH_FpR11 = 91,
+ CV_SH_FpR12 = 92,
+ CV_SH_FpR13 = 93,
+ CV_SH_FpR14 = 94,
+ CV_SH_FpR15 = 95,
+
+ CV_SH_XFpR0 = 96,
+ CV_SH_XFpR1 = 97,
+ CV_SH_XFpR2 = 98,
+ CV_SH_XFpR3 = 99,
+ CV_SH_XFpR4 = 100,
+ CV_SH_XFpR5 = 101,
+ CV_SH_XFpR6 = 102,
+ CV_SH_XFpR7 = 103,
+ CV_SH_XFpR8 = 104,
+ CV_SH_XFpR9 = 105,
+ CV_SH_XFpR10 = 106,
+ CV_SH_XFpR11 = 107,
+ CV_SH_XFpR12 = 108,
+ CV_SH_XFpR13 = 109,
+ CV_SH_XFpR14 = 110,
+ CV_SH_XFpR15 = 111,
+
+ //
+ // Register set for the ARM processor.
+ //
+
+ CV_ARM_NOREG = CV_REG_NONE,
+
+ CV_ARM_R0 = 10,
+ CV_ARM_R1 = 11,
+ CV_ARM_R2 = 12,
+ CV_ARM_R3 = 13,
+ CV_ARM_R4 = 14,
+ CV_ARM_R5 = 15,
+ CV_ARM_R6 = 16,
+ CV_ARM_R7 = 17,
+ CV_ARM_R8 = 18,
+ CV_ARM_R9 = 19,
+ CV_ARM_R10 = 20,
+ CV_ARM_R11 = 21, // Frame pointer, if allocated
+ CV_ARM_R12 = 22,
+ CV_ARM_SP = 23, // Stack pointer
+ CV_ARM_LR = 24, // Link Register
+ CV_ARM_PC = 25, // Program counter
+ CV_ARM_CPSR = 26, // Current program status register
+
+ //
+ // Register set for Intel IA64
+ //
+
+ CV_IA64_NOREG = CV_REG_NONE,
+
+ // Branch Registers
+
+ CV_IA64_Br0 = 512,
+ CV_IA64_Br1 = 513,
+ CV_IA64_Br2 = 514,
+ CV_IA64_Br3 = 515,
+ CV_IA64_Br4 = 516,
+ CV_IA64_Br5 = 517,
+ CV_IA64_Br6 = 518,
+ CV_IA64_Br7 = 519,
+
+ // Predicate Registers
+
+ CV_IA64_P0 = 704,
+ CV_IA64_P1 = 705,
+ CV_IA64_P2 = 706,
+ CV_IA64_P3 = 707,
+ CV_IA64_P4 = 708,
+ CV_IA64_P5 = 709,
+ CV_IA64_P6 = 710,
+ CV_IA64_P7 = 711,
+ CV_IA64_P8 = 712,
+ CV_IA64_P9 = 713,
+ CV_IA64_P10 = 714,
+ CV_IA64_P11 = 715,
+ CV_IA64_P12 = 716,
+ CV_IA64_P13 = 717,
+ CV_IA64_P14 = 718,
+ CV_IA64_P15 = 719,
+ CV_IA64_P16 = 720,
+ CV_IA64_P17 = 721,
+ CV_IA64_P18 = 722,
+ CV_IA64_P19 = 723,
+ CV_IA64_P20 = 724,
+ CV_IA64_P21 = 725,
+ CV_IA64_P22 = 726,
+ CV_IA64_P23 = 727,
+ CV_IA64_P24 = 728,
+ CV_IA64_P25 = 729,
+ CV_IA64_P26 = 730,
+ CV_IA64_P27 = 731,
+ CV_IA64_P28 = 732,
+ CV_IA64_P29 = 733,
+ CV_IA64_P30 = 734,
+ CV_IA64_P31 = 735,
+ CV_IA64_P32 = 736,
+ CV_IA64_P33 = 737,
+ CV_IA64_P34 = 738,
+ CV_IA64_P35 = 739,
+ CV_IA64_P36 = 740,
+ CV_IA64_P37 = 741,
+ CV_IA64_P38 = 742,
+ CV_IA64_P39 = 743,
+ CV_IA64_P40 = 744,
+ CV_IA64_P41 = 745,
+ CV_IA64_P42 = 746,
+ CV_IA64_P43 = 747,
+ CV_IA64_P44 = 748,
+ CV_IA64_P45 = 749,
+ CV_IA64_P46 = 750,
+ CV_IA64_P47 = 751,
+ CV_IA64_P48 = 752,
+ CV_IA64_P49 = 753,
+ CV_IA64_P50 = 754,
+ CV_IA64_P51 = 755,
+ CV_IA64_P52 = 756,
+ CV_IA64_P53 = 757,
+ CV_IA64_P54 = 758,
+ CV_IA64_P55 = 759,
+ CV_IA64_P56 = 760,
+ CV_IA64_P57 = 761,
+ CV_IA64_P58 = 762,
+ CV_IA64_P59 = 763,
+ CV_IA64_P60 = 764,
+ CV_IA64_P61 = 765,
+ CV_IA64_P62 = 766,
+ CV_IA64_P63 = 767,
+
+ CV_IA64_Preds = 768,
+
+ // Banked General Registers
+
+ CV_IA64_IntH0 = 832,
+ CV_IA64_IntH1 = 833,
+ CV_IA64_IntH2 = 834,
+ CV_IA64_IntH3 = 835,
+ CV_IA64_IntH4 = 836,
+ CV_IA64_IntH5 = 837,
+ CV_IA64_IntH6 = 838,
+ CV_IA64_IntH7 = 839,
+ CV_IA64_IntH8 = 840,
+ CV_IA64_IntH9 = 841,
+ CV_IA64_IntH10 = 842,
+ CV_IA64_IntH11 = 843,
+ CV_IA64_IntH12 = 844,
+ CV_IA64_IntH13 = 845,
+ CV_IA64_IntH14 = 846,
+ CV_IA64_IntH15 = 847,
+
+ // Special Registers
+
+ CV_IA64_Ip = 1016,
+ CV_IA64_Umask = 1017,
+ CV_IA64_Cfm = 1018,
+ CV_IA64_Psr = 1019,
+
+ // Banked General Registers
+
+ CV_IA64_Nats = 1020,
+ CV_IA64_Nats2 = 1021,
+ CV_IA64_Nats3 = 1022,
+
+ // General-Purpose Registers
+
+ // Integer registers
+ CV_IA64_IntR0 = 1024,
+ CV_IA64_IntR1 = 1025,
+ CV_IA64_IntR2 = 1026,
+ CV_IA64_IntR3 = 1027,
+ CV_IA64_IntR4 = 1028,
+ CV_IA64_IntR5 = 1029,
+ CV_IA64_IntR6 = 1030,
+ CV_IA64_IntR7 = 1031,
+ CV_IA64_IntR8 = 1032,
+ CV_IA64_IntR9 = 1033,
+ CV_IA64_IntR10 = 1034,
+ CV_IA64_IntR11 = 1035,
+ CV_IA64_IntR12 = 1036,
+ CV_IA64_IntR13 = 1037,
+ CV_IA64_IntR14 = 1038,
+ CV_IA64_IntR15 = 1039,
+ CV_IA64_IntR16 = 1040,
+ CV_IA64_IntR17 = 1041,
+ CV_IA64_IntR18 = 1042,
+ CV_IA64_IntR19 = 1043,
+ CV_IA64_IntR20 = 1044,
+ CV_IA64_IntR21 = 1045,
+ CV_IA64_IntR22 = 1046,
+ CV_IA64_IntR23 = 1047,
+ CV_IA64_IntR24 = 1048,
+ CV_IA64_IntR25 = 1049,
+ CV_IA64_IntR26 = 1050,
+ CV_IA64_IntR27 = 1051,
+ CV_IA64_IntR28 = 1052,
+ CV_IA64_IntR29 = 1053,
+ CV_IA64_IntR30 = 1054,
+ CV_IA64_IntR31 = 1055,
+
+ // Register Stack
+ CV_IA64_IntR32 = 1056,
+ CV_IA64_IntR33 = 1057,
+ CV_IA64_IntR34 = 1058,
+ CV_IA64_IntR35 = 1059,
+ CV_IA64_IntR36 = 1060,
+ CV_IA64_IntR37 = 1061,
+ CV_IA64_IntR38 = 1062,
+ CV_IA64_IntR39 = 1063,
+ CV_IA64_IntR40 = 1064,
+ CV_IA64_IntR41 = 1065,
+ CV_IA64_IntR42 = 1066,
+ CV_IA64_IntR43 = 1067,
+ CV_IA64_IntR44 = 1068,
+ CV_IA64_IntR45 = 1069,
+ CV_IA64_IntR46 = 1070,
+ CV_IA64_IntR47 = 1071,
+ CV_IA64_IntR48 = 1072,
+ CV_IA64_IntR49 = 1073,
+ CV_IA64_IntR50 = 1074,
+ CV_IA64_IntR51 = 1075,
+ CV_IA64_IntR52 = 1076,
+ CV_IA64_IntR53 = 1077,
+ CV_IA64_IntR54 = 1078,
+ CV_IA64_IntR55 = 1079,
+ CV_IA64_IntR56 = 1080,
+ CV_IA64_IntR57 = 1081,
+ CV_IA64_IntR58 = 1082,
+ CV_IA64_IntR59 = 1083,
+ CV_IA64_IntR60 = 1084,
+ CV_IA64_IntR61 = 1085,
+ CV_IA64_IntR62 = 1086,
+ CV_IA64_IntR63 = 1087,
+ CV_IA64_IntR64 = 1088,
+ CV_IA64_IntR65 = 1089,
+ CV_IA64_IntR66 = 1090,
+ CV_IA64_IntR67 = 1091,
+ CV_IA64_IntR68 = 1092,
+ CV_IA64_IntR69 = 1093,
+ CV_IA64_IntR70 = 1094,
+ CV_IA64_IntR71 = 1095,
+ CV_IA64_IntR72 = 1096,
+ CV_IA64_IntR73 = 1097,
+ CV_IA64_IntR74 = 1098,
+ CV_IA64_IntR75 = 1099,
+ CV_IA64_IntR76 = 1100,
+ CV_IA64_IntR77 = 1101,
+ CV_IA64_IntR78 = 1102,
+ CV_IA64_IntR79 = 1103,
+ CV_IA64_IntR80 = 1104,
+ CV_IA64_IntR81 = 1105,
+ CV_IA64_IntR82 = 1106,
+ CV_IA64_IntR83 = 1107,
+ CV_IA64_IntR84 = 1108,
+ CV_IA64_IntR85 = 1109,
+ CV_IA64_IntR86 = 1110,
+ CV_IA64_IntR87 = 1111,
+ CV_IA64_IntR88 = 1112,
+ CV_IA64_IntR89 = 1113,
+ CV_IA64_IntR90 = 1114,
+ CV_IA64_IntR91 = 1115,
+ CV_IA64_IntR92 = 1116,
+ CV_IA64_IntR93 = 1117,
+ CV_IA64_IntR94 = 1118,
+ CV_IA64_IntR95 = 1119,
+ CV_IA64_IntR96 = 1120,
+ CV_IA64_IntR97 = 1121,
+ CV_IA64_IntR98 = 1122,
+ CV_IA64_IntR99 = 1123,
+ CV_IA64_IntR100 = 1124,
+ CV_IA64_IntR101 = 1125,
+ CV_IA64_IntR102 = 1126,
+ CV_IA64_IntR103 = 1127,
+ CV_IA64_IntR104 = 1128,
+ CV_IA64_IntR105 = 1129,
+ CV_IA64_IntR106 = 1130,
+ CV_IA64_IntR107 = 1131,
+ CV_IA64_IntR108 = 1132,
+ CV_IA64_IntR109 = 1133,
+ CV_IA64_IntR110 = 1134,
+ CV_IA64_IntR111 = 1135,
+ CV_IA64_IntR112 = 1136,
+ CV_IA64_IntR113 = 1137,
+ CV_IA64_IntR114 = 1138,
+ CV_IA64_IntR115 = 1139,
+ CV_IA64_IntR116 = 1140,
+ CV_IA64_IntR117 = 1141,
+ CV_IA64_IntR118 = 1142,
+ CV_IA64_IntR119 = 1143,
+ CV_IA64_IntR120 = 1144,
+ CV_IA64_IntR121 = 1145,
+ CV_IA64_IntR122 = 1146,
+ CV_IA64_IntR123 = 1147,
+ CV_IA64_IntR124 = 1148,
+ CV_IA64_IntR125 = 1149,
+ CV_IA64_IntR126 = 1150,
+ CV_IA64_IntR127 = 1151,
+
+ // Floating-Point Registers
+
+ // Low Floating Point Registers
+ CV_IA64_FltF0 = 2048,
+ CV_IA64_FltF1 = 2049,
+ CV_IA64_FltF2 = 2050,
+ CV_IA64_FltF3 = 2051,
+ CV_IA64_FltF4 = 2052,
+ CV_IA64_FltF5 = 2053,
+ CV_IA64_FltF6 = 2054,
+ CV_IA64_FltF7 = 2055,
+ CV_IA64_FltF8 = 2056,
+ CV_IA64_FltF9 = 2057,
+ CV_IA64_FltF10 = 2058,
+ CV_IA64_FltF11 = 2059,
+ CV_IA64_FltF12 = 2060,
+ CV_IA64_FltF13 = 2061,
+ CV_IA64_FltF14 = 2062,
+ CV_IA64_FltF15 = 2063,
+ CV_IA64_FltF16 = 2064,
+ CV_IA64_FltF17 = 2065,
+ CV_IA64_FltF18 = 2066,
+ CV_IA64_FltF19 = 2067,
+ CV_IA64_FltF20 = 2068,
+ CV_IA64_FltF21 = 2069,
+ CV_IA64_FltF22 = 2070,
+ CV_IA64_FltF23 = 2071,
+ CV_IA64_FltF24 = 2072,
+ CV_IA64_FltF25 = 2073,
+ CV_IA64_FltF26 = 2074,
+ CV_IA64_FltF27 = 2075,
+ CV_IA64_FltF28 = 2076,
+ CV_IA64_FltF29 = 2077,
+ CV_IA64_FltF30 = 2078,
+ CV_IA64_FltF31 = 2079,
+
+ // High Floating Point Registers
+ CV_IA64_FltF32 = 2080,
+ CV_IA64_FltF33 = 2081,
+ CV_IA64_FltF34 = 2082,
+ CV_IA64_FltF35 = 2083,
+ CV_IA64_FltF36 = 2084,
+ CV_IA64_FltF37 = 2085,
+ CV_IA64_FltF38 = 2086,
+ CV_IA64_FltF39 = 2087,
+ CV_IA64_FltF40 = 2088,
+ CV_IA64_FltF41 = 2089,
+ CV_IA64_FltF42 = 2090,
+ CV_IA64_FltF43 = 2091,
+ CV_IA64_FltF44 = 2092,
+ CV_IA64_FltF45 = 2093,
+ CV_IA64_FltF46 = 2094,
+ CV_IA64_FltF47 = 2095,
+ CV_IA64_FltF48 = 2096,
+ CV_IA64_FltF49 = 2097,
+ CV_IA64_FltF50 = 2098,
+ CV_IA64_FltF51 = 2099,
+ CV_IA64_FltF52 = 2100,
+ CV_IA64_FltF53 = 2101,
+ CV_IA64_FltF54 = 2102,
+ CV_IA64_FltF55 = 2103,
+ CV_IA64_FltF56 = 2104,
+ CV_IA64_FltF57 = 2105,
+ CV_IA64_FltF58 = 2106,
+ CV_IA64_FltF59 = 2107,
+ CV_IA64_FltF60 = 2108,
+ CV_IA64_FltF61 = 2109,
+ CV_IA64_FltF62 = 2110,
+ CV_IA64_FltF63 = 2111,
+ CV_IA64_FltF64 = 2112,
+ CV_IA64_FltF65 = 2113,
+ CV_IA64_FltF66 = 2114,
+ CV_IA64_FltF67 = 2115,
+ CV_IA64_FltF68 = 2116,
+ CV_IA64_FltF69 = 2117,
+ CV_IA64_FltF70 = 2118,
+ CV_IA64_FltF71 = 2119,
+ CV_IA64_FltF72 = 2120,
+ CV_IA64_FltF73 = 2121,
+ CV_IA64_FltF74 = 2122,
+ CV_IA64_FltF75 = 2123,
+ CV_IA64_FltF76 = 2124,
+ CV_IA64_FltF77 = 2125,
+ CV_IA64_FltF78 = 2126,
+ CV_IA64_FltF79 = 2127,
+ CV_IA64_FltF80 = 2128,
+ CV_IA64_FltF81 = 2129,
+ CV_IA64_FltF82 = 2130,
+ CV_IA64_FltF83 = 2131,
+ CV_IA64_FltF84 = 2132,
+ CV_IA64_FltF85 = 2133,
+ CV_IA64_FltF86 = 2134,
+ CV_IA64_FltF87 = 2135,
+ CV_IA64_FltF88 = 2136,
+ CV_IA64_FltF89 = 2137,
+ CV_IA64_FltF90 = 2138,
+ CV_IA64_FltF91 = 2139,
+ CV_IA64_FltF92 = 2140,
+ CV_IA64_FltF93 = 2141,
+ CV_IA64_FltF94 = 2142,
+ CV_IA64_FltF95 = 2143,
+ CV_IA64_FltF96 = 2144,
+ CV_IA64_FltF97 = 2145,
+ CV_IA64_FltF98 = 2146,
+ CV_IA64_FltF99 = 2147,
+ CV_IA64_FltF100 = 2148,
+ CV_IA64_FltF101 = 2149,
+ CV_IA64_FltF102 = 2150,
+ CV_IA64_FltF103 = 2151,
+ CV_IA64_FltF104 = 2152,
+ CV_IA64_FltF105 = 2153,
+ CV_IA64_FltF106 = 2154,
+ CV_IA64_FltF107 = 2155,
+ CV_IA64_FltF108 = 2156,
+ CV_IA64_FltF109 = 2157,
+ CV_IA64_FltF110 = 2158,
+ CV_IA64_FltF111 = 2159,
+ CV_IA64_FltF112 = 2160,
+ CV_IA64_FltF113 = 2161,
+ CV_IA64_FltF114 = 2162,
+ CV_IA64_FltF115 = 2163,
+ CV_IA64_FltF116 = 2164,
+ CV_IA64_FltF117 = 2165,
+ CV_IA64_FltF118 = 2166,
+ CV_IA64_FltF119 = 2167,
+ CV_IA64_FltF120 = 2168,
+ CV_IA64_FltF121 = 2169,
+ CV_IA64_FltF122 = 2170,
+ CV_IA64_FltF123 = 2171,
+ CV_IA64_FltF124 = 2172,
+ CV_IA64_FltF125 = 2173,
+ CV_IA64_FltF126 = 2174,
+ CV_IA64_FltF127 = 2175,
+
+ // Application Registers
+
+ CV_IA64_ApKR0 = 3072,
+ CV_IA64_ApKR1 = 3073,
+ CV_IA64_ApKR2 = 3074,
+ CV_IA64_ApKR3 = 3075,
+ CV_IA64_ApKR4 = 3076,
+ CV_IA64_ApKR5 = 3077,
+ CV_IA64_ApKR6 = 3078,
+ CV_IA64_ApKR7 = 3079,
+ CV_IA64_AR8 = 3080,
+ CV_IA64_AR9 = 3081,
+ CV_IA64_AR10 = 3082,
+ CV_IA64_AR11 = 3083,
+ CV_IA64_AR12 = 3084,
+ CV_IA64_AR13 = 3085,
+ CV_IA64_AR14 = 3086,
+ CV_IA64_AR15 = 3087,
+ CV_IA64_RsRSC = 3088,
+ CV_IA64_RsBSP = 3089,
+ CV_IA64_RsBSPSTORE = 3090,
+ CV_IA64_RsRNAT = 3091,
+ CV_IA64_AR20 = 3092,
+ CV_IA64_StFCR = 3093,
+ CV_IA64_AR22 = 3094,
+ CV_IA64_AR23 = 3095,
+ CV_IA64_EFLAG = 3096,
+ CV_IA64_CSD = 3097,
+ CV_IA64_SSD = 3098,
+ CV_IA64_CFLG = 3099,
+ CV_IA64_StFSR = 3100,
+ CV_IA64_StFIR = 3101,
+ CV_IA64_StFDR = 3102,
+ CV_IA64_AR31 = 3103,
+ CV_IA64_ApCCV = 3104,
+ CV_IA64_AR33 = 3105,
+ CV_IA64_AR34 = 3106,
+ CV_IA64_AR35 = 3107,
+ CV_IA64_ApUNAT = 3108,
+ CV_IA64_AR37 = 3109,
+ CV_IA64_AR38 = 3110,
+ CV_IA64_AR39 = 3111,
+ CV_IA64_StFPSR = 3112,
+ CV_IA64_AR41 = 3113,
+ CV_IA64_AR42 = 3114,
+ CV_IA64_AR43 = 3115,
+ CV_IA64_ApITC = 3116,
+ CV_IA64_AR45 = 3117,
+ CV_IA64_AR46 = 3118,
+ CV_IA64_AR47 = 3119,
+ CV_IA64_AR48 = 3120,
+ CV_IA64_AR49 = 3121,
+ CV_IA64_AR50 = 3122,
+ CV_IA64_AR51 = 3123,
+ CV_IA64_AR52 = 3124,
+ CV_IA64_AR53 = 3125,
+ CV_IA64_AR54 = 3126,
+ CV_IA64_AR55 = 3127,
+ CV_IA64_AR56 = 3128,
+ CV_IA64_AR57 = 3129,
+ CV_IA64_AR58 = 3130,
+ CV_IA64_AR59 = 3131,
+ CV_IA64_AR60 = 3132,
+ CV_IA64_AR61 = 3133,
+ CV_IA64_AR62 = 3134,
+ CV_IA64_AR63 = 3135,
+ CV_IA64_RsPFS = 3136,
+ CV_IA64_ApLC = 3137,
+ CV_IA64_ApEC = 3138,
+ CV_IA64_AR67 = 3139,
+ CV_IA64_AR68 = 3140,
+ CV_IA64_AR69 = 3141,
+ CV_IA64_AR70 = 3142,
+ CV_IA64_AR71 = 3143,
+ CV_IA64_AR72 = 3144,
+ CV_IA64_AR73 = 3145,
+ CV_IA64_AR74 = 3146,
+ CV_IA64_AR75 = 3147,
+ CV_IA64_AR76 = 3148,
+ CV_IA64_AR77 = 3149,
+ CV_IA64_AR78 = 3150,
+ CV_IA64_AR79 = 3151,
+ CV_IA64_AR80 = 3152,
+ CV_IA64_AR81 = 3153,
+ CV_IA64_AR82 = 3154,
+ CV_IA64_AR83 = 3155,
+ CV_IA64_AR84 = 3156,
+ CV_IA64_AR85 = 3157,
+ CV_IA64_AR86 = 3158,
+ CV_IA64_AR87 = 3159,
+ CV_IA64_AR88 = 3160,
+ CV_IA64_AR89 = 3161,
+ CV_IA64_AR90 = 3162,
+ CV_IA64_AR91 = 3163,
+ CV_IA64_AR92 = 3164,
+ CV_IA64_AR93 = 3165,
+ CV_IA64_AR94 = 3166,
+ CV_IA64_AR95 = 3167,
+ CV_IA64_AR96 = 3168,
+ CV_IA64_AR97 = 3169,
+ CV_IA64_AR98 = 3170,
+ CV_IA64_AR99 = 3171,
+ CV_IA64_AR100 = 3172,
+ CV_IA64_AR101 = 3173,
+ CV_IA64_AR102 = 3174,
+ CV_IA64_AR103 = 3175,
+ CV_IA64_AR104 = 3176,
+ CV_IA64_AR105 = 3177,
+ CV_IA64_AR106 = 3178,
+ CV_IA64_AR107 = 3179,
+ CV_IA64_AR108 = 3180,
+ CV_IA64_AR109 = 3181,
+ CV_IA64_AR110 = 3182,
+ CV_IA64_AR111 = 3183,
+ CV_IA64_AR112 = 3184,
+ CV_IA64_AR113 = 3185,
+ CV_IA64_AR114 = 3186,
+ CV_IA64_AR115 = 3187,
+ CV_IA64_AR116 = 3188,
+ CV_IA64_AR117 = 3189,
+ CV_IA64_AR118 = 3190,
+ CV_IA64_AR119 = 3191,
+ CV_IA64_AR120 = 3192,
+ CV_IA64_AR121 = 3193,
+ CV_IA64_AR122 = 3194,
+ CV_IA64_AR123 = 3195,
+ CV_IA64_AR124 = 3196,
+ CV_IA64_AR125 = 3197,
+ CV_IA64_AR126 = 3198,
+ CV_IA64_AR127 = 3199,
+
+ // CPUID Registers
+
+ CV_IA64_CPUID0 = 3328,
+ CV_IA64_CPUID1 = 3329,
+ CV_IA64_CPUID2 = 3330,
+ CV_IA64_CPUID3 = 3331,
+ CV_IA64_CPUID4 = 3332,
+
+ // Control Registers
+
+ CV_IA64_ApDCR = 4096,
+ CV_IA64_ApITM = 4097,
+ CV_IA64_ApIVA = 4098,
+ CV_IA64_CR3 = 4099,
+ CV_IA64_CR4 = 4100,
+ CV_IA64_CR5 = 4101,
+ CV_IA64_CR6 = 4102,
+ CV_IA64_CR7 = 4103,
+ CV_IA64_ApPTA = 4104,
+ CV_IA64_ApGPTA = 4105,
+ CV_IA64_CR10 = 4106,
+ CV_IA64_CR11 = 4107,
+ CV_IA64_CR12 = 4108,
+ CV_IA64_CR13 = 4109,
+ CV_IA64_CR14 = 4110,
+ CV_IA64_CR15 = 4111,
+ CV_IA64_StIPSR = 4112,
+ CV_IA64_StISR = 4113,
+ CV_IA64_CR18 = 4114,
+ CV_IA64_StIIP = 4115,
+ CV_IA64_StIFA = 4116,
+ CV_IA64_StITIR = 4117,
+ CV_IA64_StIIPA = 4118,
+ CV_IA64_StIFS = 4119,
+ CV_IA64_StIIM = 4120,
+ CV_IA64_StIHA = 4121,
+ CV_IA64_CR26 = 4122,
+ CV_IA64_CR27 = 4123,
+ CV_IA64_CR28 = 4124,
+ CV_IA64_CR29 = 4125,
+ CV_IA64_CR30 = 4126,
+ CV_IA64_CR31 = 4127,
+ CV_IA64_CR32 = 4128,
+ CV_IA64_CR33 = 4129,
+ CV_IA64_CR34 = 4130,
+ CV_IA64_CR35 = 4131,
+ CV_IA64_CR36 = 4132,
+ CV_IA64_CR37 = 4133,
+ CV_IA64_CR38 = 4134,
+ CV_IA64_CR39 = 4135,
+ CV_IA64_CR40 = 4136,
+ CV_IA64_CR41 = 4137,
+ CV_IA64_CR42 = 4138,
+ CV_IA64_CR43 = 4139,
+ CV_IA64_CR44 = 4140,
+ CV_IA64_CR45 = 4141,
+ CV_IA64_CR46 = 4142,
+ CV_IA64_CR47 = 4143,
+ CV_IA64_CR48 = 4144,
+ CV_IA64_CR49 = 4145,
+ CV_IA64_CR50 = 4146,
+ CV_IA64_CR51 = 4147,
+ CV_IA64_CR52 = 4148,
+ CV_IA64_CR53 = 4149,
+ CV_IA64_CR54 = 4150,
+ CV_IA64_CR55 = 4151,
+ CV_IA64_CR56 = 4152,
+ CV_IA64_CR57 = 4153,
+ CV_IA64_CR58 = 4154,
+ CV_IA64_CR59 = 4155,
+ CV_IA64_CR60 = 4156,
+ CV_IA64_CR61 = 4157,
+ CV_IA64_CR62 = 4158,
+ CV_IA64_CR63 = 4159,
+ CV_IA64_SaLID = 4160,
+ CV_IA64_SaIVR = 4161,
+ CV_IA64_SaTPR = 4162,
+ CV_IA64_SaEOI = 4163,
+ CV_IA64_SaIRR0 = 4164,
+ CV_IA64_SaIRR1 = 4165,
+ CV_IA64_SaIRR2 = 4166,
+ CV_IA64_SaIRR3 = 4167,
+ CV_IA64_SaITV = 4168,
+ CV_IA64_SaPMV = 4169,
+ CV_IA64_SaCMCV = 4170,
+ CV_IA64_CR75 = 4171,
+ CV_IA64_CR76 = 4172,
+ CV_IA64_CR77 = 4173,
+ CV_IA64_CR78 = 4174,
+ CV_IA64_CR79 = 4175,
+ CV_IA64_SaLRR0 = 4176,
+ CV_IA64_SaLRR1 = 4177,
+ CV_IA64_CR82 = 4178,
+ CV_IA64_CR83 = 4179,
+ CV_IA64_CR84 = 4180,
+ CV_IA64_CR85 = 4181,
+ CV_IA64_CR86 = 4182,
+ CV_IA64_CR87 = 4183,
+ CV_IA64_CR88 = 4184,
+ CV_IA64_CR89 = 4185,
+ CV_IA64_CR90 = 4186,
+ CV_IA64_CR91 = 4187,
+ CV_IA64_CR92 = 4188,
+ CV_IA64_CR93 = 4189,
+ CV_IA64_CR94 = 4190,
+ CV_IA64_CR95 = 4191,
+ CV_IA64_CR96 = 4192,
+ CV_IA64_CR97 = 4193,
+ CV_IA64_CR98 = 4194,
+ CV_IA64_CR99 = 4195,
+ CV_IA64_CR100 = 4196,
+ CV_IA64_CR101 = 4197,
+ CV_IA64_CR102 = 4198,
+ CV_IA64_CR103 = 4199,
+ CV_IA64_CR104 = 4200,
+ CV_IA64_CR105 = 4201,
+ CV_IA64_CR106 = 4202,
+ CV_IA64_CR107 = 4203,
+ CV_IA64_CR108 = 4204,
+ CV_IA64_CR109 = 4205,
+ CV_IA64_CR110 = 4206,
+ CV_IA64_CR111 = 4207,
+ CV_IA64_CR112 = 4208,
+ CV_IA64_CR113 = 4209,
+ CV_IA64_CR114 = 4210,
+ CV_IA64_CR115 = 4211,
+ CV_IA64_CR116 = 4212,
+ CV_IA64_CR117 = 4213,
+ CV_IA64_CR118 = 4214,
+ CV_IA64_CR119 = 4215,
+ CV_IA64_CR120 = 4216,
+ CV_IA64_CR121 = 4217,
+ CV_IA64_CR122 = 4218,
+ CV_IA64_CR123 = 4219,
+ CV_IA64_CR124 = 4220,
+ CV_IA64_CR125 = 4221,
+ CV_IA64_CR126 = 4222,
+ CV_IA64_CR127 = 4223,
+
+ // Protection Key Registers
+
+ CV_IA64_Pkr0 = 5120,
+ CV_IA64_Pkr1 = 5121,
+ CV_IA64_Pkr2 = 5122,
+ CV_IA64_Pkr3 = 5123,
+ CV_IA64_Pkr4 = 5124,
+ CV_IA64_Pkr5 = 5125,
+ CV_IA64_Pkr6 = 5126,
+ CV_IA64_Pkr7 = 5127,
+ CV_IA64_Pkr8 = 5128,
+ CV_IA64_Pkr9 = 5129,
+ CV_IA64_Pkr10 = 5130,
+ CV_IA64_Pkr11 = 5131,
+ CV_IA64_Pkr12 = 5132,
+ CV_IA64_Pkr13 = 5133,
+ CV_IA64_Pkr14 = 5134,
+ CV_IA64_Pkr15 = 5135,
+
+ // Region Registers
+
+ CV_IA64_Rr0 = 6144,
+ CV_IA64_Rr1 = 6145,
+ CV_IA64_Rr2 = 6146,
+ CV_IA64_Rr3 = 6147,
+ CV_IA64_Rr4 = 6148,
+ CV_IA64_Rr5 = 6149,
+ CV_IA64_Rr6 = 6150,
+ CV_IA64_Rr7 = 6151,
+
+ // Performance Monitor Data Registers
+
+ CV_IA64_PFD0 = 7168,
+ CV_IA64_PFD1 = 7169,
+ CV_IA64_PFD2 = 7170,
+ CV_IA64_PFD3 = 7171,
+ CV_IA64_PFD4 = 7172,
+ CV_IA64_PFD5 = 7173,
+ CV_IA64_PFD6 = 7174,
+ CV_IA64_PFD7 = 7175,
+ CV_IA64_PFD8 = 7176,
+ CV_IA64_PFD9 = 7177,
+ CV_IA64_PFD10 = 7178,
+ CV_IA64_PFD11 = 7179,
+ CV_IA64_PFD12 = 7180,
+ CV_IA64_PFD13 = 7181,
+ CV_IA64_PFD14 = 7182,
+ CV_IA64_PFD15 = 7183,
+ CV_IA64_PFD16 = 7184,
+ CV_IA64_PFD17 = 7185,
+
+ // Performance Monitor Config Registers
+
+ CV_IA64_PFC0 = 7424,
+ CV_IA64_PFC1 = 7425,
+ CV_IA64_PFC2 = 7426,
+ CV_IA64_PFC3 = 7427,
+ CV_IA64_PFC4 = 7428,
+ CV_IA64_PFC5 = 7429,
+ CV_IA64_PFC6 = 7430,
+ CV_IA64_PFC7 = 7431,
+ CV_IA64_PFC8 = 7432,
+ CV_IA64_PFC9 = 7433,
+ CV_IA64_PFC10 = 7434,
+ CV_IA64_PFC11 = 7435,
+ CV_IA64_PFC12 = 7436,
+ CV_IA64_PFC13 = 7437,
+ CV_IA64_PFC14 = 7438,
+ CV_IA64_PFC15 = 7439,
+
+ // Instruction Translation Registers
+
+ CV_IA64_TrI0 = 8192,
+ CV_IA64_TrI1 = 8193,
+ CV_IA64_TrI2 = 8194,
+ CV_IA64_TrI3 = 8195,
+ CV_IA64_TrI4 = 8196,
+ CV_IA64_TrI5 = 8197,
+ CV_IA64_TrI6 = 8198,
+ CV_IA64_TrI7 = 8199,
+
+ // Data Translation Registers
+
+ CV_IA64_TrD0 = 8320,
+ CV_IA64_TrD1 = 8321,
+ CV_IA64_TrD2 = 8322,
+ CV_IA64_TrD3 = 8323,
+ CV_IA64_TrD4 = 8324,
+ CV_IA64_TrD5 = 8325,
+ CV_IA64_TrD6 = 8326,
+ CV_IA64_TrD7 = 8327,
+
+ // Instruction Breakpoint Registers
+
+ CV_IA64_DbI0 = 8448,
+ CV_IA64_DbI1 = 8449,
+ CV_IA64_DbI2 = 8450,
+ CV_IA64_DbI3 = 8451,
+ CV_IA64_DbI4 = 8452,
+ CV_IA64_DbI5 = 8453,
+ CV_IA64_DbI6 = 8454,
+ CV_IA64_DbI7 = 8455,
+
+ // Data Breakpoint Registers
+
+ CV_IA64_DbD0 = 8576,
+ CV_IA64_DbD1 = 8577,
+ CV_IA64_DbD2 = 8578,
+ CV_IA64_DbD3 = 8579,
+ CV_IA64_DbD4 = 8580,
+ CV_IA64_DbD5 = 8581,
+ CV_IA64_DbD6 = 8582,
+ CV_IA64_DbD7 = 8583,
+
+ //
+ // Register set for the TriCore processor.
+ //
+
+ CV_TRI_NOREG = CV_REG_NONE,
+
+ // General Purpose Data Registers
+
+ CV_TRI_D0 = 10,
+ CV_TRI_D1 = 11,
+ CV_TRI_D2 = 12,
+ CV_TRI_D3 = 13,
+ CV_TRI_D4 = 14,
+ CV_TRI_D5 = 15,
+ CV_TRI_D6 = 16,
+ CV_TRI_D7 = 17,
+ CV_TRI_D8 = 18,
+ CV_TRI_D9 = 19,
+ CV_TRI_D10 = 20,
+ CV_TRI_D11 = 21,
+ CV_TRI_D12 = 22,
+ CV_TRI_D13 = 23,
+ CV_TRI_D14 = 24,
+ CV_TRI_D15 = 25,
+
+ // General Purpose Address Registers
+
+ CV_TRI_A0 = 26,
+ CV_TRI_A1 = 27,
+ CV_TRI_A2 = 28,
+ CV_TRI_A3 = 29,
+ CV_TRI_A4 = 30,
+ CV_TRI_A5 = 31,
+ CV_TRI_A6 = 32,
+ CV_TRI_A7 = 33,
+ CV_TRI_A8 = 34,
+ CV_TRI_A9 = 35,
+ CV_TRI_A10 = 36,
+ CV_TRI_A11 = 37,
+ CV_TRI_A12 = 38,
+ CV_TRI_A13 = 39,
+ CV_TRI_A14 = 40,
+ CV_TRI_A15 = 41,
+
+ // Extended (64-bit) data registers
+
+ CV_TRI_E0 = 42,
+ CV_TRI_E2 = 43,
+ CV_TRI_E4 = 44,
+ CV_TRI_E6 = 45,
+ CV_TRI_E8 = 46,
+ CV_TRI_E10 = 47,
+ CV_TRI_E12 = 48,
+ CV_TRI_E14 = 49,
+
+ // Extended (64-bit) address registers
+
+ CV_TRI_EA0 = 50,
+ CV_TRI_EA2 = 51,
+ CV_TRI_EA4 = 52,
+ CV_TRI_EA6 = 53,
+ CV_TRI_EA8 = 54,
+ CV_TRI_EA10 = 55,
+ CV_TRI_EA12 = 56,
+ CV_TRI_EA14 = 57,
+
+ CV_TRI_PSW = 58,
+ CV_TRI_PCXI = 59,
+ CV_TRI_PC = 60,
+ CV_TRI_FCX = 61,
+ CV_TRI_LCX = 62,
+ CV_TRI_ISP = 63,
+ CV_TRI_ICR = 64,
+ CV_TRI_BIV = 65,
+ CV_TRI_BTV = 66,
+ CV_TRI_SYSCON = 67,
+ CV_TRI_DPRx_0 = 68,
+ CV_TRI_DPRx_1 = 69,
+ CV_TRI_DPRx_2 = 70,
+ CV_TRI_DPRx_3 = 71,
+ CV_TRI_CPRx_0 = 68,
+ CV_TRI_CPRx_1 = 69,
+ CV_TRI_CPRx_2 = 70,
+ CV_TRI_CPRx_3 = 71,
+ CV_TRI_DPMx_0 = 68,
+ CV_TRI_DPMx_1 = 69,
+ CV_TRI_DPMx_2 = 70,
+ CV_TRI_DPMx_3 = 71,
+ CV_TRI_CPMx_0 = 68,
+ CV_TRI_CPMx_1 = 69,
+ CV_TRI_CPMx_2 = 70,
+ CV_TRI_CPMx_3 = 71,
+ CV_TRI_DBGSSR = 72,
+ CV_TRI_EXEVT = 73,
+ CV_TRI_SWEVT = 74,
+ CV_TRI_CREVT = 75,
+ CV_TRI_TRnEVT = 76,
+ CV_TRI_MMUCON = 77,
+ CV_TRI_ASI = 78,
+ CV_TRI_TVA = 79,
+ CV_TRI_TPA = 80,
+ CV_TRI_TPX = 81,
+ CV_TRI_TFA = 82,
+
+ //
+ // Register set for the AM33 and related processors.
+ //
+
+ CV_AM33_NOREG = CV_REG_NONE,
+
+ // "Extended" (general purpose integer) registers
+ CV_AM33_E0 = 10,
+ CV_AM33_E1 = 11,
+ CV_AM33_E2 = 12,
+ CV_AM33_E3 = 13,
+ CV_AM33_E4 = 14,
+ CV_AM33_E5 = 15,
+ CV_AM33_E6 = 16,
+ CV_AM33_E7 = 17,
+
+ // Address registers
+ CV_AM33_A0 = 20,
+ CV_AM33_A1 = 21,
+ CV_AM33_A2 = 22,
+ CV_AM33_A3 = 23,
+
+ // Integer data registers
+ CV_AM33_D0 = 30,
+ CV_AM33_D1 = 31,
+ CV_AM33_D2 = 32,
+ CV_AM33_D3 = 33,
+
+ // (Single-precision) floating-point registers
+ CV_AM33_FS0 = 40,
+ CV_AM33_FS1 = 41,
+ CV_AM33_FS2 = 42,
+ CV_AM33_FS3 = 43,
+ CV_AM33_FS4 = 44,
+ CV_AM33_FS5 = 45,
+ CV_AM33_FS6 = 46,
+ CV_AM33_FS7 = 47,
+ CV_AM33_FS8 = 48,
+ CV_AM33_FS9 = 49,
+ CV_AM33_FS10 = 50,
+ CV_AM33_FS11 = 51,
+ CV_AM33_FS12 = 52,
+ CV_AM33_FS13 = 53,
+ CV_AM33_FS14 = 54,
+ CV_AM33_FS15 = 55,
+ CV_AM33_FS16 = 56,
+ CV_AM33_FS17 = 57,
+ CV_AM33_FS18 = 58,
+ CV_AM33_FS19 = 59,
+ CV_AM33_FS20 = 60,
+ CV_AM33_FS21 = 61,
+ CV_AM33_FS22 = 62,
+ CV_AM33_FS23 = 63,
+ CV_AM33_FS24 = 64,
+ CV_AM33_FS25 = 65,
+ CV_AM33_FS26 = 66,
+ CV_AM33_FS27 = 67,
+ CV_AM33_FS28 = 68,
+ CV_AM33_FS29 = 69,
+ CV_AM33_FS30 = 70,
+ CV_AM33_FS31 = 71,
+
+ // Special purpose registers
+
+ // Stack pointer
+ CV_AM33_SP = 80,
+
+ // Program counter
+ CV_AM33_PC = 81,
+
+ // Multiply-divide/accumulate registers
+ CV_AM33_MDR = 82,
+ CV_AM33_MDRQ = 83,
+ CV_AM33_MCRH = 84,
+ CV_AM33_MCRL = 85,
+ CV_AM33_MCVF = 86,
+
+ // CPU status words
+ CV_AM33_EPSW = 87,
+ CV_AM33_FPCR = 88,
+
+ // Loop buffer registers
+ CV_AM33_LIR = 89,
+ CV_AM33_LAR = 90,
+
+ //
+ // Register set for the Mitsubishi M32R
+ //
+
+ CV_M32R_NOREG = CV_REG_NONE,
+
+ CV_M32R_R0 = 10,
+ CV_M32R_R1 = 11,
+ CV_M32R_R2 = 12,
+ CV_M32R_R3 = 13,
+ CV_M32R_R4 = 14,
+ CV_M32R_R5 = 15,
+ CV_M32R_R6 = 16,
+ CV_M32R_R7 = 17,
+ CV_M32R_R8 = 18,
+ CV_M32R_R9 = 19,
+ CV_M32R_R10 = 20,
+ CV_M32R_R11 = 21,
+ CV_M32R_R12 = 22, // Gloabal Pointer, if used
+ CV_M32R_R13 = 23, // Frame Pointer, if allocated
+ CV_M32R_R14 = 24, // Link Register
+ CV_M32R_R15 = 25, // Stack Pointer
+ CV_M32R_PSW = 26, // Preocessor Status Register
+ CV_M32R_CBR = 27, // Condition Bit Register
+ CV_M32R_SPI = 28, // Interrupt Stack Pointer
+ CV_M32R_SPU = 29, // User Stack Pointer
+ CV_M32R_SPO = 30, // OS Stack Pointer
+ CV_M32R_BPC = 31, // Backup Program Counter
+ CV_M32R_ACHI = 32, // Accumulator High
+ CV_M32R_ACLO = 33, // Accumulator Low
+ CV_M32R_PC = 34, // Program Counter
+
+ //
+ // Register set for the SuperH SHMedia processor including compact
+ // mode
+ //
+
+ // Integer - 64 bit general registers
+ CV_SHMEDIA_NOREG = CV_REG_NONE,
+ CV_SHMEDIA_R0 = 10,
+ CV_SHMEDIA_R1 = 11,
+ CV_SHMEDIA_R2 = 12,
+ CV_SHMEDIA_R3 = 13,
+ CV_SHMEDIA_R4 = 14,
+ CV_SHMEDIA_R5 = 15,
+ CV_SHMEDIA_R6 = 16,
+ CV_SHMEDIA_R7 = 17,
+ CV_SHMEDIA_R8 = 18,
+ CV_SHMEDIA_R9 = 19,
+ CV_SHMEDIA_R10 = 20,
+ CV_SHMEDIA_R11 = 21,
+ CV_SHMEDIA_R12 = 22,
+ CV_SHMEDIA_R13 = 23,
+ CV_SHMEDIA_R14 = 24,
+ CV_SHMEDIA_R15 = 25,
+ CV_SHMEDIA_R16 = 26,
+ CV_SHMEDIA_R17 = 27,
+ CV_SHMEDIA_R18 = 28,
+ CV_SHMEDIA_R19 = 29,
+ CV_SHMEDIA_R20 = 30,
+ CV_SHMEDIA_R21 = 31,
+ CV_SHMEDIA_R22 = 32,
+ CV_SHMEDIA_R23 = 33,
+ CV_SHMEDIA_R24 = 34,
+ CV_SHMEDIA_R25 = 35,
+ CV_SHMEDIA_R26 = 36,
+ CV_SHMEDIA_R27 = 37,
+ CV_SHMEDIA_R28 = 38,
+ CV_SHMEDIA_R29 = 39,
+ CV_SHMEDIA_R30 = 40,
+ CV_SHMEDIA_R31 = 41,
+ CV_SHMEDIA_R32 = 42,
+ CV_SHMEDIA_R33 = 43,
+ CV_SHMEDIA_R34 = 44,
+ CV_SHMEDIA_R35 = 45,
+ CV_SHMEDIA_R36 = 46,
+ CV_SHMEDIA_R37 = 47,
+ CV_SHMEDIA_R38 = 48,
+ CV_SHMEDIA_R39 = 49,
+ CV_SHMEDIA_R40 = 50,
+ CV_SHMEDIA_R41 = 51,
+ CV_SHMEDIA_R42 = 52,
+ CV_SHMEDIA_R43 = 53,
+ CV_SHMEDIA_R44 = 54,
+ CV_SHMEDIA_R45 = 55,
+ CV_SHMEDIA_R46 = 56,
+ CV_SHMEDIA_R47 = 57,
+ CV_SHMEDIA_R48 = 58,
+ CV_SHMEDIA_R49 = 59,
+ CV_SHMEDIA_R50 = 60,
+ CV_SHMEDIA_R51 = 61,
+ CV_SHMEDIA_R52 = 62,
+ CV_SHMEDIA_R53 = 63,
+ CV_SHMEDIA_R54 = 64,
+ CV_SHMEDIA_R55 = 65,
+ CV_SHMEDIA_R56 = 66,
+ CV_SHMEDIA_R57 = 67,
+ CV_SHMEDIA_R58 = 68,
+ CV_SHMEDIA_R59 = 69,
+ CV_SHMEDIA_R60 = 70,
+ CV_SHMEDIA_R61 = 71,
+ CV_SHMEDIA_R62 = 72,
+ CV_SHMEDIA_R63 = 73,
+
+ // Target Registers - 32 bit
+ CV_SHMEDIA_TR0 = 74,
+ CV_SHMEDIA_TR1 = 75,
+ CV_SHMEDIA_TR2 = 76,
+ CV_SHMEDIA_TR3 = 77,
+ CV_SHMEDIA_TR4 = 78,
+ CV_SHMEDIA_TR5 = 79,
+ CV_SHMEDIA_TR6 = 80,
+ CV_SHMEDIA_TR7 = 81,
+ CV_SHMEDIA_TR8 = 82, // future-proof
+ CV_SHMEDIA_TR9 = 83, // future-proof
+ CV_SHMEDIA_TR10 = 84, // future-proof
+ CV_SHMEDIA_TR11 = 85, // future-proof
+ CV_SHMEDIA_TR12 = 86, // future-proof
+ CV_SHMEDIA_TR13 = 87, // future-proof
+ CV_SHMEDIA_TR14 = 88, // future-proof
+ CV_SHMEDIA_TR15 = 89, // future-proof
+
+ // Single - 32 bit fp registers
+ CV_SHMEDIA_FR0 = 128,
+ CV_SHMEDIA_FR1 = 129,
+ CV_SHMEDIA_FR2 = 130,
+ CV_SHMEDIA_FR3 = 131,
+ CV_SHMEDIA_FR4 = 132,
+ CV_SHMEDIA_FR5 = 133,
+ CV_SHMEDIA_FR6 = 134,
+ CV_SHMEDIA_FR7 = 135,
+ CV_SHMEDIA_FR8 = 136,
+ CV_SHMEDIA_FR9 = 137,
+ CV_SHMEDIA_FR10 = 138,
+ CV_SHMEDIA_FR11 = 139,
+ CV_SHMEDIA_FR12 = 140,
+ CV_SHMEDIA_FR13 = 141,
+ CV_SHMEDIA_FR14 = 142,
+ CV_SHMEDIA_FR15 = 143,
+ CV_SHMEDIA_FR16 = 144,
+ CV_SHMEDIA_FR17 = 145,
+ CV_SHMEDIA_FR18 = 146,
+ CV_SHMEDIA_FR19 = 147,
+ CV_SHMEDIA_FR20 = 148,
+ CV_SHMEDIA_FR21 = 149,
+ CV_SHMEDIA_FR22 = 150,
+ CV_SHMEDIA_FR23 = 151,
+ CV_SHMEDIA_FR24 = 152,
+ CV_SHMEDIA_FR25 = 153,
+ CV_SHMEDIA_FR26 = 154,
+ CV_SHMEDIA_FR27 = 155,
+ CV_SHMEDIA_FR28 = 156,
+ CV_SHMEDIA_FR29 = 157,
+ CV_SHMEDIA_FR30 = 158,
+ CV_SHMEDIA_FR31 = 159,
+ CV_SHMEDIA_FR32 = 160,
+ CV_SHMEDIA_FR33 = 161,
+ CV_SHMEDIA_FR34 = 162,
+ CV_SHMEDIA_FR35 = 163,
+ CV_SHMEDIA_FR36 = 164,
+ CV_SHMEDIA_FR37 = 165,
+ CV_SHMEDIA_FR38 = 166,
+ CV_SHMEDIA_FR39 = 167,
+ CV_SHMEDIA_FR40 = 168,
+ CV_SHMEDIA_FR41 = 169,
+ CV_SHMEDIA_FR42 = 170,
+ CV_SHMEDIA_FR43 = 171,
+ CV_SHMEDIA_FR44 = 172,
+ CV_SHMEDIA_FR45 = 173,
+ CV_SHMEDIA_FR46 = 174,
+ CV_SHMEDIA_FR47 = 175,
+ CV_SHMEDIA_FR48 = 176,
+ CV_SHMEDIA_FR49 = 177,
+ CV_SHMEDIA_FR50 = 178,
+ CV_SHMEDIA_FR51 = 179,
+ CV_SHMEDIA_FR52 = 180,
+ CV_SHMEDIA_FR53 = 181,
+ CV_SHMEDIA_FR54 = 182,
+ CV_SHMEDIA_FR55 = 183,
+ CV_SHMEDIA_FR56 = 184,
+ CV_SHMEDIA_FR57 = 185,
+ CV_SHMEDIA_FR58 = 186,
+ CV_SHMEDIA_FR59 = 187,
+ CV_SHMEDIA_FR60 = 188,
+ CV_SHMEDIA_FR61 = 189,
+ CV_SHMEDIA_FR62 = 190,
+ CV_SHMEDIA_FR63 = 191,
+
+ // Double - 64 bit synonyms for 32bit fp register pairs
+ // subtract 128 to find first base single register
+ CV_SHMEDIA_DR0 = 256,
+ CV_SHMEDIA_DR2 = 258,
+ CV_SHMEDIA_DR4 = 260,
+ CV_SHMEDIA_DR6 = 262,
+ CV_SHMEDIA_DR8 = 264,
+ CV_SHMEDIA_DR10 = 266,
+ CV_SHMEDIA_DR12 = 268,
+ CV_SHMEDIA_DR14 = 270,
+ CV_SHMEDIA_DR16 = 272,
+ CV_SHMEDIA_DR18 = 274,
+ CV_SHMEDIA_DR20 = 276,
+ CV_SHMEDIA_DR22 = 278,
+ CV_SHMEDIA_DR24 = 280,
+ CV_SHMEDIA_DR26 = 282,
+ CV_SHMEDIA_DR28 = 284,
+ CV_SHMEDIA_DR30 = 286,
+ CV_SHMEDIA_DR32 = 288,
+ CV_SHMEDIA_DR34 = 290,
+ CV_SHMEDIA_DR36 = 292,
+ CV_SHMEDIA_DR38 = 294,
+ CV_SHMEDIA_DR40 = 296,
+ CV_SHMEDIA_DR42 = 298,
+ CV_SHMEDIA_DR44 = 300,
+ CV_SHMEDIA_DR46 = 302,
+ CV_SHMEDIA_DR48 = 304,
+ CV_SHMEDIA_DR50 = 306,
+ CV_SHMEDIA_DR52 = 308,
+ CV_SHMEDIA_DR54 = 310,
+ CV_SHMEDIA_DR56 = 312,
+ CV_SHMEDIA_DR58 = 314,
+ CV_SHMEDIA_DR60 = 316,
+ CV_SHMEDIA_DR62 = 318,
+
+ // Vector - 128 bit synonyms for 32bit fp register quads
+ // subtract 384 to find first base single register
+ CV_SHMEDIA_FV0 = 512,
+ CV_SHMEDIA_FV4 = 516,
+ CV_SHMEDIA_FV8 = 520,
+ CV_SHMEDIA_FV12 = 524,
+ CV_SHMEDIA_FV16 = 528,
+ CV_SHMEDIA_FV20 = 532,
+ CV_SHMEDIA_FV24 = 536,
+ CV_SHMEDIA_FV28 = 540,
+ CV_SHMEDIA_FV32 = 544,
+ CV_SHMEDIA_FV36 = 548,
+ CV_SHMEDIA_FV40 = 552,
+ CV_SHMEDIA_FV44 = 556,
+ CV_SHMEDIA_FV48 = 560,
+ CV_SHMEDIA_FV52 = 564,
+ CV_SHMEDIA_FV56 = 568,
+ CV_SHMEDIA_FV60 = 572,
+
+ // Matrix - 512 bit synonyms for 16 adjacent 32bit fp registers
+ // subtract 896 to find first base single register
+ CV_SHMEDIA_MTRX0 = 1024,
+ CV_SHMEDIA_MTRX16 = 1040,
+ CV_SHMEDIA_MTRX32 = 1056,
+ CV_SHMEDIA_MTRX48 = 1072,
+
+ // Control - Implementation defined 64bit control registers
+ CV_SHMEDIA_CR0 = 2000,
+ CV_SHMEDIA_CR1 = 2001,
+ CV_SHMEDIA_CR2 = 2002,
+ CV_SHMEDIA_CR3 = 2003,
+ CV_SHMEDIA_CR4 = 2004,
+ CV_SHMEDIA_CR5 = 2005,
+ CV_SHMEDIA_CR6 = 2006,
+ CV_SHMEDIA_CR7 = 2007,
+ CV_SHMEDIA_CR8 = 2008,
+ CV_SHMEDIA_CR9 = 2009,
+ CV_SHMEDIA_CR10 = 2010,
+ CV_SHMEDIA_CR11 = 2011,
+ CV_SHMEDIA_CR12 = 2012,
+ CV_SHMEDIA_CR13 = 2013,
+ CV_SHMEDIA_CR14 = 2014,
+ CV_SHMEDIA_CR15 = 2015,
+ CV_SHMEDIA_CR16 = 2016,
+ CV_SHMEDIA_CR17 = 2017,
+ CV_SHMEDIA_CR18 = 2018,
+ CV_SHMEDIA_CR19 = 2019,
+ CV_SHMEDIA_CR20 = 2020,
+ CV_SHMEDIA_CR21 = 2021,
+ CV_SHMEDIA_CR22 = 2022,
+ CV_SHMEDIA_CR23 = 2023,
+ CV_SHMEDIA_CR24 = 2024,
+ CV_SHMEDIA_CR25 = 2025,
+ CV_SHMEDIA_CR26 = 2026,
+ CV_SHMEDIA_CR27 = 2027,
+ CV_SHMEDIA_CR28 = 2028,
+ CV_SHMEDIA_CR29 = 2029,
+ CV_SHMEDIA_CR30 = 2030,
+ CV_SHMEDIA_CR31 = 2031,
+ CV_SHMEDIA_CR32 = 2032,
+ CV_SHMEDIA_CR33 = 2033,
+ CV_SHMEDIA_CR34 = 2034,
+ CV_SHMEDIA_CR35 = 2035,
+ CV_SHMEDIA_CR36 = 2036,
+ CV_SHMEDIA_CR37 = 2037,
+ CV_SHMEDIA_CR38 = 2038,
+ CV_SHMEDIA_CR39 = 2039,
+ CV_SHMEDIA_CR40 = 2040,
+ CV_SHMEDIA_CR41 = 2041,
+ CV_SHMEDIA_CR42 = 2042,
+ CV_SHMEDIA_CR43 = 2043,
+ CV_SHMEDIA_CR44 = 2044,
+ CV_SHMEDIA_CR45 = 2045,
+ CV_SHMEDIA_CR46 = 2046,
+ CV_SHMEDIA_CR47 = 2047,
+ CV_SHMEDIA_CR48 = 2048,
+ CV_SHMEDIA_CR49 = 2049,
+ CV_SHMEDIA_CR50 = 2050,
+ CV_SHMEDIA_CR51 = 2051,
+ CV_SHMEDIA_CR52 = 2052,
+ CV_SHMEDIA_CR53 = 2053,
+ CV_SHMEDIA_CR54 = 2054,
+ CV_SHMEDIA_CR55 = 2055,
+ CV_SHMEDIA_CR56 = 2056,
+ CV_SHMEDIA_CR57 = 2057,
+ CV_SHMEDIA_CR58 = 2058,
+ CV_SHMEDIA_CR59 = 2059,
+ CV_SHMEDIA_CR60 = 2060,
+ CV_SHMEDIA_CR61 = 2061,
+ CV_SHMEDIA_CR62 = 2062,
+ CV_SHMEDIA_CR63 = 2063,
+
+ CV_SHMEDIA_FPSCR = 2064,
+
+ // Compact mode synonyms
+ CV_SHMEDIA_GBR = CV_SHMEDIA_R16,
+ CV_SHMEDIA_MACL = 90, // synonym for lower 32bits of media R17
+ CV_SHMEDIA_MACH = 91, // synonym for upper 32bits of media R17
+ CV_SHMEDIA_PR = CV_SHMEDIA_R18,
+ CV_SHMEDIA_T = 92, // synonym for lowest bit of media R19
+ CV_SHMEDIA_FPUL = CV_SHMEDIA_FR32,
+ CV_SHMEDIA_PC = 93,
+ CV_SHMEDIA_SR = CV_SHMEDIA_CR0,
+
+ //
+ // AMD64 registers
+ //
+
+ CV_AMD64_AL = 1,
+ CV_AMD64_CL = 2,
+ CV_AMD64_DL = 3,
+ CV_AMD64_BL = 4,
+ CV_AMD64_AH = 5,
+ CV_AMD64_CH = 6,
+ CV_AMD64_DH = 7,
+ CV_AMD64_BH = 8,
+ CV_AMD64_AX = 9,
+ CV_AMD64_CX = 10,
+ CV_AMD64_DX = 11,
+ CV_AMD64_BX = 12,
+ CV_AMD64_SP = 13,
+ CV_AMD64_BP = 14,
+ CV_AMD64_SI = 15,
+ CV_AMD64_DI = 16,
+ CV_AMD64_EAX = 17,
+ CV_AMD64_ECX = 18,
+ CV_AMD64_EDX = 19,
+ CV_AMD64_EBX = 20,
+ CV_AMD64_ESP = 21,
+ CV_AMD64_EBP = 22,
+ CV_AMD64_ESI = 23,
+ CV_AMD64_EDI = 24,
+ CV_AMD64_ES = 25,
+ CV_AMD64_CS = 26,
+ CV_AMD64_SS = 27,
+ CV_AMD64_DS = 28,
+ CV_AMD64_FS = 29,
+ CV_AMD64_GS = 30,
+ CV_AMD64_FLAGS = 32,
+ CV_AMD64_RIP = 33,
+ CV_AMD64_EFLAGS = 34,
+
+ // Control registers
+ CV_AMD64_CR0 = 80,
+ CV_AMD64_CR1 = 81,
+ CV_AMD64_CR2 = 82,
+ CV_AMD64_CR3 = 83,
+ CV_AMD64_CR4 = 84,
+ CV_AMD64_CR8 = 88,
+
+ // Debug registers
+ CV_AMD64_DR0 = 90,
+ CV_AMD64_DR1 = 91,
+ CV_AMD64_DR2 = 92,
+ CV_AMD64_DR3 = 93,
+ CV_AMD64_DR4 = 94,
+ CV_AMD64_DR5 = 95,
+ CV_AMD64_DR6 = 96,
+ CV_AMD64_DR7 = 97,
+ CV_AMD64_DR8 = 98,
+ CV_AMD64_DR9 = 99,
+ CV_AMD64_DR10 = 100,
+ CV_AMD64_DR11 = 101,
+ CV_AMD64_DR12 = 102,
+ CV_AMD64_DR13 = 103,
+ CV_AMD64_DR14 = 104,
+ CV_AMD64_DR15 = 105,
+
+ CV_AMD64_GDTR = 110,
+ CV_AMD64_GDTL = 111,
+ CV_AMD64_IDTR = 112,
+ CV_AMD64_IDTL = 113,
+ CV_AMD64_LDTR = 114,
+ CV_AMD64_TR = 115,
+
+ CV_AMD64_ST0 = 128,
+ CV_AMD64_ST1 = 129,
+ CV_AMD64_ST2 = 130,
+ CV_AMD64_ST3 = 131,
+ CV_AMD64_ST4 = 132,
+ CV_AMD64_ST5 = 133,
+ CV_AMD64_ST6 = 134,
+ CV_AMD64_ST7 = 135,
+ CV_AMD64_CTRL = 136,
+ CV_AMD64_STAT = 137,
+ CV_AMD64_TAG = 138,
+ CV_AMD64_FPIP = 139,
+ CV_AMD64_FPCS = 140,
+ CV_AMD64_FPDO = 141,
+ CV_AMD64_FPDS = 142,
+ CV_AMD64_ISEM = 143,
+ CV_AMD64_FPEIP = 144,
+ CV_AMD64_FPEDO = 145,
+
+ CV_AMD64_MM0 = 146,
+ CV_AMD64_MM1 = 147,
+ CV_AMD64_MM2 = 148,
+ CV_AMD64_MM3 = 149,
+ CV_AMD64_MM4 = 150,
+ CV_AMD64_MM5 = 151,
+ CV_AMD64_MM6 = 152,
+ CV_AMD64_MM7 = 153,
+
+ CV_AMD64_XMM0 = 154, // KATMAI registers
+ CV_AMD64_XMM1 = 155,
+ CV_AMD64_XMM2 = 156,
+ CV_AMD64_XMM3 = 157,
+ CV_AMD64_XMM4 = 158,
+ CV_AMD64_XMM5 = 159,
+ CV_AMD64_XMM6 = 160,
+ CV_AMD64_XMM7 = 161,
+
+ CV_AMD64_XMM0_0 = 162, // KATMAI sub-registers
+ CV_AMD64_XMM0_1 = 163,
+ CV_AMD64_XMM0_2 = 164,
+ CV_AMD64_XMM0_3 = 165,
+ CV_AMD64_XMM1_0 = 166,
+ CV_AMD64_XMM1_1 = 167,
+ CV_AMD64_XMM1_2 = 168,
+ CV_AMD64_XMM1_3 = 169,
+ CV_AMD64_XMM2_0 = 170,
+ CV_AMD64_XMM2_1 = 171,
+ CV_AMD64_XMM2_2 = 172,
+ CV_AMD64_XMM2_3 = 173,
+ CV_AMD64_XMM3_0 = 174,
+ CV_AMD64_XMM3_1 = 175,
+ CV_AMD64_XMM3_2 = 176,
+ CV_AMD64_XMM3_3 = 177,
+ CV_AMD64_XMM4_0 = 178,
+ CV_AMD64_XMM4_1 = 179,
+ CV_AMD64_XMM4_2 = 180,
+ CV_AMD64_XMM4_3 = 181,
+ CV_AMD64_XMM5_0 = 182,
+ CV_AMD64_XMM5_1 = 183,
+ CV_AMD64_XMM5_2 = 184,
+ CV_AMD64_XMM5_3 = 185,
+ CV_AMD64_XMM6_0 = 186,
+ CV_AMD64_XMM6_1 = 187,
+ CV_AMD64_XMM6_2 = 188,
+ CV_AMD64_XMM6_3 = 189,
+ CV_AMD64_XMM7_0 = 190,
+ CV_AMD64_XMM7_1 = 191,
+ CV_AMD64_XMM7_2 = 192,
+ CV_AMD64_XMM7_3 = 193,
+
+ CV_AMD64_XMM0L = 194,
+ CV_AMD64_XMM1L = 195,
+ CV_AMD64_XMM2L = 196,
+ CV_AMD64_XMM3L = 197,
+ CV_AMD64_XMM4L = 198,
+ CV_AMD64_XMM5L = 199,
+ CV_AMD64_XMM6L = 200,
+ CV_AMD64_XMM7L = 201,
+
+ CV_AMD64_XMM0H = 202,
+ CV_AMD64_XMM1H = 203,
+ CV_AMD64_XMM2H = 204,
+ CV_AMD64_XMM3H = 205,
+ CV_AMD64_XMM4H = 206,
+ CV_AMD64_XMM5H = 207,
+ CV_AMD64_XMM6H = 208,
+ CV_AMD64_XMM7H = 209,
+
+ CV_AMD64_MXCSR = 211, // XMM status register
+
+ CV_AMD64_EMM0L = 220, // XMM sub-registers (WNI integer)
+ CV_AMD64_EMM1L = 221,
+ CV_AMD64_EMM2L = 222,
+ CV_AMD64_EMM3L = 223,
+ CV_AMD64_EMM4L = 224,
+ CV_AMD64_EMM5L = 225,
+ CV_AMD64_EMM6L = 226,
+ CV_AMD64_EMM7L = 227,
+
+ CV_AMD64_EMM0H = 228,
+ CV_AMD64_EMM1H = 229,
+ CV_AMD64_EMM2H = 230,
+ CV_AMD64_EMM3H = 231,
+ CV_AMD64_EMM4H = 232,
+ CV_AMD64_EMM5H = 233,
+ CV_AMD64_EMM6H = 234,
+ CV_AMD64_EMM7H = 235,
+
+ // do not change the order of these regs, first one must be even too
+ CV_AMD64_MM00 = 236,
+ CV_AMD64_MM01 = 237,
+ CV_AMD64_MM10 = 238,
+ CV_AMD64_MM11 = 239,
+ CV_AMD64_MM20 = 240,
+ CV_AMD64_MM21 = 241,
+ CV_AMD64_MM30 = 242,
+ CV_AMD64_MM31 = 243,
+ CV_AMD64_MM40 = 244,
+ CV_AMD64_MM41 = 245,
+ CV_AMD64_MM50 = 246,
+ CV_AMD64_MM51 = 247,
+ CV_AMD64_MM60 = 248,
+ CV_AMD64_MM61 = 249,
+ CV_AMD64_MM70 = 250,
+ CV_AMD64_MM71 = 251,
+
+ // Extended KATMAI registers
+ CV_AMD64_XMM8 = 252, // KATMAI registers
+ CV_AMD64_XMM9 = 253,
+ CV_AMD64_XMM10 = 254,
+ CV_AMD64_XMM11 = 255,
+ CV_AMD64_XMM12 = 256,
+ CV_AMD64_XMM13 = 257,
+ CV_AMD64_XMM14 = 258,
+ CV_AMD64_XMM15 = 259,
+
+ CV_AMD64_XMM8_0 = 260, // KATMAI sub-registers
+ CV_AMD64_XMM8_1 = 261,
+ CV_AMD64_XMM8_2 = 262,
+ CV_AMD64_XMM8_3 = 263,
+ CV_AMD64_XMM9_0 = 264,
+ CV_AMD64_XMM9_1 = 265,
+ CV_AMD64_XMM9_2 = 266,
+ CV_AMD64_XMM9_3 = 267,
+ CV_AMD64_XMM10_0 = 268,
+ CV_AMD64_XMM10_1 = 269,
+ CV_AMD64_XMM10_2 = 270,
+ CV_AMD64_XMM10_3 = 271,
+ CV_AMD64_XMM11_0 = 272,
+ CV_AMD64_XMM11_1 = 273,
+ CV_AMD64_XMM11_2 = 274,
+ CV_AMD64_XMM11_3 = 275,
+ CV_AMD64_XMM12_0 = 276,
+ CV_AMD64_XMM12_1 = 277,
+ CV_AMD64_XMM12_2 = 278,
+ CV_AMD64_XMM12_3 = 279,
+ CV_AMD64_XMM13_0 = 280,
+ CV_AMD64_XMM13_1 = 281,
+ CV_AMD64_XMM13_2 = 282,
+ CV_AMD64_XMM13_3 = 283,
+ CV_AMD64_XMM14_0 = 284,
+ CV_AMD64_XMM14_1 = 285,
+ CV_AMD64_XMM14_2 = 286,
+ CV_AMD64_XMM14_3 = 287,
+ CV_AMD64_XMM15_0 = 288,
+ CV_AMD64_XMM15_1 = 289,
+ CV_AMD64_XMM15_2 = 290,
+ CV_AMD64_XMM15_3 = 291,
+
+ CV_AMD64_XMM8L = 292,
+ CV_AMD64_XMM9L = 293,
+ CV_AMD64_XMM10L = 294,
+ CV_AMD64_XMM11L = 295,
+ CV_AMD64_XMM12L = 296,
+ CV_AMD64_XMM13L = 297,
+ CV_AMD64_XMM14L = 298,
+ CV_AMD64_XMM15L = 299,
+
+ CV_AMD64_XMM8H = 300,
+ CV_AMD64_XMM9H = 301,
+ CV_AMD64_XMM10H = 302,
+ CV_AMD64_XMM11H = 303,
+ CV_AMD64_XMM12H = 304,
+ CV_AMD64_XMM13H = 305,
+ CV_AMD64_XMM14H = 306,
+ CV_AMD64_XMM15H = 307,
+
+ CV_AMD64_EMM8L = 308, // XMM sub-registers (WNI integer)
+ CV_AMD64_EMM9L = 309,
+ CV_AMD64_EMM10L = 310,
+ CV_AMD64_EMM11L = 311,
+ CV_AMD64_EMM12L = 312,
+ CV_AMD64_EMM13L = 313,
+ CV_AMD64_EMM14L = 314,
+ CV_AMD64_EMM15L = 315,
+
+ CV_AMD64_EMM8H = 316,
+ CV_AMD64_EMM9H = 317,
+ CV_AMD64_EMM10H = 318,
+ CV_AMD64_EMM11H = 319,
+ CV_AMD64_EMM12H = 320,
+ CV_AMD64_EMM13H = 321,
+ CV_AMD64_EMM14H = 322,
+ CV_AMD64_EMM15H = 323,
+
+ // Low byte forms of some standard registers
+ CV_AMD64_SIL = 324,
+ CV_AMD64_DIL = 325,
+ CV_AMD64_BPL = 326,
+ CV_AMD64_SPL = 327,
+
+ // 64-bit regular registers
+ CV_AMD64_RAX = 328,
+ CV_AMD64_RBX = 329,
+ CV_AMD64_RCX = 330,
+ CV_AMD64_RDX = 331,
+ CV_AMD64_RSI = 332,
+ CV_AMD64_RDI = 333,
+ CV_AMD64_RBP = 334,
+ CV_AMD64_RSP = 335,
+
+ // 64-bit integer registers with 8-, 16-, and 32-bit forms (B, W, and D)
+ CV_AMD64_R8 = 336,
+ CV_AMD64_R9 = 337,
+ CV_AMD64_R10 = 338,
+ CV_AMD64_R11 = 339,
+ CV_AMD64_R12 = 340,
+ CV_AMD64_R13 = 341,
+ CV_AMD64_R14 = 342,
+ CV_AMD64_R15 = 343,
+
+ CV_AMD64_R8B = 344,
+ CV_AMD64_R9B = 345,
+ CV_AMD64_R10B = 346,
+ CV_AMD64_R11B = 347,
+ CV_AMD64_R12B = 348,
+ CV_AMD64_R13B = 349,
+ CV_AMD64_R14B = 350,
+ CV_AMD64_R15B = 351,
+
+ CV_AMD64_R8W = 352,
+ CV_AMD64_R9W = 353,
+ CV_AMD64_R10W = 354,
+ CV_AMD64_R11W = 355,
+ CV_AMD64_R12W = 356,
+ CV_AMD64_R13W = 357,
+ CV_AMD64_R14W = 358,
+ CV_AMD64_R15W = 359,
+
+ CV_AMD64_R8D = 360,
+ CV_AMD64_R9D = 361,
+ CV_AMD64_R10D = 362,
+ CV_AMD64_R11D = 363,
+ CV_AMD64_R12D = 364,
+ CV_AMD64_R13D = 365,
+ CV_AMD64_R14D = 366,
+ CV_AMD64_R15D = 367,
+
+ // AVX registers 256 bits
+ CV_AMD64_YMM0 = 368,
+ CV_AMD64_YMM1 = 369,
+ CV_AMD64_YMM2 = 370,
+ CV_AMD64_YMM3 = 371,
+ CV_AMD64_YMM4 = 372,
+ CV_AMD64_YMM5 = 373,
+ CV_AMD64_YMM6 = 374,
+ CV_AMD64_YMM7 = 375,
+ CV_AMD64_YMM8 = 376,
+ CV_AMD64_YMM9 = 377,
+ CV_AMD64_YMM10 = 378,
+ CV_AMD64_YMM11 = 379,
+ CV_AMD64_YMM12 = 380,
+ CV_AMD64_YMM13 = 381,
+ CV_AMD64_YMM14 = 382,
+ CV_AMD64_YMM15 = 383,
+
+ // AVX registers upper 128 bits
+ CV_AMD64_YMM0H = 384,
+ CV_AMD64_YMM1H = 385,
+ CV_AMD64_YMM2H = 386,
+ CV_AMD64_YMM3H = 387,
+ CV_AMD64_YMM4H = 388,
+ CV_AMD64_YMM5H = 389,
+ CV_AMD64_YMM6H = 390,
+ CV_AMD64_YMM7H = 391,
+ CV_AMD64_YMM8H = 392,
+ CV_AMD64_YMM9H = 393,
+ CV_AMD64_YMM10H = 394,
+ CV_AMD64_YMM11H = 395,
+ CV_AMD64_YMM12H = 396,
+ CV_AMD64_YMM13H = 397,
+ CV_AMD64_YMM14H = 398,
+ CV_AMD64_YMM15H = 399,
+
+ //Lower/upper 8 bytes of XMM registers. Unlike CV_AMD64_XMM<regnum><H/L>, these
+ //values reprsesent the bit patterns of the registers as 64-bit integers, not
+ //the representation of these registers as a double.
+ CV_AMD64_XMM0IL = 400,
+ CV_AMD64_XMM1IL = 401,
+ CV_AMD64_XMM2IL = 402,
+ CV_AMD64_XMM3IL = 403,
+ CV_AMD64_XMM4IL = 404,
+ CV_AMD64_XMM5IL = 405,
+ CV_AMD64_XMM6IL = 406,
+ CV_AMD64_XMM7IL = 407,
+ CV_AMD64_XMM8IL = 408,
+ CV_AMD64_XMM9IL = 409,
+ CV_AMD64_XMM10IL = 410,
+ CV_AMD64_XMM11IL = 411,
+ CV_AMD64_XMM12IL = 412,
+ CV_AMD64_XMM13IL = 413,
+ CV_AMD64_XMM14IL = 414,
+ CV_AMD64_XMM15IL = 415,
+
+ CV_AMD64_XMM0IH = 416,
+ CV_AMD64_XMM1IH = 417,
+ CV_AMD64_XMM2IH = 418,
+ CV_AMD64_XMM3IH = 419,
+ CV_AMD64_XMM4IH = 420,
+ CV_AMD64_XMM5IH = 421,
+ CV_AMD64_XMM6IH = 422,
+ CV_AMD64_XMM7IH = 423,
+ CV_AMD64_XMM8IH = 424,
+ CV_AMD64_XMM9IH = 425,
+ CV_AMD64_XMM10IH = 426,
+ CV_AMD64_XMM11IH = 427,
+ CV_AMD64_XMM12IH = 428,
+ CV_AMD64_XMM13IH = 429,
+ CV_AMD64_XMM14IH = 430,
+ CV_AMD64_XMM15IH = 431,
+
+ CV_AMD64_YMM0I0 = 432, // AVX integer registers
+ CV_AMD64_YMM0I1 = 433,
+ CV_AMD64_YMM0I2 = 434,
+ CV_AMD64_YMM0I3 = 435,
+ CV_AMD64_YMM1I0 = 436,
+ CV_AMD64_YMM1I1 = 437,
+ CV_AMD64_YMM1I2 = 438,
+ CV_AMD64_YMM1I3 = 439,
+ CV_AMD64_YMM2I0 = 440,
+ CV_AMD64_YMM2I1 = 441,
+ CV_AMD64_YMM2I2 = 442,
+ CV_AMD64_YMM2I3 = 443,
+ CV_AMD64_YMM3I0 = 444,
+ CV_AMD64_YMM3I1 = 445,
+ CV_AMD64_YMM3I2 = 446,
+ CV_AMD64_YMM3I3 = 447,
+ CV_AMD64_YMM4I0 = 448,
+ CV_AMD64_YMM4I1 = 449,
+ CV_AMD64_YMM4I2 = 450,
+ CV_AMD64_YMM4I3 = 451,
+ CV_AMD64_YMM5I0 = 452,
+ CV_AMD64_YMM5I1 = 453,
+ CV_AMD64_YMM5I2 = 454,
+ CV_AMD64_YMM5I3 = 455,
+ CV_AMD64_YMM6I0 = 456,
+ CV_AMD64_YMM6I1 = 457,
+ CV_AMD64_YMM6I2 = 458,
+ CV_AMD64_YMM6I3 = 459,
+ CV_AMD64_YMM7I0 = 460,
+ CV_AMD64_YMM7I1 = 461,
+ CV_AMD64_YMM7I2 = 462,
+ CV_AMD64_YMM7I3 = 463,
+ CV_AMD64_YMM8I0 = 464,
+ CV_AMD64_YMM8I1 = 465,
+ CV_AMD64_YMM8I2 = 466,
+ CV_AMD64_YMM8I3 = 467,
+ CV_AMD64_YMM9I0 = 468,
+ CV_AMD64_YMM9I1 = 469,
+ CV_AMD64_YMM9I2 = 470,
+ CV_AMD64_YMM9I3 = 471,
+ CV_AMD64_YMM10I0 = 472,
+ CV_AMD64_YMM10I1 = 473,
+ CV_AMD64_YMM10I2 = 474,
+ CV_AMD64_YMM10I3 = 475,
+ CV_AMD64_YMM11I0 = 476,
+ CV_AMD64_YMM11I1 = 477,
+ CV_AMD64_YMM11I2 = 478,
+ CV_AMD64_YMM11I3 = 479,
+ CV_AMD64_YMM12I0 = 480,
+ CV_AMD64_YMM12I1 = 481,
+ CV_AMD64_YMM12I2 = 482,
+ CV_AMD64_YMM12I3 = 483,
+ CV_AMD64_YMM13I0 = 484,
+ CV_AMD64_YMM13I1 = 485,
+ CV_AMD64_YMM13I2 = 486,
+ CV_AMD64_YMM13I3 = 487,
+ CV_AMD64_YMM14I0 = 488,
+ CV_AMD64_YMM14I1 = 489,
+ CV_AMD64_YMM14I2 = 490,
+ CV_AMD64_YMM14I3 = 491,
+ CV_AMD64_YMM15I0 = 492,
+ CV_AMD64_YMM15I1 = 493,
+ CV_AMD64_YMM15I2 = 494,
+ CV_AMD64_YMM15I3 = 495,
+
+ CV_AMD64_YMM0F0 = 496, // AVX floating-point single precise registers
+ CV_AMD64_YMM0F1 = 497,
+ CV_AMD64_YMM0F2 = 498,
+ CV_AMD64_YMM0F3 = 499,
+ CV_AMD64_YMM0F4 = 500,
+ CV_AMD64_YMM0F5 = 501,
+ CV_AMD64_YMM0F6 = 502,
+ CV_AMD64_YMM0F7 = 503,
+ CV_AMD64_YMM1F0 = 504,
+ CV_AMD64_YMM1F1 = 505,
+ CV_AMD64_YMM1F2 = 506,
+ CV_AMD64_YMM1F3 = 507,
+ CV_AMD64_YMM1F4 = 508,
+ CV_AMD64_YMM1F5 = 509,
+ CV_AMD64_YMM1F6 = 510,
+ CV_AMD64_YMM1F7 = 511,
+ CV_AMD64_YMM2F0 = 512,
+ CV_AMD64_YMM2F1 = 513,
+ CV_AMD64_YMM2F2 = 514,
+ CV_AMD64_YMM2F3 = 515,
+ CV_AMD64_YMM2F4 = 516,
+ CV_AMD64_YMM2F5 = 517,
+ CV_AMD64_YMM2F6 = 518,
+ CV_AMD64_YMM2F7 = 519,
+ CV_AMD64_YMM3F0 = 520,
+ CV_AMD64_YMM3F1 = 521,
+ CV_AMD64_YMM3F2 = 522,
+ CV_AMD64_YMM3F3 = 523,
+ CV_AMD64_YMM3F4 = 524,
+ CV_AMD64_YMM3F5 = 525,
+ CV_AMD64_YMM3F6 = 526,
+ CV_AMD64_YMM3F7 = 527,
+ CV_AMD64_YMM4F0 = 528,
+ CV_AMD64_YMM4F1 = 529,
+ CV_AMD64_YMM4F2 = 530,
+ CV_AMD64_YMM4F3 = 531,
+ CV_AMD64_YMM4F4 = 532,
+ CV_AMD64_YMM4F5 = 533,
+ CV_AMD64_YMM4F6 = 534,
+ CV_AMD64_YMM4F7 = 535,
+ CV_AMD64_YMM5F0 = 536,
+ CV_AMD64_YMM5F1 = 537,
+ CV_AMD64_YMM5F2 = 538,
+ CV_AMD64_YMM5F3 = 539,
+ CV_AMD64_YMM5F4 = 540,
+ CV_AMD64_YMM5F5 = 541,
+ CV_AMD64_YMM5F6 = 542,
+ CV_AMD64_YMM5F7 = 543,
+ CV_AMD64_YMM6F0 = 544,
+ CV_AMD64_YMM6F1 = 545,
+ CV_AMD64_YMM6F2 = 546,
+ CV_AMD64_YMM6F3 = 547,
+ CV_AMD64_YMM6F4 = 548,
+ CV_AMD64_YMM6F5 = 549,
+ CV_AMD64_YMM6F6 = 550,
+ CV_AMD64_YMM6F7 = 551,
+ CV_AMD64_YMM7F0 = 552,
+ CV_AMD64_YMM7F1 = 553,
+ CV_AMD64_YMM7F2 = 554,
+ CV_AMD64_YMM7F3 = 555,
+ CV_AMD64_YMM7F4 = 556,
+ CV_AMD64_YMM7F5 = 557,
+ CV_AMD64_YMM7F6 = 558,
+ CV_AMD64_YMM7F7 = 559,
+ CV_AMD64_YMM8F0 = 560,
+ CV_AMD64_YMM8F1 = 561,
+ CV_AMD64_YMM8F2 = 562,
+ CV_AMD64_YMM8F3 = 563,
+ CV_AMD64_YMM8F4 = 564,
+ CV_AMD64_YMM8F5 = 565,
+ CV_AMD64_YMM8F6 = 566,
+ CV_AMD64_YMM8F7 = 567,
+ CV_AMD64_YMM9F0 = 568,
+ CV_AMD64_YMM9F1 = 569,
+ CV_AMD64_YMM9F2 = 570,
+ CV_AMD64_YMM9F3 = 571,
+ CV_AMD64_YMM9F4 = 572,
+ CV_AMD64_YMM9F5 = 573,
+ CV_AMD64_YMM9F6 = 574,
+ CV_AMD64_YMM9F7 = 575,
+ CV_AMD64_YMM10F0 = 576,
+ CV_AMD64_YMM10F1 = 577,
+ CV_AMD64_YMM10F2 = 578,
+ CV_AMD64_YMM10F3 = 579,
+ CV_AMD64_YMM10F4 = 580,
+ CV_AMD64_YMM10F5 = 581,
+ CV_AMD64_YMM10F6 = 582,
+ CV_AMD64_YMM10F7 = 583,
+ CV_AMD64_YMM11F0 = 584,
+ CV_AMD64_YMM11F1 = 585,
+ CV_AMD64_YMM11F2 = 586,
+ CV_AMD64_YMM11F3 = 587,
+ CV_AMD64_YMM11F4 = 588,
+ CV_AMD64_YMM11F5 = 589,
+ CV_AMD64_YMM11F6 = 590,
+ CV_AMD64_YMM11F7 = 591,
+ CV_AMD64_YMM12F0 = 592,
+ CV_AMD64_YMM12F1 = 593,
+ CV_AMD64_YMM12F2 = 594,
+ CV_AMD64_YMM12F3 = 595,
+ CV_AMD64_YMM12F4 = 596,
+ CV_AMD64_YMM12F5 = 597,
+ CV_AMD64_YMM12F6 = 598,
+ CV_AMD64_YMM12F7 = 599,
+ CV_AMD64_YMM13F0 = 600,
+ CV_AMD64_YMM13F1 = 601,
+ CV_AMD64_YMM13F2 = 602,
+ CV_AMD64_YMM13F3 = 603,
+ CV_AMD64_YMM13F4 = 604,
+ CV_AMD64_YMM13F5 = 605,
+ CV_AMD64_YMM13F6 = 606,
+ CV_AMD64_YMM13F7 = 607,
+ CV_AMD64_YMM14F0 = 608,
+ CV_AMD64_YMM14F1 = 609,
+ CV_AMD64_YMM14F2 = 610,
+ CV_AMD64_YMM14F3 = 611,
+ CV_AMD64_YMM14F4 = 612,
+ CV_AMD64_YMM14F5 = 613,
+ CV_AMD64_YMM14F6 = 614,
+ CV_AMD64_YMM14F7 = 615,
+ CV_AMD64_YMM15F0 = 616,
+ CV_AMD64_YMM15F1 = 617,
+ CV_AMD64_YMM15F2 = 618,
+ CV_AMD64_YMM15F3 = 619,
+ CV_AMD64_YMM15F4 = 620,
+ CV_AMD64_YMM15F5 = 621,
+ CV_AMD64_YMM15F6 = 622,
+ CV_AMD64_YMM15F7 = 623,
+
+ CV_AMD64_YMM0D0 = 624, // AVX floating-point double precise registers
+ CV_AMD64_YMM0D1 = 625,
+ CV_AMD64_YMM0D2 = 626,
+ CV_AMD64_YMM0D3 = 627,
+ CV_AMD64_YMM1D0 = 628,
+ CV_AMD64_YMM1D1 = 629,
+ CV_AMD64_YMM1D2 = 630,
+ CV_AMD64_YMM1D3 = 631,
+ CV_AMD64_YMM2D0 = 632,
+ CV_AMD64_YMM2D1 = 633,
+ CV_AMD64_YMM2D2 = 634,
+ CV_AMD64_YMM2D3 = 635,
+ CV_AMD64_YMM3D0 = 636,
+ CV_AMD64_YMM3D1 = 637,
+ CV_AMD64_YMM3D2 = 638,
+ CV_AMD64_YMM3D3 = 639,
+ CV_AMD64_YMM4D0 = 640,
+ CV_AMD64_YMM4D1 = 641,
+ CV_AMD64_YMM4D2 = 642,
+ CV_AMD64_YMM4D3 = 643,
+ CV_AMD64_YMM5D0 = 644,
+ CV_AMD64_YMM5D1 = 645,
+ CV_AMD64_YMM5D2 = 646,
+ CV_AMD64_YMM5D3 = 647,
+ CV_AMD64_YMM6D0 = 648,
+ CV_AMD64_YMM6D1 = 649,
+ CV_AMD64_YMM6D2 = 650,
+ CV_AMD64_YMM6D3 = 651,
+ CV_AMD64_YMM7D0 = 652,
+ CV_AMD64_YMM7D1 = 653,
+ CV_AMD64_YMM7D2 = 654,
+ CV_AMD64_YMM7D3 = 655,
+ CV_AMD64_YMM8D0 = 656,
+ CV_AMD64_YMM8D1 = 657,
+ CV_AMD64_YMM8D2 = 658,
+ CV_AMD64_YMM8D3 = 659,
+ CV_AMD64_YMM9D0 = 660,
+ CV_AMD64_YMM9D1 = 661,
+ CV_AMD64_YMM9D2 = 662,
+ CV_AMD64_YMM9D3 = 663,
+ CV_AMD64_YMM10D0 = 664,
+ CV_AMD64_YMM10D1 = 665,
+ CV_AMD64_YMM10D2 = 666,
+ CV_AMD64_YMM10D3 = 667,
+ CV_AMD64_YMM11D0 = 668,
+ CV_AMD64_YMM11D1 = 669,
+ CV_AMD64_YMM11D2 = 670,
+ CV_AMD64_YMM11D3 = 671,
+ CV_AMD64_YMM12D0 = 672,
+ CV_AMD64_YMM12D1 = 673,
+ CV_AMD64_YMM12D2 = 674,
+ CV_AMD64_YMM12D3 = 675,
+ CV_AMD64_YMM13D0 = 676,
+ CV_AMD64_YMM13D1 = 677,
+ CV_AMD64_YMM13D2 = 678,
+ CV_AMD64_YMM13D3 = 679,
+ CV_AMD64_YMM14D0 = 680,
+ CV_AMD64_YMM14D1 = 681,
+ CV_AMD64_YMM14D2 = 682,
+ CV_AMD64_YMM14D3 = 683,
+ CV_AMD64_YMM15D0 = 684,
+ CV_AMD64_YMM15D1 = 685,
+ CV_AMD64_YMM15D2 = 686,
+ CV_AMD64_YMM15D3 = 687
+
+
+ // Note: Next set of platform registers need to go into a new enum...
+ // this one is above 44K now.
+
+} CV_HREG_e;
+
+enum StackFrameTypeEnum
+{
+ FrameTypeFPO, // Frame pointer omitted, FPO info available
+ FrameTypeTrap, // Kernel Trap frame
+ FrameTypeTSS, // Kernel Trap frame
+ FrameTypeStandard, // Standard EBP stackframe
+ FrameTypeFrameData, // Frame pointer omitted, FrameData info available
+
+ FrameTypeUnknown = -1, // Frame which does not have any debug info
+};
+
+enum MemoryTypeEnum
+{
+ MemTypeCode, // Read only code memory
+ MemTypeData, // Read only data/stack memory
+ MemTypeStack, // Read only stack memory
+
+ MemTypeAny = -1,
+};
+
+#endif
diff --git a/src/ToolBox/PdbTypeMatch/include/dia2.h b/src/ToolBox/PdbTypeMatch/include/dia2.h
new file mode 100644
index 0000000000..3f400b0575
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/include/dia2.h
@@ -0,0 +1,7854 @@
+// 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 ALWAYS GENERATED file contains the definitions for the interfaces */
+
+
+ /* File created by MIDL compiler version 7.00.0499 */
+/* Compiler settings for dia2.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run)
+ protocol : dce , ms_ext, c_ext, robust
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
+ __declspec(uuid()), __declspec(selectany), __declspec(novtable)
+ DECLSPEC_UUID(), MIDL_INTERFACE()
+*/
+//@@MIDL_FILE_HEADING( )
+
+#pragma warning( disable: 4049 ) /* more than 64k source lines */
+
+
+/* verify that the <rpcndr.h> version is high enough to compile this file*/
+#ifndef __REQUIRED_RPCNDR_H_VERSION__
+#define __REQUIRED_RPCNDR_H_VERSION__ 475
+#endif
+
+/* verify that the <rpcsal.h> version is high enough to compile this file*/
+#ifndef __REQUIRED_RPCSAL_H_VERSION__
+#define __REQUIRED_RPCSAL_H_VERSION__ 100
+#endif
+
+#include "rpc.h"
+#include "rpcndr.h"
+
+#ifndef __RPCNDR_H_VERSION__
+#error this stub requires an updated version of <rpcndr.h>
+#endif // __RPCNDR_H_VERSION__
+
+#ifndef COM_NO_WINDOWS_H
+#include "windows.h"
+#include "ole2.h"
+#endif /*COM_NO_WINDOWS_H*/
+
+#ifndef __dia2_h__
+#define __dia2_h__
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+/* Forward Declarations */
+
+#ifndef __IDiaLoadCallback_FWD_DEFINED__
+#define __IDiaLoadCallback_FWD_DEFINED__
+typedef interface IDiaLoadCallback IDiaLoadCallback;
+#endif /* __IDiaLoadCallback_FWD_DEFINED__ */
+
+
+#ifndef __IDiaLoadCallback2_FWD_DEFINED__
+#define __IDiaLoadCallback2_FWD_DEFINED__
+typedef interface IDiaLoadCallback2 IDiaLoadCallback2;
+#endif /* __IDiaLoadCallback2_FWD_DEFINED__ */
+
+
+#ifndef __IDiaReadExeAtOffsetCallback_FWD_DEFINED__
+#define __IDiaReadExeAtOffsetCallback_FWD_DEFINED__
+typedef interface IDiaReadExeAtOffsetCallback IDiaReadExeAtOffsetCallback;
+#endif /* __IDiaReadExeAtOffsetCallback_FWD_DEFINED__ */
+
+
+#ifndef __IDiaReadExeAtRVACallback_FWD_DEFINED__
+#define __IDiaReadExeAtRVACallback_FWD_DEFINED__
+typedef interface IDiaReadExeAtRVACallback IDiaReadExeAtRVACallback;
+#endif /* __IDiaReadExeAtRVACallback_FWD_DEFINED__ */
+
+
+#ifndef __IDiaDataSource_FWD_DEFINED__
+#define __IDiaDataSource_FWD_DEFINED__
+typedef interface IDiaDataSource IDiaDataSource;
+#endif /* __IDiaDataSource_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumSymbols_FWD_DEFINED__
+#define __IDiaEnumSymbols_FWD_DEFINED__
+typedef interface IDiaEnumSymbols IDiaEnumSymbols;
+#endif /* __IDiaEnumSymbols_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumSymbolsByAddr_FWD_DEFINED__
+#define __IDiaEnumSymbolsByAddr_FWD_DEFINED__
+typedef interface IDiaEnumSymbolsByAddr IDiaEnumSymbolsByAddr;
+#endif /* __IDiaEnumSymbolsByAddr_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumSourceFiles_FWD_DEFINED__
+#define __IDiaEnumSourceFiles_FWD_DEFINED__
+typedef interface IDiaEnumSourceFiles IDiaEnumSourceFiles;
+#endif /* __IDiaEnumSourceFiles_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumLineNumbers_FWD_DEFINED__
+#define __IDiaEnumLineNumbers_FWD_DEFINED__
+typedef interface IDiaEnumLineNumbers IDiaEnumLineNumbers;
+#endif /* __IDiaEnumLineNumbers_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumInjectedSources_FWD_DEFINED__
+#define __IDiaEnumInjectedSources_FWD_DEFINED__
+typedef interface IDiaEnumInjectedSources IDiaEnumInjectedSources;
+#endif /* __IDiaEnumInjectedSources_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumSegments_FWD_DEFINED__
+#define __IDiaEnumSegments_FWD_DEFINED__
+typedef interface IDiaEnumSegments IDiaEnumSegments;
+#endif /* __IDiaEnumSegments_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumSectionContribs_FWD_DEFINED__
+#define __IDiaEnumSectionContribs_FWD_DEFINED__
+typedef interface IDiaEnumSectionContribs IDiaEnumSectionContribs;
+#endif /* __IDiaEnumSectionContribs_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumFrameData_FWD_DEFINED__
+#define __IDiaEnumFrameData_FWD_DEFINED__
+typedef interface IDiaEnumFrameData IDiaEnumFrameData;
+#endif /* __IDiaEnumFrameData_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumDebugStreamData_FWD_DEFINED__
+#define __IDiaEnumDebugStreamData_FWD_DEFINED__
+typedef interface IDiaEnumDebugStreamData IDiaEnumDebugStreamData;
+#endif /* __IDiaEnumDebugStreamData_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumDebugStreams_FWD_DEFINED__
+#define __IDiaEnumDebugStreams_FWD_DEFINED__
+typedef interface IDiaEnumDebugStreams IDiaEnumDebugStreams;
+#endif /* __IDiaEnumDebugStreams_FWD_DEFINED__ */
+
+
+#ifndef __IDiaAddressMap_FWD_DEFINED__
+#define __IDiaAddressMap_FWD_DEFINED__
+typedef interface IDiaAddressMap IDiaAddressMap;
+#endif /* __IDiaAddressMap_FWD_DEFINED__ */
+
+
+#ifndef __IDiaSession_FWD_DEFINED__
+#define __IDiaSession_FWD_DEFINED__
+typedef interface IDiaSession IDiaSession;
+#endif /* __IDiaSession_FWD_DEFINED__ */
+
+
+#ifndef __IDiaSymbol_FWD_DEFINED__
+#define __IDiaSymbol_FWD_DEFINED__
+typedef interface IDiaSymbol IDiaSymbol;
+#endif /* __IDiaSymbol_FWD_DEFINED__ */
+
+
+#ifndef __IDiaSourceFile_FWD_DEFINED__
+#define __IDiaSourceFile_FWD_DEFINED__
+typedef interface IDiaSourceFile IDiaSourceFile;
+#endif /* __IDiaSourceFile_FWD_DEFINED__ */
+
+
+#ifndef __IDiaLineNumber_FWD_DEFINED__
+#define __IDiaLineNumber_FWD_DEFINED__
+typedef interface IDiaLineNumber IDiaLineNumber;
+#endif /* __IDiaLineNumber_FWD_DEFINED__ */
+
+
+#ifndef __IDiaSectionContrib_FWD_DEFINED__
+#define __IDiaSectionContrib_FWD_DEFINED__
+typedef interface IDiaSectionContrib IDiaSectionContrib;
+#endif /* __IDiaSectionContrib_FWD_DEFINED__ */
+
+
+#ifndef __IDiaSegment_FWD_DEFINED__
+#define __IDiaSegment_FWD_DEFINED__
+typedef interface IDiaSegment IDiaSegment;
+#endif /* __IDiaSegment_FWD_DEFINED__ */
+
+
+#ifndef __IDiaInjectedSource_FWD_DEFINED__
+#define __IDiaInjectedSource_FWD_DEFINED__
+typedef interface IDiaInjectedSource IDiaInjectedSource;
+#endif /* __IDiaInjectedSource_FWD_DEFINED__ */
+
+
+#ifndef __IDiaStackWalkFrame_FWD_DEFINED__
+#define __IDiaStackWalkFrame_FWD_DEFINED__
+typedef interface IDiaStackWalkFrame IDiaStackWalkFrame;
+#endif /* __IDiaStackWalkFrame_FWD_DEFINED__ */
+
+
+#ifndef __IDiaFrameData_FWD_DEFINED__
+#define __IDiaFrameData_FWD_DEFINED__
+typedef interface IDiaFrameData IDiaFrameData;
+#endif /* __IDiaFrameData_FWD_DEFINED__ */
+
+
+#ifndef __IDiaImageData_FWD_DEFINED__
+#define __IDiaImageData_FWD_DEFINED__
+typedef interface IDiaImageData IDiaImageData;
+#endif /* __IDiaImageData_FWD_DEFINED__ */
+
+
+#ifndef __IDiaTable_FWD_DEFINED__
+#define __IDiaTable_FWD_DEFINED__
+typedef interface IDiaTable IDiaTable;
+#endif /* __IDiaTable_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumTables_FWD_DEFINED__
+#define __IDiaEnumTables_FWD_DEFINED__
+typedef interface IDiaEnumTables IDiaEnumTables;
+#endif /* __IDiaEnumTables_FWD_DEFINED__ */
+
+
+#ifndef __DiaSource_FWD_DEFINED__
+#define __DiaSource_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class DiaSource DiaSource;
+#else
+typedef struct DiaSource DiaSource;
+#endif /* __cplusplus */
+
+#endif /* __DiaSource_FWD_DEFINED__ */
+
+
+#ifndef __DiaSourceAlt_FWD_DEFINED__
+#define __DiaSourceAlt_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class DiaSourceAlt DiaSourceAlt;
+#else
+typedef struct DiaSourceAlt DiaSourceAlt;
+#endif /* __cplusplus */
+
+#endif /* __DiaSourceAlt_FWD_DEFINED__ */
+
+
+#ifndef __DiaStackWalker_FWD_DEFINED__
+#define __DiaStackWalker_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class DiaStackWalker DiaStackWalker;
+#else
+typedef struct DiaStackWalker DiaStackWalker;
+#endif /* __cplusplus */
+
+#endif /* __DiaStackWalker_FWD_DEFINED__ */
+
+
+#ifndef __IDiaPropertyStorage_FWD_DEFINED__
+#define __IDiaPropertyStorage_FWD_DEFINED__
+typedef interface IDiaPropertyStorage IDiaPropertyStorage;
+#endif /* __IDiaPropertyStorage_FWD_DEFINED__ */
+
+
+#ifndef __IDiaStackFrame_FWD_DEFINED__
+#define __IDiaStackFrame_FWD_DEFINED__
+typedef interface IDiaStackFrame IDiaStackFrame;
+#endif /* __IDiaStackFrame_FWD_DEFINED__ */
+
+
+#ifndef __IDiaEnumStackFrames_FWD_DEFINED__
+#define __IDiaEnumStackFrames_FWD_DEFINED__
+typedef interface IDiaEnumStackFrames IDiaEnumStackFrames;
+#endif /* __IDiaEnumStackFrames_FWD_DEFINED__ */
+
+
+#ifndef __IDiaStackWalkHelper_FWD_DEFINED__
+#define __IDiaStackWalkHelper_FWD_DEFINED__
+typedef interface IDiaStackWalkHelper IDiaStackWalkHelper;
+#endif /* __IDiaStackWalkHelper_FWD_DEFINED__ */
+
+
+#ifndef __IDiaStackWalker_FWD_DEFINED__
+#define __IDiaStackWalker_FWD_DEFINED__
+typedef interface IDiaStackWalker IDiaStackWalker;
+#endif /* __IDiaStackWalker_FWD_DEFINED__ */
+
+
+#ifndef __IDiaStackWalkHelper2_FWD_DEFINED__
+#define __IDiaStackWalkHelper2_FWD_DEFINED__
+typedef interface IDiaStackWalkHelper2 IDiaStackWalkHelper2;
+#endif /* __IDiaStackWalkHelper2_FWD_DEFINED__ */
+
+
+#ifndef __IDiaStackWalker2_FWD_DEFINED__
+#define __IDiaStackWalker2_FWD_DEFINED__
+typedef interface IDiaStackWalker2 IDiaStackWalker2;
+#endif /* __IDiaStackWalker2_FWD_DEFINED__ */
+
+
+/* header files for imported files */
+#include "objidl.h"
+#include "oaidl.h"
+#include "propidl.h"
+#include "cvconst.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+/* interface __MIDL_itf_dia2_0000_0000 */
+/* [local] */
+
+
+enum NameSearchOptions
+ { nsNone = 0,
+ nsfCaseSensitive = 0x1,
+ nsfCaseInsensitive = 0x2,
+ nsfFNameExt = 0x4,
+ nsfRegularExpression = 0x8,
+ nsfUndecoratedName = 0x10,
+ nsCaseSensitive = nsfCaseSensitive,
+ nsCaseInsensitive = nsfCaseInsensitive,
+ nsFNameExt = ( nsfCaseInsensitive | nsfFNameExt ) ,
+ nsRegularExpression = ( nsfRegularExpression | nsfCaseSensitive ) ,
+ nsCaseInRegularExpression = ( nsfRegularExpression | nsfCaseInsensitive )
+ } ;
+
+enum __MIDL___MIDL_itf_dia2_0000_0000_0001
+ { E_PDB_OK = ( HRESULT )(( ( ( ( unsigned long )1 << 31 ) | ( ( unsigned long )( LONG )0x6d << 16 ) ) | ( unsigned long )1 ) ),
+ E_PDB_USAGE = ( E_PDB_OK + 1 ) ,
+ E_PDB_OUT_OF_MEMORY = ( E_PDB_USAGE + 1 ) ,
+ E_PDB_FILE_SYSTEM = ( E_PDB_OUT_OF_MEMORY + 1 ) ,
+ E_PDB_NOT_FOUND = ( E_PDB_FILE_SYSTEM + 1 ) ,
+ E_PDB_INVALID_SIG = ( E_PDB_NOT_FOUND + 1 ) ,
+ E_PDB_INVALID_AGE = ( E_PDB_INVALID_SIG + 1 ) ,
+ E_PDB_PRECOMP_REQUIRED = ( E_PDB_INVALID_AGE + 1 ) ,
+ E_PDB_OUT_OF_TI = ( E_PDB_PRECOMP_REQUIRED + 1 ) ,
+ E_PDB_NOT_IMPLEMENTED = ( E_PDB_OUT_OF_TI + 1 ) ,
+ E_PDB_V1_PDB = ( E_PDB_NOT_IMPLEMENTED + 1 ) ,
+ E_PDB_FORMAT = ( E_PDB_V1_PDB + 1 ) ,
+ E_PDB_LIMIT = ( E_PDB_FORMAT + 1 ) ,
+ E_PDB_CORRUPT = ( E_PDB_LIMIT + 1 ) ,
+ E_PDB_TI16 = ( E_PDB_CORRUPT + 1 ) ,
+ E_PDB_ACCESS_DENIED = ( E_PDB_TI16 + 1 ) ,
+ E_PDB_ILLEGAL_TYPE_EDIT = ( E_PDB_ACCESS_DENIED + 1 ) ,
+ E_PDB_INVALID_EXECUTABLE = ( E_PDB_ILLEGAL_TYPE_EDIT + 1 ) ,
+ E_PDB_DBG_NOT_FOUND = ( E_PDB_INVALID_EXECUTABLE + 1 ) ,
+ E_PDB_NO_DEBUG_INFO = ( E_PDB_DBG_NOT_FOUND + 1 ) ,
+ E_PDB_INVALID_EXE_TIMESTAMP = ( E_PDB_NO_DEBUG_INFO + 1 ) ,
+ E_PDB_RESERVED = ( E_PDB_INVALID_EXE_TIMESTAMP + 1 ) ,
+ E_PDB_DEBUG_INFO_NOT_IN_PDB = ( E_PDB_RESERVED + 1 ) ,
+ E_PDB_SYMSRV_BAD_CACHE_PATH = ( E_PDB_DEBUG_INFO_NOT_IN_PDB + 1 ) ,
+ E_PDB_SYMSRV_CACHE_FULL = ( E_PDB_SYMSRV_BAD_CACHE_PATH + 1 ) ,
+ E_PDB_MAX = ( E_PDB_SYMSRV_CACHE_FULL + 1 )
+ } ;
+typedef void ( __cdecl *PfnPDBDebugDirV )(
+ BOOL __MIDL____MIDL_itf_dia2_0000_00000000,
+ void *__MIDL____MIDL_itf_dia2_0000_00000001);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0000_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0000_v0_0_s_ifspec;
+
+#ifndef __IDiaLoadCallback_INTERFACE_DEFINED__
+#define __IDiaLoadCallback_INTERFACE_DEFINED__
+
+/* interface IDiaLoadCallback */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaLoadCallback;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("C32ADB82-73F4-421b-95D5-A4706EDF5DBE")
+ IDiaLoadCallback : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE NotifyDebugDir(
+ /* [in] */ BOOL fExecutable,
+ /* [in] */ DWORD cbData,
+ /* [size_is][in] */ BYTE *pbData) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE NotifyOpenDBG(
+ /* [in] */ LPCOLESTR dbgPath,
+ /* [in] */ HRESULT resultCode) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE NotifyOpenPDB(
+ /* [in] */ LPCOLESTR pdbPath,
+ /* [in] */ HRESULT resultCode) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RestrictRegistryAccess( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RestrictSymbolServerAccess( void) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaLoadCallbackVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaLoadCallback * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaLoadCallback * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaLoadCallback * This);
+
+ HRESULT ( STDMETHODCALLTYPE *NotifyDebugDir )(
+ IDiaLoadCallback * This,
+ /* [in] */ BOOL fExecutable,
+ /* [in] */ DWORD cbData,
+ /* [size_is][in] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *NotifyOpenDBG )(
+ IDiaLoadCallback * This,
+ /* [in] */ LPCOLESTR dbgPath,
+ /* [in] */ HRESULT resultCode);
+
+ HRESULT ( STDMETHODCALLTYPE *NotifyOpenPDB )(
+ IDiaLoadCallback * This,
+ /* [in] */ LPCOLESTR pdbPath,
+ /* [in] */ HRESULT resultCode);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictRegistryAccess )(
+ IDiaLoadCallback * This);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictSymbolServerAccess )(
+ IDiaLoadCallback * This);
+
+ END_INTERFACE
+ } IDiaLoadCallbackVtbl;
+
+ interface IDiaLoadCallback
+ {
+ CONST_VTBL struct IDiaLoadCallbackVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaLoadCallback_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaLoadCallback_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaLoadCallback_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaLoadCallback_NotifyDebugDir(This,fExecutable,cbData,pbData) \
+ ( (This)->lpVtbl -> NotifyDebugDir(This,fExecutable,cbData,pbData) )
+
+#define IDiaLoadCallback_NotifyOpenDBG(This,dbgPath,resultCode) \
+ ( (This)->lpVtbl -> NotifyOpenDBG(This,dbgPath,resultCode) )
+
+#define IDiaLoadCallback_NotifyOpenPDB(This,pdbPath,resultCode) \
+ ( (This)->lpVtbl -> NotifyOpenPDB(This,pdbPath,resultCode) )
+
+#define IDiaLoadCallback_RestrictRegistryAccess(This) \
+ ( (This)->lpVtbl -> RestrictRegistryAccess(This) )
+
+#define IDiaLoadCallback_RestrictSymbolServerAccess(This) \
+ ( (This)->lpVtbl -> RestrictSymbolServerAccess(This) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaLoadCallback_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaLoadCallback2_INTERFACE_DEFINED__
+#define __IDiaLoadCallback2_INTERFACE_DEFINED__
+
+/* interface IDiaLoadCallback2 */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaLoadCallback2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("4688a074-5a4d-4486-aea8-7b90711d9f7c")
+ IDiaLoadCallback2 : public IDiaLoadCallback
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE RestrictOriginalPathAccess( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RestrictReferencePathAccess( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RestrictDBGAccess( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RestrictSystemRootAccess( void) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaLoadCallback2Vtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaLoadCallback2 * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaLoadCallback2 * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaLoadCallback2 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *NotifyDebugDir )(
+ IDiaLoadCallback2 * This,
+ /* [in] */ BOOL fExecutable,
+ /* [in] */ DWORD cbData,
+ /* [size_is][in] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *NotifyOpenDBG )(
+ IDiaLoadCallback2 * This,
+ /* [in] */ LPCOLESTR dbgPath,
+ /* [in] */ HRESULT resultCode);
+
+ HRESULT ( STDMETHODCALLTYPE *NotifyOpenPDB )(
+ IDiaLoadCallback2 * This,
+ /* [in] */ LPCOLESTR pdbPath,
+ /* [in] */ HRESULT resultCode);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictRegistryAccess )(
+ IDiaLoadCallback2 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictSymbolServerAccess )(
+ IDiaLoadCallback2 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictOriginalPathAccess )(
+ IDiaLoadCallback2 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictReferencePathAccess )(
+ IDiaLoadCallback2 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictDBGAccess )(
+ IDiaLoadCallback2 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *RestrictSystemRootAccess )(
+ IDiaLoadCallback2 * This);
+
+ END_INTERFACE
+ } IDiaLoadCallback2Vtbl;
+
+ interface IDiaLoadCallback2
+ {
+ CONST_VTBL struct IDiaLoadCallback2Vtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaLoadCallback2_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaLoadCallback2_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaLoadCallback2_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaLoadCallback2_NotifyDebugDir(This,fExecutable,cbData,pbData) \
+ ( (This)->lpVtbl -> NotifyDebugDir(This,fExecutable,cbData,pbData) )
+
+#define IDiaLoadCallback2_NotifyOpenDBG(This,dbgPath,resultCode) \
+ ( (This)->lpVtbl -> NotifyOpenDBG(This,dbgPath,resultCode) )
+
+#define IDiaLoadCallback2_NotifyOpenPDB(This,pdbPath,resultCode) \
+ ( (This)->lpVtbl -> NotifyOpenPDB(This,pdbPath,resultCode) )
+
+#define IDiaLoadCallback2_RestrictRegistryAccess(This) \
+ ( (This)->lpVtbl -> RestrictRegistryAccess(This) )
+
+#define IDiaLoadCallback2_RestrictSymbolServerAccess(This) \
+ ( (This)->lpVtbl -> RestrictSymbolServerAccess(This) )
+
+
+#define IDiaLoadCallback2_RestrictOriginalPathAccess(This) \
+ ( (This)->lpVtbl -> RestrictOriginalPathAccess(This) )
+
+#define IDiaLoadCallback2_RestrictReferencePathAccess(This) \
+ ( (This)->lpVtbl -> RestrictReferencePathAccess(This) )
+
+#define IDiaLoadCallback2_RestrictDBGAccess(This) \
+ ( (This)->lpVtbl -> RestrictDBGAccess(This) )
+
+#define IDiaLoadCallback2_RestrictSystemRootAccess(This) \
+ ( (This)->lpVtbl -> RestrictSystemRootAccess(This) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaLoadCallback2_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaReadExeAtOffsetCallback_INTERFACE_DEFINED__
+#define __IDiaReadExeAtOffsetCallback_INTERFACE_DEFINED__
+
+/* interface IDiaReadExeAtOffsetCallback */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaReadExeAtOffsetCallback;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("587A461C-B80B-4f54-9194-5032589A6319")
+ IDiaReadExeAtOffsetCallback : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE ReadExecutableAt(
+ /* [in] */ DWORDLONG fileOffset,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaReadExeAtOffsetCallbackVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaReadExeAtOffsetCallback * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaReadExeAtOffsetCallback * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaReadExeAtOffsetCallback * This);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadExecutableAt )(
+ IDiaReadExeAtOffsetCallback * This,
+ /* [in] */ DWORDLONG fileOffset,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ END_INTERFACE
+ } IDiaReadExeAtOffsetCallbackVtbl;
+
+ interface IDiaReadExeAtOffsetCallback
+ {
+ CONST_VTBL struct IDiaReadExeAtOffsetCallbackVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaReadExeAtOffsetCallback_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaReadExeAtOffsetCallback_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaReadExeAtOffsetCallback_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaReadExeAtOffsetCallback_ReadExecutableAt(This,fileOffset,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> ReadExecutableAt(This,fileOffset,cbData,pcbData,pbData) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaReadExeAtOffsetCallback_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaReadExeAtRVACallback_INTERFACE_DEFINED__
+#define __IDiaReadExeAtRVACallback_INTERFACE_DEFINED__
+
+/* interface IDiaReadExeAtRVACallback */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaReadExeAtRVACallback;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("8E3F80CA-7517-432a-BA07-285134AAEA8E")
+ IDiaReadExeAtRVACallback : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE ReadExecutableAtRVA(
+ /* [in] */ DWORD relativeVirtualAddress,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaReadExeAtRVACallbackVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaReadExeAtRVACallback * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaReadExeAtRVACallback * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaReadExeAtRVACallback * This);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadExecutableAtRVA )(
+ IDiaReadExeAtRVACallback * This,
+ /* [in] */ DWORD relativeVirtualAddress,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ END_INTERFACE
+ } IDiaReadExeAtRVACallbackVtbl;
+
+ interface IDiaReadExeAtRVACallback
+ {
+ CONST_VTBL struct IDiaReadExeAtRVACallbackVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaReadExeAtRVACallback_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaReadExeAtRVACallback_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaReadExeAtRVACallback_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaReadExeAtRVACallback_ReadExecutableAtRVA(This,relativeVirtualAddress,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> ReadExecutableAtRVA(This,relativeVirtualAddress,cbData,pcbData,pbData) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaReadExeAtRVACallback_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaDataSource_INTERFACE_DEFINED__
+#define __IDiaDataSource_INTERFACE_DEFINED__
+
+/* interface IDiaDataSource */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaDataSource;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("79F1BB5F-B66E-48e5-B6A9-1545C323CA3D")
+ IDiaDataSource : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lastError(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE loadDataFromPdb(
+ /* [in] */ LPCOLESTR pdbPath) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE loadAndValidateDataFromPdb(
+ /* [in] */ LPCOLESTR pdbPath,
+ /* [in] */ GUID *pcsig70,
+ /* [in] */ DWORD sig,
+ /* [in] */ DWORD age) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE loadDataForExe(
+ /* [in] */ LPCOLESTR executable,
+ /* [in] */ LPCOLESTR searchPath,
+ /* [in] */ IUnknown *pCallback) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE loadDataFromIStream(
+ /* [in] */ IStream *pIStream) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE openSession(
+ /* [out] */ IDiaSession **ppSession) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaDataSourceVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaDataSource * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaDataSource * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaDataSource * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lastError )(
+ IDiaDataSource * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *loadDataFromPdb )(
+ IDiaDataSource * This,
+ /* [in] */ LPCOLESTR pdbPath);
+
+ HRESULT ( STDMETHODCALLTYPE *loadAndValidateDataFromPdb )(
+ IDiaDataSource * This,
+ /* [in] */ LPCOLESTR pdbPath,
+ /* [in] */ GUID *pcsig70,
+ /* [in] */ DWORD sig,
+ /* [in] */ DWORD age);
+
+ HRESULT ( STDMETHODCALLTYPE *loadDataForExe )(
+ IDiaDataSource * This,
+ /* [in] */ LPCOLESTR executable,
+ /* [in] */ LPCOLESTR searchPath,
+ /* [in] */ IUnknown *pCallback);
+
+ HRESULT ( STDMETHODCALLTYPE *loadDataFromIStream )(
+ IDiaDataSource * This,
+ /* [in] */ IStream *pIStream);
+
+ HRESULT ( STDMETHODCALLTYPE *openSession )(
+ IDiaDataSource * This,
+ /* [out] */ IDiaSession **ppSession);
+
+ END_INTERFACE
+ } IDiaDataSourceVtbl;
+
+ interface IDiaDataSource
+ {
+ CONST_VTBL struct IDiaDataSourceVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaDataSource_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaDataSource_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaDataSource_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaDataSource_get_lastError(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lastError(This,pRetVal) )
+
+#define IDiaDataSource_loadDataFromPdb(This,pdbPath) \
+ ( (This)->lpVtbl -> loadDataFromPdb(This,pdbPath) )
+
+#define IDiaDataSource_loadAndValidateDataFromPdb(This,pdbPath,pcsig70,sig,age) \
+ ( (This)->lpVtbl -> loadAndValidateDataFromPdb(This,pdbPath,pcsig70,sig,age) )
+
+#define IDiaDataSource_loadDataForExe(This,executable,searchPath,pCallback) \
+ ( (This)->lpVtbl -> loadDataForExe(This,executable,searchPath,pCallback) )
+
+#define IDiaDataSource_loadDataFromIStream(This,pIStream) \
+ ( (This)->lpVtbl -> loadDataFromIStream(This,pIStream) )
+
+#define IDiaDataSource_openSession(This,ppSession) \
+ ( (This)->lpVtbl -> openSession(This,ppSession) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaDataSource_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumSymbols_INTERFACE_DEFINED__
+#define __IDiaEnumSymbols_INTERFACE_DEFINED__
+
+/* interface IDiaEnumSymbols */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumSymbols;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("CAB72C48-443B-48f5-9B0B-42F0820AB29A")
+ IDiaEnumSymbols : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSymbol **symbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSymbol **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumSymbols **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumSymbolsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumSymbols * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumSymbols * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumSymbols * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumSymbols * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumSymbols * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumSymbols * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSymbol **symbol);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumSymbols * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSymbol **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumSymbols * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumSymbols * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumSymbols * This,
+ /* [out] */ IDiaEnumSymbols **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumSymbolsVtbl;
+
+ interface IDiaEnumSymbols
+ {
+ CONST_VTBL struct IDiaEnumSymbolsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumSymbols_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumSymbols_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumSymbols_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumSymbols_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumSymbols_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumSymbols_Item(This,index,symbol) \
+ ( (This)->lpVtbl -> Item(This,index,symbol) )
+
+#define IDiaEnumSymbols_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumSymbols_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumSymbols_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumSymbols_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumSymbols_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumSymbolsByAddr_INTERFACE_DEFINED__
+#define __IDiaEnumSymbolsByAddr_INTERFACE_DEFINED__
+
+/* interface IDiaEnumSymbolsByAddr */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumSymbolsByAddr;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("624B7D9C-24EA-4421-9D06-3B577471C1FA")
+ IDiaEnumSymbolsByAddr : public IUnknown
+ {
+ public:
+ virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE symbolByAddr(
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [retval][out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE symbolByRVA(
+ /* [in] */ DWORD relativeVirtualAddress,
+ /* [retval][out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE symbolByVA(
+ /* [in] */ ULONGLONG virtualAddress,
+ /* [retval][out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSymbol **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Prev(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSymbol **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumSymbolsByAddr **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumSymbolsByAddrVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumSymbolsByAddr * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumSymbolsByAddr * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumSymbolsByAddr * This);
+
+ /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *symbolByAddr )(
+ IDiaEnumSymbolsByAddr * This,
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [retval][out] */ IDiaSymbol **ppSymbol);
+
+ /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *symbolByRVA )(
+ IDiaEnumSymbolsByAddr * This,
+ /* [in] */ DWORD relativeVirtualAddress,
+ /* [retval][out] */ IDiaSymbol **ppSymbol);
+
+ /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *symbolByVA )(
+ IDiaEnumSymbolsByAddr * This,
+ /* [in] */ ULONGLONG virtualAddress,
+ /* [retval][out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumSymbolsByAddr * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSymbol **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Prev )(
+ IDiaEnumSymbolsByAddr * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSymbol **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumSymbolsByAddr * This,
+ /* [out] */ IDiaEnumSymbolsByAddr **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumSymbolsByAddrVtbl;
+
+ interface IDiaEnumSymbolsByAddr
+ {
+ CONST_VTBL struct IDiaEnumSymbolsByAddrVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumSymbolsByAddr_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumSymbolsByAddr_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumSymbolsByAddr_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumSymbolsByAddr_symbolByAddr(This,isect,offset,ppSymbol) \
+ ( (This)->lpVtbl -> symbolByAddr(This,isect,offset,ppSymbol) )
+
+#define IDiaEnumSymbolsByAddr_symbolByRVA(This,relativeVirtualAddress,ppSymbol) \
+ ( (This)->lpVtbl -> symbolByRVA(This,relativeVirtualAddress,ppSymbol) )
+
+#define IDiaEnumSymbolsByAddr_symbolByVA(This,virtualAddress,ppSymbol) \
+ ( (This)->lpVtbl -> symbolByVA(This,virtualAddress,ppSymbol) )
+
+#define IDiaEnumSymbolsByAddr_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumSymbolsByAddr_Prev(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Prev(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumSymbolsByAddr_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumSymbolsByAddr_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumSourceFiles_INTERFACE_DEFINED__
+#define __IDiaEnumSourceFiles_INTERFACE_DEFINED__
+
+/* interface IDiaEnumSourceFiles */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumSourceFiles;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("10F3DBD9-664F-4469-B808-9471C7A50538")
+ IDiaEnumSourceFiles : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSourceFile **sourceFile) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSourceFile **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumSourceFiles **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumSourceFilesVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumSourceFiles * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumSourceFiles * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumSourceFiles * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumSourceFiles * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumSourceFiles * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumSourceFiles * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSourceFile **sourceFile);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumSourceFiles * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSourceFile **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumSourceFiles * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumSourceFiles * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumSourceFiles * This,
+ /* [out] */ IDiaEnumSourceFiles **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumSourceFilesVtbl;
+
+ interface IDiaEnumSourceFiles
+ {
+ CONST_VTBL struct IDiaEnumSourceFilesVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumSourceFiles_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumSourceFiles_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumSourceFiles_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumSourceFiles_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumSourceFiles_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumSourceFiles_Item(This,index,sourceFile) \
+ ( (This)->lpVtbl -> Item(This,index,sourceFile) )
+
+#define IDiaEnumSourceFiles_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumSourceFiles_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumSourceFiles_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumSourceFiles_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumSourceFiles_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumLineNumbers_INTERFACE_DEFINED__
+#define __IDiaEnumLineNumbers_INTERFACE_DEFINED__
+
+/* interface IDiaEnumLineNumbers */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumLineNumbers;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("FE30E878-54AC-44f1-81BA-39DE940F6052")
+ IDiaEnumLineNumbers : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaLineNumber **lineNumber) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaLineNumber **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumLineNumbers **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumLineNumbersVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumLineNumbers * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumLineNumbers * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumLineNumbers * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumLineNumbers * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumLineNumbers * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumLineNumbers * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaLineNumber **lineNumber);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumLineNumbers * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaLineNumber **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumLineNumbers * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumLineNumbers * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumLineNumbers * This,
+ /* [out] */ IDiaEnumLineNumbers **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumLineNumbersVtbl;
+
+ interface IDiaEnumLineNumbers
+ {
+ CONST_VTBL struct IDiaEnumLineNumbersVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumLineNumbers_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumLineNumbers_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumLineNumbers_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumLineNumbers_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumLineNumbers_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumLineNumbers_Item(This,index,lineNumber) \
+ ( (This)->lpVtbl -> Item(This,index,lineNumber) )
+
+#define IDiaEnumLineNumbers_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumLineNumbers_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumLineNumbers_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumLineNumbers_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumLineNumbers_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumInjectedSources_INTERFACE_DEFINED__
+#define __IDiaEnumInjectedSources_INTERFACE_DEFINED__
+
+/* interface IDiaEnumInjectedSources */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumInjectedSources;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("D5612573-6925-4468-8883-98CDEC8C384A")
+ IDiaEnumInjectedSources : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaInjectedSource **injectedSource) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaInjectedSource **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumInjectedSources **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumInjectedSourcesVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumInjectedSources * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumInjectedSources * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumInjectedSources * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumInjectedSources * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumInjectedSources * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumInjectedSources * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaInjectedSource **injectedSource);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumInjectedSources * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaInjectedSource **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumInjectedSources * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumInjectedSources * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumInjectedSources * This,
+ /* [out] */ IDiaEnumInjectedSources **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumInjectedSourcesVtbl;
+
+ interface IDiaEnumInjectedSources
+ {
+ CONST_VTBL struct IDiaEnumInjectedSourcesVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumInjectedSources_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumInjectedSources_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumInjectedSources_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumInjectedSources_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumInjectedSources_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumInjectedSources_Item(This,index,injectedSource) \
+ ( (This)->lpVtbl -> Item(This,index,injectedSource) )
+
+#define IDiaEnumInjectedSources_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumInjectedSources_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumInjectedSources_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumInjectedSources_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumInjectedSources_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumSegments_INTERFACE_DEFINED__
+#define __IDiaEnumSegments_INTERFACE_DEFINED__
+
+/* interface IDiaEnumSegments */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumSegments;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("E8368CA9-01D1-419d-AC0C-E31235DBDA9F")
+ IDiaEnumSegments : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSegment **segment) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSegment **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumSegments **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumSegmentsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumSegments * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumSegments * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumSegments * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumSegments * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumSegments * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumSegments * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSegment **segment);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumSegments * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSegment **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumSegments * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumSegments * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumSegments * This,
+ /* [out] */ IDiaEnumSegments **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumSegmentsVtbl;
+
+ interface IDiaEnumSegments
+ {
+ CONST_VTBL struct IDiaEnumSegmentsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumSegments_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumSegments_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumSegments_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumSegments_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumSegments_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumSegments_Item(This,index,segment) \
+ ( (This)->lpVtbl -> Item(This,index,segment) )
+
+#define IDiaEnumSegments_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumSegments_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumSegments_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumSegments_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumSegments_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumSectionContribs_INTERFACE_DEFINED__
+#define __IDiaEnumSectionContribs_INTERFACE_DEFINED__
+
+/* interface IDiaEnumSectionContribs */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumSectionContribs;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("1994DEB2-2C82-4b1d-A57F-AFF424D54A68")
+ IDiaEnumSectionContribs : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSectionContrib **section) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSectionContrib **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumSectionContribs **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumSectionContribsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumSectionContribs * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumSectionContribs * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumSectionContribs * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumSectionContribs * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumSectionContribs * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumSectionContribs * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaSectionContrib **section);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumSectionContribs * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaSectionContrib **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumSectionContribs * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumSectionContribs * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumSectionContribs * This,
+ /* [out] */ IDiaEnumSectionContribs **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumSectionContribsVtbl;
+
+ interface IDiaEnumSectionContribs
+ {
+ CONST_VTBL struct IDiaEnumSectionContribsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumSectionContribs_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumSectionContribs_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumSectionContribs_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumSectionContribs_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumSectionContribs_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumSectionContribs_Item(This,index,section) \
+ ( (This)->lpVtbl -> Item(This,index,section) )
+
+#define IDiaEnumSectionContribs_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumSectionContribs_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumSectionContribs_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumSectionContribs_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumSectionContribs_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumFrameData_INTERFACE_DEFINED__
+#define __IDiaEnumFrameData_INTERFACE_DEFINED__
+
+/* interface IDiaEnumFrameData */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumFrameData;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("9FC77A4B-3C1C-44ed-A798-6C1DEEA53E1F")
+ IDiaEnumFrameData : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaFrameData **frame) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaFrameData **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumFrameData **ppenum) = 0;
+
+ virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE frameByRVA(
+ /* [in] */ DWORD relativeVirtualAddress,
+ /* [retval][out] */ IDiaFrameData **frame) = 0;
+
+ virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE frameByVA(
+ /* [in] */ ULONGLONG virtualAddress,
+ /* [retval][out] */ IDiaFrameData **frame) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumFrameDataVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumFrameData * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumFrameData * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumFrameData * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumFrameData * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumFrameData * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumFrameData * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IDiaFrameData **frame);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumFrameData * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaFrameData **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumFrameData * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumFrameData * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumFrameData * This,
+ /* [out] */ IDiaEnumFrameData **ppenum);
+
+ /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *frameByRVA )(
+ IDiaEnumFrameData * This,
+ /* [in] */ DWORD relativeVirtualAddress,
+ /* [retval][out] */ IDiaFrameData **frame);
+
+ /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *frameByVA )(
+ IDiaEnumFrameData * This,
+ /* [in] */ ULONGLONG virtualAddress,
+ /* [retval][out] */ IDiaFrameData **frame);
+
+ END_INTERFACE
+ } IDiaEnumFrameDataVtbl;
+
+ interface IDiaEnumFrameData
+ {
+ CONST_VTBL struct IDiaEnumFrameDataVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumFrameData_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumFrameData_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumFrameData_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumFrameData_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumFrameData_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumFrameData_Item(This,index,frame) \
+ ( (This)->lpVtbl -> Item(This,index,frame) )
+
+#define IDiaEnumFrameData_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumFrameData_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumFrameData_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumFrameData_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#define IDiaEnumFrameData_frameByRVA(This,relativeVirtualAddress,frame) \
+ ( (This)->lpVtbl -> frameByRVA(This,relativeVirtualAddress,frame) )
+
+#define IDiaEnumFrameData_frameByVA(This,virtualAddress,frame) \
+ ( (This)->lpVtbl -> frameByVA(This,virtualAddress,frame) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumFrameData_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumDebugStreamData_INTERFACE_DEFINED__
+#define __IDiaEnumDebugStreamData_INTERFACE_DEFINED__
+
+/* interface IDiaEnumDebugStreamData */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumDebugStreamData;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("486943E8-D187-4a6b-A3C4-291259FFF60D")
+ IDiaEnumDebugStreamData : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_name(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumDebugStreamData **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumDebugStreamDataVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumDebugStreamData * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumDebugStreamData * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumDebugStreamData * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumDebugStreamData * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumDebugStreamData * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_name )(
+ IDiaEnumDebugStreamData * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumDebugStreamData * This,
+ /* [in] */ DWORD index,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumDebugStreamData * This,
+ /* [in] */ ULONG celt,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumDebugStreamData * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumDebugStreamData * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumDebugStreamData * This,
+ /* [out] */ IDiaEnumDebugStreamData **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumDebugStreamDataVtbl;
+
+ interface IDiaEnumDebugStreamData
+ {
+ CONST_VTBL struct IDiaEnumDebugStreamDataVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumDebugStreamData_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumDebugStreamData_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumDebugStreamData_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumDebugStreamData_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumDebugStreamData_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumDebugStreamData_get_name(This,pRetVal) \
+ ( (This)->lpVtbl -> get_name(This,pRetVal) )
+
+#define IDiaEnumDebugStreamData_Item(This,index,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> Item(This,index,cbData,pcbData,pbData) )
+
+#define IDiaEnumDebugStreamData_Next(This,celt,cbData,pcbData,pbData,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,cbData,pcbData,pbData,pceltFetched) )
+
+#define IDiaEnumDebugStreamData_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumDebugStreamData_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumDebugStreamData_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumDebugStreamData_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumDebugStreams_INTERFACE_DEFINED__
+#define __IDiaEnumDebugStreams_INTERFACE_DEFINED__
+
+/* interface IDiaEnumDebugStreams */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumDebugStreams;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("08CBB41E-47A6-4f87-92F1-1C9C87CED044")
+ IDiaEnumDebugStreams : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ VARIANT index,
+ /* [retval][out] */ IDiaEnumDebugStreamData **stream) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaEnumDebugStreamData **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumDebugStreams **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumDebugStreamsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumDebugStreams * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumDebugStreams * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumDebugStreams * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumDebugStreams * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumDebugStreams * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumDebugStreams * This,
+ /* [in] */ VARIANT index,
+ /* [retval][out] */ IDiaEnumDebugStreamData **stream);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumDebugStreams * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaEnumDebugStreamData **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumDebugStreams * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumDebugStreams * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumDebugStreams * This,
+ /* [out] */ IDiaEnumDebugStreams **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumDebugStreamsVtbl;
+
+ interface IDiaEnumDebugStreams
+ {
+ CONST_VTBL struct IDiaEnumDebugStreamsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumDebugStreams_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumDebugStreams_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumDebugStreams_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumDebugStreams_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumDebugStreams_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumDebugStreams_Item(This,index,stream) \
+ ( (This)->lpVtbl -> Item(This,index,stream) )
+
+#define IDiaEnumDebugStreams_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumDebugStreams_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumDebugStreams_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumDebugStreams_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumDebugStreams_INTERFACE_DEFINED__ */
+
+
+/* interface __MIDL_itf_dia2_0000_0015 */
+/* [local] */
+
+struct DiaAddressMapEntry
+ {
+ DWORD rva;
+ DWORD rvaTo;
+ } ;
+
+
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0015_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0015_v0_0_s_ifspec;
+
+#ifndef __IDiaAddressMap_INTERFACE_DEFINED__
+#define __IDiaAddressMap_INTERFACE_DEFINED__
+
+/* interface IDiaAddressMap */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaAddressMap;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("B62A2E7A-067A-4ea3-B598-04C09717502C")
+ IDiaAddressMap : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressMapEnabled(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propput] */ HRESULT STDMETHODCALLTYPE put_addressMapEnabled(
+ /* [in] */ BOOL NewVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relativeVirtualAddressEnabled(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propput] */ HRESULT STDMETHODCALLTYPE put_relativeVirtualAddressEnabled(
+ /* [in] */ BOOL NewVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_imageAlign(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propput] */ HRESULT STDMETHODCALLTYPE put_imageAlign(
+ /* [in] */ DWORD NewVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE set_imageHeaders(
+ /* [in] */ DWORD cbData,
+ /* [size_is][in] */ BYTE *pbData,
+ /* [in] */ BOOL originalHeaders) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE set_addressMap(
+ /* [in] */ DWORD cData,
+ /* [size_is][in] */ struct DiaAddressMapEntry *pData,
+ /* [in] */ BOOL imageToSymbols) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaAddressMapVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaAddressMap * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaAddressMap * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaAddressMap * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressMapEnabled )(
+ IDiaAddressMap * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propput] */ HRESULT ( STDMETHODCALLTYPE *put_addressMapEnabled )(
+ IDiaAddressMap * This,
+ /* [in] */ BOOL NewVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relativeVirtualAddressEnabled )(
+ IDiaAddressMap * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propput] */ HRESULT ( STDMETHODCALLTYPE *put_relativeVirtualAddressEnabled )(
+ IDiaAddressMap * This,
+ /* [in] */ BOOL NewVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_imageAlign )(
+ IDiaAddressMap * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propput] */ HRESULT ( STDMETHODCALLTYPE *put_imageAlign )(
+ IDiaAddressMap * This,
+ /* [in] */ DWORD NewVal);
+
+ HRESULT ( STDMETHODCALLTYPE *set_imageHeaders )(
+ IDiaAddressMap * This,
+ /* [in] */ DWORD cbData,
+ /* [size_is][in] */ BYTE *pbData,
+ /* [in] */ BOOL originalHeaders);
+
+ HRESULT ( STDMETHODCALLTYPE *set_addressMap )(
+ IDiaAddressMap * This,
+ /* [in] */ DWORD cData,
+ /* [size_is][in] */ struct DiaAddressMapEntry *pData,
+ /* [in] */ BOOL imageToSymbols);
+
+ END_INTERFACE
+ } IDiaAddressMapVtbl;
+
+ interface IDiaAddressMap
+ {
+ CONST_VTBL struct IDiaAddressMapVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaAddressMap_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaAddressMap_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaAddressMap_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaAddressMap_get_addressMapEnabled(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressMapEnabled(This,pRetVal) )
+
+#define IDiaAddressMap_put_addressMapEnabled(This,NewVal) \
+ ( (This)->lpVtbl -> put_addressMapEnabled(This,NewVal) )
+
+#define IDiaAddressMap_get_relativeVirtualAddressEnabled(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relativeVirtualAddressEnabled(This,pRetVal) )
+
+#define IDiaAddressMap_put_relativeVirtualAddressEnabled(This,NewVal) \
+ ( (This)->lpVtbl -> put_relativeVirtualAddressEnabled(This,NewVal) )
+
+#define IDiaAddressMap_get_imageAlign(This,pRetVal) \
+ ( (This)->lpVtbl -> get_imageAlign(This,pRetVal) )
+
+#define IDiaAddressMap_put_imageAlign(This,NewVal) \
+ ( (This)->lpVtbl -> put_imageAlign(This,NewVal) )
+
+#define IDiaAddressMap_set_imageHeaders(This,cbData,pbData,originalHeaders) \
+ ( (This)->lpVtbl -> set_imageHeaders(This,cbData,pbData,originalHeaders) )
+
+#define IDiaAddressMap_set_addressMap(This,cData,pData,imageToSymbols) \
+ ( (This)->lpVtbl -> set_addressMap(This,cData,pData,imageToSymbols) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaAddressMap_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaSession_INTERFACE_DEFINED__
+#define __IDiaSession_INTERFACE_DEFINED__
+
+/* interface IDiaSession */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaSession;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("67138B34-79CD-4b42-B74A-A18ADBB799DF")
+ IDiaSession : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_loadAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propput] */ HRESULT STDMETHODCALLTYPE put_loadAddress(
+ /* [in] */ ULONGLONG NewVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_globalScope(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE getEnumTables(
+ /* [out] */ IDiaEnumTables **ppEnumTables) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE getSymbolsByAddr(
+ /* [out] */ IDiaEnumSymbolsByAddr **ppEnumbyAddr) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildren(
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenEx(
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenExByAddr(
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenExByVA(
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenExByRVA(
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD rva,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findSymbolByAddr(
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findSymbolByRVA(
+ /* [in] */ DWORD rva,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findSymbolByVA(
+ /* [in] */ ULONGLONG va,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findSymbolByToken(
+ /* [in] */ ULONG token,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE symsAreEquiv(
+ /* [in] */ IDiaSymbol *symbolA,
+ /* [in] */ IDiaSymbol *symbolB) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE symbolById(
+ /* [in] */ DWORD id,
+ /* [out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findSymbolByRVAEx(
+ /* [in] */ DWORD rva,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol,
+ /* [out] */ long *displacement) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findSymbolByVAEx(
+ /* [in] */ ULONGLONG va,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol,
+ /* [out] */ long *displacement) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findFile(
+ /* [in] */ IDiaSymbol *pCompiland,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSourceFiles **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findFileById(
+ /* [in] */ DWORD uniqueId,
+ /* [out] */ IDiaSourceFile **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findLines(
+ /* [in] */ IDiaSymbol *compiland,
+ /* [in] */ IDiaSourceFile *file,
+ /* [out] */ IDiaEnumLineNumbers **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findLinesByAddr(
+ /* [in] */ DWORD seg,
+ /* [in] */ DWORD offset,
+ /* [in] */ DWORD length,
+ /* [out] */ IDiaEnumLineNumbers **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findLinesByRVA(
+ /* [in] */ DWORD rva,
+ /* [in] */ DWORD length,
+ /* [out] */ IDiaEnumLineNumbers **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findLinesByVA(
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD length,
+ /* [out] */ IDiaEnumLineNumbers **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findLinesByLinenum(
+ /* [in] */ IDiaSymbol *compiland,
+ /* [in] */ IDiaSourceFile *file,
+ /* [in] */ DWORD linenum,
+ /* [in] */ DWORD column,
+ /* [out] */ IDiaEnumLineNumbers **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findInjectedSource(
+ /* [in] */ LPCOLESTR srcFile,
+ /* [out] */ IDiaEnumInjectedSources **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE getEnumDebugStreams(
+ /* [out] */ IDiaEnumDebugStreams **ppEnumDebugStreams) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaSessionVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaSession * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaSession * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaSession * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_loadAddress )(
+ IDiaSession * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propput] */ HRESULT ( STDMETHODCALLTYPE *put_loadAddress )(
+ IDiaSession * This,
+ /* [in] */ ULONGLONG NewVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_globalScope )(
+ IDiaSession * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *getEnumTables )(
+ IDiaSession * This,
+ /* [out] */ IDiaEnumTables **ppEnumTables);
+
+ HRESULT ( STDMETHODCALLTYPE *getSymbolsByAddr )(
+ IDiaSession * This,
+ /* [out] */ IDiaEnumSymbolsByAddr **ppEnumbyAddr);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildren )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenEx )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenExByAddr )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenExByVA )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenExByRVA )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *parent,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD rva,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findSymbolByAddr )(
+ IDiaSession * This,
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *findSymbolByRVA )(
+ IDiaSession * This,
+ /* [in] */ DWORD rva,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *findSymbolByVA )(
+ IDiaSession * This,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *findSymbolByToken )(
+ IDiaSession * This,
+ /* [in] */ ULONG token,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *symsAreEquiv )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *symbolA,
+ /* [in] */ IDiaSymbol *symbolB);
+
+ HRESULT ( STDMETHODCALLTYPE *symbolById )(
+ IDiaSession * This,
+ /* [in] */ DWORD id,
+ /* [out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *findSymbolByRVAEx )(
+ IDiaSession * This,
+ /* [in] */ DWORD rva,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol,
+ /* [out] */ long *displacement);
+
+ HRESULT ( STDMETHODCALLTYPE *findSymbolByVAEx )(
+ IDiaSession * This,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [out] */ IDiaSymbol **ppSymbol,
+ /* [out] */ long *displacement);
+
+ HRESULT ( STDMETHODCALLTYPE *findFile )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *pCompiland,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSourceFiles **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findFileById )(
+ IDiaSession * This,
+ /* [in] */ DWORD uniqueId,
+ /* [out] */ IDiaSourceFile **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findLines )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *compiland,
+ /* [in] */ IDiaSourceFile *file,
+ /* [out] */ IDiaEnumLineNumbers **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findLinesByAddr )(
+ IDiaSession * This,
+ /* [in] */ DWORD seg,
+ /* [in] */ DWORD offset,
+ /* [in] */ DWORD length,
+ /* [out] */ IDiaEnumLineNumbers **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findLinesByRVA )(
+ IDiaSession * This,
+ /* [in] */ DWORD rva,
+ /* [in] */ DWORD length,
+ /* [out] */ IDiaEnumLineNumbers **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findLinesByVA )(
+ IDiaSession * This,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD length,
+ /* [out] */ IDiaEnumLineNumbers **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findLinesByLinenum )(
+ IDiaSession * This,
+ /* [in] */ IDiaSymbol *compiland,
+ /* [in] */ IDiaSourceFile *file,
+ /* [in] */ DWORD linenum,
+ /* [in] */ DWORD column,
+ /* [out] */ IDiaEnumLineNumbers **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findInjectedSource )(
+ IDiaSession * This,
+ /* [in] */ LPCOLESTR srcFile,
+ /* [out] */ IDiaEnumInjectedSources **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *getEnumDebugStreams )(
+ IDiaSession * This,
+ /* [out] */ IDiaEnumDebugStreams **ppEnumDebugStreams);
+
+ END_INTERFACE
+ } IDiaSessionVtbl;
+
+ interface IDiaSession
+ {
+ CONST_VTBL struct IDiaSessionVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaSession_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaSession_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaSession_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaSession_get_loadAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_loadAddress(This,pRetVal) )
+
+#define IDiaSession_put_loadAddress(This,NewVal) \
+ ( (This)->lpVtbl -> put_loadAddress(This,NewVal) )
+
+#define IDiaSession_get_globalScope(This,pRetVal) \
+ ( (This)->lpVtbl -> get_globalScope(This,pRetVal) )
+
+#define IDiaSession_getEnumTables(This,ppEnumTables) \
+ ( (This)->lpVtbl -> getEnumTables(This,ppEnumTables) )
+
+#define IDiaSession_getSymbolsByAddr(This,ppEnumbyAddr) \
+ ( (This)->lpVtbl -> getSymbolsByAddr(This,ppEnumbyAddr) )
+
+#define IDiaSession_findChildren(This,parent,symtag,name,compareFlags,ppResult) \
+ ( (This)->lpVtbl -> findChildren(This,parent,symtag,name,compareFlags,ppResult) )
+
+#define IDiaSession_findChildrenEx(This,parent,symtag,name,compareFlags,ppResult) \
+ ( (This)->lpVtbl -> findChildrenEx(This,parent,symtag,name,compareFlags,ppResult) )
+
+#define IDiaSession_findChildrenExByAddr(This,parent,symtag,name,compareFlags,isect,offset,ppResult) \
+ ( (This)->lpVtbl -> findChildrenExByAddr(This,parent,symtag,name,compareFlags,isect,offset,ppResult) )
+
+#define IDiaSession_findChildrenExByVA(This,parent,symtag,name,compareFlags,va,ppResult) \
+ ( (This)->lpVtbl -> findChildrenExByVA(This,parent,symtag,name,compareFlags,va,ppResult) )
+
+#define IDiaSession_findChildrenExByRVA(This,parent,symtag,name,compareFlags,rva,ppResult) \
+ ( (This)->lpVtbl -> findChildrenExByRVA(This,parent,symtag,name,compareFlags,rva,ppResult) )
+
+#define IDiaSession_findSymbolByAddr(This,isect,offset,symtag,ppSymbol) \
+ ( (This)->lpVtbl -> findSymbolByAddr(This,isect,offset,symtag,ppSymbol) )
+
+#define IDiaSession_findSymbolByRVA(This,rva,symtag,ppSymbol) \
+ ( (This)->lpVtbl -> findSymbolByRVA(This,rva,symtag,ppSymbol) )
+
+#define IDiaSession_findSymbolByVA(This,va,symtag,ppSymbol) \
+ ( (This)->lpVtbl -> findSymbolByVA(This,va,symtag,ppSymbol) )
+
+#define IDiaSession_findSymbolByToken(This,token,symtag,ppSymbol) \
+ ( (This)->lpVtbl -> findSymbolByToken(This,token,symtag,ppSymbol) )
+
+#define IDiaSession_symsAreEquiv(This,symbolA,symbolB) \
+ ( (This)->lpVtbl -> symsAreEquiv(This,symbolA,symbolB) )
+
+#define IDiaSession_symbolById(This,id,ppSymbol) \
+ ( (This)->lpVtbl -> symbolById(This,id,ppSymbol) )
+
+#define IDiaSession_findSymbolByRVAEx(This,rva,symtag,ppSymbol,displacement) \
+ ( (This)->lpVtbl -> findSymbolByRVAEx(This,rva,symtag,ppSymbol,displacement) )
+
+#define IDiaSession_findSymbolByVAEx(This,va,symtag,ppSymbol,displacement) \
+ ( (This)->lpVtbl -> findSymbolByVAEx(This,va,symtag,ppSymbol,displacement) )
+
+#define IDiaSession_findFile(This,pCompiland,name,compareFlags,ppResult) \
+ ( (This)->lpVtbl -> findFile(This,pCompiland,name,compareFlags,ppResult) )
+
+#define IDiaSession_findFileById(This,uniqueId,ppResult) \
+ ( (This)->lpVtbl -> findFileById(This,uniqueId,ppResult) )
+
+#define IDiaSession_findLines(This,compiland,file,ppResult) \
+ ( (This)->lpVtbl -> findLines(This,compiland,file,ppResult) )
+
+#define IDiaSession_findLinesByAddr(This,seg,offset,length,ppResult) \
+ ( (This)->lpVtbl -> findLinesByAddr(This,seg,offset,length,ppResult) )
+
+#define IDiaSession_findLinesByRVA(This,rva,length,ppResult) \
+ ( (This)->lpVtbl -> findLinesByRVA(This,rva,length,ppResult) )
+
+#define IDiaSession_findLinesByVA(This,va,length,ppResult) \
+ ( (This)->lpVtbl -> findLinesByVA(This,va,length,ppResult) )
+
+#define IDiaSession_findLinesByLinenum(This,compiland,file,linenum,column,ppResult) \
+ ( (This)->lpVtbl -> findLinesByLinenum(This,compiland,file,linenum,column,ppResult) )
+
+#define IDiaSession_findInjectedSource(This,srcFile,ppResult) \
+ ( (This)->lpVtbl -> findInjectedSource(This,srcFile,ppResult) )
+
+#define IDiaSession_getEnumDebugStreams(This,ppEnumDebugStreams) \
+ ( (This)->lpVtbl -> getEnumDebugStreams(This,ppEnumDebugStreams) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaSession_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaSymbol_INTERFACE_DEFINED__
+#define __IDiaSymbol_INTERFACE_DEFINED__
+
+/* interface IDiaSymbol */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaSymbol;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("cb787b2f-bd6c-4635-ba52-933126bd2dcd")
+ IDiaSymbol : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_symIndexId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_symTag(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_name(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lexicalParent(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_classParent(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_type(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_dataKind(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_locationType(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressSection(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressOffset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_registerId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_offset(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_length(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_slot(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_volatileType(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_constType(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_unalignedType(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_access(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_libraryName(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_platform(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_language(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_editAndContinueEnabled(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_frontEndMajor(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_frontEndMinor(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_frontEndBuild(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_backEndMajor(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_backEndMinor(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_backEndBuild(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_sourceFileName(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_unused(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_thunkOrdinal(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_thisAdjust(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualBaseOffset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtual(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_intro(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_pure(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_callingConvention(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_value(
+ /* [retval][out] */ VARIANT *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_baseType(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_token(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_timeStamp(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_guid(
+ /* [retval][out] */ GUID *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_symbolsFileName(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_reference(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_count(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_bitPosition(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_arrayIndexType(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_packed(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_constructor(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_overloadedOperator(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_nested(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasNestedTypes(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasAssignmentOperator(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasCastOperator(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_scoped(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualBaseClass(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_indirectVirtualBaseClass(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualBasePointerOffset(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualTableShape(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lexicalParentId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_classParentId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_typeId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_arrayIndexTypeId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualTableShapeId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_code(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_function(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_managed(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_msil(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualBaseDispIndex(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_undecoratedName(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_age(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_signature(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_compilerGenerated(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressTaken(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_rank(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lowerBound(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_upperBound(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lowerBoundId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_upperBoundId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_dataBytes(
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildren(
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenEx(
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenExByAddr(
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenExByVA(
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE findChildrenExByRVA(
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD rva,
+ /* [out] */ IDiaEnumSymbols **ppResult) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_targetSection(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_targetOffset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_targetRelativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_targetVirtualAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_machineType(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_oemId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_oemSymbolId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_types(
+ /* [in] */ DWORD cTypes,
+ /* [out] */ DWORD *pcTypes,
+ /* [size_is][size_is][out] */ IDiaSymbol **pTypes) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_typeIds(
+ /* [in] */ DWORD cTypeIds,
+ /* [out] */ DWORD *pcTypeIds,
+ /* [size_is][out] */ DWORD *pdwTypeIds) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_objectPointerType(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_udtKind(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_undecoratedNameEx(
+ /* [in] */ DWORD undecorateOptions,
+ /* [out] */ BSTR *name) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_noReturn(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_customCallingConvention(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_noInline(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_optimizedCodeDebugInfo(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_notReached(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_interruptReturn(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_farReturn(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isStatic(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasDebugInfo(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isLTCG(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isDataAligned(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasSecurityChecks(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_compilerName(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasAlloca(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasSetJump(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasLongJump(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasInlAsm(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasEH(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasSEH(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasEHa(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isNaked(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isAggregated(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isSplitted(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_container(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_inlSpec(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_noStackOrdering(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualBaseTableType(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hasManagedCode(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isHotpatchable(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isCVTCIL(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isMSILNetmodule(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isCTypes(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isStripped(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_frontEndQFE(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_backEndQFE(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_wasInlined(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_strictGSCheck(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isCxxReturnUdt(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isConstructorVirtualBase(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_RValueReference(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_unmodifiedType(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_framePointerPresent(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_isSafeBuffers(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_intrinsic(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_sealed(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hfaFloat(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_hfaDouble(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_liveRangeStartAddressSection(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_liveRangeStartAddressOffset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_liveRangeStartRelativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_countLiveRanges(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_liveRangeLength(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_offsetInUdt(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_paramBasePointerRegisterId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_localBasePointerRegisterId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaSymbolVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaSymbol * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaSymbol * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaSymbol * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_symIndexId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_symTag )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_name )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lexicalParent )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_classParent )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_type )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_dataKind )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_locationType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressSection )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressOffset )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relativeVirtualAddress )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualAddress )(
+ IDiaSymbol * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_registerId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_offset )(
+ IDiaSymbol * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_length )(
+ IDiaSymbol * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_slot )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_volatileType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_constType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_unalignedType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_access )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_libraryName )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_platform )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_language )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_editAndContinueEnabled )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_frontEndMajor )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_frontEndMinor )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_frontEndBuild )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_backEndMajor )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_backEndMinor )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_backEndBuild )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_sourceFileName )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_unused )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_thunkOrdinal )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_thisAdjust )(
+ IDiaSymbol * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualBaseOffset )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtual )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_intro )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_pure )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_callingConvention )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_value )(
+ IDiaSymbol * This,
+ /* [retval][out] */ VARIANT *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_baseType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_token )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_timeStamp )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_guid )(
+ IDiaSymbol * This,
+ /* [retval][out] */ GUID *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_symbolsFileName )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_reference )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_count )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_bitPosition )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_arrayIndexType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_packed )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_constructor )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_overloadedOperator )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_nested )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasNestedTypes )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasAssignmentOperator )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasCastOperator )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_scoped )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualBaseClass )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_indirectVirtualBaseClass )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualBasePointerOffset )(
+ IDiaSymbol * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualTableShape )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lexicalParentId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_classParentId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_typeId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_arrayIndexTypeId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualTableShapeId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_code )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_function )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_managed )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_msil )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualBaseDispIndex )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_undecoratedName )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_age )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_signature )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_compilerGenerated )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressTaken )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_rank )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lowerBound )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_upperBound )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lowerBoundId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_upperBoundId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *get_dataBytes )(
+ IDiaSymbol * This,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildren )(
+ IDiaSymbol * This,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenEx )(
+ IDiaSymbol * This,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenExByAddr )(
+ IDiaSymbol * This,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD isect,
+ /* [in] */ DWORD offset,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenExByVA )(
+ IDiaSymbol * This,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ HRESULT ( STDMETHODCALLTYPE *findChildrenExByRVA )(
+ IDiaSymbol * This,
+ /* [in] */ enum SymTagEnum symtag,
+ /* [in] */ LPCOLESTR name,
+ /* [in] */ DWORD compareFlags,
+ /* [in] */ DWORD rva,
+ /* [out] */ IDiaEnumSymbols **ppResult);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_targetSection )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_targetOffset )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_targetRelativeVirtualAddress )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_targetVirtualAddress )(
+ IDiaSymbol * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_machineType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_oemId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_oemSymbolId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *get_types )(
+ IDiaSymbol * This,
+ /* [in] */ DWORD cTypes,
+ /* [out] */ DWORD *pcTypes,
+ /* [size_is][size_is][out] */ IDiaSymbol **pTypes);
+
+ HRESULT ( STDMETHODCALLTYPE *get_typeIds )(
+ IDiaSymbol * This,
+ /* [in] */ DWORD cTypeIds,
+ /* [out] */ DWORD *pcTypeIds,
+ /* [size_is][out] */ DWORD *pdwTypeIds);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_objectPointerType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_udtKind )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *get_undecoratedNameEx )(
+ IDiaSymbol * This,
+ /* [in] */ DWORD undecorateOptions,
+ /* [out] */ BSTR *name);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_noReturn )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_customCallingConvention )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_noInline )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_optimizedCodeDebugInfo )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_notReached )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_interruptReturn )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_farReturn )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isStatic )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasDebugInfo )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isLTCG )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isDataAligned )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasSecurityChecks )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_compilerName )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasAlloca )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasSetJump )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasLongJump )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasInlAsm )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasEH )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasSEH )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasEHa )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isNaked )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isAggregated )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isSplitted )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_container )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_inlSpec )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_noStackOrdering )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualBaseTableType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hasManagedCode )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isHotpatchable )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isCVTCIL )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isMSILNetmodule )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isCTypes )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isStripped )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_frontEndQFE )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_backEndQFE )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_wasInlined )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_strictGSCheck )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isCxxReturnUdt )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isConstructorVirtualBase )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_RValueReference )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_unmodifiedType )(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_framePointerPresent )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_isSafeBuffers )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_intrinsic )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_sealed )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hfaFloat )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_hfaDouble )(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_liveRangeStartAddressSection )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_liveRangeStartAddressOffset )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_liveRangeStartRelativeVirtualAddress )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_countLiveRanges )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_liveRangeLength )(
+ IDiaSymbol * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_offsetInUdt )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_paramBasePointerRegisterId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_localBasePointerRegisterId )(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ END_INTERFACE
+ } IDiaSymbolVtbl;
+
+ interface IDiaSymbol
+ {
+ CONST_VTBL struct IDiaSymbolVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaSymbol_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaSymbol_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaSymbol_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaSymbol_get_symIndexId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_symIndexId(This,pRetVal) )
+
+#define IDiaSymbol_get_symTag(This,pRetVal) \
+ ( (This)->lpVtbl -> get_symTag(This,pRetVal) )
+
+#define IDiaSymbol_get_name(This,pRetVal) \
+ ( (This)->lpVtbl -> get_name(This,pRetVal) )
+
+#define IDiaSymbol_get_lexicalParent(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lexicalParent(This,pRetVal) )
+
+#define IDiaSymbol_get_classParent(This,pRetVal) \
+ ( (This)->lpVtbl -> get_classParent(This,pRetVal) )
+
+#define IDiaSymbol_get_type(This,pRetVal) \
+ ( (This)->lpVtbl -> get_type(This,pRetVal) )
+
+#define IDiaSymbol_get_dataKind(This,pRetVal) \
+ ( (This)->lpVtbl -> get_dataKind(This,pRetVal) )
+
+#define IDiaSymbol_get_locationType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_locationType(This,pRetVal) )
+
+#define IDiaSymbol_get_addressSection(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressSection(This,pRetVal) )
+
+#define IDiaSymbol_get_addressOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressOffset(This,pRetVal) )
+
+#define IDiaSymbol_get_relativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relativeVirtualAddress(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualAddress(This,pRetVal) )
+
+#define IDiaSymbol_get_registerId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_registerId(This,pRetVal) )
+
+#define IDiaSymbol_get_offset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_offset(This,pRetVal) )
+
+#define IDiaSymbol_get_length(This,pRetVal) \
+ ( (This)->lpVtbl -> get_length(This,pRetVal) )
+
+#define IDiaSymbol_get_slot(This,pRetVal) \
+ ( (This)->lpVtbl -> get_slot(This,pRetVal) )
+
+#define IDiaSymbol_get_volatileType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_volatileType(This,pRetVal) )
+
+#define IDiaSymbol_get_constType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_constType(This,pRetVal) )
+
+#define IDiaSymbol_get_unalignedType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_unalignedType(This,pRetVal) )
+
+#define IDiaSymbol_get_access(This,pRetVal) \
+ ( (This)->lpVtbl -> get_access(This,pRetVal) )
+
+#define IDiaSymbol_get_libraryName(This,pRetVal) \
+ ( (This)->lpVtbl -> get_libraryName(This,pRetVal) )
+
+#define IDiaSymbol_get_platform(This,pRetVal) \
+ ( (This)->lpVtbl -> get_platform(This,pRetVal) )
+
+#define IDiaSymbol_get_language(This,pRetVal) \
+ ( (This)->lpVtbl -> get_language(This,pRetVal) )
+
+#define IDiaSymbol_get_editAndContinueEnabled(This,pRetVal) \
+ ( (This)->lpVtbl -> get_editAndContinueEnabled(This,pRetVal) )
+
+#define IDiaSymbol_get_frontEndMajor(This,pRetVal) \
+ ( (This)->lpVtbl -> get_frontEndMajor(This,pRetVal) )
+
+#define IDiaSymbol_get_frontEndMinor(This,pRetVal) \
+ ( (This)->lpVtbl -> get_frontEndMinor(This,pRetVal) )
+
+#define IDiaSymbol_get_frontEndBuild(This,pRetVal) \
+ ( (This)->lpVtbl -> get_frontEndBuild(This,pRetVal) )
+
+#define IDiaSymbol_get_backEndMajor(This,pRetVal) \
+ ( (This)->lpVtbl -> get_backEndMajor(This,pRetVal) )
+
+#define IDiaSymbol_get_backEndMinor(This,pRetVal) \
+ ( (This)->lpVtbl -> get_backEndMinor(This,pRetVal) )
+
+#define IDiaSymbol_get_backEndBuild(This,pRetVal) \
+ ( (This)->lpVtbl -> get_backEndBuild(This,pRetVal) )
+
+#define IDiaSymbol_get_sourceFileName(This,pRetVal) \
+ ( (This)->lpVtbl -> get_sourceFileName(This,pRetVal) )
+
+#define IDiaSymbol_get_unused(This,pRetVal) \
+ ( (This)->lpVtbl -> get_unused(This,pRetVal) )
+
+#define IDiaSymbol_get_thunkOrdinal(This,pRetVal) \
+ ( (This)->lpVtbl -> get_thunkOrdinal(This,pRetVal) )
+
+#define IDiaSymbol_get_thisAdjust(This,pRetVal) \
+ ( (This)->lpVtbl -> get_thisAdjust(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualBaseOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualBaseOffset(This,pRetVal) )
+
+#define IDiaSymbol_get_virtual(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtual(This,pRetVal) )
+
+#define IDiaSymbol_get_intro(This,pRetVal) \
+ ( (This)->lpVtbl -> get_intro(This,pRetVal) )
+
+#define IDiaSymbol_get_pure(This,pRetVal) \
+ ( (This)->lpVtbl -> get_pure(This,pRetVal) )
+
+#define IDiaSymbol_get_callingConvention(This,pRetVal) \
+ ( (This)->lpVtbl -> get_callingConvention(This,pRetVal) )
+
+#define IDiaSymbol_get_value(This,pRetVal) \
+ ( (This)->lpVtbl -> get_value(This,pRetVal) )
+
+#define IDiaSymbol_get_baseType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_baseType(This,pRetVal) )
+
+#define IDiaSymbol_get_token(This,pRetVal) \
+ ( (This)->lpVtbl -> get_token(This,pRetVal) )
+
+#define IDiaSymbol_get_timeStamp(This,pRetVal) \
+ ( (This)->lpVtbl -> get_timeStamp(This,pRetVal) )
+
+#define IDiaSymbol_get_guid(This,pRetVal) \
+ ( (This)->lpVtbl -> get_guid(This,pRetVal) )
+
+#define IDiaSymbol_get_symbolsFileName(This,pRetVal) \
+ ( (This)->lpVtbl -> get_symbolsFileName(This,pRetVal) )
+
+#define IDiaSymbol_get_reference(This,pRetVal) \
+ ( (This)->lpVtbl -> get_reference(This,pRetVal) )
+
+#define IDiaSymbol_get_count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_count(This,pRetVal) )
+
+#define IDiaSymbol_get_bitPosition(This,pRetVal) \
+ ( (This)->lpVtbl -> get_bitPosition(This,pRetVal) )
+
+#define IDiaSymbol_get_arrayIndexType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_arrayIndexType(This,pRetVal) )
+
+#define IDiaSymbol_get_packed(This,pRetVal) \
+ ( (This)->lpVtbl -> get_packed(This,pRetVal) )
+
+#define IDiaSymbol_get_constructor(This,pRetVal) \
+ ( (This)->lpVtbl -> get_constructor(This,pRetVal) )
+
+#define IDiaSymbol_get_overloadedOperator(This,pRetVal) \
+ ( (This)->lpVtbl -> get_overloadedOperator(This,pRetVal) )
+
+#define IDiaSymbol_get_nested(This,pRetVal) \
+ ( (This)->lpVtbl -> get_nested(This,pRetVal) )
+
+#define IDiaSymbol_get_hasNestedTypes(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasNestedTypes(This,pRetVal) )
+
+#define IDiaSymbol_get_hasAssignmentOperator(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasAssignmentOperator(This,pRetVal) )
+
+#define IDiaSymbol_get_hasCastOperator(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasCastOperator(This,pRetVal) )
+
+#define IDiaSymbol_get_scoped(This,pRetVal) \
+ ( (This)->lpVtbl -> get_scoped(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualBaseClass(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualBaseClass(This,pRetVal) )
+
+#define IDiaSymbol_get_indirectVirtualBaseClass(This,pRetVal) \
+ ( (This)->lpVtbl -> get_indirectVirtualBaseClass(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualBasePointerOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualBasePointerOffset(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualTableShape(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualTableShape(This,pRetVal) )
+
+#define IDiaSymbol_get_lexicalParentId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lexicalParentId(This,pRetVal) )
+
+#define IDiaSymbol_get_classParentId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_classParentId(This,pRetVal) )
+
+#define IDiaSymbol_get_typeId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_typeId(This,pRetVal) )
+
+#define IDiaSymbol_get_arrayIndexTypeId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_arrayIndexTypeId(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualTableShapeId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualTableShapeId(This,pRetVal) )
+
+#define IDiaSymbol_get_code(This,pRetVal) \
+ ( (This)->lpVtbl -> get_code(This,pRetVal) )
+
+#define IDiaSymbol_get_function(This,pRetVal) \
+ ( (This)->lpVtbl -> get_function(This,pRetVal) )
+
+#define IDiaSymbol_get_managed(This,pRetVal) \
+ ( (This)->lpVtbl -> get_managed(This,pRetVal) )
+
+#define IDiaSymbol_get_msil(This,pRetVal) \
+ ( (This)->lpVtbl -> get_msil(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualBaseDispIndex(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualBaseDispIndex(This,pRetVal) )
+
+#define IDiaSymbol_get_undecoratedName(This,pRetVal) \
+ ( (This)->lpVtbl -> get_undecoratedName(This,pRetVal) )
+
+#define IDiaSymbol_get_age(This,pRetVal) \
+ ( (This)->lpVtbl -> get_age(This,pRetVal) )
+
+#define IDiaSymbol_get_signature(This,pRetVal) \
+ ( (This)->lpVtbl -> get_signature(This,pRetVal) )
+
+#define IDiaSymbol_get_compilerGenerated(This,pRetVal) \
+ ( (This)->lpVtbl -> get_compilerGenerated(This,pRetVal) )
+
+#define IDiaSymbol_get_addressTaken(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressTaken(This,pRetVal) )
+
+#define IDiaSymbol_get_rank(This,pRetVal) \
+ ( (This)->lpVtbl -> get_rank(This,pRetVal) )
+
+#define IDiaSymbol_get_lowerBound(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lowerBound(This,pRetVal) )
+
+#define IDiaSymbol_get_upperBound(This,pRetVal) \
+ ( (This)->lpVtbl -> get_upperBound(This,pRetVal) )
+
+#define IDiaSymbol_get_lowerBoundId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lowerBoundId(This,pRetVal) )
+
+#define IDiaSymbol_get_upperBoundId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_upperBoundId(This,pRetVal) )
+
+#define IDiaSymbol_get_dataBytes(This,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> get_dataBytes(This,cbData,pcbData,pbData) )
+
+#define IDiaSymbol_findChildren(This,symtag,name,compareFlags,ppResult) \
+ ( (This)->lpVtbl -> findChildren(This,symtag,name,compareFlags,ppResult) )
+
+#define IDiaSymbol_findChildrenEx(This,symtag,name,compareFlags,ppResult) \
+ ( (This)->lpVtbl -> findChildrenEx(This,symtag,name,compareFlags,ppResult) )
+
+#define IDiaSymbol_findChildrenExByAddr(This,symtag,name,compareFlags,isect,offset,ppResult) \
+ ( (This)->lpVtbl -> findChildrenExByAddr(This,symtag,name,compareFlags,isect,offset,ppResult) )
+
+#define IDiaSymbol_findChildrenExByVA(This,symtag,name,compareFlags,va,ppResult) \
+ ( (This)->lpVtbl -> findChildrenExByVA(This,symtag,name,compareFlags,va,ppResult) )
+
+#define IDiaSymbol_findChildrenExByRVA(This,symtag,name,compareFlags,rva,ppResult) \
+ ( (This)->lpVtbl -> findChildrenExByRVA(This,symtag,name,compareFlags,rva,ppResult) )
+
+#define IDiaSymbol_get_targetSection(This,pRetVal) \
+ ( (This)->lpVtbl -> get_targetSection(This,pRetVal) )
+
+#define IDiaSymbol_get_targetOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_targetOffset(This,pRetVal) )
+
+#define IDiaSymbol_get_targetRelativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_targetRelativeVirtualAddress(This,pRetVal) )
+
+#define IDiaSymbol_get_targetVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_targetVirtualAddress(This,pRetVal) )
+
+#define IDiaSymbol_get_machineType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_machineType(This,pRetVal) )
+
+#define IDiaSymbol_get_oemId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_oemId(This,pRetVal) )
+
+#define IDiaSymbol_get_oemSymbolId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_oemSymbolId(This,pRetVal) )
+
+#define IDiaSymbol_get_types(This,cTypes,pcTypes,pTypes) \
+ ( (This)->lpVtbl -> get_types(This,cTypes,pcTypes,pTypes) )
+
+#define IDiaSymbol_get_typeIds(This,cTypeIds,pcTypeIds,pdwTypeIds) \
+ ( (This)->lpVtbl -> get_typeIds(This,cTypeIds,pcTypeIds,pdwTypeIds) )
+
+#define IDiaSymbol_get_objectPointerType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_objectPointerType(This,pRetVal) )
+
+#define IDiaSymbol_get_udtKind(This,pRetVal) \
+ ( (This)->lpVtbl -> get_udtKind(This,pRetVal) )
+
+#define IDiaSymbol_get_undecoratedNameEx(This,undecorateOptions,name) \
+ ( (This)->lpVtbl -> get_undecoratedNameEx(This,undecorateOptions,name) )
+
+#define IDiaSymbol_get_noReturn(This,pRetVal) \
+ ( (This)->lpVtbl -> get_noReturn(This,pRetVal) )
+
+#define IDiaSymbol_get_customCallingConvention(This,pRetVal) \
+ ( (This)->lpVtbl -> get_customCallingConvention(This,pRetVal) )
+
+#define IDiaSymbol_get_noInline(This,pRetVal) \
+ ( (This)->lpVtbl -> get_noInline(This,pRetVal) )
+
+#define IDiaSymbol_get_optimizedCodeDebugInfo(This,pRetVal) \
+ ( (This)->lpVtbl -> get_optimizedCodeDebugInfo(This,pRetVal) )
+
+#define IDiaSymbol_get_notReached(This,pRetVal) \
+ ( (This)->lpVtbl -> get_notReached(This,pRetVal) )
+
+#define IDiaSymbol_get_interruptReturn(This,pRetVal) \
+ ( (This)->lpVtbl -> get_interruptReturn(This,pRetVal) )
+
+#define IDiaSymbol_get_farReturn(This,pRetVal) \
+ ( (This)->lpVtbl -> get_farReturn(This,pRetVal) )
+
+#define IDiaSymbol_get_isStatic(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isStatic(This,pRetVal) )
+
+#define IDiaSymbol_get_hasDebugInfo(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasDebugInfo(This,pRetVal) )
+
+#define IDiaSymbol_get_isLTCG(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isLTCG(This,pRetVal) )
+
+#define IDiaSymbol_get_isDataAligned(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isDataAligned(This,pRetVal) )
+
+#define IDiaSymbol_get_hasSecurityChecks(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasSecurityChecks(This,pRetVal) )
+
+#define IDiaSymbol_get_compilerName(This,pRetVal) \
+ ( (This)->lpVtbl -> get_compilerName(This,pRetVal) )
+
+#define IDiaSymbol_get_hasAlloca(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasAlloca(This,pRetVal) )
+
+#define IDiaSymbol_get_hasSetJump(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasSetJump(This,pRetVal) )
+
+#define IDiaSymbol_get_hasLongJump(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasLongJump(This,pRetVal) )
+
+#define IDiaSymbol_get_hasInlAsm(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasInlAsm(This,pRetVal) )
+
+#define IDiaSymbol_get_hasEH(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasEH(This,pRetVal) )
+
+#define IDiaSymbol_get_hasSEH(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasSEH(This,pRetVal) )
+
+#define IDiaSymbol_get_hasEHa(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasEHa(This,pRetVal) )
+
+#define IDiaSymbol_get_isNaked(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isNaked(This,pRetVal) )
+
+#define IDiaSymbol_get_isAggregated(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isAggregated(This,pRetVal) )
+
+#define IDiaSymbol_get_isSplitted(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isSplitted(This,pRetVal) )
+
+#define IDiaSymbol_get_container(This,pRetVal) \
+ ( (This)->lpVtbl -> get_container(This,pRetVal) )
+
+#define IDiaSymbol_get_inlSpec(This,pRetVal) \
+ ( (This)->lpVtbl -> get_inlSpec(This,pRetVal) )
+
+#define IDiaSymbol_get_noStackOrdering(This,pRetVal) \
+ ( (This)->lpVtbl -> get_noStackOrdering(This,pRetVal) )
+
+#define IDiaSymbol_get_virtualBaseTableType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualBaseTableType(This,pRetVal) )
+
+#define IDiaSymbol_get_hasManagedCode(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hasManagedCode(This,pRetVal) )
+
+#define IDiaSymbol_get_isHotpatchable(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isHotpatchable(This,pRetVal) )
+
+#define IDiaSymbol_get_isCVTCIL(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isCVTCIL(This,pRetVal) )
+
+#define IDiaSymbol_get_isMSILNetmodule(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isMSILNetmodule(This,pRetVal) )
+
+#define IDiaSymbol_get_isCTypes(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isCTypes(This,pRetVal) )
+
+#define IDiaSymbol_get_isStripped(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isStripped(This,pRetVal) )
+
+#define IDiaSymbol_get_frontEndQFE(This,pRetVal) \
+ ( (This)->lpVtbl -> get_frontEndQFE(This,pRetVal) )
+
+#define IDiaSymbol_get_backEndQFE(This,pRetVal) \
+ ( (This)->lpVtbl -> get_backEndQFE(This,pRetVal) )
+
+#define IDiaSymbol_get_wasInlined(This,pRetVal) \
+ ( (This)->lpVtbl -> get_wasInlined(This,pRetVal) )
+
+#define IDiaSymbol_get_strictGSCheck(This,pRetVal) \
+ ( (This)->lpVtbl -> get_strictGSCheck(This,pRetVal) )
+
+#define IDiaSymbol_get_isCxxReturnUdt(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isCxxReturnUdt(This,pRetVal) )
+
+#define IDiaSymbol_get_isConstructorVirtualBase(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isConstructorVirtualBase(This,pRetVal) )
+
+#define IDiaSymbol_get_RValueReference(This,pRetVal) \
+ ( (This)->lpVtbl -> get_RValueReference(This,pRetVal) )
+
+#define IDiaSymbol_get_unmodifiedType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_unmodifiedType(This,pRetVal) )
+
+#define IDiaSymbol_get_framePointerPresent(This,pRetVal) \
+ ( (This)->lpVtbl -> get_framePointerPresent(This,pRetVal) )
+
+#define IDiaSymbol_get_isSafeBuffers(This,pRetVal) \
+ ( (This)->lpVtbl -> get_isSafeBuffers(This,pRetVal) )
+
+#define IDiaSymbol_get_intrinsic(This,pRetVal) \
+ ( (This)->lpVtbl -> get_intrinsic(This,pRetVal) )
+
+#define IDiaSymbol_get_sealed(This,pRetVal) \
+ ( (This)->lpVtbl -> get_sealed(This,pRetVal) )
+
+#define IDiaSymbol_get_hfaFloat(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hfaFloat(This,pRetVal) )
+
+#define IDiaSymbol_get_hfaDouble(This,pRetVal) \
+ ( (This)->lpVtbl -> get_hfaDouble(This,pRetVal) )
+
+#define IDiaSymbol_get_liveRangeStartAddressSection(This,pRetVal) \
+ ( (This)->lpVtbl -> get_liveRangeStartAddressSection(This,pRetVal) )
+
+#define IDiaSymbol_get_liveRangeStartAddressOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_liveRangeStartAddressOffset(This,pRetVal) )
+
+#define IDiaSymbol_get_liveRangeStartRelativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_liveRangeStartRelativeVirtualAddress(This,pRetVal) )
+
+#define IDiaSymbol_get_countLiveRanges(This,pRetVal) \
+ ( (This)->lpVtbl -> get_countLiveRanges(This,pRetVal) )
+
+#define IDiaSymbol_get_liveRangeLength(This,pRetVal) \
+ ( (This)->lpVtbl -> get_liveRangeLength(This,pRetVal) )
+
+#define IDiaSymbol_get_offsetInUdt(This,pRetVal) \
+ ( (This)->lpVtbl -> get_offsetInUdt(This,pRetVal) )
+
+#define IDiaSymbol_get_paramBasePointerRegisterId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_paramBasePointerRegisterId(This,pRetVal) )
+
+#define IDiaSymbol_get_localBasePointerRegisterId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_localBasePointerRegisterId(This,pRetVal) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_virtualBaseTableType_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_virtualBaseTableType_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_hasManagedCode_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_hasManagedCode_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isHotpatchable_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isHotpatchable_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isCVTCIL_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isCVTCIL_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isMSILNetmodule_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isMSILNetmodule_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isCTypes_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isCTypes_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isStripped_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isStripped_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_frontEndQFE_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_frontEndQFE_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_backEndQFE_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_backEndQFE_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_wasInlined_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_wasInlined_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_strictGSCheck_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_strictGSCheck_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isCxxReturnUdt_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isCxxReturnUdt_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isConstructorVirtualBase_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isConstructorVirtualBase_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_RValueReference_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_RValueReference_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_unmodifiedType_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_unmodifiedType_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_framePointerPresent_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_framePointerPresent_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_isSafeBuffers_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_isSafeBuffers_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_intrinsic_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_intrinsic_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_sealed_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_sealed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_hfaFloat_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_hfaFloat_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_hfaDouble_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_hfaDouble_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_liveRangeStartAddressSection_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_liveRangeStartAddressSection_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_liveRangeStartAddressOffset_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_liveRangeStartAddressOffset_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_liveRangeStartRelativeVirtualAddress_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_liveRangeStartRelativeVirtualAddress_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_countLiveRanges_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_countLiveRanges_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_liveRangeLength_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_liveRangeLength_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_offsetInUdt_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_offsetInUdt_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_paramBasePointerRegisterId_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_paramBasePointerRegisterId_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE IDiaSymbol_get_localBasePointerRegisterId_Proxy(
+ IDiaSymbol * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+
+void __RPC_STUB IDiaSymbol_get_localBasePointerRegisterId_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __IDiaSymbol_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaSourceFile_INTERFACE_DEFINED__
+#define __IDiaSourceFile_INTERFACE_DEFINED__
+
+/* interface IDiaSourceFile */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaSourceFile;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("A2EF5353-F5A8-4eb3-90D2-CB526ACB3CDD")
+ IDiaSourceFile : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_uniqueId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_fileName(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_checksumType(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_compilands(
+ /* [retval][out] */ IDiaEnumSymbols **pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_checksum(
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaSourceFileVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaSourceFile * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaSourceFile * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaSourceFile * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_uniqueId )(
+ IDiaSourceFile * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_fileName )(
+ IDiaSourceFile * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_checksumType )(
+ IDiaSourceFile * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_compilands )(
+ IDiaSourceFile * This,
+ /* [retval][out] */ IDiaEnumSymbols **pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *get_checksum )(
+ IDiaSourceFile * This,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ END_INTERFACE
+ } IDiaSourceFileVtbl;
+
+ interface IDiaSourceFile
+ {
+ CONST_VTBL struct IDiaSourceFileVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaSourceFile_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaSourceFile_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaSourceFile_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaSourceFile_get_uniqueId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_uniqueId(This,pRetVal) )
+
+#define IDiaSourceFile_get_fileName(This,pRetVal) \
+ ( (This)->lpVtbl -> get_fileName(This,pRetVal) )
+
+#define IDiaSourceFile_get_checksumType(This,pRetVal) \
+ ( (This)->lpVtbl -> get_checksumType(This,pRetVal) )
+
+#define IDiaSourceFile_get_compilands(This,pRetVal) \
+ ( (This)->lpVtbl -> get_compilands(This,pRetVal) )
+
+#define IDiaSourceFile_get_checksum(This,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> get_checksum(This,cbData,pcbData,pbData) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaSourceFile_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaLineNumber_INTERFACE_DEFINED__
+#define __IDiaLineNumber_INTERFACE_DEFINED__
+
+/* interface IDiaLineNumber */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaLineNumber;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("B388EB14-BE4D-421d-A8A1-6CF7AB057086")
+ IDiaLineNumber : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_compiland(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_sourceFile(
+ /* [retval][out] */ IDiaSourceFile **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lineNumber(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lineNumberEnd(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_columnNumber(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_columnNumberEnd(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressSection(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressOffset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_length(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_sourceFileId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_statement(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_compilandId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaLineNumberVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaLineNumber * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaLineNumber * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaLineNumber * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_compiland )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_sourceFile )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ IDiaSourceFile **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lineNumber )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lineNumberEnd )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnNumber )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnNumberEnd )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressSection )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressOffset )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relativeVirtualAddress )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualAddress )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_length )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_sourceFileId )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_statement )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_compilandId )(
+ IDiaLineNumber * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ END_INTERFACE
+ } IDiaLineNumberVtbl;
+
+ interface IDiaLineNumber
+ {
+ CONST_VTBL struct IDiaLineNumberVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaLineNumber_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaLineNumber_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaLineNumber_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaLineNumber_get_compiland(This,pRetVal) \
+ ( (This)->lpVtbl -> get_compiland(This,pRetVal) )
+
+#define IDiaLineNumber_get_sourceFile(This,pRetVal) \
+ ( (This)->lpVtbl -> get_sourceFile(This,pRetVal) )
+
+#define IDiaLineNumber_get_lineNumber(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lineNumber(This,pRetVal) )
+
+#define IDiaLineNumber_get_lineNumberEnd(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lineNumberEnd(This,pRetVal) )
+
+#define IDiaLineNumber_get_columnNumber(This,pRetVal) \
+ ( (This)->lpVtbl -> get_columnNumber(This,pRetVal) )
+
+#define IDiaLineNumber_get_columnNumberEnd(This,pRetVal) \
+ ( (This)->lpVtbl -> get_columnNumberEnd(This,pRetVal) )
+
+#define IDiaLineNumber_get_addressSection(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressSection(This,pRetVal) )
+
+#define IDiaLineNumber_get_addressOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressOffset(This,pRetVal) )
+
+#define IDiaLineNumber_get_relativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relativeVirtualAddress(This,pRetVal) )
+
+#define IDiaLineNumber_get_virtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualAddress(This,pRetVal) )
+
+#define IDiaLineNumber_get_length(This,pRetVal) \
+ ( (This)->lpVtbl -> get_length(This,pRetVal) )
+
+#define IDiaLineNumber_get_sourceFileId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_sourceFileId(This,pRetVal) )
+
+#define IDiaLineNumber_get_statement(This,pRetVal) \
+ ( (This)->lpVtbl -> get_statement(This,pRetVal) )
+
+#define IDiaLineNumber_get_compilandId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_compilandId(This,pRetVal) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaLineNumber_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaSectionContrib_INTERFACE_DEFINED__
+#define __IDiaSectionContrib_INTERFACE_DEFINED__
+
+/* interface IDiaSectionContrib */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaSectionContrib;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("0CF4B60E-35B1-4c6c-BDD8-854B9C8E3857")
+ IDiaSectionContrib : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_compiland(
+ /* [retval][out] */ IDiaSymbol **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressSection(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressOffset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_length(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_notPaged(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_code(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_initializedData(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_uninitializedData(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_remove(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_comdat(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_discardable(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_notCached(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_share(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_execute(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_read(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_write(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_dataCrc(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relocationsCrc(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_compilandId(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_code16bit(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaSectionContribVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaSectionContrib * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaSectionContrib * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaSectionContrib * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_compiland )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ IDiaSymbol **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressSection )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressOffset )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relativeVirtualAddress )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualAddress )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_length )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_notPaged )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_code )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_initializedData )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_uninitializedData )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_remove )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_comdat )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_discardable )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_notCached )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_share )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_execute )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_read )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_write )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_dataCrc )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relocationsCrc )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_compilandId )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_code16bit )(
+ IDiaSectionContrib * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ END_INTERFACE
+ } IDiaSectionContribVtbl;
+
+ interface IDiaSectionContrib
+ {
+ CONST_VTBL struct IDiaSectionContribVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaSectionContrib_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaSectionContrib_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaSectionContrib_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaSectionContrib_get_compiland(This,pRetVal) \
+ ( (This)->lpVtbl -> get_compiland(This,pRetVal) )
+
+#define IDiaSectionContrib_get_addressSection(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressSection(This,pRetVal) )
+
+#define IDiaSectionContrib_get_addressOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressOffset(This,pRetVal) )
+
+#define IDiaSectionContrib_get_relativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relativeVirtualAddress(This,pRetVal) )
+
+#define IDiaSectionContrib_get_virtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualAddress(This,pRetVal) )
+
+#define IDiaSectionContrib_get_length(This,pRetVal) \
+ ( (This)->lpVtbl -> get_length(This,pRetVal) )
+
+#define IDiaSectionContrib_get_notPaged(This,pRetVal) \
+ ( (This)->lpVtbl -> get_notPaged(This,pRetVal) )
+
+#define IDiaSectionContrib_get_code(This,pRetVal) \
+ ( (This)->lpVtbl -> get_code(This,pRetVal) )
+
+#define IDiaSectionContrib_get_initializedData(This,pRetVal) \
+ ( (This)->lpVtbl -> get_initializedData(This,pRetVal) )
+
+#define IDiaSectionContrib_get_uninitializedData(This,pRetVal) \
+ ( (This)->lpVtbl -> get_uninitializedData(This,pRetVal) )
+
+#define IDiaSectionContrib_get_remove(This,pRetVal) \
+ ( (This)->lpVtbl -> get_remove(This,pRetVal) )
+
+#define IDiaSectionContrib_get_comdat(This,pRetVal) \
+ ( (This)->lpVtbl -> get_comdat(This,pRetVal) )
+
+#define IDiaSectionContrib_get_discardable(This,pRetVal) \
+ ( (This)->lpVtbl -> get_discardable(This,pRetVal) )
+
+#define IDiaSectionContrib_get_notCached(This,pRetVal) \
+ ( (This)->lpVtbl -> get_notCached(This,pRetVal) )
+
+#define IDiaSectionContrib_get_share(This,pRetVal) \
+ ( (This)->lpVtbl -> get_share(This,pRetVal) )
+
+#define IDiaSectionContrib_get_execute(This,pRetVal) \
+ ( (This)->lpVtbl -> get_execute(This,pRetVal) )
+
+#define IDiaSectionContrib_get_read(This,pRetVal) \
+ ( (This)->lpVtbl -> get_read(This,pRetVal) )
+
+#define IDiaSectionContrib_get_write(This,pRetVal) \
+ ( (This)->lpVtbl -> get_write(This,pRetVal) )
+
+#define IDiaSectionContrib_get_dataCrc(This,pRetVal) \
+ ( (This)->lpVtbl -> get_dataCrc(This,pRetVal) )
+
+#define IDiaSectionContrib_get_relocationsCrc(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relocationsCrc(This,pRetVal) )
+
+#define IDiaSectionContrib_get_compilandId(This,pRetVal) \
+ ( (This)->lpVtbl -> get_compilandId(This,pRetVal) )
+
+#define IDiaSectionContrib_get_code16bit(This,pRetVal) \
+ ( (This)->lpVtbl -> get_code16bit(This,pRetVal) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaSectionContrib_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaSegment_INTERFACE_DEFINED__
+#define __IDiaSegment_INTERFACE_DEFINED__
+
+/* interface IDiaSegment */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaSegment;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("0775B784-C75B-4449-848B-B7BD3159545B")
+ IDiaSegment : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_frame(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_offset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_length(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_read(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_write(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_execute(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressSection(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaSegmentVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaSegment * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaSegment * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaSegment * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_frame )(
+ IDiaSegment * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_offset )(
+ IDiaSegment * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_length )(
+ IDiaSegment * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_read )(
+ IDiaSegment * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_write )(
+ IDiaSegment * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_execute )(
+ IDiaSegment * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressSection )(
+ IDiaSegment * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relativeVirtualAddress )(
+ IDiaSegment * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualAddress )(
+ IDiaSegment * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ END_INTERFACE
+ } IDiaSegmentVtbl;
+
+ interface IDiaSegment
+ {
+ CONST_VTBL struct IDiaSegmentVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaSegment_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaSegment_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaSegment_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaSegment_get_frame(This,pRetVal) \
+ ( (This)->lpVtbl -> get_frame(This,pRetVal) )
+
+#define IDiaSegment_get_offset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_offset(This,pRetVal) )
+
+#define IDiaSegment_get_length(This,pRetVal) \
+ ( (This)->lpVtbl -> get_length(This,pRetVal) )
+
+#define IDiaSegment_get_read(This,pRetVal) \
+ ( (This)->lpVtbl -> get_read(This,pRetVal) )
+
+#define IDiaSegment_get_write(This,pRetVal) \
+ ( (This)->lpVtbl -> get_write(This,pRetVal) )
+
+#define IDiaSegment_get_execute(This,pRetVal) \
+ ( (This)->lpVtbl -> get_execute(This,pRetVal) )
+
+#define IDiaSegment_get_addressSection(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressSection(This,pRetVal) )
+
+#define IDiaSegment_get_relativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relativeVirtualAddress(This,pRetVal) )
+
+#define IDiaSegment_get_virtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualAddress(This,pRetVal) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaSegment_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaInjectedSource_INTERFACE_DEFINED__
+#define __IDiaInjectedSource_INTERFACE_DEFINED__
+
+/* interface IDiaInjectedSource */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaInjectedSource;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("AE605CDC-8105-4a23-B710-3259F1E26112")
+ IDiaInjectedSource : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_crc(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_length(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_filename(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_objectFilename(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualFilename(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_sourceCompression(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_source(
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaInjectedSourceVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaInjectedSource * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaInjectedSource * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaInjectedSource * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_crc )(
+ IDiaInjectedSource * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_length )(
+ IDiaInjectedSource * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_filename )(
+ IDiaInjectedSource * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_objectFilename )(
+ IDiaInjectedSource * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualFilename )(
+ IDiaInjectedSource * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_sourceCompression )(
+ IDiaInjectedSource * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *get_source )(
+ IDiaInjectedSource * This,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ END_INTERFACE
+ } IDiaInjectedSourceVtbl;
+
+ interface IDiaInjectedSource
+ {
+ CONST_VTBL struct IDiaInjectedSourceVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaInjectedSource_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaInjectedSource_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaInjectedSource_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaInjectedSource_get_crc(This,pRetVal) \
+ ( (This)->lpVtbl -> get_crc(This,pRetVal) )
+
+#define IDiaInjectedSource_get_length(This,pRetVal) \
+ ( (This)->lpVtbl -> get_length(This,pRetVal) )
+
+#define IDiaInjectedSource_get_filename(This,pRetVal) \
+ ( (This)->lpVtbl -> get_filename(This,pRetVal) )
+
+#define IDiaInjectedSource_get_objectFilename(This,pRetVal) \
+ ( (This)->lpVtbl -> get_objectFilename(This,pRetVal) )
+
+#define IDiaInjectedSource_get_virtualFilename(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualFilename(This,pRetVal) )
+
+#define IDiaInjectedSource_get_sourceCompression(This,pRetVal) \
+ ( (This)->lpVtbl -> get_sourceCompression(This,pRetVal) )
+
+#define IDiaInjectedSource_get_source(This,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> get_source(This,cbData,pcbData,pbData) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaInjectedSource_INTERFACE_DEFINED__ */
+
+
+/* interface __MIDL_itf_dia2_0000_0023 */
+/* [local] */
+
+
+enum __MIDL___MIDL_itf_dia2_0000_0023_0001
+ { E_DIA_INPROLOG = ( HRESULT )(( ( ( ( unsigned long )1 << 31 ) | ( ( unsigned long )( LONG )0x6d << 16 ) ) | ( unsigned long )100 ) ),
+ E_DIA_SYNTAX = ( E_DIA_INPROLOG + 1 ) ,
+ E_DIA_FRAME_ACCESS = ( E_DIA_SYNTAX + 1 ) ,
+ E_DIA_VALUE = ( E_DIA_FRAME_ACCESS + 1 )
+ } ;
+
+
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0023_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0023_v0_0_s_ifspec;
+
+#ifndef __IDiaStackWalkFrame_INTERFACE_DEFINED__
+#define __IDiaStackWalkFrame_INTERFACE_DEFINED__
+
+/* interface IDiaStackWalkFrame */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaStackWalkFrame;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("97F0F1A6-E04E-4ea4-B4F9-B0D0E8D90F5D")
+ IDiaStackWalkFrame : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_registerValue(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propput] */ HRESULT STDMETHODCALLTYPE put_registerValue(
+ /* [in] */ DWORD index,
+ /* [in] */ ULONGLONG NewVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE readMemory(
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE searchForReturnAddress(
+ /* [in] */ IDiaFrameData *frame,
+ /* [out] */ ULONGLONG *returnAddress) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE searchForReturnAddressStart(
+ /* [in] */ IDiaFrameData *frame,
+ /* [in] */ ULONGLONG startAddress,
+ /* [out] */ ULONGLONG *returnAddress) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaStackWalkFrameVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaStackWalkFrame * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaStackWalkFrame * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaStackWalkFrame * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_registerValue )(
+ IDiaStackWalkFrame * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propput] */ HRESULT ( STDMETHODCALLTYPE *put_registerValue )(
+ IDiaStackWalkFrame * This,
+ /* [in] */ DWORD index,
+ /* [in] */ ULONGLONG NewVal);
+
+ HRESULT ( STDMETHODCALLTYPE *readMemory )(
+ IDiaStackWalkFrame * This,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *searchForReturnAddress )(
+ IDiaStackWalkFrame * This,
+ /* [in] */ IDiaFrameData *frame,
+ /* [out] */ ULONGLONG *returnAddress);
+
+ HRESULT ( STDMETHODCALLTYPE *searchForReturnAddressStart )(
+ IDiaStackWalkFrame * This,
+ /* [in] */ IDiaFrameData *frame,
+ /* [in] */ ULONGLONG startAddress,
+ /* [out] */ ULONGLONG *returnAddress);
+
+ END_INTERFACE
+ } IDiaStackWalkFrameVtbl;
+
+ interface IDiaStackWalkFrame
+ {
+ CONST_VTBL struct IDiaStackWalkFrameVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaStackWalkFrame_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaStackWalkFrame_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaStackWalkFrame_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaStackWalkFrame_get_registerValue(This,index,pRetVal) \
+ ( (This)->lpVtbl -> get_registerValue(This,index,pRetVal) )
+
+#define IDiaStackWalkFrame_put_registerValue(This,index,NewVal) \
+ ( (This)->lpVtbl -> put_registerValue(This,index,NewVal) )
+
+#define IDiaStackWalkFrame_readMemory(This,va,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> readMemory(This,va,cbData,pcbData,pbData) )
+
+#define IDiaStackWalkFrame_searchForReturnAddress(This,frame,returnAddress) \
+ ( (This)->lpVtbl -> searchForReturnAddress(This,frame,returnAddress) )
+
+#define IDiaStackWalkFrame_searchForReturnAddressStart(This,frame,startAddress,returnAddress) \
+ ( (This)->lpVtbl -> searchForReturnAddressStart(This,frame,startAddress,returnAddress) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaStackWalkFrame_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaFrameData_INTERFACE_DEFINED__
+#define __IDiaFrameData_INTERFACE_DEFINED__
+
+/* interface IDiaFrameData */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaFrameData;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("A39184B7-6A36-42de-8EEC-7DF9F3F59F33")
+ IDiaFrameData : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressSection(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_addressOffset(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthBlock(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthLocals(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthParams(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_maxStack(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthProlog(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthSavedRegisters(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_program(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_systemExceptionHandling(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_cplusplusExceptionHandling(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_functionStart(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_allocatesBasePointer(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_type(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_functionParent(
+ /* [retval][out] */ IDiaFrameData **pRetVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE execute(
+ IDiaStackWalkFrame *frame) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaFrameDataVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaFrameData * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaFrameData * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaFrameData * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressSection )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_addressOffset )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relativeVirtualAddress )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualAddress )(
+ IDiaFrameData * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthBlock )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthLocals )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthParams )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_maxStack )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthProlog )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthSavedRegisters )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_program )(
+ IDiaFrameData * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_systemExceptionHandling )(
+ IDiaFrameData * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_cplusplusExceptionHandling )(
+ IDiaFrameData * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_functionStart )(
+ IDiaFrameData * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_allocatesBasePointer )(
+ IDiaFrameData * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_type )(
+ IDiaFrameData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_functionParent )(
+ IDiaFrameData * This,
+ /* [retval][out] */ IDiaFrameData **pRetVal);
+
+ HRESULT ( STDMETHODCALLTYPE *execute )(
+ IDiaFrameData * This,
+ IDiaStackWalkFrame *frame);
+
+ END_INTERFACE
+ } IDiaFrameDataVtbl;
+
+ interface IDiaFrameData
+ {
+ CONST_VTBL struct IDiaFrameDataVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaFrameData_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaFrameData_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaFrameData_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaFrameData_get_addressSection(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressSection(This,pRetVal) )
+
+#define IDiaFrameData_get_addressOffset(This,pRetVal) \
+ ( (This)->lpVtbl -> get_addressOffset(This,pRetVal) )
+
+#define IDiaFrameData_get_relativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relativeVirtualAddress(This,pRetVal) )
+
+#define IDiaFrameData_get_virtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualAddress(This,pRetVal) )
+
+#define IDiaFrameData_get_lengthBlock(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthBlock(This,pRetVal) )
+
+#define IDiaFrameData_get_lengthLocals(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthLocals(This,pRetVal) )
+
+#define IDiaFrameData_get_lengthParams(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthParams(This,pRetVal) )
+
+#define IDiaFrameData_get_maxStack(This,pRetVal) \
+ ( (This)->lpVtbl -> get_maxStack(This,pRetVal) )
+
+#define IDiaFrameData_get_lengthProlog(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthProlog(This,pRetVal) )
+
+#define IDiaFrameData_get_lengthSavedRegisters(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthSavedRegisters(This,pRetVal) )
+
+#define IDiaFrameData_get_program(This,pRetVal) \
+ ( (This)->lpVtbl -> get_program(This,pRetVal) )
+
+#define IDiaFrameData_get_systemExceptionHandling(This,pRetVal) \
+ ( (This)->lpVtbl -> get_systemExceptionHandling(This,pRetVal) )
+
+#define IDiaFrameData_get_cplusplusExceptionHandling(This,pRetVal) \
+ ( (This)->lpVtbl -> get_cplusplusExceptionHandling(This,pRetVal) )
+
+#define IDiaFrameData_get_functionStart(This,pRetVal) \
+ ( (This)->lpVtbl -> get_functionStart(This,pRetVal) )
+
+#define IDiaFrameData_get_allocatesBasePointer(This,pRetVal) \
+ ( (This)->lpVtbl -> get_allocatesBasePointer(This,pRetVal) )
+
+#define IDiaFrameData_get_type(This,pRetVal) \
+ ( (This)->lpVtbl -> get_type(This,pRetVal) )
+
+#define IDiaFrameData_get_functionParent(This,pRetVal) \
+ ( (This)->lpVtbl -> get_functionParent(This,pRetVal) )
+
+#define IDiaFrameData_execute(This,frame) \
+ ( (This)->lpVtbl -> execute(This,frame) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaFrameData_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaImageData_INTERFACE_DEFINED__
+#define __IDiaImageData_INTERFACE_DEFINED__
+
+/* interface IDiaImageData */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaImageData;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("C8E40ED2-A1D9-4221-8692-3CE661184B44")
+ IDiaImageData : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_relativeVirtualAddress(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_virtualAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_imageBase(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaImageDataVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaImageData * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaImageData * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaImageData * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_relativeVirtualAddress )(
+ IDiaImageData * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_virtualAddress )(
+ IDiaImageData * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_imageBase )(
+ IDiaImageData * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ END_INTERFACE
+ } IDiaImageDataVtbl;
+
+ interface IDiaImageData
+ {
+ CONST_VTBL struct IDiaImageDataVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaImageData_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaImageData_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaImageData_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaImageData_get_relativeVirtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_relativeVirtualAddress(This,pRetVal) )
+
+#define IDiaImageData_get_virtualAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_virtualAddress(This,pRetVal) )
+
+#define IDiaImageData_get_imageBase(This,pRetVal) \
+ ( (This)->lpVtbl -> get_imageBase(This,pRetVal) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaImageData_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaTable_INTERFACE_DEFINED__
+#define __IDiaTable_INTERFACE_DEFINED__
+
+/* interface IDiaTable */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaTable;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("4A59FB77-ABAC-469b-A30B-9ECC85BFEF14")
+ IDiaTable : public IEnumUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_name(
+ /* [retval][out] */ BSTR *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IUnknown **element) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaTableVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaTable * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaTable * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaTable * This);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaTable * This,
+ /* [in] */
+ __in ULONG celt,
+ /* [out] */
+ __out_ecount_part(celt,*pceltFetched) IUnknown **rgelt,
+ /* [out] */
+ __out_opt ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaTable * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaTable * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaTable * This,
+ /* [out] */ IEnumUnknown **ppenum);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaTable * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_name )(
+ IDiaTable * This,
+ /* [retval][out] */ BSTR *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaTable * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaTable * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ IUnknown **element);
+
+ END_INTERFACE
+ } IDiaTableVtbl;
+
+ interface IDiaTable
+ {
+ CONST_VTBL struct IDiaTableVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaTable_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaTable_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaTable_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaTable_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaTable_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaTable_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaTable_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+
+#define IDiaTable_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaTable_get_name(This,pRetVal) \
+ ( (This)->lpVtbl -> get_name(This,pRetVal) )
+
+#define IDiaTable_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaTable_Item(This,index,element) \
+ ( (This)->lpVtbl -> Item(This,index,element) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaTable_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumTables_INTERFACE_DEFINED__
+#define __IDiaEnumTables_INTERFACE_DEFINED__
+
+/* interface IDiaEnumTables */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumTables;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("C65C2B0A-1150-4d7a-AFCC-E05BF3DEE81E")
+ IDiaEnumTables : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ LONG *pRetVal) = 0;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ VARIANT index,
+ /* [retval][out] */ IDiaTable **table) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ ULONG celt,
+ IDiaTable **rgelt,
+ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(
+ /* [in] */ ULONG celt) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ /* [out] */ IDiaEnumTables **ppenum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumTablesVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumTables * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumTables * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumTables * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ IDiaEnumTables * This,
+ /* [retval][out] */ IUnknown **pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ IDiaEnumTables * This,
+ /* [retval][out] */ LONG *pRetVal);
+
+ /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ IDiaEnumTables * This,
+ /* [in] */ VARIANT index,
+ /* [retval][out] */ IDiaTable **table);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumTables * This,
+ ULONG celt,
+ IDiaTable **rgelt,
+ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Skip )(
+ IDiaEnumTables * This,
+ /* [in] */ ULONG celt);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumTables * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Clone )(
+ IDiaEnumTables * This,
+ /* [out] */ IDiaEnumTables **ppenum);
+
+ END_INTERFACE
+ } IDiaEnumTablesVtbl;
+
+ interface IDiaEnumTables
+ {
+ CONST_VTBL struct IDiaEnumTablesVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumTables_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumTables_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumTables_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumTables_get__NewEnum(This,pRetVal) \
+ ( (This)->lpVtbl -> get__NewEnum(This,pRetVal) )
+
+#define IDiaEnumTables_get_Count(This,pRetVal) \
+ ( (This)->lpVtbl -> get_Count(This,pRetVal) )
+
+#define IDiaEnumTables_Item(This,index,table) \
+ ( (This)->lpVtbl -> Item(This,index,table) )
+
+#define IDiaEnumTables_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumTables_Skip(This,celt) \
+ ( (This)->lpVtbl -> Skip(This,celt) )
+
+#define IDiaEnumTables_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#define IDiaEnumTables_Clone(This,ppenum) \
+ ( (This)->lpVtbl -> Clone(This,ppenum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumTables_INTERFACE_DEFINED__ */
+
+
+
+#ifndef __Dia2Lib_LIBRARY_DEFINED__
+#define __Dia2Lib_LIBRARY_DEFINED__
+
+/* library Dia2Lib */
+/* [helpstring][version][uuid] */
+
+
+EXTERN_C const IID LIBID_Dia2Lib;
+
+EXTERN_C const CLSID CLSID_DiaSource;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("B86AE24D-BF2F-4ac9-B5A2-34B14E4CE11D")
+DiaSource;
+#endif
+
+EXTERN_C const CLSID CLSID_DiaSourceAlt;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("E3E90253-8E14-49a5-AA30-2E7B798AB839")
+DiaSourceAlt;
+#endif
+
+EXTERN_C const CLSID CLSID_DiaStackWalker;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("EBA05B6F-BD22-490e-A7B0-32D821C9046C")
+DiaStackWalker;
+#endif
+#endif /* __Dia2Lib_LIBRARY_DEFINED__ */
+
+/* interface __MIDL_itf_dia2_0000_0028 */
+/* [local] */
+
+#define DiaTable_Symbols ( L"Symbols" )
+
+#define DiaTable_Sections ( L"Sections" )
+
+#define DiaTable_SrcFiles ( L"SourceFiles" )
+
+#define DiaTable_LineNums ( L"LineNumbers" )
+
+#define DiaTable_SegMap ( L"SegmentMap" )
+
+#define DiaTable_Dbg ( L"Dbg" )
+
+#define DiaTable_InjSrc ( L"InjectedSource" )
+
+#define DiaTable_FrameData ( L"FrameData" )
+
+
+
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0028_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0028_v0_0_s_ifspec;
+
+#ifndef __IDiaPropertyStorage_INTERFACE_DEFINED__
+#define __IDiaPropertyStorage_INTERFACE_DEFINED__
+
+/* interface IDiaPropertyStorage */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaPropertyStorage;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("9d416f9c-e184-45b2-a4f0-ce517f719e9b")
+ IDiaPropertyStorage : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE ReadMultiple(
+ /* [in] */ ULONG cpspec,
+ /* [size_is][in] */ const PROPSPEC *rgpspec,
+ /* [size_is][out] */ PROPVARIANT *rgvar) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ReadPropertyNames(
+ /* [in] */ ULONG cpropid,
+ /* [size_is][in] */ const PROPID *rgpropid,
+ /* [size_is][out][in] */ BSTR *rglpwstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Enum(
+ /* [out] */ IEnumSTATPROPSTG **ppenum) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ReadDWORD(
+ /* [in] */ PROPID id,
+ /* [out] */ DWORD *pValue) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ReadLONG(
+ /* [in] */ PROPID id,
+ /* [out] */ LONG *pValue) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ReadBOOL(
+ /* [in] */ PROPID id,
+ /* [out] */ BOOL *pValue) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ReadULONGLONG(
+ /* [in] */ PROPID id,
+ /* [out] */ ULONGLONG *pValue) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ReadBSTR(
+ /* [in] */ PROPID id,
+ /* [out] */ BSTR *pValue) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaPropertyStorageVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaPropertyStorage * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaPropertyStorage * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaPropertyStorage * This);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadMultiple )(
+ IDiaPropertyStorage * This,
+ /* [in] */ ULONG cpspec,
+ /* [size_is][in] */ const PROPSPEC *rgpspec,
+ /* [size_is][out] */ PROPVARIANT *rgvar);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadPropertyNames )(
+ IDiaPropertyStorage * This,
+ /* [in] */ ULONG cpropid,
+ /* [size_is][in] */ const PROPID *rgpropid,
+ /* [size_is][out][in] */ BSTR *rglpwstrName);
+
+ HRESULT ( STDMETHODCALLTYPE *Enum )(
+ IDiaPropertyStorage * This,
+ /* [out] */ IEnumSTATPROPSTG **ppenum);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadDWORD )(
+ IDiaPropertyStorage * This,
+ /* [in] */ PROPID id,
+ /* [out] */ DWORD *pValue);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadLONG )(
+ IDiaPropertyStorage * This,
+ /* [in] */ PROPID id,
+ /* [out] */ LONG *pValue);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadBOOL )(
+ IDiaPropertyStorage * This,
+ /* [in] */ PROPID id,
+ /* [out] */ BOOL *pValue);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadULONGLONG )(
+ IDiaPropertyStorage * This,
+ /* [in] */ PROPID id,
+ /* [out] */ ULONGLONG *pValue);
+
+ HRESULT ( STDMETHODCALLTYPE *ReadBSTR )(
+ IDiaPropertyStorage * This,
+ /* [in] */ PROPID id,
+ /* [out] */ BSTR *pValue);
+
+ END_INTERFACE
+ } IDiaPropertyStorageVtbl;
+
+ interface IDiaPropertyStorage
+ {
+ CONST_VTBL struct IDiaPropertyStorageVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaPropertyStorage_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaPropertyStorage_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaPropertyStorage_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaPropertyStorage_ReadMultiple(This,cpspec,rgpspec,rgvar) \
+ ( (This)->lpVtbl -> ReadMultiple(This,cpspec,rgpspec,rgvar) )
+
+#define IDiaPropertyStorage_ReadPropertyNames(This,cpropid,rgpropid,rglpwstrName) \
+ ( (This)->lpVtbl -> ReadPropertyNames(This,cpropid,rgpropid,rglpwstrName) )
+
+#define IDiaPropertyStorage_Enum(This,ppenum) \
+ ( (This)->lpVtbl -> Enum(This,ppenum) )
+
+#define IDiaPropertyStorage_ReadDWORD(This,id,pValue) \
+ ( (This)->lpVtbl -> ReadDWORD(This,id,pValue) )
+
+#define IDiaPropertyStorage_ReadLONG(This,id,pValue) \
+ ( (This)->lpVtbl -> ReadLONG(This,id,pValue) )
+
+#define IDiaPropertyStorage_ReadBOOL(This,id,pValue) \
+ ( (This)->lpVtbl -> ReadBOOL(This,id,pValue) )
+
+#define IDiaPropertyStorage_ReadULONGLONG(This,id,pValue) \
+ ( (This)->lpVtbl -> ReadULONGLONG(This,id,pValue) )
+
+#define IDiaPropertyStorage_ReadBSTR(This,id,pValue) \
+ ( (This)->lpVtbl -> ReadBSTR(This,id,pValue) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaPropertyStorage_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaStackFrame_INTERFACE_DEFINED__
+#define __IDiaStackFrame_INTERFACE_DEFINED__
+
+/* interface IDiaStackFrame */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaStackFrame;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("5edbc96d-cdd6-4792-afbe-cc89007d9610")
+ IDiaStackFrame : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_type(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_base(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_size(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_returnAddress(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_localsBase(
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthLocals(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthParams(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthProlog(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_lengthSavedRegisters(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_systemExceptionHandling(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_cplusplusExceptionHandling(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_functionStart(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_allocatesBasePointer(
+ /* [retval][out] */ BOOL *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_maxStack(
+ /* [retval][out] */ DWORD *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_registerValue(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaStackFrameVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaStackFrame * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaStackFrame * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaStackFrame * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_type )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_base )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_size )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_returnAddress )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_localsBase )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthLocals )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthParams )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthProlog )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_lengthSavedRegisters )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_systemExceptionHandling )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_cplusplusExceptionHandling )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_functionStart )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_allocatesBasePointer )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ BOOL *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_maxStack )(
+ IDiaStackFrame * This,
+ /* [retval][out] */ DWORD *pRetVal);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_registerValue )(
+ IDiaStackFrame * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ END_INTERFACE
+ } IDiaStackFrameVtbl;
+
+ interface IDiaStackFrame
+ {
+ CONST_VTBL struct IDiaStackFrameVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaStackFrame_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaStackFrame_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaStackFrame_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaStackFrame_get_type(This,pRetVal) \
+ ( (This)->lpVtbl -> get_type(This,pRetVal) )
+
+#define IDiaStackFrame_get_base(This,pRetVal) \
+ ( (This)->lpVtbl -> get_base(This,pRetVal) )
+
+#define IDiaStackFrame_get_size(This,pRetVal) \
+ ( (This)->lpVtbl -> get_size(This,pRetVal) )
+
+#define IDiaStackFrame_get_returnAddress(This,pRetVal) \
+ ( (This)->lpVtbl -> get_returnAddress(This,pRetVal) )
+
+#define IDiaStackFrame_get_localsBase(This,pRetVal) \
+ ( (This)->lpVtbl -> get_localsBase(This,pRetVal) )
+
+#define IDiaStackFrame_get_lengthLocals(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthLocals(This,pRetVal) )
+
+#define IDiaStackFrame_get_lengthParams(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthParams(This,pRetVal) )
+
+#define IDiaStackFrame_get_lengthProlog(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthProlog(This,pRetVal) )
+
+#define IDiaStackFrame_get_lengthSavedRegisters(This,pRetVal) \
+ ( (This)->lpVtbl -> get_lengthSavedRegisters(This,pRetVal) )
+
+#define IDiaStackFrame_get_systemExceptionHandling(This,pRetVal) \
+ ( (This)->lpVtbl -> get_systemExceptionHandling(This,pRetVal) )
+
+#define IDiaStackFrame_get_cplusplusExceptionHandling(This,pRetVal) \
+ ( (This)->lpVtbl -> get_cplusplusExceptionHandling(This,pRetVal) )
+
+#define IDiaStackFrame_get_functionStart(This,pRetVal) \
+ ( (This)->lpVtbl -> get_functionStart(This,pRetVal) )
+
+#define IDiaStackFrame_get_allocatesBasePointer(This,pRetVal) \
+ ( (This)->lpVtbl -> get_allocatesBasePointer(This,pRetVal) )
+
+#define IDiaStackFrame_get_maxStack(This,pRetVal) \
+ ( (This)->lpVtbl -> get_maxStack(This,pRetVal) )
+
+#define IDiaStackFrame_get_registerValue(This,index,pRetVal) \
+ ( (This)->lpVtbl -> get_registerValue(This,index,pRetVal) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaStackFrame_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaEnumStackFrames_INTERFACE_DEFINED__
+#define __IDiaEnumStackFrames_INTERFACE_DEFINED__
+
+/* interface IDiaEnumStackFrames */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaEnumStackFrames;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("ec9d461d-ce74-4711-a020-7d8f9a1dd255")
+ IDiaEnumStackFrames : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE Next(
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaStackFrame **rgelt,
+ /* [out] */ ULONG *pceltFetched) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaEnumStackFramesVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaEnumStackFrames * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaEnumStackFrames * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaEnumStackFrames * This);
+
+ HRESULT ( STDMETHODCALLTYPE *Next )(
+ IDiaEnumStackFrames * This,
+ /* [in] */ ULONG celt,
+ /* [out] */ IDiaStackFrame **rgelt,
+ /* [out] */ ULONG *pceltFetched);
+
+ HRESULT ( STDMETHODCALLTYPE *Reset )(
+ IDiaEnumStackFrames * This);
+
+ END_INTERFACE
+ } IDiaEnumStackFramesVtbl;
+
+ interface IDiaEnumStackFrames
+ {
+ CONST_VTBL struct IDiaEnumStackFramesVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaEnumStackFrames_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaEnumStackFrames_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaEnumStackFrames_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaEnumStackFrames_Next(This,celt,rgelt,pceltFetched) \
+ ( (This)->lpVtbl -> Next(This,celt,rgelt,pceltFetched) )
+
+#define IDiaEnumStackFrames_Reset(This) \
+ ( (This)->lpVtbl -> Reset(This) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaEnumStackFrames_INTERFACE_DEFINED__ */
+
+
+/* interface __MIDL_itf_dia2_0000_0031 */
+/* [local] */
+
+typedef /* [public] */ struct __MIDL___MIDL_itf_dia2_0000_0031_0001
+ {
+ DWORD ulOffStart;
+ DWORD cbProcSize;
+ DWORD cdwLocals;
+ WORD cdwParams;
+ WORD cdwFlags;
+ } FPODATA;
+
+
+
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0031_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_dia2_0000_0031_v0_0_s_ifspec;
+
+#ifndef __IDiaStackWalkHelper_INTERFACE_DEFINED__
+#define __IDiaStackWalkHelper_INTERFACE_DEFINED__
+
+/* interface IDiaStackWalkHelper */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaStackWalkHelper;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("feb0155d-35a8-42d4-8328-bf458f429a3a")
+ IDiaStackWalkHelper : public IUnknown
+ {
+ public:
+ virtual /* [id][helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_registerValue(
+ /* [in] */ DWORD index,
+ /* [retval][out] */ ULONGLONG *pRetVal) = 0;
+
+ virtual /* [id][helpstring][propput] */ HRESULT STDMETHODCALLTYPE put_registerValue(
+ /* [in] */ DWORD index,
+ /* [in] */ ULONGLONG NewVal) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE readMemory(
+ /* [in] */ enum MemoryTypeEnum type,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE searchForReturnAddress(
+ /* [in] */ IDiaFrameData *frame,
+ /* [out] */ ULONGLONG *returnAddress) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE searchForReturnAddressStart(
+ /* [in] */ IDiaFrameData *frame,
+ /* [in] */ ULONGLONG startAddress,
+ /* [out] */ ULONGLONG *returnAddress) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE frameForVA(
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaFrameData **ppFrame) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE symbolForVA(
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaSymbol **ppSymbol) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE pdataForVA(
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE imageForVA(
+ /* [in] */ ULONGLONG vaContext,
+ /* [out] */ ULONGLONG *pvaImageStart) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaStackWalkHelperVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaStackWalkHelper * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaStackWalkHelper * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_registerValue )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propput] */ HRESULT ( STDMETHODCALLTYPE *put_registerValue )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ DWORD index,
+ /* [in] */ ULONGLONG NewVal);
+
+ HRESULT ( STDMETHODCALLTYPE *readMemory )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ enum MemoryTypeEnum type,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *searchForReturnAddress )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ IDiaFrameData *frame,
+ /* [out] */ ULONGLONG *returnAddress);
+
+ HRESULT ( STDMETHODCALLTYPE *searchForReturnAddressStart )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ IDiaFrameData *frame,
+ /* [in] */ ULONGLONG startAddress,
+ /* [out] */ ULONGLONG *returnAddress);
+
+ HRESULT ( STDMETHODCALLTYPE *frameForVA )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaFrameData **ppFrame);
+
+ HRESULT ( STDMETHODCALLTYPE *symbolForVA )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *pdataForVA )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *imageForVA )(
+ IDiaStackWalkHelper * This,
+ /* [in] */ ULONGLONG vaContext,
+ /* [out] */ ULONGLONG *pvaImageStart);
+
+ END_INTERFACE
+ } IDiaStackWalkHelperVtbl;
+
+ interface IDiaStackWalkHelper
+ {
+ CONST_VTBL struct IDiaStackWalkHelperVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaStackWalkHelper_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaStackWalkHelper_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaStackWalkHelper_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaStackWalkHelper_get_registerValue(This,index,pRetVal) \
+ ( (This)->lpVtbl -> get_registerValue(This,index,pRetVal) )
+
+#define IDiaStackWalkHelper_put_registerValue(This,index,NewVal) \
+ ( (This)->lpVtbl -> put_registerValue(This,index,NewVal) )
+
+#define IDiaStackWalkHelper_readMemory(This,type,va,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> readMemory(This,type,va,cbData,pcbData,pbData) )
+
+#define IDiaStackWalkHelper_searchForReturnAddress(This,frame,returnAddress) \
+ ( (This)->lpVtbl -> searchForReturnAddress(This,frame,returnAddress) )
+
+#define IDiaStackWalkHelper_searchForReturnAddressStart(This,frame,startAddress,returnAddress) \
+ ( (This)->lpVtbl -> searchForReturnAddressStart(This,frame,startAddress,returnAddress) )
+
+#define IDiaStackWalkHelper_frameForVA(This,va,ppFrame) \
+ ( (This)->lpVtbl -> frameForVA(This,va,ppFrame) )
+
+#define IDiaStackWalkHelper_symbolForVA(This,va,ppSymbol) \
+ ( (This)->lpVtbl -> symbolForVA(This,va,ppSymbol) )
+
+#define IDiaStackWalkHelper_pdataForVA(This,va,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> pdataForVA(This,va,cbData,pcbData,pbData) )
+
+#define IDiaStackWalkHelper_imageForVA(This,vaContext,pvaImageStart) \
+ ( (This)->lpVtbl -> imageForVA(This,vaContext,pvaImageStart) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaStackWalkHelper_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaStackWalker_INTERFACE_DEFINED__
+#define __IDiaStackWalker_INTERFACE_DEFINED__
+
+/* interface IDiaStackWalker */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaStackWalker;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("5485216b-a54c-469f-9670-52b24d5229bb")
+ IDiaStackWalker : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE getEnumFrames(
+ /* [in] */ IDiaStackWalkHelper *pHelper,
+ /* [out] */ IDiaEnumStackFrames **ppEnum) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE getEnumFrames2(
+ /* [in] */ enum CV_CPU_TYPE_e cpuid,
+ /* [in] */ IDiaStackWalkHelper *pHelper,
+ /* [out] */ IDiaEnumStackFrames **ppEnum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaStackWalkerVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaStackWalker * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaStackWalker * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaStackWalker * This);
+
+ HRESULT ( STDMETHODCALLTYPE *getEnumFrames )(
+ IDiaStackWalker * This,
+ /* [in] */ IDiaStackWalkHelper *pHelper,
+ /* [out] */ IDiaEnumStackFrames **ppEnum);
+
+ HRESULT ( STDMETHODCALLTYPE *getEnumFrames2 )(
+ IDiaStackWalker * This,
+ /* [in] */ enum CV_CPU_TYPE_e cpuid,
+ /* [in] */ IDiaStackWalkHelper *pHelper,
+ /* [out] */ IDiaEnumStackFrames **ppEnum);
+
+ END_INTERFACE
+ } IDiaStackWalkerVtbl;
+
+ interface IDiaStackWalker
+ {
+ CONST_VTBL struct IDiaStackWalkerVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaStackWalker_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaStackWalker_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaStackWalker_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaStackWalker_getEnumFrames(This,pHelper,ppEnum) \
+ ( (This)->lpVtbl -> getEnumFrames(This,pHelper,ppEnum) )
+
+#define IDiaStackWalker_getEnumFrames2(This,cpuid,pHelper,ppEnum) \
+ ( (This)->lpVtbl -> getEnumFrames2(This,cpuid,pHelper,ppEnum) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaStackWalker_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaStackWalkHelper2_INTERFACE_DEFINED__
+#define __IDiaStackWalkHelper2_INTERFACE_DEFINED__
+
+/* interface IDiaStackWalkHelper2 */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaStackWalkHelper2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("8222c490-507b-4bef-b3bd-41dca7b5934c")
+ IDiaStackWalkHelper2 : public IDiaStackWalkHelper
+ {
+ public:
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaStackWalkHelper2Vtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaStackWalkHelper2 * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaStackWalkHelper2 * This);
+
+ /* [id][helpstring][propget] */ HRESULT ( STDMETHODCALLTYPE *get_registerValue )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ DWORD index,
+ /* [retval][out] */ ULONGLONG *pRetVal);
+
+ /* [id][helpstring][propput] */ HRESULT ( STDMETHODCALLTYPE *put_registerValue )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ DWORD index,
+ /* [in] */ ULONGLONG NewVal);
+
+ HRESULT ( STDMETHODCALLTYPE *readMemory )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ enum MemoryTypeEnum type,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *searchForReturnAddress )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ IDiaFrameData *frame,
+ /* [out] */ ULONGLONG *returnAddress);
+
+ HRESULT ( STDMETHODCALLTYPE *searchForReturnAddressStart )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ IDiaFrameData *frame,
+ /* [in] */ ULONGLONG startAddress,
+ /* [out] */ ULONGLONG *returnAddress);
+
+ HRESULT ( STDMETHODCALLTYPE *frameForVA )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaFrameData **ppFrame);
+
+ HRESULT ( STDMETHODCALLTYPE *symbolForVA )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ ULONGLONG va,
+ /* [out] */ IDiaSymbol **ppSymbol);
+
+ HRESULT ( STDMETHODCALLTYPE *pdataForVA )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ ULONGLONG va,
+ /* [in] */ DWORD cbData,
+ /* [out] */ DWORD *pcbData,
+ /* [size_is][out] */ BYTE *pbData);
+
+ HRESULT ( STDMETHODCALLTYPE *imageForVA )(
+ IDiaStackWalkHelper2 * This,
+ /* [in] */ ULONGLONG vaContext,
+ /* [out] */ ULONGLONG *pvaImageStart);
+
+ END_INTERFACE
+ } IDiaStackWalkHelper2Vtbl;
+
+ interface IDiaStackWalkHelper2
+ {
+ CONST_VTBL struct IDiaStackWalkHelper2Vtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaStackWalkHelper2_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaStackWalkHelper2_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaStackWalkHelper2_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaStackWalkHelper2_get_registerValue(This,index,pRetVal) \
+ ( (This)->lpVtbl -> get_registerValue(This,index,pRetVal) )
+
+#define IDiaStackWalkHelper2_put_registerValue(This,index,NewVal) \
+ ( (This)->lpVtbl -> put_registerValue(This,index,NewVal) )
+
+#define IDiaStackWalkHelper2_readMemory(This,type,va,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> readMemory(This,type,va,cbData,pcbData,pbData) )
+
+#define IDiaStackWalkHelper2_searchForReturnAddress(This,frame,returnAddress) \
+ ( (This)->lpVtbl -> searchForReturnAddress(This,frame,returnAddress) )
+
+#define IDiaStackWalkHelper2_searchForReturnAddressStart(This,frame,startAddress,returnAddress) \
+ ( (This)->lpVtbl -> searchForReturnAddressStart(This,frame,startAddress,returnAddress) )
+
+#define IDiaStackWalkHelper2_frameForVA(This,va,ppFrame) \
+ ( (This)->lpVtbl -> frameForVA(This,va,ppFrame) )
+
+#define IDiaStackWalkHelper2_symbolForVA(This,va,ppSymbol) \
+ ( (This)->lpVtbl -> symbolForVA(This,va,ppSymbol) )
+
+#define IDiaStackWalkHelper2_pdataForVA(This,va,cbData,pcbData,pbData) \
+ ( (This)->lpVtbl -> pdataForVA(This,va,cbData,pcbData,pbData) )
+
+#define IDiaStackWalkHelper2_imageForVA(This,vaContext,pvaImageStart) \
+ ( (This)->lpVtbl -> imageForVA(This,vaContext,pvaImageStart) )
+
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaStackWalkHelper2_INTERFACE_DEFINED__ */
+
+
+#ifndef __IDiaStackWalker2_INTERFACE_DEFINED__
+#define __IDiaStackWalker2_INTERFACE_DEFINED__
+
+/* interface IDiaStackWalker2 */
+/* [unique][helpstring][local][uuid][object] */
+
+
+EXTERN_C const IID IID_IDiaStackWalker2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("7c185885-a015-4cac-9411-0f4fb39b1f3a")
+ IDiaStackWalker2 : public IDiaStackWalker
+ {
+ public:
+ };
+
+#else /* C style interface */
+
+ typedef struct IDiaStackWalker2Vtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IDiaStackWalker2 * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */
+ __RPC__deref_out void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IDiaStackWalker2 * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IDiaStackWalker2 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *getEnumFrames )(
+ IDiaStackWalker2 * This,
+ /* [in] */ IDiaStackWalkHelper *pHelper,
+ /* [out] */ IDiaEnumStackFrames **ppEnum);
+
+ HRESULT ( STDMETHODCALLTYPE *getEnumFrames2 )(
+ IDiaStackWalker2 * This,
+ /* [in] */ enum CV_CPU_TYPE_e cpuid,
+ /* [in] */ IDiaStackWalkHelper *pHelper,
+ /* [out] */ IDiaEnumStackFrames **ppEnum);
+
+ END_INTERFACE
+ } IDiaStackWalker2Vtbl;
+
+ interface IDiaStackWalker2
+ {
+ CONST_VTBL struct IDiaStackWalker2Vtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define IDiaStackWalker2_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define IDiaStackWalker2_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define IDiaStackWalker2_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define IDiaStackWalker2_getEnumFrames(This,pHelper,ppEnum) \
+ ( (This)->lpVtbl -> getEnumFrames(This,pHelper,ppEnum) )
+
+#define IDiaStackWalker2_getEnumFrames2(This,cpuid,pHelper,ppEnum) \
+ ( (This)->lpVtbl -> getEnumFrames2(This,cpuid,pHelper,ppEnum) )
+
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __IDiaStackWalker2_INTERFACE_DEFINED__ */
+
+
+/* Additional Prototypes for ALL interfaces */
+
+/* end of Additional Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/src/ToolBox/PdbTypeMatch/include/diacreate.h b/src/ToolBox/PdbTypeMatch/include/diacreate.h
new file mode 100644
index 0000000000..8ed82d2ff6
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/include/diacreate.h
@@ -0,0 +1,40 @@
+// 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.
+// diacreate.h - creation helper functions for DIA initialization
+#ifndef _DIACREATE_H_
+#define _DIACREATE_H_
+
+//
+// Create a dia data source object from the dia dll (by dll name - does not access the registry).
+//
+
+HRESULT STDMETHODCALLTYPE NoRegCoCreate( const __wchar_t *dllName,
+ REFCLSID rclsid,
+ REFIID riid,
+ void **ppv);
+
+#ifndef _NATIVE_WCHAR_T_DEFINED
+#ifdef __cplusplus
+
+HRESULT STDMETHODCALLTYPE NoRegCoCreate( const wchar_t *dllName,
+ REFCLSID rclsid,
+ REFIID riid,
+ void **ppv)
+{
+ return NoRegCoCreate( (const __wchar_t *)dllName, rclsid, riid, ppv );
+}
+
+#endif
+#endif
+
+
+
+//
+// Create a dia data source object from the dia dll (looks up the class id in the registry).
+//
+HRESULT STDMETHODCALLTYPE NoOleCoCreate( REFCLSID rclsid,
+ REFIID riid,
+ void **ppv);
+
+#endif
diff --git a/src/ToolBox/PdbTypeMatch/native.rc b/src/ToolBox/PdbTypeMatch/native.rc
new file mode 100644
index 0000000000..31e9f49b36
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/native.rc
@@ -0,0 +1,8 @@
+// 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\0"
+
+#include <fxver.h>
+#include <fxver.rc>
diff --git a/src/ToolBox/PdbTypeMatch/regs.cpp b/src/ToolBox/PdbTypeMatch/regs.cpp
new file mode 100644
index 0000000000..aa56cbd3f7
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/regs.cpp
@@ -0,0 +1,1708 @@
+// 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 "stdafx.h"
+#include "cvconst.h"
+#include "regs.h"
+
+const wchar_t * const rgRegX86[] = {
+ L"None", // 0 CV_REG_NONE
+ L"al", // 1 CV_REG_AL
+ L"cl", // 2 CV_REG_CL
+ L"dl", // 3 CV_REG_DL
+ L"bl", // 4 CV_REG_BL
+ L"ah", // 5 CV_REG_AH
+ L"ch", // 6 CV_REG_CH
+ L"dh", // 7 CV_REG_DH
+ L"bh", // 8 CV_REG_BH
+ L"ax", // 9 CV_REG_AX
+ L"cx", // 10 CV_REG_CX
+ L"dx", // 11 CV_REG_DX
+ L"bx", // 12 CV_REG_BX
+ L"sp", // 13 CV_REG_SP
+ L"bp", // 14 CV_REG_BP
+ L"si", // 15 CV_REG_SI
+ L"di", // 16 CV_REG_DI
+ L"eax", // 17 CV_REG_EAX
+ L"ecx", // 18 CV_REG_ECX
+ L"edx", // 19 CV_REG_EDX
+ L"ebx", // 20 CV_REG_EBX
+ L"esp", // 21 CV_REG_ESP
+ L"ebp", // 22 CV_REG_EBP
+ L"esi", // 23 CV_REG_ESI
+ L"edi", // 24 CV_REG_EDI
+ L"es", // 25 CV_REG_ES
+ L"cs", // 26 CV_REG_CS
+ L"ss", // 27 CV_REG_SS
+ L"ds", // 28 CV_REG_DS
+ L"fs", // 29 CV_REG_FS
+ L"gs", // 30 CV_REG_GS
+ L"IP", // 31 CV_REG_IP
+ L"FLAGS", // 32 CV_REG_FLAGS
+ L"EIP", // 33 CV_REG_EIP
+ L"EFLAGS", // 34 CV_REG_EFLAG
+ L"???", // 35
+ L"???", // 36
+ L"???", // 37
+ L"???", // 38
+ L"???", // 39
+ L"TEMP", // 40 CV_REG_TEMP
+ L"TEMPH" // 41 CV_REG_TEMPH
+ L"QUOTE", // 42 CV_REG_QUOTE
+ L"PCDR3", // 43 CV_REG_PCDR3
+ L"PCDR4", // 44 CV_REG_PCDR4
+ L"PCDR5", // 45 CV_REG_PCDR5
+ L"PCDR6", // 46 CV_REG_PCDR6
+ L"PCDR7", // 47 CV_REG_PCDR7
+ L"???", // 48
+ L"???", // 49
+ L"???", // 50
+ L"???", // 51
+ L"???", // 52
+ L"???", // 53
+ L"???", // 54
+ L"???", // 55
+ L"???", // 56
+ L"???", // 57
+ L"???", // 58
+ L"???", // 59
+ L"???", // 60
+ L"???", // 61
+ L"???", // 62
+ L"???", // 63
+ L"???", // 64
+ L"???", // 65
+ L"???", // 66
+ L"???", // 67
+ L"???", // 68
+ L"???", // 69
+ L"???", // 70
+ L"???", // 71
+ L"???", // 72
+ L"???", // 73
+ L"???", // 74
+ L"???", // 75
+ L"???", // 76
+ L"???", // 77
+ L"???", // 78
+ L"???", // 79
+ L"cr0", // 80 CV_REG_CR0
+ L"cr1", // 81 CV_REG_CR1
+ L"cr2", // 82 CV_REG_CR2
+ L"cr3", // 83 CV_REG_CR3
+ L"cr4", // 84 CV_REG_CR4
+ L"???", // 85
+ L"???", // 86
+ L"???", // 87
+ L"???", // 88
+ L"???", // 89
+ L"dr0", // 90 CV_REG_DR0
+ L"dr1", // 91 CV_REG_DR1
+ L"dr2", // 92 CV_REG_DR2
+ L"dr3", // 93 CV_REG_DR3
+ L"dr4", // 94 CV_REG_DR4
+ L"dr5", // 95 CV_REG_DR5
+ L"dr6", // 96 CV_REG_DR6
+ L"dr7", // 97 CV_REG_DR7
+ L"???", // 98
+ L"???", // 99
+ L"???", // 10
+ L"???", // 101
+ L"???", // 102
+ L"???", // 103
+ L"???", // 104
+ L"???", // 105
+ L"???", // 106
+ L"???", // 107
+ L"???", // 108
+ L"???", // 109
+ L"GDTR", // 110 CV_REG_GDTR
+ L"GDTL", // 111 CV_REG_GDTL
+ L"IDTR", // 112 CV_REG_IDTR
+ L"IDTL", // 113 CV_REG_IDTL
+ L"LDTR", // 114 CV_REG_LDTR
+ L"TR", // 115 CV_REG_TR
+ L"???", // 116
+ L"???", // 117
+ L"???", // 118
+ L"???", // 119
+ L"???", // 120
+ L"???", // 121
+ L"???", // 122
+ L"???", // 123
+ L"???", // 124
+ L"???", // 125
+ L"???", // 126
+ L"???", // 127
+ L"st(0)", // 128 CV_REG_ST0
+ L"st(1)", // 129 CV_REG_ST1
+ L"st(2)", // 130 CV_REG_ST2
+ L"st(3)", // 131 CV_REG_ST3
+ L"st(4)", // 132 CV_REG_ST4
+ L"st(5)", // 133 CV_REG_ST5
+ L"st(6)", // 134 CV_REG_ST6
+ L"st(7)", // 135 CV_REG_ST7
+ L"CTRL", // 136 CV_REG_CTRL
+ L"STAT", // 137 CV_REG_STAT
+ L"TAG", // 138 CV_REG_TAG
+ L"FPIP", // 139 CV_REG_FPIP
+ L"FPCS", // 140 CV_REG_FPCS
+ L"FPDO", // 141 CV_REG_FPDO
+ L"FPDS", // 142 CV_REG_FPDS
+ L"ISEM", // 143 CV_REG_ISEM
+ L"FPEIP", // 144 CV_REG_FPEIP
+ L"FPED0" // 145 CV_REG_FPEDO
+};
+
+const wchar_t * const rgRegAMD64[] = {
+ L"None", // 0 CV_REG_NONE
+ L"al", // 1 CV_AMD64_AL
+ L"cl", // 2 CV_AMD64_CL
+ L"dl", // 3 CV_AMD64_DL
+ L"bl", // 4 CV_AMD64_BL
+ L"ah", // 5 CV_AMD64_AH
+ L"ch", // 6 CV_AMD64_CH
+ L"dh", // 7 CV_AMD64_DH
+ L"bh", // 8 CV_AMD64_BH
+ L"ax", // 9 CV_AMD64_AX
+ L"cx", // 10 CV_AMD64_CX
+ L"dx", // 11 CV_AMD64_DX
+ L"bx", // 12 CV_AMD64_BX
+ L"sp", // 13 CV_AMD64_SP
+ L"bp", // 14 CV_AMD64_BP
+ L"si", // 15 CV_AMD64_SI
+ L"di", // 16 CV_AMD64_DI
+ L"eax", // 17 CV_AMD64_EAX
+ L"ecx", // 18 CV_AMD64_ECX
+ L"edx", // 19 CV_AMD64_EDX
+ L"ebx", // 20 CV_AMD64_EBX
+ L"esp", // 21 CV_AMD64_ESP
+ L"ebp", // 22 CV_AMD64_EBP
+ L"esi", // 23 CV_AMD64_ESI
+ L"edi", // 24 CV_AMD64_EDI
+ L"es", // 25 CV_AMD64_ES
+ L"cs", // 26 CV_AMD64_CS
+ L"ss", // 27 CV_AMD64_SS
+ L"ds", // 28 CV_AMD64_DS
+ L"fs", // 29 CV_AMD64_FS
+ L"gs", // 30 CV_AMD64_GS
+ L"???", // 31 Not filled up
+ L"flags", // 32 CV_AMD64_FLAGS
+ L"rip", // 33 CV_AMD64_RIP
+ L"eflags", // 34 CV_AMD64_EFLAGS
+ L"???", // 35
+ L"???", // 36
+ L"???", // 37
+ L"???", // 38
+ L"???", // 39
+ L"???", // 40
+ L"???", // 41
+ L"???", // 42
+ L"???", // 43
+ L"???", // 44
+ L"???", // 45
+ L"???", // 46
+ L"???", // 47
+ L"???", // 48
+ L"???", // 49
+ L"???", // 50
+ L"???", // 51
+ L"???", // 52
+ L"???", // 53
+ L"???", // 54
+ L"???", // 55
+ L"???", // 56
+ L"???", // 57
+ L"???", // 58
+ L"???", // 59
+ L"???", // 60
+ L"???", // 61
+ L"???", // 62
+ L"???", // 63
+ L"???", // 64
+ L"???", // 65
+ L"???", // 66
+ L"???", // 67
+ L"???", // 68
+ L"???", // 69
+ L"???", // 70
+ L"???", // 71
+ L"???", // 72
+ L"???", // 73
+ L"???", // 74
+ L"???", // 75
+ L"???", // 76
+ L"???", // 77
+ L"???", // 78
+ L"???", // 79
+ L"cr0", // 80 CV_AMD64_CR0
+ L"cr1", // 81 CV_AMD64_CR1
+ L"cr2", // 82 CV_AMD64_CR2
+ L"cr3", // 83 CV_AMD64_CR3
+ L"cr4", // 84 CV_AMD64_CR4
+ L"???", // 85
+ L"???", // 86
+ L"???", // 87
+ L"cr8", // 88 CV_AMD64_CR8
+ L"???", // 89
+ L"dr0", // 90 CV_AMD64_DR0
+ L"dr1", // 91 CV_AMD64_DR1
+ L"dr2", // 92 CV_AMD64_DR2
+ L"dr3", // 93 CV_AMD64_DR3
+ L"dr4", // 94 CV_AMD64_DR4
+ L"dr5", // 95 CV_AMD64_DR5
+ L"dr6", // 96 CV_AMD64_DR6
+ L"dr7", // 97 CV_AMD64_DR7
+ L"dr8", // 98 CV_AMD64_DR8
+ L"dr9", // 99 CV_AMD64_DR9
+ L"dr10", // 100 CV_AMD64_DR10
+ L"dr11", // 101 CV_AMD64_DR11
+ L"dr12", // 102 CV_AMD64_DR12
+ L"dr13", // 103 CV_AMD64_DR13
+ L"dr14", // 104 CV_AMD64_DR14
+ L"dr15", // 105 CV_AMD64_DR15
+ L"???", // 106
+ L"???", // 107
+ L"???", // 108
+ L"???", // 109
+ L"gdtr", // 110 CV_AMD64_GDTR
+ L"gdt", // 111 CV_AMD64_GDTL
+ L"idtr", // 112 CV_AMD64_IDTR
+ L"idt", // 113 CV_AMD64_IDTL
+ L"ldtr", // 114 CV_AMD64_LDTR
+ L"tr", // 115 CV_AMD64_TR
+ L"???", // 116
+ L"???", // 117
+ L"???", // 118
+ L"???", // 119
+ L"???", // 120
+ L"???", // 121
+ L"???", // 122
+ L"???", // 123
+ L"???", // 124
+ L"???", // 125
+ L"???", // 126
+ L"???", // 127
+ L"st(0)", // 128 CV_AMD64_ST0
+ L"st(1)", // 129 CV_AMD64_ST1
+ L"st(2)", // 130 CV_AMD64_ST2
+ L"st(3)", // 131 CV_AMD64_ST3
+ L"st(4)", // 132 CV_AMD64_ST4
+ L"st(5)", // 133 CV_AMD64_ST5
+ L"st(6)", // 134 CV_AMD64_ST6
+ L"st(7)", // 135 CV_AMD64_ST7
+ L"ctr", // 136 CV_AMD64_CTRL
+ L"stat", // 137 CV_AMD64_STAT
+ L"tag", // 138 CV_AMD64_TAG
+ L"fpip", // 139 CV_AMD64_FPIP
+ L"fpcs", // 140 CV_AMD64_FPCS
+ L"fpdo", // 141 CV_AMD64_FPDO
+ L"fpds", // 142 CV_AMD64_FPDS
+ L"isem", // 143 CV_AMD64_ISEM
+ L"fpeip", // 144 CV_AMD64_FPEIP
+ L"fped0", // 145 CV_AMD64_FPEDO
+ L"mm0", // 146 CV_AMD64_MM0
+ L"mm1", // 147 CV_AMD64_MM1
+ L"mm2", // 148 CV_AMD64_MM2
+ L"mm3", // 149 CV_AMD64_MM3
+ L"mm4", // 150 CV_AMD64_MM4
+ L"mm5", // 151 CV_AMD64_MM5
+ L"mm6", // 152 CV_AMD64_MM6
+ L"mm7", // 153 CV_AMD64_MM7
+ L"xmm0", // 154 CV_AMD64_XMM0
+ L"xmm1", // 155 CV_AMD64_XMM1
+ L"xmm2", // 156 CV_AMD64_XMM2
+ L"xmm3", // 157 CV_AMD64_XMM3
+ L"xmm4", // 158 CV_AMD64_XMM4
+ L"xmm5", // 159 CV_AMD64_XMM5
+ L"xmm6", // 160 CV_AMD64_XMM6
+ L"xmm7", // 161 CV_AMD64_XMM7
+ L"xmm0_0", // 162 CV_AMD64_XMM0_0
+ L"xmm0_1", // 163 CV_AMD64_XMM0_1
+ L"xmm0_2", // 164 CV_AMD64_XMM0_2
+ L"xmm0_3", // 165 CV_AMD64_XMM0_3
+ L"xmm1_0", // 166 CV_AMD64_XMM1_0
+ L"xmm1_1", // 167 CV_AMD64_XMM1_1
+ L"xmm1_2", // 168 CV_AMD64_XMM1_2
+ L"xmm1_3", // 169 CV_AMD64_XMM1_3
+ L"xmm2_0", // 170 CV_AMD64_XMM2_0
+ L"xmm2_1", // 171 CV_AMD64_XMM2_1
+ L"xmm2_2", // 172 CV_AMD64_XMM2_2
+ L"xmm2_3", // 173 CV_AMD64_XMM2_3
+ L"xmm3_0", // 174 CV_AMD64_XMM3_0
+ L"xmm3_1", // 175 CV_AMD64_XMM3_1
+ L"xmm3_2", // 176 CV_AMD64_XMM3_2
+ L"xmm3_3", // 177 CV_AMD64_XMM3_3
+ L"xmm4_0", // 178 CV_AMD64_XMM4_0
+ L"xmm4_1", // 179 CV_AMD64_XMM4_1
+ L"xmm4_2", // 180 CV_AMD64_XMM4_2
+ L"xmm4_3", // 181 CV_AMD64_XMM4_3
+ L"xmm5_0", // 182 CV_AMD64_XMM5_0
+ L"xmm5_1", // 183 CV_AMD64_XMM5_1
+ L"xmm5_2", // 184 CV_AMD64_XMM5_2
+ L"xmm5_3", // 185 CV_AMD64_XMM5_3
+ L"xmm6_0", // 186 CV_AMD64_XMM6_0
+ L"xmm6_1", // 187 CV_AMD64_XMM6_1
+ L"xmm6_2", // 188 CV_AMD64_XMM6_2
+ L"xmm6_3", // 189 CV_AMD64_XMM6_3
+ L"xmm7_0", // 190 CV_AMD64_XMM7_0
+ L"xmm7_1", // 191 CV_AMD64_XMM7_1
+ L"xmm7_2", // 192 CV_AMD64_XMM7_2
+ L"xmm7_3", // 193 CV_AMD64_XMM7_3
+ L"xmm0", // 194 CV_AMD64_XMM0L
+ L"xmm1", // 195 CV_AMD64_XMM1L
+ L"xmm2", // 196 CV_AMD64_XMM2L
+ L"xmm3", // 197 CV_AMD64_XMM3L
+ L"xmm4", // 198 CV_AMD64_XMM4L
+ L"xmm5", // 199 CV_AMD64_XMM5L
+ L"xmm6", // 200 CV_AMD64_XMM6L
+ L"xmm7", // 201 CV_AMD64_XMM7L
+ L"xmm0h", // 202 CV_AMD64_XMM0H
+ L"xmm1h", // 203 CV_AMD64_XMM1H
+ L"xmm2h", // 204 CV_AMD64_XMM2H
+ L"xmm3h", // 205 CV_AMD64_XMM3H
+ L"xmm4h", // 206 CV_AMD64_XMM4H
+ L"xmm5h", // 207 CV_AMD64_XMM5H
+ L"xmm6h", // 208 CV_AMD64_XMM6H
+ L"xmm7h", // 209 CV_AMD64_XMM7H
+ L"???", // 210
+ L"mxcsr", // 211 CV_AMD64_MXCSR
+ L"???", // 212
+ L"???", // 213
+ L"???", // 214
+ L"???", // 215
+ L"???", // 216
+ L"???", // 217
+ L"???", // 218
+ L"???", // 219
+ L"emm0", // 220 CV_AMD64_EMM0L
+ L"emm1", // 221 CV_AMD64_EMM1L
+ L"emm2", // 222 CV_AMD64_EMM2L
+ L"emm3", // 223 CV_AMD64_EMM3L
+ L"emm4", // 224 CV_AMD64_EMM4L
+ L"emm5", // 225 CV_AMD64_EMM5L
+ L"emm6", // 226 CV_AMD64_EMM6L
+ L"emm7", // 227 CV_AMD64_EMM7L
+ L"emm0h", // 228 CV_AMD64_EMM0H
+ L"emm1h", // 229 CV_AMD64_EMM1H
+ L"emm2h", // 230 CV_AMD64_EMM2H
+ L"emm3h", // 231 CV_AMD64_EMM3H
+ L"emm4h", // 232 CV_AMD64_EMM4H
+ L"emm5h", // 233 CV_AMD64_EMM5H
+ L"emm6h", // 234 CV_AMD64_EMM6H
+ L"emm7h", // 235 CV_AMD64_EMM7H
+ L"mm00", // 236 CV_AMD64_MM00
+ L"mm01", // 237 CV_AMD64_MM01
+ L"mm10", // 238 CV_AMD64_MM10
+ L"mm11", // 239 CV_AMD64_MM11
+ L"mm20", // 240 CV_AMD64_MM20
+ L"mm21", // 241 CV_AMD64_MM21
+ L"mm30", // 242 CV_AMD64_MM30
+ L"mm31", // 243 CV_AMD64_MM31
+ L"mm40", // 244 CV_AMD64_MM40
+ L"mm41", // 245 CV_AMD64_MM41
+ L"mm50", // 246 CV_AMD64_MM50
+ L"mm51", // 247 CV_AMD64_MM51
+ L"mm60", // 248 CV_AMD64_MM60
+ L"mm61", // 249 CV_AMD64_MM61
+ L"mm70", // 250 CV_AMD64_MM70
+ L"mm71", // 251 CV_AMD64_MM71
+ L"xmm8", // 252 CV_AMD64_XMM8
+ L"xmm9", // 253 CV_AMD64_XMM9
+ L"xmm10", // 254 CV_AMD64_XMM10
+ L"xmm11", // 255 CV_AMD64_XMM11
+ L"xmm12", // 256 CV_AMD64_XMM12
+ L"xmm13", // 257 CV_AMD64_XMM13
+ L"xmm14", // 258 CV_AMD64_XMM14
+ L"xmm15", // 259 CV_AMD64_XMM15
+ L"xmm8_0", // 260 CV_AMD64_XMM8_0
+ L"xmm8_1", // 261 CV_AMD64_XMM8_1
+ L"xmm8_2", // 262 CV_AMD64_XMM8_2
+ L"xmm8_3", // 263 CV_AMD64_XMM8_3
+ L"xmm9_0", // 264 CV_AMD64_XMM9_0
+ L"xmm9_1", // 265 CV_AMD64_XMM9_1
+ L"xmm9_2", // 266 CV_AMD64_XMM9_2
+ L"xmm9_3", // 267 CV_AMD64_XMM9_3
+ L"xmm10_0", // 268 CV_AMD64_XMM10_0
+ L"xmm10_1", // 269 CV_AMD64_XMM10_1
+ L"xmm10_2", // 270 CV_AMD64_XMM10_2
+ L"xmm10_3", // 271 CV_AMD64_XMM10_3
+ L"xmm11_0", // 272 CV_AMD64_XMM11_0
+ L"xmm11_1", // 273 CV_AMD64_XMM11_1
+ L"xmm11_2", // 274 CV_AMD64_XMM11_2
+ L"xmm11_3", // 275 CV_AMD64_XMM11_3
+ L"xmm12_0", // 276 CV_AMD64_XMM12_0
+ L"xmm12_1", // 277 CV_AMD64_XMM12_1
+ L"xmm12_2", // 278 CV_AMD64_XMM12_2
+ L"xmm12_3", // 279 CV_AMD64_XMM12_3
+ L"xmm13_0", // 280 CV_AMD64_XMM13_0
+ L"xmm13_1", // 281 CV_AMD64_XMM13_1
+ L"xmm13_2", // 282 CV_AMD64_XMM13_2
+ L"xmm13_3", // 283 CV_AMD64_XMM13_3
+ L"xmm14_0", // 284 CV_AMD64_XMM14_0
+ L"xmm14_1", // 285 CV_AMD64_XMM14_1
+ L"xmm14_2", // 286 CV_AMD64_XMM14_2
+ L"xmm14_3", // 287 CV_AMD64_XMM14_3
+ L"xmm15_0", // 288 CV_AMD64_XMM15_0
+ L"xmm15_1", // 289 CV_AMD64_XMM15_1
+ L"xmm15_2", // 290 CV_AMD64_XMM15_2
+ L"xmm15_3", // 291 CV_AMD64_XMM15_3
+ L"xmm8", // 292 CV_AMD64_XMM8L
+ L"xmm9", // 293 CV_AMD64_XMM9L
+ L"xmm10", // 294 CV_AMD64_XMM10L
+ L"xmm11", // 295 CV_AMD64_XMM11L
+ L"xmm12", // 296 CV_AMD64_XMM12L
+ L"xmm13", // 297 CV_AMD64_XMM13L
+ L"xmm14", // 298 CV_AMD64_XMM14L
+ L"xmm15", // 299 CV_AMD64_XMM15L
+ L"xmm8h", // 300 CV_AMD64_XMM8H
+ L"xmm9h", // 301 CV_AMD64_XMM9H
+ L"xmm10h", // 302 CV_AMD64_XMM10H
+ L"xmm11h", // 303 CV_AMD64_XMM11H
+ L"xmm12h", // 304 CV_AMD64_XMM12H
+ L"xmm13h", // 305 CV_AMD64_XMM13H
+ L"xmm14h", // 306 CV_AMD64_XMM14H
+ L"xmm15h", // 307 CV_AMD64_XMM15H
+ L"emm8", // 308 CV_AMD64_EMM8L
+ L"emm9", // 309 CV_AMD64_EMM9L
+ L"emm10", // 310 CV_AMD64_EMM10L
+ L"emm11", // 311 CV_AMD64_EMM11L
+ L"emm12", // 312 CV_AMD64_EMM12L
+ L"emm13", // 313 CV_AMD64_EMM13L
+ L"emm14", // 314 CV_AMD64_EMM14L
+ L"emm15", // 315 CV_AMD64_EMM15L
+ L"emm8h", // 316 CV_AMD64_EMM8H
+ L"emm9h", // 317 CV_AMD64_EMM9H
+ L"emm10h", // 318 CV_AMD64_EMM10H
+ L"emm11h", // 319 CV_AMD64_EMM11H
+ L"emm12h", // 320 CV_AMD64_EMM12H
+ L"emm13h", // 321 CV_AMD64_EMM13H
+ L"emm14h", // 322 CV_AMD64_EMM14H
+ L"emm15h", // 323 CV_AMD64_EMM15H
+ L"si", // 324 CV_AMD64_SIL
+ L"di", // 325 CV_AMD64_DIL
+ L"bp", // 326 CV_AMD64_BPL
+ L"sp", // 327 CV_AMD64_SPL
+ L"rax", // 328 CV_AMD64_RAX
+ L"rbx", // 329 CV_AMD64_RBX
+ L"rcx", // 330 CV_AMD64_RCX
+ L"rdx", // 331 CV_AMD64_RDX
+ L"rsi", // 332 CV_AMD64_RSI
+ L"rdi", // 333 CV_AMD64_RDI
+ L"rbp", // 334 CV_AMD64_RBP
+ L"rsp", // 335 CV_AMD64_RSP
+ L"r8", // 336 CV_AMD64_R8
+ L"r9", // 337 CV_AMD64_R9
+ L"r10", // 338 CV_AMD64_R10
+ L"r11", // 339 CV_AMD64_R11
+ L"r12", // 340 CV_AMD64_R12
+ L"r13", // 341 CV_AMD64_R13
+ L"r14", // 342 CV_AMD64_R14
+ L"r15", // 343 CV_AMD64_R15
+ L"r8b", // 344 CV_AMD64_R8B
+ L"r9b", // 345 CV_AMD64_R9B
+ L"r10b", // 346 CV_AMD64_R10B
+ L"r11b", // 347 CV_AMD64_R11B
+ L"r12b", // 348 CV_AMD64_R12B
+ L"r13b", // 349 CV_AMD64_R13B
+ L"r14b", // 350 CV_AMD64_R14B
+ L"r15b", // 351 CV_AMD64_R15B
+ L"r8w", // 352 CV_AMD64_R8W
+ L"r9w", // 353 CV_AMD64_R9W
+ L"r10w", // 354 CV_AMD64_R10W
+ L"r11w", // 355 CV_AMD64_R11W
+ L"r12w", // 356 CV_AMD64_R12W
+ L"r13w", // 357 CV_AMD64_R13W
+ L"r14w", // 358 CV_AMD64_R14W
+ L"r15w", // 359 CV_AMD64_R15W
+ L"r8d", // 360 CV_AMD64_R8D
+ L"r9d", // 361 CV_AMD64_R9D
+ L"r10d", // 362 CV_AMD64_R10D
+ L"r11d", // 363 CV_AMD64_R11D
+ L"r12d", // 364 CV_AMD64_R12D
+ L"r13d", // 365 CV_AMD64_R13D
+ L"r14d", // 366 CV_AMD64_R14D
+ L"r15d" // 367 CV_AMD64_R15D
+};
+
+const wchar_t * const rgRegMips[] = {
+ L"None", // 0 CV_M4_NOREG
+ L"???", // 1
+ L"???", // 2
+ L"???", // 3
+ L"???", // 4
+ L"???", // 5
+ L"???", // 6
+ L"???", // 7
+ L"???", // 8
+ L"???", // 9
+ L"zero", // 10 CV_M4_IntZERO
+ L"at", // 11 CV_M4_IntAT
+ L"v0", // 12 CV_M4_IntV0
+ L"v1", // 13 CV_M4_IntV1
+ L"a0", // 14 CV_M4_IntA0
+ L"a1", // 15 CV_M4_IntA1
+ L"a2", // 16 CV_M4_IntA2
+ L"a3", // 17 CV_M4_IntA3
+ L"t0", // 18 CV_M4_IntT0
+ L"t1", // 19 CV_M4_IntT1
+ L"t2", // 20 CV_M4_IntT2
+ L"t3", // 21 CV_M4_IntT3
+ L"t4", // 22 CV_M4_IntT4
+ L"t5", // 23 CV_M4_IntT5
+ L"t6", // 24 CV_M4_IntT6
+ L"t7", // 25 CV_M4_IntT7
+ L"s0", // 26 CV_M4_IntS0
+ L"s1", // 27 CV_M4_IntS1
+ L"s2", // 28 CV_M4_IntS2
+ L"s3", // 29 CV_M4_IntS3
+ L"s4", // 30 CV_M4_IntS4
+ L"s5", // 31 CV_M4_IntS5
+ L"s6", // 32 CV_M4_IntS6
+ L"s7", // 33 CV_M4_IntS7
+ L"t8", // 34 CV_M4_IntT8
+ L"t9", // 35 CV_M4_IntT9
+ L"k0", // 36 CV_M4_IntKT0
+ L"k1", // 37 CV_M4_IntKT1
+ L"gp", // 38 CV_M4_IntGP
+ L"sp", // 39 CV_M4_IntSP
+ L"s8", // 40 CV_M4_IntS8
+ L"ra", // 41 CV_M4_IntRA
+ L"lo", // 42 CV_M4_IntLO
+ L"hi", // 43 CV_M4_IntHI
+ L"???", // 44
+ L"???", // 45
+ L"???", // 46
+ L"???", // 47
+ L"???", // 48
+ L"???", // 49
+ L"Fir", // 50 CV_M4_Fir
+ L"Psr", // 51 CV_M4_Psr
+ L"???", // 52
+ L"???", // 53
+ L"???", // 54
+ L"???", // 55
+ L"???", // 56
+ L"???", // 57
+ L"???", // 58
+ L"???", // 59
+ L"$f0", // 60 CV_M4_FltF0
+ L"$f1", // 61 CV_M4_FltF1
+ L"$f2", // 62 CV_M4_FltF2
+ L"$f3", // 63 CV_M4_FltF3
+ L"$f4", // 64 CV_M4_FltF4
+ L"$f5", // 65 CV_M4_FltF5
+ L"$f6", // 66 CV_M4_FltF6
+ L"$f7", // 67 CV_M4_FltF7
+ L"$f8", // 68 CV_M4_FltF8
+ L"$f9", // 69 CV_M4_FltF9
+ L"$f10", // 70 CV_M4_FltF10
+ L"$f11", // 71 CV_M4_FltF11
+ L"$f12", // 72 CV_M4_FltF12
+ L"$f13", // 73 CV_M4_FltF13
+ L"$f14", // 74 CV_M4_FltF14
+ L"$f15", // 75 CV_M4_FltF15
+ L"$f16", // 76 CV_M4_FltF16
+ L"$f17", // 77 CV_M4_FltF17
+ L"$f18", // 78 CV_M4_FltF18
+ L"$f19", // 79 CV_M4_FltF19
+ L"$f20", // 80 CV_M4_FltF20
+ L"$f21", // 81 CV_M4_FltF21
+ L"$f22", // 82 CV_M4_FltF22
+ L"$f23", // 83 CV_M4_FltF23
+ L"$f24", // 84 CV_M4_FltF24
+ L"$f25", // 85 CV_M4_FltF25
+ L"$f26", // 86 CV_M4_FltF26
+ L"$f27", // 87 CV_M4_FltF27
+ L"$f28", // 88 CV_M4_FltF28
+ L"$f29", // 89 CV_M4_FltF29
+ L"$f30", // 90 CV_M4_FltF30
+ L"$f31", // 91 CV_M4_FltF31
+ L"Fsr" // 92 CV_M4_FltFsr
+};
+
+const wchar_t * const rgReg68k[] = {
+ L"D0", // 0 CV_R68_D0
+ L"D1", // 1 CV_R68_D1
+ L"D2", // 2 CV_R68_D2
+ L"D3", // 3 CV_R68_D3
+ L"D4", // 4 CV_R68_D4
+ L"D5", // 5 CV_R68_D5
+ L"D6", // 6 CV_R68_D6
+ L"D7", // 7 CV_R68_D7
+ L"A0", // 8 CV_R68_A0
+ L"A1", // 9 CV_R68_A1
+ L"A2", // 10 CV_R68_A2
+ L"A3", // 11 CV_R68_A3
+ L"A4", // 12 CV_R68_A4
+ L"A5", // 13 CV_R68_A5
+ L"A6", // 14 CV_R68_A6
+ L"A7", // 15 CV_R68_A7
+ L"CCR", // 16 CV_R68_CCR
+ L"SR", // 17 CV_R68_SR
+ L"USP", // 18 CV_R68_USP
+ L"MSP", // 19 CV_R68_MSP
+ L"SFC", // 20 CV_R68_SFC
+ L"DFC", // 21 CV_R68_DFC
+ L"CACR", // 22 CV_R68_CACR
+ L"VBR", // 23 CV_R68_VBR
+ L"CAAR", // 24 CV_R68_CAAR
+ L"ISP", // 25 CV_R68_ISP
+ L"PC", // 26 CV_R68_PC
+ L"???", // 27
+ L"FPCR", // 28 CV_R68_FPCR
+ L"FPSR", // 29 CV_R68_FPSR
+ L"FPIAR", // 30 CV_R68_FPIAR
+ L"???", // 31
+ L"FP0", // 32 CV_R68_FP0
+ L"FP1", // 33 CV_R68_FP1
+ L"FP2", // 34 CV_R68_FP2
+ L"FP3", // 35 CV_R68_FP3
+ L"FP4", // 36 CV_R68_FP4
+ L"FP5", // 37 CV_R68_FP5
+ L"FP6", // 38 CV_R68_FP6
+ L"FP7", // 39 CV_R68_FP7
+ L"???", // 40
+ L"???", // 41 CV_R68_MMUSR030
+ L"???", // 42 CV_R68_MMUSR
+ L"???", // 43 CV_R68_URP
+ L"???", // 44 CV_R68_DTT0
+ L"???", // 45 CV_R68_DTT1
+ L"???", // 46 CV_R68_ITT0
+ L"???", // 47 CV_R68_ITT1
+ L"???", // 48
+ L"???", // 49
+ L"???", // 50
+ L"PSR", // 51 CV_R68_PSR
+ L"PCSR", // 52 CV_R68_PCSR
+ L"VAL", // 53 CV_R68_VAL
+ L"CRP", // 54 CV_R68_CRP
+ L"SRP", // 55 CV_R68_SRP
+ L"DRP", // 56 CV_R68_DRP
+ L"TC", // 57 CV_R68_TC
+ L"AC", // 58 CV_R68_AC
+ L"SCC", // 59 CV_R68_SCC
+ L"CAL", // 60 CV_R68_CAL
+ L"TT0", // 61 CV_R68_TT0
+ L"TT1", // 62 CV_R68_TT1
+ L"???", // 63
+ L"BAD0", // 64 CV_R68_BAD0
+ L"BAD1", // 65 CV_R68_BAD1
+ L"BAD2", // 66 CV_R68_BAD2
+ L"BAD3", // 67 CV_R68_BAD3
+ L"BAD4", // 68 CV_R68_BAD4
+ L"BAD5", // 69 CV_R68_BAD5
+ L"BAD6", // 70 CV_R68_BAD6
+ L"BAD7", // 71 CV_R68_BAD7
+ L"BAC0", // 72 CV_R68_BAC0
+ L"BAC1", // 73 CV_R68_BAC1
+ L"BAC2", // 74 CV_R68_BAC2
+ L"BAC3", // 75 CV_R68_BAC3
+ L"BAC4", // 76 CV_R68_BAC4
+ L"BAC5", // 77 CV_R68_BAC5
+ L"BAC6", // 78 CV_R68_BAC6
+ L"BAC7" // 79 CV_R68_BAC7
+};
+
+const wchar_t * const rgRegAlpha[] = {
+ L"None", // 0 CV_ALPHA_NOREG
+ L"???", // 1
+ L"???", // 2
+ L"???", // 3
+ L"???", // 4
+ L"???", // 5
+ L"???", // 6
+ L"???", // 7
+ L"???", // 8
+ L"???", // 9
+ L"$f0", // 10 CV_ALPHA_FltF0
+ L"$f1", // 11 CV_ALPHA_FltF1
+ L"$f2", // 12 CV_ALPHA_FltF2
+ L"$f3", // 13 CV_ALPHA_FltF3
+ L"$f4", // 14 CV_ALPHA_FltF4
+ L"$f5", // 15 CV_ALPHA_FltF5
+ L"$f6", // 16 CV_ALPHA_FltF6
+ L"$f7", // 17 CV_ALPHA_FltF7
+ L"$f8", // 18 CV_ALPHA_FltF8
+ L"$f9", // 19 CV_ALPHA_FltF9
+ L"$f10", // 20 CV_ALPHA_FltF10
+ L"$f11", // 21 CV_ALPHA_FltF11
+ L"$f12", // 22 CV_ALPHA_FltF12
+ L"$f13", // 23 CV_ALPHA_FltF13
+ L"$f14", // 24 CV_ALPHA_FltF14
+ L"$f15", // 25 CV_ALPHA_FltF15
+ L"$f16", // 26 CV_ALPHA_FltF16
+ L"$f17", // 27 CV_ALPHA_FltF17
+ L"$f18", // 28 CV_ALPHA_FltF18
+ L"$f19", // 29 CV_ALPHA_FltF19
+ L"$f20", // 30 CV_ALPHA_FltF20
+ L"$f21", // 31 CV_ALPHA_FltF21
+ L"$f22", // 32 CV_ALPHA_FltF22
+ L"$f23", // 33 CV_ALPHA_FltF23
+ L"$f24", // 34 CV_ALPHA_FltF24
+ L"$f25", // 35 CV_ALPHA_FltF25
+ L"$f26", // 36 CV_ALPHA_FltF26
+ L"$f27", // 37 CV_ALPHA_FltF27
+ L"$f28", // 38 CV_ALPHA_FltF28
+ L"$f29", // 39 CV_ALPHA_FltF29
+ L"$f30", // 40 CV_ALPHA_FltF30
+ L"$f31", // 41 CV_ALPHA_FltF31
+ L"v0", // 42 CV_ALPHA_IntV0
+ L"t0", // 43 CV_ALPHA_IntT0
+ L"t1", // 44 CV_ALPHA_IntT1
+ L"t2", // 45 CV_ALPHA_IntT2
+ L"t3", // 46 CV_ALPHA_IntT3
+ L"t4", // 47 CV_ALPHA_IntT4
+ L"t5", // 48 CV_ALPHA_IntT5
+ L"t6", // 49 CV_ALPHA_IntT6
+ L"t7", // 50 CV_ALPHA_IntT7
+ L"s0", // 51 CV_ALPHA_IntS0
+ L"s1", // 52 CV_ALPHA_IntS1
+ L"s2", // 53 CV_ALPHA_IntS2
+ L"s3", // 54 CV_ALPHA_IntS3
+ L"s4", // 55 CV_ALPHA_IntS4
+ L"s5", // 56 CV_ALPHA_IntS5
+ L"fp", // 57 CV_ALPHA_IntFP
+ L"a0", // 58 CV_ALPHA_IntA0
+ L"a1", // 59 CV_ALPHA_IntA1
+ L"a2", // 60 CV_ALPHA_IntA2
+ L"a3", // 61 CV_ALPHA_IntA3
+ L"a4", // 62 CV_ALPHA_IntA4
+ L"a5", // 63 CV_ALPHA_IntA5
+ L"t8", // 64 CV_ALPHA_IntT8
+ L"t9", // 65 CV_ALPHA_IntT9
+ L"t10", // 66 CV_ALPHA_IntT10
+ L"t11", // 67 CV_ALPHA_IntT11
+ L"ra", // 68 CV_ALPHA_IntRA
+ L"t12", // 69 CV_ALPHA_IntT12
+ L"at", // 70 CV_ALPHA_IntAT
+ L"gp", // 71 CV_ALPHA_IntGP
+ L"sp", // 72 CV_ALPHA_IntSP
+ L"zero", // 73 CV_ALPHA_IntZERO
+ L"Fpcr", // 74 CV_ALPHA_Fpcr
+ L"Fir", // 75 CV_ALPHA_Fir
+ L"Psr", // 76 CV_ALPHA_Psr
+ L"FltFsr" // 77 CV_ALPHA_FltFsr
+};
+
+const wchar_t * const rgRegPpc[] = {
+ L"None", // 0
+ L"r0", // 1 CV_PPC_GPR0
+ L"r1", // 2 CV_PPC_GPR1
+ L"r2", // 3 CV_PPC_GPR2
+ L"r3", // 4 CV_PPC_GPR3
+ L"r4", // 5 CV_PPC_GPR4
+ L"r5", // 6 CV_PPC_GPR5
+ L"r6", // 7 CV_PPC_GPR6
+ L"r7", // 8 CV_PPC_GPR7
+ L"r8", // 9 CV_PPC_GPR8
+ L"r9", // 10 CV_PPC_GPR9
+ L"r10", // 11 CV_PPC_GPR10
+ L"r11", // 12 CV_PPC_GPR11
+ L"r12", // 13 CV_PPC_GPR12
+ L"r13", // 14 CV_PPC_GPR13
+ L"r14", // 15 CV_PPC_GPR14
+ L"r15", // 16 CV_PPC_GPR15
+ L"r16", // 17 CV_PPC_GPR16
+ L"r17", // 18 CV_PPC_GPR17
+ L"r18", // 19 CV_PPC_GPR18
+ L"r19", // 20 CV_PPC_GPR19
+ L"r20", // 21 CV_PPC_GPR20
+ L"r21", // 22 CV_PPC_GPR21
+ L"r22", // 23 CV_PPC_GPR22
+ L"r23", // 24 CV_PPC_GPR23
+ L"r24", // 25 CV_PPC_GPR24
+ L"r25", // 26 CV_PPC_GPR25
+ L"r26", // 27 CV_PPC_GPR26
+ L"r27", // 28 CV_PPC_GPR27
+ L"r28", // 29 CV_PPC_GPR28
+ L"r29", // 30 CV_PPC_GPR29
+ L"r30", // 31 CV_PPC_GPR30
+ L"r31", // 32 CV_PPC_GPR31
+ L"cr", // 33 CV_PPC_CR
+ L"cr0", // 34 CV_PPC_CR0
+ L"cr1", // 35 CV_PPC_CR1
+ L"cr2", // 36 CV_PPC_CR2
+ L"cr3", // 37 CV_PPC_CR3
+ L"cr4", // 38 CV_PPC_CR4
+ L"cr5", // 39 CV_PPC_CR5
+ L"cr6", // 40 CV_PPC_CR6
+ L"cr7", // 41 CV_PPC_CR7
+ L"f0", // 42 CV_PPC_FPR0
+ L"f1", // 43 CV_PPC_FPR1
+ L"f2", // 44 CV_PPC_FPR2
+ L"f3", // 45 CV_PPC_FPR3
+ L"f4", // 46 CV_PPC_FPR4
+ L"f5", // 47 CV_PPC_FPR5
+ L"f6", // 48 CV_PPC_FPR6
+ L"f7", // 49 CV_PPC_FPR7
+ L"f8", // 50 CV_PPC_FPR8
+ L"f9", // 51 CV_PPC_FPR9
+ L"f10", // 52 CV_PPC_FPR10
+ L"f11", // 53 CV_PPC_FPR11
+ L"f12", // 54 CV_PPC_FPR12
+ L"f13", // 55 CV_PPC_FPR13
+ L"f14", // 56 CV_PPC_FPR14
+ L"f15", // 57 CV_PPC_FPR15
+ L"f16", // 58 CV_PPC_FPR16
+ L"f17", // 59 CV_PPC_FPR17
+ L"f18", // 60 CV_PPC_FPR18
+ L"f19", // 61 CV_PPC_FPR19
+ L"f20", // 62 CV_PPC_FPR20
+ L"f21", // 63 CV_PPC_FPR21
+ L"f22", // 64 CV_PPC_FPR22
+ L"f23", // 65 CV_PPC_FPR23
+ L"f24", // 66 CV_PPC_FPR24
+ L"f25", // 67 CV_PPC_FPR25
+ L"f26", // 68 CV_PPC_FPR26
+ L"f27", // 69 CV_PPC_FPR27
+ L"f28", // 70 CV_PPC_FPR28
+ L"f29", // 71 CV_PPC_FPR29
+ L"f30", // 72 CV_PPC_FPR30
+ L"f31", // 73 CV_PPC_FPR31
+ L"Fpscr", // 74 CV_PPC_FPSCR
+ L"Msr" // 75 CV_PPC_MSR
+};
+
+const wchar_t * const rgRegSh[] = {
+ L"None", // 0 CV_SH3_NOREG
+ L"???", // 1
+ L"???", // 2
+ L"???", // 3
+ L"???", // 4
+ L"???", // 5
+ L"???", // 6
+ L"???", // 7
+ L"???", // 8
+ L"???", // 9
+ L"r0", // 10 CV_SH3_IntR0
+ L"r1", // 11 CV_SH3_IntR1
+ L"r2", // 12 CV_SH3_IntR2
+ L"r3", // 13 CV_SH3_IntR3
+ L"r4", // 14 CV_SH3_IntR4
+ L"r5", // 15 CV_SH3_IntR5
+ L"r6", // 16 CV_SH3_IntR6
+ L"r7", // 17 CV_SH3_IntR7
+ L"r8", // 18 CV_SH3_IntR8
+ L"r9", // 19 CV_SH3_IntR9
+ L"r10", // 20 CV_SH3_IntR10
+ L"r11", // 21 CV_SH3_IntR11
+ L"r12", // 22 CV_SH3_IntR12
+ L"r13", // 23 CV_SH3_IntR13
+ L"fp", // 24 CV_SH3_IntFp
+ L"sp", // 25 CV_SH3_IntSp
+ L"???", // 26
+ L"???", // 27
+ L"???", // 28
+ L"???", // 29
+ L"???", // 30
+ L"???", // 31
+ L"???", // 32
+ L"???", // 33
+ L"???", // 34
+ L"???", // 35
+ L"???", // 36
+ L"???", // 37
+ L"gbr", // 38 CV_SH3_Gbr
+ L"pr", // 39 CV_SH3_Pr
+ L"mach", // 40 CV_SH3_Mach
+ L"macl", // 41 CV_SH3_Macl
+ L"???", // 42
+ L"???", // 43
+ L"???", // 44
+ L"???", // 45
+ L"???", // 46
+ L"???", // 47
+ L"???", // 48
+ L"???", // 49
+ L"pc", // 50
+ L"sr", // 51
+ L"???", // 52
+ L"???", // 53
+ L"???", // 54
+ L"???", // 55
+ L"???", // 56
+ L"???", // 57
+ L"???", // 58
+ L"???", // 59
+ L"bara", // 60 CV_SH3_BarA
+ L"basra", // 61 CV_SH3_BasrA
+ L"bamra", // 62 CV_SH3_BamrA
+ L"bbra", // 63 CV_SH3_BbrA
+ L"barb", // 64 CV_SH3_BarB
+ L"basrb", // 65 CV_SH3_BasrB
+ L"bamrb", // 66 CV_SH3_BamrB
+ L"bbrb", // 67 CV_SH3_BbrB
+ L"bdrb", // 68 CV_SH3_BdrB
+ L"bdmrb", // 69 CV_SH3_BdmrB
+ L"brcr" // 70 CV_SH3_Brcr
+};
+
+const wchar_t * const rgRegArm[] = {
+ L"None", // 0 CV_ARM_NOREG
+ L"???", // 1
+ L"???", // 2
+ L"???", // 3
+ L"???", // 4
+ L"???", // 5
+ L"???", // 6
+ L"???", // 7
+ L"???", // 8
+ L"???", // 9
+ L"r0", // 10 CV_ARM_R0
+ L"r1", // 11 CV_ARM_R1
+ L"r2", // 12 CV_ARM_R2
+ L"r3", // 13 CV_ARM_R3
+ L"r4", // 14 CV_ARM_R4
+ L"r5", // 15 CV_ARM_R5
+ L"r6", // 16 CV_ARM_R6
+ L"r7", // 17 CV_ARM_R7
+ L"r8", // 18 CV_ARM_R8
+ L"r9", // 19 CV_ARM_R9
+ L"r10", // 20 CV_ARM_R10
+ L"r11", // 21 CV_ARM_R11
+ L"r12", // 22 CV_ARM_R12
+ L"sp", // 23 CV_ARM_SP
+ L"lr", // 24 CV_ARM_LR
+ L"pc", // 25 CV_ARM_PC
+ L"cpsr" // 26 CV_ARM_CPSR
+};
+
+const MapIa64Reg mpIa64regSz[] = {
+ { CV_IA64_Br0, L"Br0" },
+ { CV_IA64_Br1, L"Br1" },
+ { CV_IA64_Br2, L"Br2" },
+ { CV_IA64_Br3, L"Br3" },
+ { CV_IA64_Br4, L"Br4" },
+ { CV_IA64_Br5, L"Br5" },
+ { CV_IA64_Br6, L"Br6" },
+ { CV_IA64_Br7, L"Br7" },
+ { CV_IA64_Preds, L"Preds" },
+ { CV_IA64_IntH0, L"IntH0" },
+ { CV_IA64_IntH1, L"IntH1" },
+ { CV_IA64_IntH2, L"IntH2" },
+ { CV_IA64_IntH3, L"IntH3" },
+ { CV_IA64_IntH4, L"IntH4" },
+ { CV_IA64_IntH5, L"IntH5" },
+ { CV_IA64_IntH6, L"IntH6" },
+ { CV_IA64_IntH7, L"IntH7" },
+ { CV_IA64_IntH8, L"IntH8" },
+ { CV_IA64_IntH9, L"IntH9" },
+ { CV_IA64_IntH10, L"IntH10" },
+ { CV_IA64_IntH11, L"IntH11" },
+ { CV_IA64_IntH12, L"IntH12" },
+ { CV_IA64_IntH13, L"IntH13" },
+ { CV_IA64_IntH14, L"IntH14" },
+ { CV_IA64_IntH15, L"IntH15" },
+ { CV_IA64_Ip, L"Ip" },
+ { CV_IA64_Umask, L"Umask" },
+ { CV_IA64_Cfm, L"Cfm" },
+ { CV_IA64_Psr, L"Psr" },
+ { CV_IA64_Nats, L"Nats" },
+ { CV_IA64_Nats2, L"Nats2" },
+ { CV_IA64_Nats3, L"Nats3" },
+ { CV_IA64_IntR0, L"IntR0" },
+ { CV_IA64_IntR1, L"IntR1" },
+ { CV_IA64_IntR2, L"IntR2" },
+ { CV_IA64_IntR3, L"IntR3" },
+ { CV_IA64_IntR4, L"IntR4" },
+ { CV_IA64_IntR5, L"IntR5" },
+ { CV_IA64_IntR6, L"IntR6" },
+ { CV_IA64_IntR7, L"IntR7" },
+ { CV_IA64_IntR8, L"IntR8" },
+ { CV_IA64_IntR9, L"IntR9" },
+ { CV_IA64_IntR10, L"IntR10" },
+ { CV_IA64_IntR11, L"IntR11" },
+ { CV_IA64_IntR12, L"IntR12" },
+ { CV_IA64_IntR13, L"IntR13" },
+ { CV_IA64_IntR14, L"IntR14" },
+ { CV_IA64_IntR15, L"IntR15" },
+ { CV_IA64_IntR16, L"IntR16" },
+ { CV_IA64_IntR17, L"IntR17" },
+ { CV_IA64_IntR18, L"IntR18" },
+ { CV_IA64_IntR19, L"IntR19" },
+ { CV_IA64_IntR20, L"IntR20" },
+ { CV_IA64_IntR21, L"IntR21" },
+ { CV_IA64_IntR22, L"IntR22" },
+ { CV_IA64_IntR23, L"IntR23" },
+ { CV_IA64_IntR24, L"IntR24" },
+ { CV_IA64_IntR25, L"IntR25" },
+ { CV_IA64_IntR26, L"IntR26" },
+ { CV_IA64_IntR27, L"IntR27" },
+ { CV_IA64_IntR28, L"IntR28" },
+ { CV_IA64_IntR29, L"IntR29" },
+ { CV_IA64_IntR30, L"IntR30" },
+ { CV_IA64_IntR31, L"IntR31" },
+ { CV_IA64_IntR32, L"IntR32" },
+ { CV_IA64_IntR33, L"IntR33" },
+ { CV_IA64_IntR34, L"IntR34" },
+ { CV_IA64_IntR35, L"IntR35" },
+ { CV_IA64_IntR36, L"IntR36" },
+ { CV_IA64_IntR37, L"IntR37" },
+ { CV_IA64_IntR38, L"IntR38" },
+ { CV_IA64_IntR39, L"IntR39" },
+ { CV_IA64_IntR40, L"IntR40" },
+ { CV_IA64_IntR41, L"IntR41" },
+ { CV_IA64_IntR42, L"IntR42" },
+ { CV_IA64_IntR43, L"IntR43" },
+ { CV_IA64_IntR44, L"IntR44" },
+ { CV_IA64_IntR45, L"IntR45" },
+ { CV_IA64_IntR46, L"IntR46" },
+ { CV_IA64_IntR47, L"IntR47" },
+ { CV_IA64_IntR48, L"IntR48" },
+ { CV_IA64_IntR49, L"IntR49" },
+ { CV_IA64_IntR50, L"IntR50" },
+ { CV_IA64_IntR51, L"IntR51" },
+ { CV_IA64_IntR52, L"IntR52" },
+ { CV_IA64_IntR53, L"IntR53" },
+ { CV_IA64_IntR54, L"IntR54" },
+ { CV_IA64_IntR55, L"IntR55" },
+ { CV_IA64_IntR56, L"IntR56" },
+ { CV_IA64_IntR57, L"IntR57" },
+ { CV_IA64_IntR58, L"IntR58" },
+ { CV_IA64_IntR59, L"IntR59" },
+ { CV_IA64_IntR60, L"IntR60" },
+ { CV_IA64_IntR61, L"IntR61" },
+ { CV_IA64_IntR62, L"IntR62" },
+ { CV_IA64_IntR63, L"IntR63" },
+ { CV_IA64_IntR64, L"IntR64" },
+ { CV_IA64_IntR65, L"IntR65" },
+ { CV_IA64_IntR66, L"IntR66" },
+ { CV_IA64_IntR67, L"IntR67" },
+ { CV_IA64_IntR68, L"IntR68" },
+ { CV_IA64_IntR69, L"IntR69" },
+ { CV_IA64_IntR70, L"IntR70" },
+ { CV_IA64_IntR71, L"IntR71" },
+ { CV_IA64_IntR72, L"IntR72" },
+ { CV_IA64_IntR73, L"IntR73" },
+ { CV_IA64_IntR74, L"IntR74" },
+ { CV_IA64_IntR75, L"IntR75" },
+ { CV_IA64_IntR76, L"IntR76" },
+ { CV_IA64_IntR77, L"IntR77" },
+ { CV_IA64_IntR78, L"IntR78" },
+ { CV_IA64_IntR79, L"IntR79" },
+ { CV_IA64_IntR80, L"IntR80" },
+ { CV_IA64_IntR81, L"IntR81" },
+ { CV_IA64_IntR82, L"IntR82" },
+ { CV_IA64_IntR83, L"IntR83" },
+ { CV_IA64_IntR84, L"IntR84" },
+ { CV_IA64_IntR85, L"IntR85" },
+ { CV_IA64_IntR86, L"IntR86" },
+ { CV_IA64_IntR87, L"IntR87" },
+ { CV_IA64_IntR88, L"IntR88" },
+ { CV_IA64_IntR89, L"IntR89" },
+ { CV_IA64_IntR90, L"IntR90" },
+ { CV_IA64_IntR91, L"IntR91" },
+ { CV_IA64_IntR92, L"IntR92" },
+ { CV_IA64_IntR93, L"IntR93" },
+ { CV_IA64_IntR94, L"IntR94" },
+ { CV_IA64_IntR95, L"IntR95" },
+ { CV_IA64_IntR96, L"IntR96" },
+ { CV_IA64_IntR97, L"IntR97" },
+ { CV_IA64_IntR98, L"IntR98" },
+ { CV_IA64_IntR99, L"IntR99" },
+ { CV_IA64_IntR100, L"IntR100" },
+ { CV_IA64_IntR101, L"IntR101" },
+ { CV_IA64_IntR102, L"IntR102" },
+ { CV_IA64_IntR103, L"IntR103" },
+ { CV_IA64_IntR104, L"IntR104" },
+ { CV_IA64_IntR105, L"IntR105" },
+ { CV_IA64_IntR106, L"IntR106" },
+ { CV_IA64_IntR107, L"IntR107" },
+ { CV_IA64_IntR108, L"IntR108" },
+ { CV_IA64_IntR109, L"IntR109" },
+ { CV_IA64_IntR110, L"IntR110" },
+ { CV_IA64_IntR111, L"IntR111" },
+ { CV_IA64_IntR112, L"IntR112" },
+ { CV_IA64_IntR113, L"IntR113" },
+ { CV_IA64_IntR114, L"IntR114" },
+ { CV_IA64_IntR115, L"IntR115" },
+ { CV_IA64_IntR116, L"IntR116" },
+ { CV_IA64_IntR117, L"IntR117" },
+ { CV_IA64_IntR118, L"IntR118" },
+ { CV_IA64_IntR119, L"IntR119" },
+ { CV_IA64_IntR120, L"IntR120" },
+ { CV_IA64_IntR121, L"IntR121" },
+ { CV_IA64_IntR122, L"IntR122" },
+ { CV_IA64_IntR123, L"IntR123" },
+ { CV_IA64_IntR124, L"IntR124" },
+ { CV_IA64_IntR125, L"IntR125" },
+ { CV_IA64_IntR126, L"IntR126" },
+ { CV_IA64_IntR127, L"IntR127" },
+ { CV_IA64_FltF0, L"FltF0" },
+ { CV_IA64_FltF1, L"FltF1" },
+ { CV_IA64_FltF2, L"FltF2" },
+ { CV_IA64_FltF3, L"FltF3" },
+ { CV_IA64_FltF4, L"FltF4" },
+ { CV_IA64_FltF5, L"FltF5" },
+ { CV_IA64_FltF6, L"FltF6" },
+ { CV_IA64_FltF7, L"FltF7" },
+ { CV_IA64_FltF8, L"FltF8" },
+ { CV_IA64_FltF9, L"FltF9" },
+ { CV_IA64_FltF10, L"FltF10" },
+ { CV_IA64_FltF11, L"FltF11" },
+ { CV_IA64_FltF12, L"FltF12" },
+ { CV_IA64_FltF13, L"FltF13" },
+ { CV_IA64_FltF14, L"FltF14" },
+ { CV_IA64_FltF15, L"FltF15" },
+ { CV_IA64_FltF16, L"FltF16" },
+ { CV_IA64_FltF17, L"FltF17" },
+ { CV_IA64_FltF18, L"FltF18" },
+ { CV_IA64_FltF19, L"FltF19" },
+ { CV_IA64_FltF20, L"FltF20" },
+ { CV_IA64_FltF21, L"FltF21" },
+ { CV_IA64_FltF22, L"FltF22" },
+ { CV_IA64_FltF23, L"FltF23" },
+ { CV_IA64_FltF24, L"FltF24" },
+ { CV_IA64_FltF25, L"FltF25" },
+ { CV_IA64_FltF26, L"FltF26" },
+ { CV_IA64_FltF27, L"FltF27" },
+ { CV_IA64_FltF28, L"FltF28" },
+ { CV_IA64_FltF29, L"FltF29" },
+ { CV_IA64_FltF30, L"FltF30" },
+ { CV_IA64_FltF31, L"FltF31" },
+ { CV_IA64_FltF32, L"FltF32" },
+ { CV_IA64_FltF33, L"FltF33" },
+ { CV_IA64_FltF34, L"FltF34" },
+ { CV_IA64_FltF35, L"FltF35" },
+ { CV_IA64_FltF36, L"FltF36" },
+ { CV_IA64_FltF37, L"FltF37" },
+ { CV_IA64_FltF38, L"FltF38" },
+ { CV_IA64_FltF39, L"FltF39" },
+ { CV_IA64_FltF40, L"FltF40" },
+ { CV_IA64_FltF41, L"FltF41" },
+ { CV_IA64_FltF42, L"FltF42" },
+ { CV_IA64_FltF43, L"FltF43" },
+ { CV_IA64_FltF44, L"FltF44" },
+ { CV_IA64_FltF45, L"FltF45" },
+ { CV_IA64_FltF46, L"FltF46" },
+ { CV_IA64_FltF47, L"FltF47" },
+ { CV_IA64_FltF48, L"FltF48" },
+ { CV_IA64_FltF49, L"FltF49" },
+ { CV_IA64_FltF50, L"FltF50" },
+ { CV_IA64_FltF51, L"FltF51" },
+ { CV_IA64_FltF52, L"FltF52" },
+ { CV_IA64_FltF53, L"FltF53" },
+ { CV_IA64_FltF54, L"FltF54" },
+ { CV_IA64_FltF55, L"FltF55" },
+ { CV_IA64_FltF56, L"FltF56" },
+ { CV_IA64_FltF57, L"FltF57" },
+ { CV_IA64_FltF58, L"FltF58" },
+ { CV_IA64_FltF59, L"FltF59" },
+ { CV_IA64_FltF60, L"FltF60" },
+ { CV_IA64_FltF61, L"FltF61" },
+ { CV_IA64_FltF62, L"FltF62" },
+ { CV_IA64_FltF63, L"FltF63" },
+ { CV_IA64_FltF64, L"FltF64" },
+ { CV_IA64_FltF65, L"FltF65" },
+ { CV_IA64_FltF66, L"FltF66" },
+ { CV_IA64_FltF67, L"FltF67" },
+ { CV_IA64_FltF68, L"FltF68" },
+ { CV_IA64_FltF69, L"FltF69" },
+ { CV_IA64_FltF70, L"FltF70" },
+ { CV_IA64_FltF71, L"FltF71" },
+ { CV_IA64_FltF72, L"FltF72" },
+ { CV_IA64_FltF73, L"FltF73" },
+ { CV_IA64_FltF74, L"FltF74" },
+ { CV_IA64_FltF75, L"FltF75" },
+ { CV_IA64_FltF76, L"FltF76" },
+ { CV_IA64_FltF77, L"FltF77" },
+ { CV_IA64_FltF78, L"FltF78" },
+ { CV_IA64_FltF79, L"FltF79" },
+ { CV_IA64_FltF80, L"FltF80" },
+ { CV_IA64_FltF81, L"FltF81" },
+ { CV_IA64_FltF82, L"FltF82" },
+ { CV_IA64_FltF83, L"FltF83" },
+ { CV_IA64_FltF84, L"FltF84" },
+ { CV_IA64_FltF85, L"FltF85" },
+ { CV_IA64_FltF86, L"FltF86" },
+ { CV_IA64_FltF87, L"FltF87" },
+ { CV_IA64_FltF88, L"FltF88" },
+ { CV_IA64_FltF89, L"FltF89" },
+ { CV_IA64_FltF90, L"FltF90" },
+ { CV_IA64_FltF91, L"FltF91" },
+ { CV_IA64_FltF92, L"FltF92" },
+ { CV_IA64_FltF93, L"FltF93" },
+ { CV_IA64_FltF94, L"FltF94" },
+ { CV_IA64_FltF95, L"FltF95" },
+ { CV_IA64_FltF96, L"FltF96" },
+ { CV_IA64_FltF97, L"FltF97" },
+ { CV_IA64_FltF98, L"FltF98" },
+ { CV_IA64_FltF99, L"FltF99" },
+ { CV_IA64_FltF100, L"FltF100" },
+ { CV_IA64_FltF101, L"FltF101" },
+ { CV_IA64_FltF102, L"FltF102" },
+ { CV_IA64_FltF103, L"FltF103" },
+ { CV_IA64_FltF104, L"FltF104" },
+ { CV_IA64_FltF105, L"FltF105" },
+ { CV_IA64_FltF106, L"FltF106" },
+ { CV_IA64_FltF107, L"FltF107" },
+ { CV_IA64_FltF108, L"FltF108" },
+ { CV_IA64_FltF109, L"FltF109" },
+ { CV_IA64_FltF110, L"FltF110" },
+ { CV_IA64_FltF111, L"FltF111" },
+ { CV_IA64_FltF112, L"FltF112" },
+ { CV_IA64_FltF113, L"FltF113" },
+ { CV_IA64_FltF114, L"FltF114" },
+ { CV_IA64_FltF115, L"FltF115" },
+ { CV_IA64_FltF116, L"FltF116" },
+ { CV_IA64_FltF117, L"FltF117" },
+ { CV_IA64_FltF118, L"FltF118" },
+ { CV_IA64_FltF119, L"FltF119" },
+ { CV_IA64_FltF120, L"FltF120" },
+ { CV_IA64_FltF121, L"FltF121" },
+ { CV_IA64_FltF122, L"FltF122" },
+ { CV_IA64_FltF123, L"FltF123" },
+ { CV_IA64_FltF124, L"FltF124" },
+ { CV_IA64_FltF125, L"FltF125" },
+ { CV_IA64_FltF126, L"FltF126" },
+ { CV_IA64_FltF127, L"FltF127" },
+ { CV_IA64_ApKR0, L"ApKR0" },
+ { CV_IA64_ApKR1, L"ApKR1" },
+ { CV_IA64_ApKR2, L"ApKR2" },
+ { CV_IA64_ApKR3, L"ApKR3" },
+ { CV_IA64_ApKR4, L"ApKR4" },
+ { CV_IA64_ApKR5, L"ApKR5" },
+ { CV_IA64_ApKR6, L"ApKR6" },
+ { CV_IA64_ApKR7, L"ApKR7" },
+ { CV_IA64_AR8, L"AR8" },
+ { CV_IA64_AR9, L"AR9" },
+ { CV_IA64_AR10, L"AR10" },
+ { CV_IA64_AR11, L"AR11" },
+ { CV_IA64_AR12, L"AR12" },
+ { CV_IA64_AR13, L"AR13" },
+ { CV_IA64_AR14, L"AR14" },
+ { CV_IA64_AR15, L"AR15" },
+ { CV_IA64_RsRSC, L"RsRSC" },
+ { CV_IA64_RsBSP, L"RsBSP" },
+ { CV_IA64_RsBSPSTORE, L"RsBSPSTORE" },
+ { CV_IA64_RsRNAT, L"RsRNAT" },
+ { CV_IA64_AR20, L"AR20" },
+ { CV_IA64_StFCR, L"StFCR" },
+ { CV_IA64_AR22, L"AR22" },
+ { CV_IA64_AR23, L"AR23" },
+ { CV_IA64_EFLAG, L"EFLAG" },
+ { CV_IA64_CSD, L"CSD" },
+ { CV_IA64_SSD, L"SSD" },
+ { CV_IA64_CFLG, L"CFLG" },
+ { CV_IA64_StFSR, L"StFSR" },
+ { CV_IA64_StFIR, L"StFIR" },
+ { CV_IA64_StFDR, L"StFDR" },
+ { CV_IA64_AR31, L"AR31" },
+ { CV_IA64_ApCCV, L"ApCCV" },
+ { CV_IA64_AR33, L"AR33" },
+ { CV_IA64_AR34, L"AR34" },
+ { CV_IA64_AR35, L"AR35" },
+ { CV_IA64_ApUNAT, L"ApUNAT" },
+ { CV_IA64_AR37, L"AR37" },
+ { CV_IA64_AR38, L"AR38" },
+ { CV_IA64_AR39, L"AR39" },
+ { CV_IA64_StFPSR, L"StFPSR" },
+ { CV_IA64_AR41, L"AR41" },
+ { CV_IA64_AR42, L"AR42" },
+ { CV_IA64_AR43, L"AR43" },
+ { CV_IA64_ApITC, L"ApITC" },
+ { CV_IA64_AR45, L"AR45" },
+ { CV_IA64_AR46, L"AR46" },
+ { CV_IA64_AR47, L"AR47" },
+ { CV_IA64_AR48, L"AR48" },
+ { CV_IA64_AR49, L"AR49" },
+ { CV_IA64_AR50, L"AR50" },
+ { CV_IA64_AR51, L"AR51" },
+ { CV_IA64_AR52, L"AR52" },
+ { CV_IA64_AR53, L"AR53" },
+ { CV_IA64_AR54, L"AR54" },
+ { CV_IA64_AR55, L"AR55" },
+ { CV_IA64_AR56, L"AR56" },
+ { CV_IA64_AR57, L"AR57" },
+ { CV_IA64_AR58, L"AR58" },
+ { CV_IA64_AR59, L"AR59" },
+ { CV_IA64_AR60, L"AR60" },
+ { CV_IA64_AR61, L"AR61" },
+ { CV_IA64_AR62, L"AR62" },
+ { CV_IA64_AR63, L"AR63" },
+ { CV_IA64_RsPFS, L"RsPFS" },
+ { CV_IA64_ApLC, L"ApLC" },
+ { CV_IA64_ApEC, L"ApEC" },
+ { CV_IA64_AR67, L"AR67" },
+ { CV_IA64_AR68, L"AR68" },
+ { CV_IA64_AR69, L"AR69" },
+ { CV_IA64_AR70, L"AR70" },
+ { CV_IA64_AR71, L"AR71" },
+ { CV_IA64_AR72, L"AR72" },
+ { CV_IA64_AR73, L"AR73" },
+ { CV_IA64_AR74, L"AR74" },
+ { CV_IA64_AR75, L"AR75" },
+ { CV_IA64_AR76, L"AR76" },
+ { CV_IA64_AR77, L"AR77" },
+ { CV_IA64_AR78, L"AR78" },
+ { CV_IA64_AR79, L"AR79" },
+ { CV_IA64_AR80, L"AR80" },
+ { CV_IA64_AR81, L"AR81" },
+ { CV_IA64_AR82, L"AR82" },
+ { CV_IA64_AR83, L"AR83" },
+ { CV_IA64_AR84, L"AR84" },
+ { CV_IA64_AR85, L"AR85" },
+ { CV_IA64_AR86, L"AR86" },
+ { CV_IA64_AR87, L"AR87" },
+ { CV_IA64_AR88, L"AR88" },
+ { CV_IA64_AR89, L"AR89" },
+ { CV_IA64_AR90, L"AR90" },
+ { CV_IA64_AR91, L"AR91" },
+ { CV_IA64_AR92, L"AR92" },
+ { CV_IA64_AR93, L"AR93" },
+ { CV_IA64_AR94, L"AR94" },
+ { CV_IA64_AR95, L"AR95" },
+ { CV_IA64_AR96, L"AR96" },
+ { CV_IA64_AR97, L"AR97" },
+ { CV_IA64_AR98, L"AR98" },
+ { CV_IA64_AR99, L"AR99" },
+ { CV_IA64_AR100, L"AR100" },
+ { CV_IA64_AR101, L"AR101" },
+ { CV_IA64_AR102, L"AR102" },
+ { CV_IA64_AR103, L"AR103" },
+ { CV_IA64_AR104, L"AR104" },
+ { CV_IA64_AR105, L"AR105" },
+ { CV_IA64_AR106, L"AR106" },
+ { CV_IA64_AR107, L"AR107" },
+ { CV_IA64_AR108, L"AR108" },
+ { CV_IA64_AR109, L"AR109" },
+ { CV_IA64_AR110, L"AR110" },
+ { CV_IA64_AR111, L"AR111" },
+ { CV_IA64_AR112, L"AR112" },
+ { CV_IA64_AR113, L"AR113" },
+ { CV_IA64_AR114, L"AR114" },
+ { CV_IA64_AR115, L"AR115" },
+ { CV_IA64_AR116, L"AR116" },
+ { CV_IA64_AR117, L"AR117" },
+ { CV_IA64_AR118, L"AR118" },
+ { CV_IA64_AR119, L"AR119" },
+ { CV_IA64_AR120, L"AR120" },
+ { CV_IA64_AR121, L"AR121" },
+ { CV_IA64_AR122, L"AR122" },
+ { CV_IA64_AR123, L"AR123" },
+ { CV_IA64_AR124, L"AR124" },
+ { CV_IA64_AR125, L"AR125" },
+ { CV_IA64_AR126, L"AR126" },
+ { CV_IA64_AR127, L"AR127" },
+ { CV_IA64_ApDCR, L"ApDCR" },
+ { CV_IA64_ApITM, L"ApITM" },
+ { CV_IA64_ApIVA, L"ApIVA" },
+ { CV_IA64_CR3, L"CR3" },
+ { CV_IA64_CR4, L"CR4" },
+ { CV_IA64_CR5, L"CR5" },
+ { CV_IA64_CR6, L"CR6" },
+ { CV_IA64_CR7, L"CR7" },
+ { CV_IA64_ApPTA, L"ApPTA" },
+ { CV_IA64_ApGPTA, L"ApGPTA" },
+ { CV_IA64_CR10, L"CR10" },
+ { CV_IA64_CR11, L"CR11" },
+ { CV_IA64_CR12, L"CR12" },
+ { CV_IA64_CR13, L"CR13" },
+ { CV_IA64_CR14, L"CR14" },
+ { CV_IA64_CR15, L"CR15" },
+ { CV_IA64_StIPSR, L"StIPSR" },
+ { CV_IA64_StISR, L"StISR" },
+ { CV_IA64_CR18, L"CR18" },
+ { CV_IA64_StIIP, L"StIIP" },
+ { CV_IA64_StIFA, L"StIFA" },
+ { CV_IA64_StITIR, L"StITIR" },
+ { CV_IA64_StIIPA, L"StIIPA" },
+ { CV_IA64_StIFS, L"StIFS" },
+ { CV_IA64_StIIM, L"StIIM" },
+ { CV_IA64_StIHA, L"StIHA" },
+ { CV_IA64_CR26, L"CR26" },
+ { CV_IA64_CR27, L"CR27" },
+ { CV_IA64_CR28, L"CR28" },
+ { CV_IA64_CR29, L"CR29" },
+ { CV_IA64_CR30, L"CR30" },
+ { CV_IA64_CR31, L"CR31" },
+ { CV_IA64_CR32, L"CR32" },
+ { CV_IA64_CR33, L"CR33" },
+ { CV_IA64_CR34, L"CR34" },
+ { CV_IA64_CR35, L"CR35" },
+ { CV_IA64_CR36, L"CR36" },
+ { CV_IA64_CR37, L"CR37" },
+ { CV_IA64_CR38, L"CR38" },
+ { CV_IA64_CR39, L"CR39" },
+ { CV_IA64_CR40, L"CR40" },
+ { CV_IA64_CR41, L"CR41" },
+ { CV_IA64_CR42, L"CR42" },
+ { CV_IA64_CR43, L"CR43" },
+ { CV_IA64_CR44, L"CR44" },
+ { CV_IA64_CR45, L"CR45" },
+ { CV_IA64_CR46, L"CR46" },
+ { CV_IA64_CR47, L"CR47" },
+ { CV_IA64_CR48, L"CR48" },
+ { CV_IA64_CR49, L"CR49" },
+ { CV_IA64_CR50, L"CR50" },
+ { CV_IA64_CR51, L"CR51" },
+ { CV_IA64_CR52, L"CR52" },
+ { CV_IA64_CR53, L"CR53" },
+ { CV_IA64_CR54, L"CR54" },
+ { CV_IA64_CR55, L"CR55" },
+ { CV_IA64_CR56, L"CR56" },
+ { CV_IA64_CR57, L"CR57" },
+ { CV_IA64_CR58, L"CR58" },
+ { CV_IA64_CR59, L"CR59" },
+ { CV_IA64_CR60, L"CR60" },
+ { CV_IA64_CR61, L"CR61" },
+ { CV_IA64_CR62, L"CR62" },
+ { CV_IA64_CR63, L"CR63" },
+ { CV_IA64_SaLID, L"SaLID" },
+ { CV_IA64_SaIVR, L"SaIVR" },
+ { CV_IA64_SaTPR, L"SaTPR" },
+ { CV_IA64_SaEOI, L"SaEOI" },
+ { CV_IA64_SaIRR0, L"SaIRR0" },
+ { CV_IA64_SaIRR1, L"SaIRR1" },
+ { CV_IA64_SaIRR2, L"SaIRR2" },
+ { CV_IA64_SaIRR3, L"SaIRR3" },
+ { CV_IA64_SaITV, L"SaITV" },
+ { CV_IA64_SaPMV, L"SaPMV" },
+ { CV_IA64_SaCMCV, L"SaCMCV" },
+ { CV_IA64_CR75, L"CR75" },
+ { CV_IA64_CR76, L"CR76" },
+ { CV_IA64_CR77, L"CR77" },
+ { CV_IA64_CR78, L"CR78" },
+ { CV_IA64_CR79, L"CR79" },
+ { CV_IA64_SaLRR0, L"SaLRR0" },
+ { CV_IA64_SaLRR1, L"SaLRR1" },
+ { CV_IA64_CR82, L"CR82" },
+ { CV_IA64_CR83, L"CR83" },
+ { CV_IA64_CR84, L"CR84" },
+ { CV_IA64_CR85, L"CR85" },
+ { CV_IA64_CR86, L"CR86" },
+ { CV_IA64_CR87, L"CR87" },
+ { CV_IA64_CR88, L"CR88" },
+ { CV_IA64_CR89, L"CR89" },
+ { CV_IA64_CR90, L"CR90" },
+ { CV_IA64_CR91, L"CR91" },
+ { CV_IA64_CR92, L"CR92" },
+ { CV_IA64_CR93, L"CR93" },
+ { CV_IA64_CR94, L"CR94" },
+ { CV_IA64_CR95, L"CR95" },
+ { CV_IA64_SaIRR0, L"SaIRR0" },
+ { CV_IA64_CR97, L"CR97" },
+ { CV_IA64_SaIRR1, L"SaIRR1" },
+ { CV_IA64_CR99, L"CR99" },
+ { CV_IA64_SaIRR2, L"SaIRR2" },
+ { CV_IA64_CR101, L"CR101" },
+ { CV_IA64_SaIRR3, L"SaIRR3" },
+ { CV_IA64_CR103, L"CR103" },
+ { CV_IA64_CR104, L"CR104" },
+ { CV_IA64_CR105, L"CR105" },
+ { CV_IA64_CR106, L"CR106" },
+ { CV_IA64_CR107, L"CR107" },
+ { CV_IA64_CR108, L"CR108" },
+ { CV_IA64_CR109, L"CR109" },
+ { CV_IA64_CR110, L"CR110" },
+ { CV_IA64_CR111, L"CR111" },
+ { CV_IA64_CR112, L"CR112" },
+ { CV_IA64_CR113, L"CR113" },
+ { CV_IA64_SaITV, L"SaITV" },
+ { CV_IA64_CR115, L"CR115" },
+ { CV_IA64_SaPMV, L"SaPMV" },
+ { CV_IA64_SaLRR0, L"SaLRR0" },
+ { CV_IA64_SaLRR1, L"SaLRR1" },
+ { CV_IA64_SaCMCV, L"SaCMCV" },
+ { CV_IA64_CR120, L"CR120" },
+ { CV_IA64_CR121, L"CR121" },
+ { CV_IA64_CR122, L"CR122" },
+ { CV_IA64_CR123, L"CR123" },
+ { CV_IA64_CR124, L"CR124" },
+ { CV_IA64_CR125, L"CR125" },
+ { CV_IA64_CR126, L"CR126" },
+ { CV_IA64_CR127, L"CR127" },
+ { CV_IA64_Pkr0, L"Pkr0" },
+ { CV_IA64_Pkr1, L"Pkr1" },
+ { CV_IA64_Pkr2, L"Pkr2" },
+ { CV_IA64_Pkr3, L"Pkr3" },
+ { CV_IA64_Pkr4, L"Pkr4" },
+ { CV_IA64_Pkr5, L"Pkr5" },
+ { CV_IA64_Pkr6, L"Pkr6" },
+ { CV_IA64_Pkr7, L"Pkr7" },
+ { CV_IA64_Pkr8, L"Pkr8" },
+ { CV_IA64_Pkr9, L"Pkr9" },
+ { CV_IA64_Pkr10, L"Pkr10" },
+ { CV_IA64_Pkr11, L"Pkr11" },
+ { CV_IA64_Pkr12, L"Pkr12" },
+ { CV_IA64_Pkr13, L"Pkr13" },
+ { CV_IA64_Pkr14, L"Pkr14" },
+ { CV_IA64_Pkr15, L"Pkr15" },
+ { CV_IA64_Rr0, L"Rr0" },
+ { CV_IA64_Rr1, L"Rr1" },
+ { CV_IA64_Rr2, L"Rr2" },
+ { CV_IA64_Rr3, L"Rr3" },
+ { CV_IA64_Rr4, L"Rr4" },
+ { CV_IA64_Rr5, L"Rr5" },
+ { CV_IA64_Rr6, L"Rr6" },
+ { CV_IA64_Rr7, L"Rr7" },
+ { CV_IA64_PFD0, L"PFD0" },
+ { CV_IA64_PFD1, L"PFD1" },
+ { CV_IA64_PFD2, L"PFD2" },
+ { CV_IA64_PFD3, L"PFD3" },
+ { CV_IA64_PFD4, L"PFD4" },
+ { CV_IA64_PFD5, L"PFD5" },
+ { CV_IA64_PFD6, L"PFD6" },
+ { CV_IA64_PFD7, L"PFD7" },
+ { CV_IA64_PFC0, L"PFC0" },
+ { CV_IA64_PFC1, L"PFC1" },
+ { CV_IA64_PFC2, L"PFC2" },
+ { CV_IA64_PFC3, L"PFC3" },
+ { CV_IA64_PFC4, L"PFC4" },
+ { CV_IA64_PFC5, L"PFC5" },
+ { CV_IA64_PFC6, L"PFC6" },
+ { CV_IA64_PFC7, L"PFC7" },
+ { CV_IA64_TrI0, L"TrI0" },
+ { CV_IA64_TrI1, L"TrI1" },
+ { CV_IA64_TrI2, L"TrI2" },
+ { CV_IA64_TrI3, L"TrI3" },
+ { CV_IA64_TrI4, L"TrI4" },
+ { CV_IA64_TrI5, L"TrI5" },
+ { CV_IA64_TrI6, L"TrI6" },
+ { CV_IA64_TrI7, L"TrI7" },
+ { CV_IA64_TrD0, L"TrD0" },
+ { CV_IA64_TrD1, L"TrD1" },
+ { CV_IA64_TrD2, L"TrD2" },
+ { CV_IA64_TrD3, L"TrD3" },
+ { CV_IA64_TrD4, L"TrD4" },
+ { CV_IA64_TrD5, L"TrD5" },
+ { CV_IA64_TrD6, L"TrD6" },
+ { CV_IA64_TrD7, L"TrD7" },
+ { CV_IA64_DbI0, L"DbI0" },
+ { CV_IA64_DbI1, L"DbI1" },
+ { CV_IA64_DbI2, L"DbI2" },
+ { CV_IA64_DbI3, L"DbI3" },
+ { CV_IA64_DbI4, L"DbI4" },
+ { CV_IA64_DbI5, L"DbI5" },
+ { CV_IA64_DbI6, L"DbI6" },
+ { CV_IA64_DbI7, L"DbI7" },
+ { CV_IA64_DbD0, L"DbD0" },
+ { CV_IA64_DbD1, L"DbD1" },
+ { CV_IA64_DbD2, L"DbD2" },
+ { CV_IA64_DbD3, L"DbD3" },
+ { CV_IA64_DbD4, L"DbD4" },
+ { CV_IA64_DbD5, L"DbD5" },
+ { CV_IA64_DbD6, L"DbD6" },
+ { CV_IA64_DbD7, L"DbD7" }
+};
+
+////////////////////////////////////////////////////////////
+// Map an IA64 registry ID with the corresponding string name
+//
+int __cdecl cmpIa64regSz(const void *pv1, const void *pv2) {
+ const MapIa64Reg *p1 = (MapIa64Reg *) pv1;
+ const MapIa64Reg *p2 = (MapIa64Reg *) pv2;
+
+ if(p1->iCvReg < p2->iCvReg){
+ return -1;
+ }
+ if(p1->iCvReg > p2->iCvReg){
+ return 1;
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+// Map a registry id code with the corresponding string name
+//
+const wchar_t* SzNameC7Reg(USHORT reg, DWORD MachineType){
+ static wchar_t wszRegNum[64];
+
+ switch(reg){
+ case CV_ALLREG_LOCALS : return L"BaseOfLocals";
+ case CV_ALLREG_PARAMS : return L"BaseOfParams";
+ case CV_ALLREG_VFRAME : return L"VFrame";
+ }
+ swprintf_s(wszRegNum, L"???(0x%x)", reg);
+ switch(MachineType) {
+ case CV_CFL_8080:
+ case CV_CFL_8086:
+ case CV_CFL_80286:
+ case CV_CFL_80386:
+ case CV_CFL_80486:
+ case CV_CFL_PENTIUM:
+ if(reg < (sizeof(rgRegX86)/sizeof(*rgRegX86))){
+ return(rgRegX86[reg]);
+ }
+ return wszRegNum;
+ break;
+ case CV_CFL_ALPHA:
+ if(reg < (sizeof(rgRegAlpha)/sizeof(*rgRegAlpha))){
+ return(rgRegAlpha[reg]);
+ }
+ return wszRegNum;
+ break;
+ case CV_CFL_MIPSR4000:
+ case CV_CFL_MIPS16:
+ if(reg < (sizeof(rgRegMips)/sizeof(*rgRegMips))) {
+ return(rgRegMips[reg]);
+ }
+ return wszRegNum;
+ break;
+ case CV_CFL_M68000:
+ case CV_CFL_M68010:
+ case CV_CFL_M68020:
+ case CV_CFL_M68030:
+ case CV_CFL_M68040:
+ if(reg < (sizeof(rgReg68k)/sizeof(*rgReg68k))){
+ return(rgReg68k[reg]);
+ }
+ return wszRegNum;
+ break;
+ case CV_CFL_PPC601:
+ case CV_CFL_PPC603:
+ case CV_CFL_PPC604:
+ case CV_CFL_PPC620:
+ if(reg < (sizeof(rgRegPpc)/sizeof(*rgRegPpc))){
+ return(rgRegPpc[reg]);
+ }
+ return wszRegNum;
+ break;
+ case CV_CFL_SH3:
+ if(reg < (sizeof(rgRegSh)/sizeof(*rgRegSh))){
+ return(rgRegSh[reg]);
+ }
+ return wszRegNum;
+ break;
+ case CV_CFL_ARM3:
+ case CV_CFL_ARM4:
+ case CV_CFL_ARM4T:
+ if(reg < (sizeof(rgRegArm)/sizeof(*rgRegArm))){
+ return(rgRegArm[reg]);
+ }
+ return wszRegNum;
+ break;
+ case CV_CFL_IA64: {
+ MapIa64Reg *p;
+ MapIa64Reg m = {(CV_HREG_e) reg};
+ p = (MapIa64Reg *) bsearch(&m,
+ mpIa64regSz,
+ sizeof(mpIa64regSz)/sizeof(*mpIa64regSz),
+ sizeof(MapIa64Reg),
+ cmpIa64regSz);
+ if (p) {
+ return p->wszRegName;
+ }else{
+ return wszRegNum;
+ }
+ break;
+ }
+ case CV_CFL_AMD64 :
+ if (reg < sizeof(rgRegAMD64)/sizeof(*rgRegAMD64)) {
+ return rgRegAMD64[reg];
+ }else{
+ return wszRegNum;
+ }
+ break;
+ default:
+ return wszRegNum;
+ break;
+ }
+}
+
+const wchar_t* SzNameC7Reg(USHORT reg){
+ return SzNameC7Reg(reg, g_dwMachineType);
+}
+
diff --git a/src/ToolBox/PdbTypeMatch/regs.h b/src/ToolBox/PdbTypeMatch/regs.h
new file mode 100644
index 0000000000..946d5b8f49
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/regs.h
@@ -0,0 +1,23 @@
+// 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.
+
+extern const wchar_t * const rgRegX86[];
+extern const wchar_t * const rgRegAMD64[];
+extern const wchar_t * const rgRegMips[];
+extern const wchar_t * const rgReg68k[];
+extern const wchar_t * const rgRegAlpha[];
+extern const wchar_t * const rgRegPpc[];
+extern const wchar_t * const rgRegSh[];
+extern const wchar_t * const rgRegArm[];
+
+typedef struct MapIa64Reg{
+ CV_HREG_e iCvReg;
+ const wchar_t* wszRegName;
+}MapIa64Reg;
+extern const MapIa64Reg mpIa64regSz[];
+int __cdecl cmpIa64regSz( const void* , const void* );
+
+extern DWORD g_dwMachineType;
+const wchar_t* SzNameC7Reg( USHORT , DWORD );
+const wchar_t* SzNameC7Reg( USHORT );
diff --git a/src/ToolBox/PdbTypeMatch/stdafx.cpp b/src/ToolBox/PdbTypeMatch/stdafx.cpp
new file mode 100644
index 0000000000..f6da4386ea
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/stdafx.cpp
@@ -0,0 +1,9 @@
+// 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.
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/src/ToolBox/PdbTypeMatch/stdafx.h b/src/ToolBox/PdbTypeMatch/stdafx.h
new file mode 100644
index 0000000000..7d1423147d
--- /dev/null
+++ b/src/ToolBox/PdbTypeMatch/stdafx.h
@@ -0,0 +1,25 @@
+// 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.
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#endif
+
+#include <stdio.h>
+#include <tchar.h>
+#include <string.h>
+#include <comdef.h>
+
+#define _WIN32_FUSION 0x0100 // this causes activation context
+
+// TODO: reference additional headers your program requires here
+#include "dia2.h"
+
diff --git a/src/ToolBox/SOS/.gitmirror b/src/ToolBox/SOS/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/CMakeLists.txt b/src/ToolBox/SOS/CMakeLists.txt
new file mode 100644
index 0000000000..9380dc1de6
--- /dev/null
+++ b/src/ToolBox/SOS/CMakeLists.txt
@@ -0,0 +1,3 @@
+if(WIN32)
+ add_subdirectory(Strike)
+endif(WIN32)
diff --git a/src/ToolBox/SOS/DacTableGen/.gitmirror b/src/ToolBox/SOS/DacTableGen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/DacTableGen/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/DacTableGen/CMakeLists.txt b/src/ToolBox/SOS/DacTableGen/CMakeLists.txt
new file mode 100644
index 0000000000..01ee51f7a4
--- /dev/null
+++ b/src/ToolBox/SOS/DacTableGen/CMakeLists.txt
@@ -0,0 +1,19 @@
+set(DACTABLEGEN_SOURCES
+ cvconst.cs
+ diautil.cs
+ main.cs
+ MapSymbolProvider.cs
+)
+
+# Cmake does not support csharp sources so add custom command
+add_custom_target(dactablegen ALL
+ COMMAND csc.exe /t:exe /platform:anycpu /r:System.dll /r:$<TARGET_FILE:dialib_dll> /out:${CMAKE_CURRENT_BINARY_DIR}/dactablegen.exe ${DACTABLEGEN_SOURCES}
+ COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:dialib_dll> $<TARGET_FILE_DIR:dactablegen_exe>
+ DEPENDS ${DACTABLEGEN_SOURCES} dialib
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+# In order to use dactablegen as an executable target it needs to be imported.
+# Target is used in dll/mscoree/coreclr/cmakelists.txt
+add_executable(dactablegen_exe IMPORTED GLOBAL)
+set_property(TARGET dactablegen_exe PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/dactablegen.exe) \ No newline at end of file
diff --git a/src/ToolBox/SOS/DacTableGen/DacTableGen.csproj b/src/ToolBox/SOS/DacTableGen/DacTableGen.csproj
new file mode 100644
index 0000000000..064780e326
--- /dev/null
+++ b/src/ToolBox/SOS/DacTableGen/DacTableGen.csproj
@@ -0,0 +1,55 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <AssemblyName>DacTableGen</AssemblyName>
+ <OutputType>EXE</OutputType>
+ <DefineConstants>VERBOSE;DEBUG</DefineConstants>
+ <!--
+ # We define /platform:x86 because
+ # 1) this is a build tool, only used in building
+ # 2) In all the places we check this tool into the build system, we
+ # make sure to run it under the WOW if built on an AMD64 machine.
+ #
+ # Checking in /platform:x86 will prevent bugs that occur when this DLL
+ # needs to be updated in the build tools directory, but the person
+ # forgot to rebuild with that flag (happened several times).
+ -->
+ <PlatformTarget>x86</PlatformTarget>
+ <AssemblyAttributeClsCompliant>false</AssemblyAttributeClsCompliant>
+ <SignAssemblyAttribute>false</SignAssemblyAttribute>
+ <!--
+ # We don't want a generated embedded manifest, because we use an explicit
+ # .manifest file in tools\devdiv\x86 to do reg-free COM activation of
+ # msdia80.dll
+ -->
+ <NoWin32Manifest>true</NoWin32Manifest>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <Compile Include="cvconst.cs" />
+ <Compile Include="diautil.cs" />
+ <Compile Include="mapsymbolprovider.cs" />
+ <Compile Include="main.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(ClrSrcDirectory)toolbox\sos\diasdk\diasdk.nativeproj" />
+ <Reference Include="DiaLib">
+ <HintPath>$(ClrIntraLibPath)\DiaLib.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="System">
+ <HintPath>$(NDP_FXRefPath)\System.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs b/src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs
new file mode 100644
index 0000000000..018ed9fa89
--- /dev/null
+++ b/src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs
@@ -0,0 +1,503 @@
+// 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 DACTABLEGEN_DEBUG
+
+using System;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Collections;
+using System.Globalization;
+/***************************************************************************************
+ *
+ ***************************************************************************************/
+
+public class MapSymbolProvider : SymbolProvider
+{
+ public MapSymbolProvider(String symbolFilename) {
+ mf = new MapFile(symbolFilename);
+ }
+
+ public override UInt32 GetGlobalRVA(String symbolName,
+ SymType symType) {
+ return GetRVA(symbolName);
+
+ }
+
+ public override UInt32 GetVTableRVA(String symbolName,
+ String keyBaseName) {
+ if (keyBaseName != null) {
+ return GetRVA(symbolName + "__" + keyBaseName);
+ } else {
+ return GetRVA(symbolName);
+ }
+ }
+
+ UInt32 GetRVA(String symbolName) {
+
+ SymbolInfo si = mf.FindSymbol(symbolName);
+
+ if (si == null) {
+ // Ideally this would throw an exception and
+ // cause the whole process to fail but
+ // currently it's too complicated to get
+ // all the ifdef'ing right for all the
+ // mix of debug/checked/free multiplied by
+ // x86/AMD64/IA64/etc.
+ return UInt32.MaxValue;
+ }
+
+ return mf.GetRVA(si);
+ }
+
+ MapFile mf = null;
+}
+
+
+public class SymbolInfo
+{
+ public SymbolInfo(int Segment, UInt32 Address)
+ {
+ m_Segment = Segment;
+ m_Address = Address;
+ m_dupFound = false;
+ }
+
+ public int Segment
+ {
+ get
+ { return m_Segment; }
+ }
+
+ public UInt32 Address
+ {
+ get
+ { return m_Address; }
+ }
+
+ public bool dupFound
+ {
+ get
+ { return m_dupFound; }
+ set
+ { m_dupFound = value; }
+ }
+
+ int m_Segment; // The segment index. (Used only in Windows MAP file.)
+ UInt32 m_Address;
+
+ bool m_dupFound; // Have we found a duplicated entry for this key?
+}
+
+
+public class MapFile
+{
+ const String Reg_ExWhiteSpaces = @"\s+";
+
+ //
+ // These are regular expression strings for Windows MAP file.
+ //
+ const String Reg_MapAddress = @"^ (?<addrPart1>[0-9a-f]{4}):(?<addrPart2>[0-9a-f]{8})";
+
+ enum WindowsSymbolTypes {
+ ModuleNameClassNameFieldName,
+ ClassNameFieldName,
+ GlobalVarName,
+ GlobalVarName2,
+ GlobalVarName3,
+ SingleVtAddr,
+ MultiVtAddr,
+ };
+
+ readonly String[] RegExps_WindowsMapfile = {
+
+ // ModuleNameClassNameFieldName
+ // Example: ?ephemeral_heap_segment@gc_heap@WKS@@
+ Reg_MapAddress +
+ Reg_ExWhiteSpaces +
+ @"\?(?<fieldName>[^\?@]+)@(?<className>[^\?@]+)@(?<moduleName>[^\?@]+)@@",
+
+ // ClassNameFieldName
+ // Example: ?m_RangeTree@ExecutionManager@@
+ Reg_MapAddress +
+ Reg_ExWhiteSpaces +
+ @"\?(?<fieldName>[^\?@]+)@(?<className>[^\?@]+)@@",
+
+ // GlobalVarName
+ // Example: ?g_pNotificationTable@@
+ // (or) ?JIT_LMul@@
+ Reg_MapAddress +
+ Reg_ExWhiteSpaces +
+ @"\?(?<globalVarName>[^\?@]+)@@",
+
+ // GlobalVarName2
+ // Example: @JIT_WriteBarrier@
+ // (or) _JIT_FltRem@
+ // (or) _JIT_Dbl2Lng@
+ // (or) _JIT_LLsh@
+ Reg_MapAddress +
+ Reg_ExWhiteSpaces +
+ @"[@_](?<globalVarName>[^\?@]+)@",
+
+ // GlobalVarName3
+ // Example: _g_card_table 795e53a4
+ Reg_MapAddress +
+ Reg_ExWhiteSpaces +
+ @"_(?<globalVarName>[^\s@]+)" +
+ Reg_ExWhiteSpaces +
+ @"[0-9a-f]{8}",
+
+ // Single-inheritance VtAddr
+ // Example: ??_7Thread@@6B@
+ Reg_MapAddress +
+ Reg_ExWhiteSpaces +
+ @"\?\?_7(?<FunctionName>[^\?@]+)@@6B@",
+
+ // Multiple-inheritance VtAddr
+ // Example: ??_7CompilationDomain@@6BAppDomain@@@
+ Reg_MapAddress +
+ Reg_ExWhiteSpaces +
+ @"\?\?_7(?<FunctionName>[^\?@]+)@@6B(?<BaseName>[^@]+)@@@"
+ };
+ const String Reg_Length = @"(?<length>[0-9a-f]{8})H";
+ const String Reg_SegmentInfo = Reg_MapAddress + " " + Reg_Length;
+
+ //
+ // These are regular expression strings for Unix NM file.
+ //
+ const String Reg_NmAddress = @"^(?<address>[0-9a-f]{8})";
+ const String Reg_TypeChar = @"[a-zA-Z]";
+
+ enum UnixSymbolTypes {
+ CxxFieldName,
+ CxxFunctionName,
+ CGlobal,
+ VtAddr,
+ };
+
+ // Rather than try and encode a particular C++ mangling format here, we rely on the nm output
+ // having been unmanagled through the c++filt tool.
+ readonly String[] RegExps_UnixNmfile = {
+
+ // C++ field name
+ // Examples:
+ // d WKS::gc_heap::ephemeral_heap_segment
+ // d WKS::GCHeap::hEventFinalizer
+ // s ExecutionManager::m_CodeRangeList
+ // d ExecutionManager::m_dwReaderCount
+ Reg_NmAddress +
+ " " +
+ Reg_TypeChar +
+ " " +
+ @"(?<symName>\w+(::\w+)+)$",
+
+ // C++ function/method name
+ // Note: may have duplicates due to overloading by argument types
+ // Examples:
+ // t JIT_LMulOvf(int, int, int, long long, long long)
+ // t ThreadpoolMgr::AsyncCallbackCompletion(void*)
+ //
+ // Counter-examples we don't want to include:
+ // b JIT_NewFast(int, int, CORINFO_CLASS_STRUCT_*)::__haveCheckedRestoreState
+ //
+ Reg_NmAddress +
+ " " +
+ Reg_TypeChar +
+ " " +
+ @"(?<symName>\w+(::\w+)*)\(.*\)$",
+
+ // C global variable / function name
+ // Examples:
+ // s _g_pNotificationTable
+ // t _JIT_LLsh
+ Reg_NmAddress +
+ " " +
+ Reg_TypeChar +
+ " " +
+ @"_(?<symName>\w+)$",
+
+ // VtAddr
+ // Examples:
+ // s vtable for Thread
+ //
+ // Note that at the moment we don't have any multiple-inheritance vtables that we need
+ // on UNIX (CompilationDomain is the only one which is only present on FEATURE_PREJIT).
+ // It looks like classes with multiple inheritance only list a single vtable in nm output
+ // (eg. RegMeta).
+ // We also don't support nested classes here because there is currently no syntax for them
+ // for daccess.i.
+ //
+ Reg_NmAddress +
+ " " +
+ Reg_TypeChar +
+ " " +
+ @"vtable for (?<symName>\w+)$",
+ };
+
+ public UInt32 GetRVA(SymbolInfo si)
+ {
+ UInt32 addr = si.Address;
+ if (bIsWindowsMapfile)
+ {
+ addr += (UInt32)SegmentBase[si.Segment];
+ }
+ return addr;
+ }
+
+ public MapFile(String symdumpFile)
+ {
+ m_symdumpFile = symdumpFile;
+ loadDataFromMapFile();
+ }
+
+ void ReadMapHeader(StreamReader strm)
+ {
+ Regex regExNmFile = new Regex("^[0-9a-f]{8} ");
+ Regex regEx = new Regex(Reg_SegmentInfo);
+ Regex regHeaderSection = new Regex(@"\s*Address\s*Publics by Value\s*Rva\+Base\s*Lib:Object", RegexOptions.IgnoreCase);
+ Regex regnonNMHeader = new Regex(@"\s*Start\s*Length\s*Name\s*Class", RegexOptions.IgnoreCase);
+ Match match = null;
+
+ UInt32 lastSegmentIndex = 0;
+ UInt32 segmentIndex;
+ UInt32 sectionStart = 0;
+ UInt32 sectionLength = 0;
+
+ String line;
+ bool bInSegmentDecl = false;
+
+ const UInt32 baseOfCode = 0x1000;
+ SegmentBase.Add((UInt32)baseOfCode);
+
+ for (;;)
+ {
+ line = strm.ReadLine();
+
+ if (bInSegmentDecl) {
+
+ if (regHeaderSection.IsMatch(line)) {
+ // Header section ends.
+ break;
+ }
+#if DACTABLEGEN_DEBUG
+ Console.WriteLine("SegmentDecl: " + line);
+#endif
+
+ match = regEx.Match(line);
+ if (match.Success) {
+ segmentIndex = UInt32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier);
+ if (segmentIndex != lastSegmentIndex) {
+ // Enter the new segment. Record what we have.
+ // Note, SegmentBase[i] is built upon SegmentBase[i-1]
+ SegmentBase.Add((UInt32)SegmentBase[SegmentBase.Count-1] + (sectionStart + sectionLength + (UInt32)0xFFF) & (~((UInt32)0xFFF)));
+ lastSegmentIndex = segmentIndex;
+ }
+ sectionStart = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier);
+ sectionLength = UInt32.Parse(match.Groups["length"].ToString(), NumberStyles.AllowHexSpecifier);
+ }
+
+ if (line == null)
+ {
+ throw new InvalidOperationException("Invalid MAP header format.");
+ }
+ }
+ else {
+ if (!regnonNMHeader.IsMatch(line)) {
+ match = regExNmFile.Match(line);
+ if (match.Success) {
+ // It's a nm file format. There is no header for it.
+ break;
+ }
+ continue;
+ }
+ bInSegmentDecl = true;
+ bIsWindowsMapfile = true;
+
+ }
+ }
+
+ if (bIsWindowsMapfile) {
+ //
+ // Only Windows map file has SegmentBase
+ //
+ for (int i=1 ; i<= lastSegmentIndex; i++) {
+#if DACTABLEGEN_DEBUG
+ Console.WriteLine("SegmentBase[{0}] = {1:x8}", i, SegmentBase[i]);
+#endif
+ }
+ }
+
+ }
+
+ public void loadDataFromMapFile()
+ {
+
+ StreamReader strm =
+ new StreamReader(m_symdumpFile, System.Text.Encoding.ASCII);
+ String line;
+ int i;
+ String[] RegExps;
+
+ // Read the head of the symbol dump file and
+ // determind the format of it.
+ ReadMapHeader(strm);
+
+ //
+ // Scan through the symbol dump file looking
+ // for the globals structure.
+ //
+ if (bIsWindowsMapfile) {
+ RegExps = RegExps_WindowsMapfile;
+ }
+ else {
+ RegExps = RegExps_UnixNmfile;
+ }
+
+ Console.WriteLine("It is a {0} file.", (bIsWindowsMapfile)?"Windows MAP":"Unix NM");
+
+ Regex[] RegExs = new Regex[RegExps.Length];
+ for (i = 0; i < RegExps.Length; i++) {
+#if DACTABLEGEN_DEBUG
+ Console.WriteLine("RegEx[{0}]: {1}", i, RegExps[i]);
+#endif
+ RegExs[i] = new Regex(RegExps[i]);
+ }
+
+ Match match = null;
+ SymbolInfo si;
+ String key;
+ int segment;
+ UInt32 address;
+
+ for (;;)
+ {
+ line = strm.ReadLine();
+
+ if (line == null)
+ {
+ // No more to read.
+ break;
+ }
+
+ // Console.WriteLine(">{0}", line);
+ for (i = 0; i < RegExps.Length; i++) {
+
+ match = RegExs[i].Match(line);
+ if (match.Success) {
+ // Console.WriteLine(line);
+
+ segment = 0;
+ address = 0;
+ if (bIsWindowsMapfile) {
+ switch ((WindowsSymbolTypes)i) {
+
+ case WindowsSymbolTypes.ModuleNameClassNameFieldName:
+ key = match.Groups["moduleName"].ToString() + "::" + match.Groups["className"].ToString() + "::" + match.Groups["fieldName"];
+ segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier);
+ address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier);
+ break;
+
+ case WindowsSymbolTypes.ClassNameFieldName:
+ key = match.Groups["className"].ToString() + "::" + match.Groups["fieldName"];
+ segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier);
+ address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier);
+ break;
+
+ case WindowsSymbolTypes.GlobalVarName:
+ case WindowsSymbolTypes.GlobalVarName2:
+ case WindowsSymbolTypes.GlobalVarName3:
+ key = match.Groups["globalVarName"].ToString();
+ segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier);
+ address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier);
+ break;
+
+ case WindowsSymbolTypes.SingleVtAddr:
+ key = match.Groups["FunctionName"].ToString();
+ segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier);
+ address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier);
+ break;
+
+ case WindowsSymbolTypes.MultiVtAddr:
+ key = match.Groups["FunctionName"].ToString() +
+ "__" +
+ match.Groups["BaseName"].ToString();
+ segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier);
+ address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier);
+ break;
+
+ default:
+ throw new ApplicationException("Unknown symbolType" + i);
+ }
+ }
+ else
+ {
+ // We've got a UNIX nm file
+ // The full unmanaged symbol name is already included in the RegEx
+ // We could consider treating VTable's differently (a vt-specific key or different
+ // hash), but we want to be consistent with the windows map case here.
+
+ key = match.Groups["symName"].ToString();
+ address = UInt32.Parse(match.Groups["address"].ToString(), NumberStyles.AllowHexSpecifier);
+
+ if (i == (int)UnixSymbolTypes.VtAddr)
+ {
+ // For VTables, what we really want is the vtAddr used at offset zero of objects.
+ // GCC has the vtAddr point to the 3rd slot of the VTable (in contrast to MSVC where
+ // the vtAddr points to the base of the VTable).
+ // Slot 0 appears to typically be NULL
+ // Slot 1 points to the run-time type info for the type
+ // Slot 2 is the first virtual method
+ // Fix up the symbol address here to match the value used in object headers.
+ // Note that we don't know a lot about the target here (eg. what platform it's
+ // running on), so it might be better to do this adjustment in DAC itself. But
+ // for now doing it here is simpler and more reliable (only one place to update
+ // to make it consistent).
+ address += 2 * 4; // assumes 32-bit
+ }
+ }
+
+ si = (SymbolInfo)SymbolHash[key];
+ if (si != null)
+ {
+ // Some duplicates are expected (eg. functions with overloads), but we should never
+ // actually care about the address of such symbols. Record that this is a dup
+ // so that we can warn/fail if we try to use it.
+ si.dupFound = true;
+ }
+ else {
+ si = new SymbolInfo(segment, address);
+ // Console.WriteLine("{0:x8} {1}", si.Segment, si.Address, key);
+ SymbolHash.Add(key, si);
+ }
+
+ }
+
+ }
+
+ }
+
+ strm.Close();
+ }
+
+
+ public SymbolInfo FindSymbol(String key)
+ {
+ SymbolInfo si = (SymbolInfo)SymbolHash[key];
+ if (si != null)
+ {
+ if (si.dupFound)
+ {
+ Console.WriteLine("Warning: Symbol " + key + " has duplicated entry in the symbol dump file.");
+ }
+ }
+
+ return si;
+ }
+
+ bool bIsWindowsMapfile = false;
+ String m_symdumpFile;
+ Hashtable SymbolHash = new Hashtable();
+
+ ArrayList SegmentBase = new ArrayList();
+}
diff --git a/src/ToolBox/SOS/DacTableGen/cvconst.cs b/src/ToolBox/SOS/DacTableGen/cvconst.cs
new file mode 100644
index 0000000000..fee66f0b5f
--- /dev/null
+++ b/src/ToolBox/SOS/DacTableGen/cvconst.cs
@@ -0,0 +1,2037 @@
+// 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.
+
+// cvconst.h - codeview constant definitions
+
+// Enumeration for function call type
+
+namespace Dia
+{
+
+public enum NameSearchOptions
+{
+ nsNone = 0,
+
+ nsfCaseSensitive = 0x1, // apply a case sensitive match
+ nsfCaseInsensitive = 0x2, // apply a case insensitive match
+ nsfFNameExt = 0x4, // treat names as paths and apply a filename.ext match
+ nsfRegularExpression = 0x8, // regular expression
+ nsfUndecoratedName = 0x10, // applies only to symbols that have both undecorated and decorated names
+
+ // predefined names for backward source compatibility
+
+ nsCaseSensitive = nsfCaseSensitive, // apply a case sensitive match
+ nsCaseInsensitive = nsfCaseInsensitive, // apply a case insensitive match
+ nsFNameExt = nsfCaseInsensitive | nsfFNameExt, // treat names as paths and apply a filename.ext match
+ nsRegularExpression = nsfRegularExpression | nsfCaseSensitive, // regular expression (using only '*' and '?')
+ nsCaseInRegularExpression = nsfRegularExpression | nsfCaseInsensitive, // case insensitive regular expression
+}
+
+
+// the following are error HRESULTS returned by an IDiaDataSource they
+// are based on the FACILITY_VISUALCPP (0x6d) defined in delayimp.h
+
+public enum E_PDB
+{
+ E_PDB_OK= unchecked((int)(((long)(1)<<31) | ((long)(((long)0x6d))<<16) | ((long)(1)))),
+ E_PDB_USAGE ,
+ E_PDB_OUT_OF_MEMORY , // not used, use E_OUTOFMEMORY
+ E_PDB_FILE_SYSTEM ,
+ E_PDB_NOT_FOUND ,
+ E_PDB_INVALID_SIG ,
+ E_PDB_INVALID_AGE ,
+ E_PDB_PRECOMP_REQUIRED ,
+ E_PDB_OUT_OF_TI ,
+ E_PDB_NOT_IMPLEMENTED , // use E_NOTIMPL
+ E_PDB_V1_PDB ,
+ E_PDB_FORMAT ,
+ E_PDB_LIMIT ,
+ E_PDB_CORRUPT ,
+ E_PDB_TI16 ,
+ E_PDB_ACCESS_DENIED , // use E_ACCESSDENIED
+ E_PDB_ILLEGAL_TYPE_EDIT ,
+ E_PDB_INVALID_EXECUTABLE ,
+ E_PDB_DBG_NOT_FOUND ,
+ E_PDB_NO_DEBUG_INFO ,
+ E_PDB_INVALID_EXE_TIMESTAMP ,
+ E_PDB_RESERVED ,
+ E_PDB_DEBUG_INFO_NOT_IN_PDB ,
+ E_PDB_MAX
+}
+
+//
+// Errors in finding dynamically loaded dlls or functions.
+//
+public enum DIA_E
+{
+ DIA_E_MODNOTFOUND = E_PDB.E_PDB_MAX+1,
+ DIA_E_PROCNOTFOUND,
+}
+
+public enum CV_call_e {
+ CV_CALL_NEAR_C = 0x00, // near right to left push, caller pops stack
+ CV_CALL_FAR_C = 0x01, // far right to left push, caller pops stack
+ CV_CALL_NEAR_PASCAL = 0x02, // near left to right push, callee pops stack
+ CV_CALL_FAR_PASCAL = 0x03, // far left to right push, callee pops stack
+ CV_CALL_NEAR_FAST = 0x04, // near left to right push with regs, callee pops stack
+ CV_CALL_FAR_FAST = 0x05, // far left to right push with regs, callee pops stack
+ CV_CALL_SKIPPED = 0x06, // skipped (unused) call index
+ CV_CALL_NEAR_STD = 0x07, // near standard call
+ CV_CALL_FAR_STD = 0x08, // far standard call
+ CV_CALL_NEAR_SYS = 0x09, // near sys call
+ CV_CALL_FAR_SYS = 0x0a, // far sys call
+ CV_CALL_THISCALL = 0x0b, // this call (this passed in register)
+ CV_CALL_MIPSCALL = 0x0c, // Mips call
+ CV_CALL_GENERIC = 0x0d, // Generic call sequence
+ CV_CALL_ALPHACALL = 0x0e, // Alpha call
+ CV_CALL_PPCCALL = 0x0f, // PPC call
+ CV_CALL_SHCALL = 0x10, // Hitachi SuperH call
+ CV_CALL_ARMCALL = 0x11, // ARM call
+ CV_CALL_AM33CALL = 0x12, // AM33 call
+ CV_CALL_TRICALL = 0x13, // TriCore Call
+ CV_CALL_SH5CALL = 0x14, // Hitachi SuperH-5 call
+ CV_CALL_M32RCALL = 0x15, // M32R Call
+ CV_CALL_RESERVED = 0x16 // first unused call enumeration
+}
+
+
+
+
+// Values for the access protection of class attributes
+
+
+public enum CV_access_e {
+ CV_private = 1,
+ CV_protected = 2,
+ CV_public = 3
+}
+
+public enum THUNK_ORDINAL {
+ THUNK_ORDINAL_NOTYPE, // standard thunk
+ THUNK_ORDINAL_ADJUSTOR, // "this" adjustor thunk
+ THUNK_ORDINAL_VCALL, // virtual call thunk
+ THUNK_ORDINAL_PCODE, // pcode thunk
+ THUNK_ORDINAL_LOAD, // thunk which loads the address to jump to
+ // via unknown means...
+
+ // trampoline thunk ordinals - only for use in Trampoline thunk symbols
+ THUNK_ORDINAL_TRAMP_INCREMENTAL,
+ THUNK_ORDINAL_TRAMP_BRANCHISLAND,
+
+}
+
+
+public enum CV_SourceChksum_t {
+ CHKSUM_TYPE_NONE = 0, // indicates no checksum is available
+ CHKSUM_TYPE_MD5
+}
+
+//
+// DIA enums
+//
+
+public enum LocationType
+{
+ LocIsNull,
+ LocIsStatic,
+ LocIsTLS,
+ LocIsRegRel,
+ LocIsThisRel,
+ LocIsEnregistered,
+ LocIsBitField,
+ LocIsSlot,
+ LocIsIlRel,
+ LocInMetaData,
+ LocIsConstant,
+ LocTypeMax
+};
+
+public enum DataKind
+{
+ DataIsUnknown,
+ DataIsLocal,
+ DataIsStaticLocal,
+ DataIsParam,
+ DataIsObjectPtr,
+ DataIsFileStatic,
+ DataIsGlobal,
+ DataIsMember,
+ DataIsStaticMember,
+ DataIsConstant
+};
+
+public enum UdtKind
+{
+ UdtStruct,
+ UdtClass,
+ UdtUnion
+};
+
+public enum BasicType
+{
+ btNoType = 0,
+ btVoid = 1,
+ btChar = 2,
+ btWChar = 3,
+ btInt = 6,
+ btUInt = 7,
+ btFloat = 8,
+ btBCD = 9,
+ btBool = 10,
+ btLong = 13,
+ btULong = 14,
+ btCurrency = 25,
+ btDate = 26,
+ btVariant = 27,
+ btComplex = 28,
+ btBit = 29,
+ btBSTR = 30,
+ btHresult = 31
+};
+
+
+// enum describing the compile flag source language
+
+
+public enum CV_CFL_LANG {
+ CV_CFL_C = 0x00,
+ CV_CFL_CXX = 0x01,
+ CV_CFL_FORTRAN = 0x02,
+ CV_CFL_MASM = 0x03,
+ CV_CFL_PASCAL = 0x04,
+ CV_CFL_BASIC = 0x05,
+ CV_CFL_COBOL = 0x06,
+ CV_CFL_LINK = 0x07,
+ CV_CFL_CVTRES = 0x08,
+ CV_CFL_CVTPGD = 0x09,
+}
+
+
+// enum describing target processor
+
+
+public enum CV_CPU_TYPE_e {
+ CV_CFL_8080 = 0x00,
+ CV_CFL_8086 = 0x01,
+ CV_CFL_80286 = 0x02,
+ CV_CFL_80386 = 0x03,
+ CV_CFL_80486 = 0x04,
+ CV_CFL_PENTIUM = 0x05,
+ CV_CFL_PENTIUMII = 0x06,
+ CV_CFL_PENTIUMPRO = CV_CFL_PENTIUMII,
+ CV_CFL_PENTIUMIII = 0x07,
+ CV_CFL_MIPS = 0x10,
+ CV_CFL_MIPSR4000 = CV_CFL_MIPS, // don't break current code
+ CV_CFL_MIPS16 = 0x11,
+ CV_CFL_MIPS32 = 0x12,
+ CV_CFL_MIPS64 = 0x13,
+ CV_CFL_MIPSI = 0x14,
+ CV_CFL_MIPSII = 0x15,
+ CV_CFL_MIPSIII = 0x16,
+ CV_CFL_MIPSIV = 0x17,
+ CV_CFL_MIPSV = 0x18,
+ CV_CFL_M68000 = 0x20,
+ CV_CFL_M68010 = 0x21,
+ CV_CFL_M68020 = 0x22,
+ CV_CFL_M68030 = 0x23,
+ CV_CFL_M68040 = 0x24,
+ CV_CFL_ALPHA = 0x30,
+ CV_CFL_ALPHA_21064 = 0x30,
+ CV_CFL_ALPHA_21164 = 0x31,
+ CV_CFL_ALPHA_21164A = 0x32,
+ CV_CFL_ALPHA_21264 = 0x33,
+ CV_CFL_ALPHA_21364 = 0x34,
+ CV_CFL_PPC601 = 0x40,
+ CV_CFL_PPC603 = 0x41,
+ CV_CFL_PPC604 = 0x42,
+ CV_CFL_PPC620 = 0x43,
+ CV_CFL_PPCFP = 0x44,
+ CV_CFL_SH3 = 0x50,
+ CV_CFL_SH3E = 0x51,
+ CV_CFL_SH3DSP = 0x52,
+ CV_CFL_SH4 = 0x53,
+ CV_CFL_SHMEDIA = 0x54,
+ CV_CFL_ARM3 = 0x60,
+ CV_CFL_ARM4 = 0x61,
+ CV_CFL_ARM4T = 0x62,
+ CV_CFL_ARM5 = 0x63,
+ CV_CFL_ARM5T = 0x64,
+ CV_CFL_OMNI = 0x70,
+ CV_CFL_IA64 = 0x80,
+ CV_CFL_IA64_1 = 0x80,
+ CV_CFL_IA64_2 = 0x81,
+ CV_CFL_CEE = 0x90,
+ CV_CFL_AM33 = 0xA0,
+ CV_CFL_M32R = 0xB0,
+ CV_CFL_TRICORE = 0xC0,
+ CV_CFL_RESERVED1 = 0xD0,
+ CV_CFL_EBC = 0xE0,
+ CV_CFL_THUMB = 0xF0,
+}
+
+public enum CV_HREG_e {
+ // Register subset shared by all processor types,
+ // must not overlap with any of the ranges below, hence the high values
+
+ CV_ALLREG_ERR = 30000,
+ CV_ALLREG_TEB = 30001,
+ CV_ALLREG_TIMER = 30002,
+ CV_ALLREG_EFAD1 = 30003,
+ CV_ALLREG_EFAD2 = 30004,
+ CV_ALLREG_EFAD3 = 30005,
+ CV_ALLREG_VFRAME= 30006,
+ CV_ALLREG_HANDLE= 30007,
+ CV_ALLREG_PARAMS= 30008,
+ CV_ALLREG_LOCALS= 30009,
+
+
+ // Register set for the Intel 80x86 and ix86 processor series
+ // (plus PCODE registers)
+
+ CV_REG_NONE = 0,
+ CV_REG_AL = 1,
+ CV_REG_CL = 2,
+ CV_REG_DL = 3,
+ CV_REG_BL = 4,
+ CV_REG_AH = 5,
+ CV_REG_CH = 6,
+ CV_REG_DH = 7,
+ CV_REG_BH = 8,
+ CV_REG_AX = 9,
+ CV_REG_CX = 10,
+ CV_REG_DX = 11,
+ CV_REG_BX = 12,
+ CV_REG_SP = 13,
+ CV_REG_BP = 14,
+ CV_REG_SI = 15,
+ CV_REG_DI = 16,
+ CV_REG_EAX = 17,
+ CV_REG_ECX = 18,
+ CV_REG_EDX = 19,
+ CV_REG_EBX = 20,
+ CV_REG_ESP = 21,
+ CV_REG_EBP = 22,
+ CV_REG_ESI = 23,
+ CV_REG_EDI = 24,
+ CV_REG_ES = 25,
+ CV_REG_CS = 26,
+ CV_REG_SS = 27,
+ CV_REG_DS = 28,
+ CV_REG_FS = 29,
+ CV_REG_GS = 30,
+ CV_REG_IP = 31,
+ CV_REG_FLAGS = 32,
+ CV_REG_EIP = 33,
+ CV_REG_EFLAGS = 34,
+ CV_REG_TEMP = 40, // PCODE Temp
+ CV_REG_TEMPH = 41, // PCODE TempH
+ CV_REG_QUOTE = 42, // PCODE Quote
+ CV_REG_PCDR3 = 43, // PCODE reserved
+ CV_REG_PCDR4 = 44, // PCODE reserved
+ CV_REG_PCDR5 = 45, // PCODE reserved
+ CV_REG_PCDR6 = 46, // PCODE reserved
+ CV_REG_PCDR7 = 47, // PCODE reserved
+ CV_REG_CR0 = 80, // CR0 -- control registers
+ CV_REG_CR1 = 81,
+ CV_REG_CR2 = 82,
+ CV_REG_CR3 = 83,
+ CV_REG_CR4 = 84, // Pentium
+ CV_REG_DR0 = 90, // Debug register
+ CV_REG_DR1 = 91,
+ CV_REG_DR2 = 92,
+ CV_REG_DR3 = 93,
+ CV_REG_DR4 = 94,
+ CV_REG_DR5 = 95,
+ CV_REG_DR6 = 96,
+ CV_REG_DR7 = 97,
+ CV_REG_GDTR = 110,
+ CV_REG_GDTL = 111,
+ CV_REG_IDTR = 112,
+ CV_REG_IDTL = 113,
+ CV_REG_LDTR = 114,
+ CV_REG_TR = 115,
+
+ CV_REG_PSEUDO1 = 116,
+ CV_REG_PSEUDO2 = 117,
+ CV_REG_PSEUDO3 = 118,
+ CV_REG_PSEUDO4 = 119,
+ CV_REG_PSEUDO5 = 120,
+ CV_REG_PSEUDO6 = 121,
+ CV_REG_PSEUDO7 = 122,
+ CV_REG_PSEUDO8 = 123,
+ CV_REG_PSEUDO9 = 124,
+
+ CV_REG_ST0 = 128,
+ CV_REG_ST1 = 129,
+ CV_REG_ST2 = 130,
+ CV_REG_ST3 = 131,
+ CV_REG_ST4 = 132,
+ CV_REG_ST5 = 133,
+ CV_REG_ST6 = 134,
+ CV_REG_ST7 = 135,
+ CV_REG_CTRL = 136,
+ CV_REG_STAT = 137,
+ CV_REG_TAG = 138,
+ CV_REG_FPIP = 139,
+ CV_REG_FPCS = 140,
+ CV_REG_FPDO = 141,
+ CV_REG_FPDS = 142,
+ CV_REG_ISEM = 143,
+ CV_REG_FPEIP = 144,
+ CV_REG_FPEDO = 145,
+
+ CV_REG_MM0 = 146,
+ CV_REG_MM1 = 147,
+ CV_REG_MM2 = 148,
+ CV_REG_MM3 = 149,
+ CV_REG_MM4 = 150,
+ CV_REG_MM5 = 151,
+ CV_REG_MM6 = 152,
+ CV_REG_MM7 = 153,
+
+ CV_REG_XMM0 = 154, // KATMAI registers
+ CV_REG_XMM1 = 155,
+ CV_REG_XMM2 = 156,
+ CV_REG_XMM3 = 157,
+ CV_REG_XMM4 = 158,
+ CV_REG_XMM5 = 159,
+ CV_REG_XMM6 = 160,
+ CV_REG_XMM7 = 161,
+
+ CV_REG_XMM00 = 162, // KATMAI sub-registers
+ CV_REG_XMM01 = 163,
+ CV_REG_XMM02 = 164,
+ CV_REG_XMM03 = 165,
+ CV_REG_XMM10 = 166,
+ CV_REG_XMM11 = 167,
+ CV_REG_XMM12 = 168,
+ CV_REG_XMM13 = 169,
+ CV_REG_XMM20 = 170,
+ CV_REG_XMM21 = 171,
+ CV_REG_XMM22 = 172,
+ CV_REG_XMM23 = 173,
+ CV_REG_XMM30 = 174,
+ CV_REG_XMM31 = 175,
+ CV_REG_XMM32 = 176,
+ CV_REG_XMM33 = 177,
+ CV_REG_XMM40 = 178,
+ CV_REG_XMM41 = 179,
+ CV_REG_XMM42 = 180,
+ CV_REG_XMM43 = 181,
+ CV_REG_XMM50 = 182,
+ CV_REG_XMM51 = 183,
+ CV_REG_XMM52 = 184,
+ CV_REG_XMM53 = 185,
+ CV_REG_XMM60 = 186,
+ CV_REG_XMM61 = 187,
+ CV_REG_XMM62 = 188,
+ CV_REG_XMM63 = 189,
+ CV_REG_XMM70 = 190,
+ CV_REG_XMM71 = 191,
+ CV_REG_XMM72 = 192,
+ CV_REG_XMM73 = 193,
+
+ CV_REG_XMM0L = 194,
+ CV_REG_XMM1L = 195,
+ CV_REG_XMM2L = 196,
+ CV_REG_XMM3L = 197,
+ CV_REG_XMM4L = 198,
+ CV_REG_XMM5L = 199,
+ CV_REG_XMM6L = 200,
+ CV_REG_XMM7L = 201,
+
+ CV_REG_XMM0H = 202,
+ CV_REG_XMM1H = 203,
+ CV_REG_XMM2H = 204,
+ CV_REG_XMM3H = 205,
+ CV_REG_XMM4H = 206,
+ CV_REG_XMM5H = 207,
+ CV_REG_XMM6H = 208,
+ CV_REG_XMM7H = 209,
+
+ CV_REG_MXCSR = 211, // XMM status register
+
+ CV_REG_EDXEAX = 212, // EDX:EAX pair
+
+ CV_REG_EMM0L = 220, // XMM sub-registers (WNI integer)
+ CV_REG_EMM1L = 221,
+ CV_REG_EMM2L = 222,
+ CV_REG_EMM3L = 223,
+ CV_REG_EMM4L = 224,
+ CV_REG_EMM5L = 225,
+ CV_REG_EMM6L = 226,
+ CV_REG_EMM7L = 227,
+
+ CV_REG_EMM0H = 228,
+ CV_REG_EMM1H = 229,
+ CV_REG_EMM2H = 230,
+ CV_REG_EMM3H = 231,
+ CV_REG_EMM4H = 232,
+ CV_REG_EMM5H = 233,
+ CV_REG_EMM6H = 234,
+ CV_REG_EMM7H = 235,
+
+ // do not change the order of these regs, first one must be even too
+ CV_REG_MM00 = 236,
+ CV_REG_MM01 = 237,
+ CV_REG_MM10 = 238,
+ CV_REG_MM11 = 239,
+ CV_REG_MM20 = 240,
+ CV_REG_MM21 = 241,
+ CV_REG_MM30 = 242,
+ CV_REG_MM31 = 243,
+ CV_REG_MM40 = 244,
+ CV_REG_MM41 = 245,
+ CV_REG_MM50 = 246,
+ CV_REG_MM51 = 247,
+ CV_REG_MM60 = 248,
+ CV_REG_MM61 = 249,
+ CV_REG_MM70 = 250,
+ CV_REG_MM71 = 251,
+
+ // registers for the 68K processors
+
+ CV_R68_D0 = 0,
+ CV_R68_D1 = 1,
+ CV_R68_D2 = 2,
+ CV_R68_D3 = 3,
+ CV_R68_D4 = 4,
+ CV_R68_D5 = 5,
+ CV_R68_D6 = 6,
+ CV_R68_D7 = 7,
+ CV_R68_A0 = 8,
+ CV_R68_A1 = 9,
+ CV_R68_A2 = 10,
+ CV_R68_A3 = 11,
+ CV_R68_A4 = 12,
+ CV_R68_A5 = 13,
+ CV_R68_A6 = 14,
+ CV_R68_A7 = 15,
+ CV_R68_CCR = 16,
+ CV_R68_SR = 17,
+ CV_R68_USP = 18,
+ CV_R68_MSP = 19,
+ CV_R68_SFC = 20,
+ CV_R68_DFC = 21,
+ CV_R68_CACR = 22,
+ CV_R68_VBR = 23,
+ CV_R68_CAAR = 24,
+ CV_R68_ISP = 25,
+ CV_R68_PC = 26,
+ //reserved 27
+ CV_R68_FPCR = 28,
+ CV_R68_FPSR = 29,
+ CV_R68_FPIAR = 30,
+ //reserved 31
+ CV_R68_FP0 = 32,
+ CV_R68_FP1 = 33,
+ CV_R68_FP2 = 34,
+ CV_R68_FP3 = 35,
+ CV_R68_FP4 = 36,
+ CV_R68_FP5 = 37,
+ CV_R68_FP6 = 38,
+ CV_R68_FP7 = 39,
+ //reserved 40
+ CV_R68_MMUSR030 = 41,
+ CV_R68_MMUSR = 42,
+ CV_R68_URP = 43,
+ CV_R68_DTT0 = 44,
+ CV_R68_DTT1 = 45,
+ CV_R68_ITT0 = 46,
+ CV_R68_ITT1 = 47,
+ //reserved 50
+ CV_R68_PSR = 51,
+ CV_R68_PCSR = 52,
+ CV_R68_VAL = 53,
+ CV_R68_CRP = 54,
+ CV_R68_SRP = 55,
+ CV_R68_DRP = 56,
+ CV_R68_TC = 57,
+ CV_R68_AC = 58,
+ CV_R68_SCC = 59,
+ CV_R68_CAL = 60,
+ CV_R68_TT0 = 61,
+ CV_R68_TT1 = 62,
+ //reserved 63
+ CV_R68_BAD0 = 64,
+ CV_R68_BAD1 = 65,
+ CV_R68_BAD2 = 66,
+ CV_R68_BAD3 = 67,
+ CV_R68_BAD4 = 68,
+ CV_R68_BAD5 = 69,
+ CV_R68_BAD6 = 70,
+ CV_R68_BAD7 = 71,
+ CV_R68_BAC0 = 72,
+ CV_R68_BAC1 = 73,
+ CV_R68_BAC2 = 74,
+ CV_R68_BAC3 = 75,
+ CV_R68_BAC4 = 76,
+ CV_R68_BAC5 = 77,
+ CV_R68_BAC6 = 78,
+ CV_R68_BAC7 = 79,
+
+ // Register set for the MIPS 4000
+
+ CV_M4_NOREG = CV_REG_NONE,
+
+ CV_M4_IntZERO = 10, /* CPU REGISTER */
+ CV_M4_IntAT = 11,
+ CV_M4_IntV0 = 12,
+ CV_M4_IntV1 = 13,
+ CV_M4_IntA0 = 14,
+ CV_M4_IntA1 = 15,
+ CV_M4_IntA2 = 16,
+ CV_M4_IntA3 = 17,
+ CV_M4_IntT0 = 18,
+ CV_M4_IntT1 = 19,
+ CV_M4_IntT2 = 20,
+ CV_M4_IntT3 = 21,
+ CV_M4_IntT4 = 22,
+ CV_M4_IntT5 = 23,
+ CV_M4_IntT6 = 24,
+ CV_M4_IntT7 = 25,
+ CV_M4_IntS0 = 26,
+ CV_M4_IntS1 = 27,
+ CV_M4_IntS2 = 28,
+ CV_M4_IntS3 = 29,
+ CV_M4_IntS4 = 30,
+ CV_M4_IntS5 = 31,
+ CV_M4_IntS6 = 32,
+ CV_M4_IntS7 = 33,
+ CV_M4_IntT8 = 34,
+ CV_M4_IntT9 = 35,
+ CV_M4_IntKT0 = 36,
+ CV_M4_IntKT1 = 37,
+ CV_M4_IntGP = 38,
+ CV_M4_IntSP = 39,
+ CV_M4_IntS8 = 40,
+ CV_M4_IntRA = 41,
+ CV_M4_IntLO = 42,
+ CV_M4_IntHI = 43,
+
+ CV_M4_Fir = 50,
+ CV_M4_Psr = 51,
+
+ CV_M4_FltF0 = 60, /* Floating point registers */
+ CV_M4_FltF1 = 61,
+ CV_M4_FltF2 = 62,
+ CV_M4_FltF3 = 63,
+ CV_M4_FltF4 = 64,
+ CV_M4_FltF5 = 65,
+ CV_M4_FltF6 = 66,
+ CV_M4_FltF7 = 67,
+ CV_M4_FltF8 = 68,
+ CV_M4_FltF9 = 69,
+ CV_M4_FltF10 = 70,
+ CV_M4_FltF11 = 71,
+ CV_M4_FltF12 = 72,
+ CV_M4_FltF13 = 73,
+ CV_M4_FltF14 = 74,
+ CV_M4_FltF15 = 75,
+ CV_M4_FltF16 = 76,
+ CV_M4_FltF17 = 77,
+ CV_M4_FltF18 = 78,
+ CV_M4_FltF19 = 79,
+ CV_M4_FltF20 = 80,
+ CV_M4_FltF21 = 81,
+ CV_M4_FltF22 = 82,
+ CV_M4_FltF23 = 83,
+ CV_M4_FltF24 = 84,
+ CV_M4_FltF25 = 85,
+ CV_M4_FltF26 = 86,
+ CV_M4_FltF27 = 87,
+ CV_M4_FltF28 = 88,
+ CV_M4_FltF29 = 89,
+ CV_M4_FltF30 = 90,
+ CV_M4_FltF31 = 91,
+ CV_M4_FltFsr = 92,
+
+
+ // Register set for the ALPHA AXP
+
+ CV_ALPHA_NOREG = CV_REG_NONE,
+
+ CV_ALPHA_FltF0 = 10, // Floating point registers
+ CV_ALPHA_FltF1 = 11,
+ CV_ALPHA_FltF2 = 12,
+ CV_ALPHA_FltF3 = 13,
+ CV_ALPHA_FltF4 = 14,
+ CV_ALPHA_FltF5 = 15,
+ CV_ALPHA_FltF6 = 16,
+ CV_ALPHA_FltF7 = 17,
+ CV_ALPHA_FltF8 = 18,
+ CV_ALPHA_FltF9 = 19,
+ CV_ALPHA_FltF10 = 20,
+ CV_ALPHA_FltF11 = 21,
+ CV_ALPHA_FltF12 = 22,
+ CV_ALPHA_FltF13 = 23,
+ CV_ALPHA_FltF14 = 24,
+ CV_ALPHA_FltF15 = 25,
+ CV_ALPHA_FltF16 = 26,
+ CV_ALPHA_FltF17 = 27,
+ CV_ALPHA_FltF18 = 28,
+ CV_ALPHA_FltF19 = 29,
+ CV_ALPHA_FltF20 = 30,
+ CV_ALPHA_FltF21 = 31,
+ CV_ALPHA_FltF22 = 32,
+ CV_ALPHA_FltF23 = 33,
+ CV_ALPHA_FltF24 = 34,
+ CV_ALPHA_FltF25 = 35,
+ CV_ALPHA_FltF26 = 36,
+ CV_ALPHA_FltF27 = 37,
+ CV_ALPHA_FltF28 = 38,
+ CV_ALPHA_FltF29 = 39,
+ CV_ALPHA_FltF30 = 40,
+ CV_ALPHA_FltF31 = 41,
+
+ CV_ALPHA_IntV0 = 42, // Integer registers
+ CV_ALPHA_IntT0 = 43,
+ CV_ALPHA_IntT1 = 44,
+ CV_ALPHA_IntT2 = 45,
+ CV_ALPHA_IntT3 = 46,
+ CV_ALPHA_IntT4 = 47,
+ CV_ALPHA_IntT5 = 48,
+ CV_ALPHA_IntT6 = 49,
+ CV_ALPHA_IntT7 = 50,
+ CV_ALPHA_IntS0 = 51,
+ CV_ALPHA_IntS1 = 52,
+ CV_ALPHA_IntS2 = 53,
+ CV_ALPHA_IntS3 = 54,
+ CV_ALPHA_IntS4 = 55,
+ CV_ALPHA_IntS5 = 56,
+ CV_ALPHA_IntFP = 57,
+ CV_ALPHA_IntA0 = 58,
+ CV_ALPHA_IntA1 = 59,
+ CV_ALPHA_IntA2 = 60,
+ CV_ALPHA_IntA3 = 61,
+ CV_ALPHA_IntA4 = 62,
+ CV_ALPHA_IntA5 = 63,
+ CV_ALPHA_IntT8 = 64,
+ CV_ALPHA_IntT9 = 65,
+ CV_ALPHA_IntT10 = 66,
+ CV_ALPHA_IntT11 = 67,
+ CV_ALPHA_IntRA = 68,
+ CV_ALPHA_IntT12 = 69,
+ CV_ALPHA_IntAT = 70,
+ CV_ALPHA_IntGP = 71,
+ CV_ALPHA_IntSP = 72,
+ CV_ALPHA_IntZERO = 73,
+
+
+ CV_ALPHA_Fpcr = 74, // Control registers
+ CV_ALPHA_Fir = 75,
+ CV_ALPHA_Psr = 76,
+ CV_ALPHA_FltFsr = 77,
+ CV_ALPHA_SoftFpcr = 78,
+
+ // Register Set for Motorola/IBM PowerPC
+
+ /*
+ ** PowerPC General Registers ( User Level )
+ */
+ CV_PPC_GPR0 = 1,
+ CV_PPC_GPR1 = 2,
+ CV_PPC_GPR2 = 3,
+ CV_PPC_GPR3 = 4,
+ CV_PPC_GPR4 = 5,
+ CV_PPC_GPR5 = 6,
+ CV_PPC_GPR6 = 7,
+ CV_PPC_GPR7 = 8,
+ CV_PPC_GPR8 = 9,
+ CV_PPC_GPR9 = 10,
+ CV_PPC_GPR10 = 11,
+ CV_PPC_GPR11 = 12,
+ CV_PPC_GPR12 = 13,
+ CV_PPC_GPR13 = 14,
+ CV_PPC_GPR14 = 15,
+ CV_PPC_GPR15 = 16,
+ CV_PPC_GPR16 = 17,
+ CV_PPC_GPR17 = 18,
+ CV_PPC_GPR18 = 19,
+ CV_PPC_GPR19 = 20,
+ CV_PPC_GPR20 = 21,
+ CV_PPC_GPR21 = 22,
+ CV_PPC_GPR22 = 23,
+ CV_PPC_GPR23 = 24,
+ CV_PPC_GPR24 = 25,
+ CV_PPC_GPR25 = 26,
+ CV_PPC_GPR26 = 27,
+ CV_PPC_GPR27 = 28,
+ CV_PPC_GPR28 = 29,
+ CV_PPC_GPR29 = 30,
+ CV_PPC_GPR30 = 31,
+ CV_PPC_GPR31 = 32,
+
+ /*
+ ** PowerPC Condition Register ( User Level )
+ */
+ CV_PPC_CR = 33,
+ CV_PPC_CR0 = 34,
+ CV_PPC_CR1 = 35,
+ CV_PPC_CR2 = 36,
+ CV_PPC_CR3 = 37,
+ CV_PPC_CR4 = 38,
+ CV_PPC_CR5 = 39,
+ CV_PPC_CR6 = 40,
+ CV_PPC_CR7 = 41,
+
+ /*
+ ** PowerPC Floating Point Registers ( User Level )
+ */
+ CV_PPC_FPR0 = 42,
+ CV_PPC_FPR1 = 43,
+ CV_PPC_FPR2 = 44,
+ CV_PPC_FPR3 = 45,
+ CV_PPC_FPR4 = 46,
+ CV_PPC_FPR5 = 47,
+ CV_PPC_FPR6 = 48,
+ CV_PPC_FPR7 = 49,
+ CV_PPC_FPR8 = 50,
+ CV_PPC_FPR9 = 51,
+ CV_PPC_FPR10 = 52,
+ CV_PPC_FPR11 = 53,
+ CV_PPC_FPR12 = 54,
+ CV_PPC_FPR13 = 55,
+ CV_PPC_FPR14 = 56,
+ CV_PPC_FPR15 = 57,
+ CV_PPC_FPR16 = 58,
+ CV_PPC_FPR17 = 59,
+ CV_PPC_FPR18 = 60,
+ CV_PPC_FPR19 = 61,
+ CV_PPC_FPR20 = 62,
+ CV_PPC_FPR21 = 63,
+ CV_PPC_FPR22 = 64,
+ CV_PPC_FPR23 = 65,
+ CV_PPC_FPR24 = 66,
+ CV_PPC_FPR25 = 67,
+ CV_PPC_FPR26 = 68,
+ CV_PPC_FPR27 = 69,
+ CV_PPC_FPR28 = 70,
+ CV_PPC_FPR29 = 71,
+ CV_PPC_FPR30 = 72,
+ CV_PPC_FPR31 = 73,
+
+ /*
+ ** PowerPC Floating Point Status and Control Register ( User Level )
+ */
+ CV_PPC_FPSCR = 74,
+
+ /*
+ ** PowerPC Machine State Register ( Supervisor Level )
+ */
+ CV_PPC_MSR = 75,
+
+ /*
+ ** PowerPC Segment Registers ( Supervisor Level )
+ */
+ CV_PPC_SR0 = 76,
+ CV_PPC_SR1 = 77,
+ CV_PPC_SR2 = 78,
+ CV_PPC_SR3 = 79,
+ CV_PPC_SR4 = 80,
+ CV_PPC_SR5 = 81,
+ CV_PPC_SR6 = 82,
+ CV_PPC_SR7 = 83,
+ CV_PPC_SR8 = 84,
+ CV_PPC_SR9 = 85,
+ CV_PPC_SR10 = 86,
+ CV_PPC_SR11 = 87,
+ CV_PPC_SR12 = 88,
+ CV_PPC_SR13 = 89,
+ CV_PPC_SR14 = 90,
+ CV_PPC_SR15 = 91,
+
+ /*
+ ** For all of the special purpose registers add 100 to the SPR# that the
+ ** Motorola/IBM documentation gives with the exception of any imaginary
+ ** registers.
+ */
+
+ /*
+ ** PowerPC Special Purpose Registers ( User Level )
+ */
+ CV_PPC_PC = 99, // PC (imaginary register)
+
+ CV_PPC_MQ = 100, // MPC601
+ CV_PPC_XER = 101,
+ CV_PPC_RTCU = 104, // MPC601
+ CV_PPC_RTCL = 105, // MPC601
+ CV_PPC_LR = 108,
+ CV_PPC_CTR = 109,
+
+ CV_PPC_COMPARE = 110, // part of XER (internal to the debugger only)
+ CV_PPC_COUNT = 111, // part of XER (internal to the debugger only)
+
+ /*
+ ** PowerPC Special Purpose Registers ( Supervisor Level )
+ */
+ CV_PPC_DSISR = 118,
+ CV_PPC_DAR = 119,
+ CV_PPC_DEC = 122,
+ CV_PPC_SDR1 = 125,
+ CV_PPC_SRR0 = 126,
+ CV_PPC_SRR1 = 127,
+ CV_PPC_SPRG0 = 372,
+ CV_PPC_SPRG1 = 373,
+ CV_PPC_SPRG2 = 374,
+ CV_PPC_SPRG3 = 375,
+ CV_PPC_ASR = 280, // 64-bit implementations only
+ CV_PPC_EAR = 382,
+ CV_PPC_PVR = 287,
+ CV_PPC_BAT0U = 628,
+ CV_PPC_BAT0L = 629,
+ CV_PPC_BAT1U = 630,
+ CV_PPC_BAT1L = 631,
+ CV_PPC_BAT2U = 632,
+ CV_PPC_BAT2L = 633,
+ CV_PPC_BAT3U = 634,
+ CV_PPC_BAT3L = 635,
+ CV_PPC_DBAT0U = 636,
+ CV_PPC_DBAT0L = 637,
+ CV_PPC_DBAT1U = 638,
+ CV_PPC_DBAT1L = 639,
+ CV_PPC_DBAT2U = 640,
+ CV_PPC_DBAT2L = 641,
+ CV_PPC_DBAT3U = 642,
+ CV_PPC_DBAT3L = 643,
+
+ /*
+ ** PowerPC Special Purpose Registers Implementation Dependent ( Supervisor Level )
+ */
+
+ /*
+ ** Doesn't appear that IBM/Motorola has finished defining these.
+ */
+
+ CV_PPC_PMR0 = 1044, // MPC620,
+ CV_PPC_PMR1 = 1045, // MPC620,
+ CV_PPC_PMR2 = 1046, // MPC620,
+ CV_PPC_PMR3 = 1047, // MPC620,
+ CV_PPC_PMR4 = 1048, // MPC620,
+ CV_PPC_PMR5 = 1049, // MPC620,
+ CV_PPC_PMR6 = 1050, // MPC620,
+ CV_PPC_PMR7 = 1051, // MPC620,
+ CV_PPC_PMR8 = 1052, // MPC620,
+ CV_PPC_PMR9 = 1053, // MPC620,
+ CV_PPC_PMR10 = 1054, // MPC620,
+ CV_PPC_PMR11 = 1055, // MPC620,
+ CV_PPC_PMR12 = 1056, // MPC620,
+ CV_PPC_PMR13 = 1057, // MPC620,
+ CV_PPC_PMR14 = 1058, // MPC620,
+ CV_PPC_PMR15 = 1059, // MPC620,
+
+ CV_PPC_DMISS = 1076, // MPC603
+ CV_PPC_DCMP = 1077, // MPC603
+ CV_PPC_HASH1 = 1078, // MPC603
+ CV_PPC_HASH2 = 1079, // MPC603
+ CV_PPC_IMISS = 1080, // MPC603
+ CV_PPC_ICMP = 1081, // MPC603
+ CV_PPC_RPA = 1082, // MPC603
+
+ CV_PPC_HID0 = 1108, // MPC601, MPC603, MPC620
+ CV_PPC_HID1 = 1109, // MPC601
+ CV_PPC_HID2 = 1110, // MPC601, MPC603, MPC620 ( IABR )
+ CV_PPC_HID3 = 1111, // Not Defined
+ CV_PPC_HID4 = 1112, // Not Defined
+ CV_PPC_HID5 = 1113, // MPC601, MPC604, MPC620 ( DABR )
+ CV_PPC_HID6 = 1114, // Not Defined
+ CV_PPC_HID7 = 1115, // Not Defined
+ CV_PPC_HID8 = 1116, // MPC620 ( BUSCSR )
+ CV_PPC_HID9 = 1117, // MPC620 ( L2CSR )
+ CV_PPC_HID10 = 1118, // Not Defined
+ CV_PPC_HID11 = 1119, // Not Defined
+ CV_PPC_HID12 = 1120, // Not Defined
+ CV_PPC_HID13 = 1121, // MPC604 ( HCR )
+ CV_PPC_HID14 = 1122, // Not Defined
+ CV_PPC_HID15 = 1123, // MPC601, MPC604, MPC620 ( PIR )
+
+ //
+ // JAVA VM registers
+ //
+
+ CV_JAVA_PC = 1,
+
+ //
+ // Register set for the Hitachi SH3
+ //
+
+ CV_SH3_NOREG = CV_REG_NONE,
+
+ CV_SH3_IntR0 = 10, // CPU REGISTER
+ CV_SH3_IntR1 = 11,
+ CV_SH3_IntR2 = 12,
+ CV_SH3_IntR3 = 13,
+ CV_SH3_IntR4 = 14,
+ CV_SH3_IntR5 = 15,
+ CV_SH3_IntR6 = 16,
+ CV_SH3_IntR7 = 17,
+ CV_SH3_IntR8 = 18,
+ CV_SH3_IntR9 = 19,
+ CV_SH3_IntR10 = 20,
+ CV_SH3_IntR11 = 21,
+ CV_SH3_IntR12 = 22,
+ CV_SH3_IntR13 = 23,
+ CV_SH3_IntFp = 24,
+ CV_SH3_IntSp = 25,
+ CV_SH3_Gbr = 38,
+ CV_SH3_Pr = 39,
+ CV_SH3_Mach = 40,
+ CV_SH3_Macl = 41,
+
+ CV_SH3_Pc = 50,
+ CV_SH3_Sr = 51,
+
+ CV_SH3_BarA = 60,
+ CV_SH3_BasrA = 61,
+ CV_SH3_BamrA = 62,
+ CV_SH3_BbrA = 63,
+ CV_SH3_BarB = 64,
+ CV_SH3_BasrB = 65,
+ CV_SH3_BamrB = 66,
+ CV_SH3_BbrB = 67,
+ CV_SH3_BdrB = 68,
+ CV_SH3_BdmrB = 69,
+ CV_SH3_Brcr = 70,
+
+ //
+ // Additional registers for Hitachi SH processors
+ //
+
+ CV_SH_Fpscr = 75, // floating point status/control register
+ CV_SH_Fpul = 76, // floating point communication register
+
+ CV_SH_FpR0 = 80, // Floating point registers
+ CV_SH_FpR1 = 81,
+ CV_SH_FpR2 = 82,
+ CV_SH_FpR3 = 83,
+ CV_SH_FpR4 = 84,
+ CV_SH_FpR5 = 85,
+ CV_SH_FpR6 = 86,
+ CV_SH_FpR7 = 87,
+ CV_SH_FpR8 = 88,
+ CV_SH_FpR9 = 89,
+ CV_SH_FpR10 = 90,
+ CV_SH_FpR11 = 91,
+ CV_SH_FpR12 = 92,
+ CV_SH_FpR13 = 93,
+ CV_SH_FpR14 = 94,
+ CV_SH_FpR15 = 95,
+
+ CV_SH_XFpR0 = 96,
+ CV_SH_XFpR1 = 97,
+ CV_SH_XFpR2 = 98,
+ CV_SH_XFpR3 = 99,
+ CV_SH_XFpR4 = 100,
+ CV_SH_XFpR5 = 101,
+ CV_SH_XFpR6 = 102,
+ CV_SH_XFpR7 = 103,
+ CV_SH_XFpR8 = 104,
+ CV_SH_XFpR9 = 105,
+ CV_SH_XFpR10 = 106,
+ CV_SH_XFpR11 = 107,
+ CV_SH_XFpR12 = 108,
+ CV_SH_XFpR13 = 109,
+ CV_SH_XFpR14 = 110,
+ CV_SH_XFpR15 = 111,
+
+ //
+ // Register set for the ARM processor.
+ //
+
+ CV_ARM_NOREG = CV_REG_NONE,
+
+ CV_ARM_R0 = 10,
+ CV_ARM_R1 = 11,
+ CV_ARM_R2 = 12,
+ CV_ARM_R3 = 13,
+ CV_ARM_R4 = 14,
+ CV_ARM_R5 = 15,
+ CV_ARM_R6 = 16,
+ CV_ARM_R7 = 17,
+ CV_ARM_R8 = 18,
+ CV_ARM_R9 = 19,
+ CV_ARM_R10 = 20,
+ CV_ARM_R11 = 21, // Frame pointer, if allocated
+ CV_ARM_R12 = 22,
+ CV_ARM_SP = 23, // Stack pointer
+ CV_ARM_LR = 24, // Link Register
+ CV_ARM_PC = 25, // Program counter
+ CV_ARM_CPSR = 26, // Current program status register
+
+ //
+ // Register set for Intel IA64
+ //
+
+ CV_IA64_NOREG = CV_REG_NONE,
+
+ // Branch Registers
+
+ CV_IA64_Br0 = 512,
+ CV_IA64_Br1 = 513,
+ CV_IA64_Br2 = 514,
+ CV_IA64_Br3 = 515,
+ CV_IA64_Br4 = 516,
+ CV_IA64_Br5 = 517,
+ CV_IA64_Br6 = 518,
+ CV_IA64_Br7 = 519,
+
+ // Predicate Registers
+
+ CV_IA64_P0 = 704,
+ CV_IA64_P1 = 705,
+ CV_IA64_P2 = 706,
+ CV_IA64_P3 = 707,
+ CV_IA64_P4 = 708,
+ CV_IA64_P5 = 709,
+ CV_IA64_P6 = 710,
+ CV_IA64_P7 = 711,
+ CV_IA64_P8 = 712,
+ CV_IA64_P9 = 713,
+ CV_IA64_P10 = 714,
+ CV_IA64_P11 = 715,
+ CV_IA64_P12 = 716,
+ CV_IA64_P13 = 717,
+ CV_IA64_P14 = 718,
+ CV_IA64_P15 = 719,
+ CV_IA64_P16 = 720,
+ CV_IA64_P17 = 721,
+ CV_IA64_P18 = 722,
+ CV_IA64_P19 = 723,
+ CV_IA64_P20 = 724,
+ CV_IA64_P21 = 725,
+ CV_IA64_P22 = 726,
+ CV_IA64_P23 = 727,
+ CV_IA64_P24 = 728,
+ CV_IA64_P25 = 729,
+ CV_IA64_P26 = 730,
+ CV_IA64_P27 = 731,
+ CV_IA64_P28 = 732,
+ CV_IA64_P29 = 733,
+ CV_IA64_P30 = 734,
+ CV_IA64_P31 = 735,
+ CV_IA64_P32 = 736,
+ CV_IA64_P33 = 737,
+ CV_IA64_P34 = 738,
+ CV_IA64_P35 = 739,
+ CV_IA64_P36 = 740,
+ CV_IA64_P37 = 741,
+ CV_IA64_P38 = 742,
+ CV_IA64_P39 = 743,
+ CV_IA64_P40 = 744,
+ CV_IA64_P41 = 745,
+ CV_IA64_P42 = 746,
+ CV_IA64_P43 = 747,
+ CV_IA64_P44 = 748,
+ CV_IA64_P45 = 749,
+ CV_IA64_P46 = 750,
+ CV_IA64_P47 = 751,
+ CV_IA64_P48 = 752,
+ CV_IA64_P49 = 753,
+ CV_IA64_P50 = 754,
+ CV_IA64_P51 = 755,
+ CV_IA64_P52 = 756,
+ CV_IA64_P53 = 757,
+ CV_IA64_P54 = 758,
+ CV_IA64_P55 = 759,
+ CV_IA64_P56 = 760,
+ CV_IA64_P57 = 761,
+ CV_IA64_P58 = 762,
+ CV_IA64_P59 = 763,
+ CV_IA64_P60 = 764,
+ CV_IA64_P61 = 765,
+ CV_IA64_P62 = 766,
+ CV_IA64_P63 = 767,
+
+ CV_IA64_Preds = 768,
+
+ // Banked General Registers
+
+ CV_IA64_IntH0 = 832,
+ CV_IA64_IntH1 = 833,
+ CV_IA64_IntH2 = 834,
+ CV_IA64_IntH3 = 835,
+ CV_IA64_IntH4 = 836,
+ CV_IA64_IntH5 = 837,
+ CV_IA64_IntH6 = 838,
+ CV_IA64_IntH7 = 839,
+ CV_IA64_IntH8 = 840,
+ CV_IA64_IntH9 = 841,
+ CV_IA64_IntH10 = 842,
+ CV_IA64_IntH11 = 843,
+ CV_IA64_IntH12 = 844,
+ CV_IA64_IntH13 = 845,
+ CV_IA64_IntH14 = 846,
+ CV_IA64_IntH15 = 847,
+
+ // Special Registers
+
+ CV_IA64_Ip = 1016,
+ CV_IA64_Umask = 1017,
+ CV_IA64_Cfm = 1018,
+ CV_IA64_Psr = 1019,
+
+ // Banked General Registers
+
+ CV_IA64_Nats = 1020,
+ CV_IA64_Nats2 = 1021,
+ CV_IA64_Nats3 = 1022,
+
+ // General-Purpose Registers
+
+ // Integer registers
+ CV_IA64_IntR0 = 1024,
+ CV_IA64_IntR1 = 1025,
+ CV_IA64_IntR2 = 1026,
+ CV_IA64_IntR3 = 1027,
+ CV_IA64_IntR4 = 1028,
+ CV_IA64_IntR5 = 1029,
+ CV_IA64_IntR6 = 1030,
+ CV_IA64_IntR7 = 1031,
+ CV_IA64_IntR8 = 1032,
+ CV_IA64_IntR9 = 1033,
+ CV_IA64_IntR10 = 1034,
+ CV_IA64_IntR11 = 1035,
+ CV_IA64_IntR12 = 1036,
+ CV_IA64_IntR13 = 1037,
+ CV_IA64_IntR14 = 1038,
+ CV_IA64_IntR15 = 1039,
+ CV_IA64_IntR16 = 1040,
+ CV_IA64_IntR17 = 1041,
+ CV_IA64_IntR18 = 1042,
+ CV_IA64_IntR19 = 1043,
+ CV_IA64_IntR20 = 1044,
+ CV_IA64_IntR21 = 1045,
+ CV_IA64_IntR22 = 1046,
+ CV_IA64_IntR23 = 1047,
+ CV_IA64_IntR24 = 1048,
+ CV_IA64_IntR25 = 1049,
+ CV_IA64_IntR26 = 1050,
+ CV_IA64_IntR27 = 1051,
+ CV_IA64_IntR28 = 1052,
+ CV_IA64_IntR29 = 1053,
+ CV_IA64_IntR30 = 1054,
+ CV_IA64_IntR31 = 1055,
+
+ // Register Stack
+ CV_IA64_IntR32 = 1056,
+ CV_IA64_IntR33 = 1057,
+ CV_IA64_IntR34 = 1058,
+ CV_IA64_IntR35 = 1059,
+ CV_IA64_IntR36 = 1060,
+ CV_IA64_IntR37 = 1061,
+ CV_IA64_IntR38 = 1062,
+ CV_IA64_IntR39 = 1063,
+ CV_IA64_IntR40 = 1064,
+ CV_IA64_IntR41 = 1065,
+ CV_IA64_IntR42 = 1066,
+ CV_IA64_IntR43 = 1067,
+ CV_IA64_IntR44 = 1068,
+ CV_IA64_IntR45 = 1069,
+ CV_IA64_IntR46 = 1070,
+ CV_IA64_IntR47 = 1071,
+ CV_IA64_IntR48 = 1072,
+ CV_IA64_IntR49 = 1073,
+ CV_IA64_IntR50 = 1074,
+ CV_IA64_IntR51 = 1075,
+ CV_IA64_IntR52 = 1076,
+ CV_IA64_IntR53 = 1077,
+ CV_IA64_IntR54 = 1078,
+ CV_IA64_IntR55 = 1079,
+ CV_IA64_IntR56 = 1080,
+ CV_IA64_IntR57 = 1081,
+ CV_IA64_IntR58 = 1082,
+ CV_IA64_IntR59 = 1083,
+ CV_IA64_IntR60 = 1084,
+ CV_IA64_IntR61 = 1085,
+ CV_IA64_IntR62 = 1086,
+ CV_IA64_IntR63 = 1087,
+ CV_IA64_IntR64 = 1088,
+ CV_IA64_IntR65 = 1089,
+ CV_IA64_IntR66 = 1090,
+ CV_IA64_IntR67 = 1091,
+ CV_IA64_IntR68 = 1092,
+ CV_IA64_IntR69 = 1093,
+ CV_IA64_IntR70 = 1094,
+ CV_IA64_IntR71 = 1095,
+ CV_IA64_IntR72 = 1096,
+ CV_IA64_IntR73 = 1097,
+ CV_IA64_IntR74 = 1098,
+ CV_IA64_IntR75 = 1099,
+ CV_IA64_IntR76 = 1100,
+ CV_IA64_IntR77 = 1101,
+ CV_IA64_IntR78 = 1102,
+ CV_IA64_IntR79 = 1103,
+ CV_IA64_IntR80 = 1104,
+ CV_IA64_IntR81 = 1105,
+ CV_IA64_IntR82 = 1106,
+ CV_IA64_IntR83 = 1107,
+ CV_IA64_IntR84 = 1108,
+ CV_IA64_IntR85 = 1109,
+ CV_IA64_IntR86 = 1110,
+ CV_IA64_IntR87 = 1111,
+ CV_IA64_IntR88 = 1112,
+ CV_IA64_IntR89 = 1113,
+ CV_IA64_IntR90 = 1114,
+ CV_IA64_IntR91 = 1115,
+ CV_IA64_IntR92 = 1116,
+ CV_IA64_IntR93 = 1117,
+ CV_IA64_IntR94 = 1118,
+ CV_IA64_IntR95 = 1119,
+ CV_IA64_IntR96 = 1120,
+ CV_IA64_IntR97 = 1121,
+ CV_IA64_IntR98 = 1122,
+ CV_IA64_IntR99 = 1123,
+ CV_IA64_IntR100 = 1124,
+ CV_IA64_IntR101 = 1125,
+ CV_IA64_IntR102 = 1126,
+ CV_IA64_IntR103 = 1127,
+ CV_IA64_IntR104 = 1128,
+ CV_IA64_IntR105 = 1129,
+ CV_IA64_IntR106 = 1130,
+ CV_IA64_IntR107 = 1131,
+ CV_IA64_IntR108 = 1132,
+ CV_IA64_IntR109 = 1133,
+ CV_IA64_IntR110 = 1134,
+ CV_IA64_IntR111 = 1135,
+ CV_IA64_IntR112 = 1136,
+ CV_IA64_IntR113 = 1137,
+ CV_IA64_IntR114 = 1138,
+ CV_IA64_IntR115 = 1139,
+ CV_IA64_IntR116 = 1140,
+ CV_IA64_IntR117 = 1141,
+ CV_IA64_IntR118 = 1142,
+ CV_IA64_IntR119 = 1143,
+ CV_IA64_IntR120 = 1144,
+ CV_IA64_IntR121 = 1145,
+ CV_IA64_IntR122 = 1146,
+ CV_IA64_IntR123 = 1147,
+ CV_IA64_IntR124 = 1148,
+ CV_IA64_IntR125 = 1149,
+ CV_IA64_IntR126 = 1150,
+ CV_IA64_IntR127 = 1151,
+
+ // Floating-Point Registers
+
+ // Low Floating Point Registers
+ CV_IA64_FltF0 = 2048,
+ CV_IA64_FltF1 = 2049,
+ CV_IA64_FltF2 = 2050,
+ CV_IA64_FltF3 = 2051,
+ CV_IA64_FltF4 = 2052,
+ CV_IA64_FltF5 = 2053,
+ CV_IA64_FltF6 = 2054,
+ CV_IA64_FltF7 = 2055,
+ CV_IA64_FltF8 = 2056,
+ CV_IA64_FltF9 = 2057,
+ CV_IA64_FltF10 = 2058,
+ CV_IA64_FltF11 = 2059,
+ CV_IA64_FltF12 = 2060,
+ CV_IA64_FltF13 = 2061,
+ CV_IA64_FltF14 = 2062,
+ CV_IA64_FltF15 = 2063,
+ CV_IA64_FltF16 = 2064,
+ CV_IA64_FltF17 = 2065,
+ CV_IA64_FltF18 = 2066,
+ CV_IA64_FltF19 = 2067,
+ CV_IA64_FltF20 = 2068,
+ CV_IA64_FltF21 = 2069,
+ CV_IA64_FltF22 = 2070,
+ CV_IA64_FltF23 = 2071,
+ CV_IA64_FltF24 = 2072,
+ CV_IA64_FltF25 = 2073,
+ CV_IA64_FltF26 = 2074,
+ CV_IA64_FltF27 = 2075,
+ CV_IA64_FltF28 = 2076,
+ CV_IA64_FltF29 = 2077,
+ CV_IA64_FltF30 = 2078,
+ CV_IA64_FltF31 = 2079,
+
+ // High Floating Point Registers
+ CV_IA64_FltF32 = 2080,
+ CV_IA64_FltF33 = 2081,
+ CV_IA64_FltF34 = 2082,
+ CV_IA64_FltF35 = 2083,
+ CV_IA64_FltF36 = 2084,
+ CV_IA64_FltF37 = 2085,
+ CV_IA64_FltF38 = 2086,
+ CV_IA64_FltF39 = 2087,
+ CV_IA64_FltF40 = 2088,
+ CV_IA64_FltF41 = 2089,
+ CV_IA64_FltF42 = 2090,
+ CV_IA64_FltF43 = 2091,
+ CV_IA64_FltF44 = 2092,
+ CV_IA64_FltF45 = 2093,
+ CV_IA64_FltF46 = 2094,
+ CV_IA64_FltF47 = 2095,
+ CV_IA64_FltF48 = 2096,
+ CV_IA64_FltF49 = 2097,
+ CV_IA64_FltF50 = 2098,
+ CV_IA64_FltF51 = 2099,
+ CV_IA64_FltF52 = 2100,
+ CV_IA64_FltF53 = 2101,
+ CV_IA64_FltF54 = 2102,
+ CV_IA64_FltF55 = 2103,
+ CV_IA64_FltF56 = 2104,
+ CV_IA64_FltF57 = 2105,
+ CV_IA64_FltF58 = 2106,
+ CV_IA64_FltF59 = 2107,
+ CV_IA64_FltF60 = 2108,
+ CV_IA64_FltF61 = 2109,
+ CV_IA64_FltF62 = 2110,
+ CV_IA64_FltF63 = 2111,
+ CV_IA64_FltF64 = 2112,
+ CV_IA64_FltF65 = 2113,
+ CV_IA64_FltF66 = 2114,
+ CV_IA64_FltF67 = 2115,
+ CV_IA64_FltF68 = 2116,
+ CV_IA64_FltF69 = 2117,
+ CV_IA64_FltF70 = 2118,
+ CV_IA64_FltF71 = 2119,
+ CV_IA64_FltF72 = 2120,
+ CV_IA64_FltF73 = 2121,
+ CV_IA64_FltF74 = 2122,
+ CV_IA64_FltF75 = 2123,
+ CV_IA64_FltF76 = 2124,
+ CV_IA64_FltF77 = 2125,
+ CV_IA64_FltF78 = 2126,
+ CV_IA64_FltF79 = 2127,
+ CV_IA64_FltF80 = 2128,
+ CV_IA64_FltF81 = 2129,
+ CV_IA64_FltF82 = 2130,
+ CV_IA64_FltF83 = 2131,
+ CV_IA64_FltF84 = 2132,
+ CV_IA64_FltF85 = 2133,
+ CV_IA64_FltF86 = 2134,
+ CV_IA64_FltF87 = 2135,
+ CV_IA64_FltF88 = 2136,
+ CV_IA64_FltF89 = 2137,
+ CV_IA64_FltF90 = 2138,
+ CV_IA64_FltF91 = 2139,
+ CV_IA64_FltF92 = 2140,
+ CV_IA64_FltF93 = 2141,
+ CV_IA64_FltF94 = 2142,
+ CV_IA64_FltF95 = 2143,
+ CV_IA64_FltF96 = 2144,
+ CV_IA64_FltF97 = 2145,
+ CV_IA64_FltF98 = 2146,
+ CV_IA64_FltF99 = 2147,
+ CV_IA64_FltF100 = 2148,
+ CV_IA64_FltF101 = 2149,
+ CV_IA64_FltF102 = 2150,
+ CV_IA64_FltF103 = 2151,
+ CV_IA64_FltF104 = 2152,
+ CV_IA64_FltF105 = 2153,
+ CV_IA64_FltF106 = 2154,
+ CV_IA64_FltF107 = 2155,
+ CV_IA64_FltF108 = 2156,
+ CV_IA64_FltF109 = 2157,
+ CV_IA64_FltF110 = 2158,
+ CV_IA64_FltF111 = 2159,
+ CV_IA64_FltF112 = 2160,
+ CV_IA64_FltF113 = 2161,
+ CV_IA64_FltF114 = 2162,
+ CV_IA64_FltF115 = 2163,
+ CV_IA64_FltF116 = 2164,
+ CV_IA64_FltF117 = 2165,
+ CV_IA64_FltF118 = 2166,
+ CV_IA64_FltF119 = 2167,
+ CV_IA64_FltF120 = 2168,
+ CV_IA64_FltF121 = 2169,
+ CV_IA64_FltF122 = 2170,
+ CV_IA64_FltF123 = 2171,
+ CV_IA64_FltF124 = 2172,
+ CV_IA64_FltF125 = 2173,
+ CV_IA64_FltF126 = 2174,
+ CV_IA64_FltF127 = 2175,
+
+ // Application Registers
+
+ CV_IA64_ApKR0 = 3072,
+ CV_IA64_ApKR1 = 3073,
+ CV_IA64_ApKR2 = 3074,
+ CV_IA64_ApKR3 = 3075,
+ CV_IA64_ApKR4 = 3076,
+ CV_IA64_ApKR5 = 3077,
+ CV_IA64_ApKR6 = 3078,
+ CV_IA64_ApKR7 = 3079,
+ CV_IA64_AR8 = 3080,
+ CV_IA64_AR9 = 3081,
+ CV_IA64_AR10 = 3082,
+ CV_IA64_AR11 = 3083,
+ CV_IA64_AR12 = 3084,
+ CV_IA64_AR13 = 3085,
+ CV_IA64_AR14 = 3086,
+ CV_IA64_AR15 = 3087,
+ CV_IA64_RsRSC = 3088,
+ CV_IA64_RsBSP = 3089,
+ CV_IA64_RsBSPSTORE = 3090,
+ CV_IA64_RsRNAT = 3091,
+ CV_IA64_AR20 = 3092,
+ CV_IA64_StFCR = 3093,
+ CV_IA64_AR22 = 3094,
+ CV_IA64_AR23 = 3095,
+ CV_IA64_EFLAG = 3096,
+ CV_IA64_CSD = 3097,
+ CV_IA64_SSD = 3098,
+ CV_IA64_CFLG = 3099,
+ CV_IA64_StFSR = 3100,
+ CV_IA64_StFIR = 3101,
+ CV_IA64_StFDR = 3102,
+ CV_IA64_AR31 = 3103,
+ CV_IA64_ApCCV = 3104,
+ CV_IA64_AR33 = 3105,
+ CV_IA64_AR34 = 3106,
+ CV_IA64_AR35 = 3107,
+ CV_IA64_ApUNAT = 3108,
+ CV_IA64_AR37 = 3109,
+ CV_IA64_AR38 = 3110,
+ CV_IA64_AR39 = 3111,
+ CV_IA64_StFPSR = 3112,
+ CV_IA64_AR41 = 3113,
+ CV_IA64_AR42 = 3114,
+ CV_IA64_AR43 = 3115,
+ CV_IA64_ApITC = 3116,
+ CV_IA64_AR45 = 3117,
+ CV_IA64_AR46 = 3118,
+ CV_IA64_AR47 = 3119,
+ CV_IA64_AR48 = 3120,
+ CV_IA64_AR49 = 3121,
+ CV_IA64_AR50 = 3122,
+ CV_IA64_AR51 = 3123,
+ CV_IA64_AR52 = 3124,
+ CV_IA64_AR53 = 3125,
+ CV_IA64_AR54 = 3126,
+ CV_IA64_AR55 = 3127,
+ CV_IA64_AR56 = 3128,
+ CV_IA64_AR57 = 3129,
+ CV_IA64_AR58 = 3130,
+ CV_IA64_AR59 = 3131,
+ CV_IA64_AR60 = 3132,
+ CV_IA64_AR61 = 3133,
+ CV_IA64_AR62 = 3134,
+ CV_IA64_AR63 = 3135,
+ CV_IA64_RsPFS = 3136,
+ CV_IA64_ApLC = 3137,
+ CV_IA64_ApEC = 3138,
+ CV_IA64_AR67 = 3139,
+ CV_IA64_AR68 = 3140,
+ CV_IA64_AR69 = 3141,
+ CV_IA64_AR70 = 3142,
+ CV_IA64_AR71 = 3143,
+ CV_IA64_AR72 = 3144,
+ CV_IA64_AR73 = 3145,
+ CV_IA64_AR74 = 3146,
+ CV_IA64_AR75 = 3147,
+ CV_IA64_AR76 = 3148,
+ CV_IA64_AR77 = 3149,
+ CV_IA64_AR78 = 3150,
+ CV_IA64_AR79 = 3151,
+ CV_IA64_AR80 = 3152,
+ CV_IA64_AR81 = 3153,
+ CV_IA64_AR82 = 3154,
+ CV_IA64_AR83 = 3155,
+ CV_IA64_AR84 = 3156,
+ CV_IA64_AR85 = 3157,
+ CV_IA64_AR86 = 3158,
+ CV_IA64_AR87 = 3159,
+ CV_IA64_AR88 = 3160,
+ CV_IA64_AR89 = 3161,
+ CV_IA64_AR90 = 3162,
+ CV_IA64_AR91 = 3163,
+ CV_IA64_AR92 = 3164,
+ CV_IA64_AR93 = 3165,
+ CV_IA64_AR94 = 3166,
+ CV_IA64_AR95 = 3167,
+ CV_IA64_AR96 = 3168,
+ CV_IA64_AR97 = 3169,
+ CV_IA64_AR98 = 3170,
+ CV_IA64_AR99 = 3171,
+ CV_IA64_AR100 = 3172,
+ CV_IA64_AR101 = 3173,
+ CV_IA64_AR102 = 3174,
+ CV_IA64_AR103 = 3175,
+ CV_IA64_AR104 = 3176,
+ CV_IA64_AR105 = 3177,
+ CV_IA64_AR106 = 3178,
+ CV_IA64_AR107 = 3179,
+ CV_IA64_AR108 = 3180,
+ CV_IA64_AR109 = 3181,
+ CV_IA64_AR110 = 3182,
+ CV_IA64_AR111 = 3183,
+ CV_IA64_AR112 = 3184,
+ CV_IA64_AR113 = 3185,
+ CV_IA64_AR114 = 3186,
+ CV_IA64_AR115 = 3187,
+ CV_IA64_AR116 = 3188,
+ CV_IA64_AR117 = 3189,
+ CV_IA64_AR118 = 3190,
+ CV_IA64_AR119 = 3191,
+ CV_IA64_AR120 = 3192,
+ CV_IA64_AR121 = 3193,
+ CV_IA64_AR122 = 3194,
+ CV_IA64_AR123 = 3195,
+ CV_IA64_AR124 = 3196,
+ CV_IA64_AR125 = 3197,
+ CV_IA64_AR126 = 3198,
+ CV_IA64_AR127 = 3199,
+
+ // CPUID Registers
+
+ CV_IA64_CPUID0 = 3328,
+ CV_IA64_CPUID1 = 3329,
+ CV_IA64_CPUID2 = 3330,
+ CV_IA64_CPUID3 = 3331,
+ CV_IA64_CPUID4 = 3332,
+
+ // Control Registers
+
+ CV_IA64_ApDCR = 4096,
+ CV_IA64_ApITM = 4097,
+ CV_IA64_ApIVA = 4098,
+ CV_IA64_CR3 = 4099,
+ CV_IA64_CR4 = 4100,
+ CV_IA64_CR5 = 4101,
+ CV_IA64_CR6 = 4102,
+ CV_IA64_CR7 = 4103,
+ CV_IA64_ApPTA = 4104,
+ CV_IA64_ApGPTA = 4105,
+ CV_IA64_CR10 = 4106,
+ CV_IA64_CR11 = 4107,
+ CV_IA64_CR12 = 4108,
+ CV_IA64_CR13 = 4109,
+ CV_IA64_CR14 = 4110,
+ CV_IA64_CR15 = 4111,
+ CV_IA64_StIPSR = 4112,
+ CV_IA64_StISR = 4113,
+ CV_IA64_CR18 = 4114,
+ CV_IA64_StIIP = 4115,
+ CV_IA64_StIFA = 4116,
+ CV_IA64_StITIR = 4117,
+ CV_IA64_StIIPA = 4118,
+ CV_IA64_StIFS = 4119,
+ CV_IA64_StIIM = 4120,
+ CV_IA64_StIHA = 4121,
+ CV_IA64_CR26 = 4122,
+ CV_IA64_CR27 = 4123,
+ CV_IA64_CR28 = 4124,
+ CV_IA64_CR29 = 4125,
+ CV_IA64_CR30 = 4126,
+ CV_IA64_CR31 = 4127,
+ CV_IA64_CR32 = 4128,
+ CV_IA64_CR33 = 4129,
+ CV_IA64_CR34 = 4130,
+ CV_IA64_CR35 = 4131,
+ CV_IA64_CR36 = 4132,
+ CV_IA64_CR37 = 4133,
+ CV_IA64_CR38 = 4134,
+ CV_IA64_CR39 = 4135,
+ CV_IA64_CR40 = 4136,
+ CV_IA64_CR41 = 4137,
+ CV_IA64_CR42 = 4138,
+ CV_IA64_CR43 = 4139,
+ CV_IA64_CR44 = 4140,
+ CV_IA64_CR45 = 4141,
+ CV_IA64_CR46 = 4142,
+ CV_IA64_CR47 = 4143,
+ CV_IA64_CR48 = 4144,
+ CV_IA64_CR49 = 4145,
+ CV_IA64_CR50 = 4146,
+ CV_IA64_CR51 = 4147,
+ CV_IA64_CR52 = 4148,
+ CV_IA64_CR53 = 4149,
+ CV_IA64_CR54 = 4150,
+ CV_IA64_CR55 = 4151,
+ CV_IA64_CR56 = 4152,
+ CV_IA64_CR57 = 4153,
+ CV_IA64_CR58 = 4154,
+ CV_IA64_CR59 = 4155,
+ CV_IA64_CR60 = 4156,
+ CV_IA64_CR61 = 4157,
+ CV_IA64_CR62 = 4158,
+ CV_IA64_CR63 = 4159,
+ CV_IA64_SaLID = 4160,
+ CV_IA64_SaIVR = 4161,
+ CV_IA64_SaTPR = 4162,
+ CV_IA64_SaEOI = 4163,
+ CV_IA64_SaIRR0 = 4164,
+ CV_IA64_SaIRR1 = 4165,
+ CV_IA64_SaIRR2 = 4166,
+ CV_IA64_SaIRR3 = 4167,
+ CV_IA64_SaITV = 4168,
+ CV_IA64_SaPMV = 4169,
+ CV_IA64_SaCMCV = 4170,
+ CV_IA64_CR75 = 4171,
+ CV_IA64_CR76 = 4172,
+ CV_IA64_CR77 = 4173,
+ CV_IA64_CR78 = 4174,
+ CV_IA64_CR79 = 4175,
+ CV_IA64_SaLRR0 = 4176,
+ CV_IA64_SaLRR1 = 4177,
+ CV_IA64_CR82 = 4178,
+ CV_IA64_CR83 = 4179,
+ CV_IA64_CR84 = 4180,
+ CV_IA64_CR85 = 4181,
+ CV_IA64_CR86 = 4182,
+ CV_IA64_CR87 = 4183,
+ CV_IA64_CR88 = 4184,
+ CV_IA64_CR89 = 4185,
+ CV_IA64_CR90 = 4186,
+ CV_IA64_CR91 = 4187,
+ CV_IA64_CR92 = 4188,
+ CV_IA64_CR93 = 4189,
+ CV_IA64_CR94 = 4190,
+ CV_IA64_CR95 = 4191,
+ CV_IA64_CR96 = 4192,
+ CV_IA64_CR97 = 4193,
+ CV_IA64_CR98 = 4194,
+ CV_IA64_CR99 = 4195,
+ CV_IA64_CR100 = 4196,
+ CV_IA64_CR101 = 4197,
+ CV_IA64_CR102 = 4198,
+ CV_IA64_CR103 = 4199,
+ CV_IA64_CR104 = 4200,
+ CV_IA64_CR105 = 4201,
+ CV_IA64_CR106 = 4202,
+ CV_IA64_CR107 = 4203,
+ CV_IA64_CR108 = 4204,
+ CV_IA64_CR109 = 4205,
+ CV_IA64_CR110 = 4206,
+ CV_IA64_CR111 = 4207,
+ CV_IA64_CR112 = 4208,
+ CV_IA64_CR113 = 4209,
+ CV_IA64_CR114 = 4210,
+ CV_IA64_CR115 = 4211,
+ CV_IA64_CR116 = 4212,
+ CV_IA64_CR117 = 4213,
+ CV_IA64_CR118 = 4214,
+ CV_IA64_CR119 = 4215,
+ CV_IA64_CR120 = 4216,
+ CV_IA64_CR121 = 4217,
+ CV_IA64_CR122 = 4218,
+ CV_IA64_CR123 = 4219,
+ CV_IA64_CR124 = 4220,
+ CV_IA64_CR125 = 4221,
+ CV_IA64_CR126 = 4222,
+ CV_IA64_CR127 = 4223,
+
+ // Protection Key Registers
+
+ CV_IA64_Pkr0 = 5120,
+ CV_IA64_Pkr1 = 5121,
+ CV_IA64_Pkr2 = 5122,
+ CV_IA64_Pkr3 = 5123,
+ CV_IA64_Pkr4 = 5124,
+ CV_IA64_Pkr5 = 5125,
+ CV_IA64_Pkr6 = 5126,
+ CV_IA64_Pkr7 = 5127,
+ CV_IA64_Pkr8 = 5128,
+ CV_IA64_Pkr9 = 5129,
+ CV_IA64_Pkr10 = 5130,
+ CV_IA64_Pkr11 = 5131,
+ CV_IA64_Pkr12 = 5132,
+ CV_IA64_Pkr13 = 5133,
+ CV_IA64_Pkr14 = 5134,
+ CV_IA64_Pkr15 = 5135,
+
+ // Region Registers
+
+ CV_IA64_Rr0 = 6144,
+ CV_IA64_Rr1 = 6145,
+ CV_IA64_Rr2 = 6146,
+ CV_IA64_Rr3 = 6147,
+ CV_IA64_Rr4 = 6148,
+ CV_IA64_Rr5 = 6149,
+ CV_IA64_Rr6 = 6150,
+ CV_IA64_Rr7 = 6151,
+
+ // Performance Monitor Data Registers
+
+ CV_IA64_PFD0 = 7168,
+ CV_IA64_PFD1 = 7169,
+ CV_IA64_PFD2 = 7170,
+ CV_IA64_PFD3 = 7171,
+ CV_IA64_PFD4 = 7172,
+ CV_IA64_PFD5 = 7173,
+ CV_IA64_PFD6 = 7174,
+ CV_IA64_PFD7 = 7175,
+
+ // Performance Monitor Config Registers
+
+ CV_IA64_PFC0 = 7424,
+ CV_IA64_PFC1 = 7425,
+ CV_IA64_PFC2 = 7426,
+ CV_IA64_PFC3 = 7427,
+ CV_IA64_PFC4 = 7428,
+ CV_IA64_PFC5 = 7429,
+ CV_IA64_PFC6 = 7430,
+ CV_IA64_PFC7 = 7431,
+
+ // Instruction Translation Registers
+
+ CV_IA64_TrI0 = 8192,
+ CV_IA64_TrI1 = 8193,
+ CV_IA64_TrI2 = 8194,
+ CV_IA64_TrI3 = 8195,
+ CV_IA64_TrI4 = 8196,
+ CV_IA64_TrI5 = 8197,
+ CV_IA64_TrI6 = 8198,
+ CV_IA64_TrI7 = 8199,
+
+ // Data Translation Registers
+
+ CV_IA64_TrD0 = 8320,
+ CV_IA64_TrD1 = 8321,
+ CV_IA64_TrD2 = 8322,
+ CV_IA64_TrD3 = 8323,
+ CV_IA64_TrD4 = 8324,
+ CV_IA64_TrD5 = 8325,
+ CV_IA64_TrD6 = 8326,
+ CV_IA64_TrD7 = 8327,
+
+ // Instruction Breakpoint Registers
+
+ CV_IA64_DbI0 = 8448,
+ CV_IA64_DbI1 = 8449,
+ CV_IA64_DbI2 = 8450,
+ CV_IA64_DbI3 = 8451,
+ CV_IA64_DbI4 = 8452,
+ CV_IA64_DbI5 = 8453,
+ CV_IA64_DbI6 = 8454,
+ CV_IA64_DbI7 = 8455,
+
+ // Data Breakpoint Registers
+
+ CV_IA64_DbD0 = 8576,
+ CV_IA64_DbD1 = 8577,
+ CV_IA64_DbD2 = 8578,
+ CV_IA64_DbD3 = 8579,
+ CV_IA64_DbD4 = 8580,
+ CV_IA64_DbD5 = 8581,
+ CV_IA64_DbD6 = 8582,
+ CV_IA64_DbD7 = 8583,
+
+ //
+ // Register set for the TriCore processor.
+ //
+
+ CV_TRI_NOREG = CV_REG_NONE,
+
+ // General Purpose Data Registers
+
+ CV_TRI_D0 = 10,
+ CV_TRI_D1 = 11,
+ CV_TRI_D2 = 12,
+ CV_TRI_D3 = 13,
+ CV_TRI_D4 = 14,
+ CV_TRI_D5 = 15,
+ CV_TRI_D6 = 16,
+ CV_TRI_D7 = 17,
+ CV_TRI_D8 = 18,
+ CV_TRI_D9 = 19,
+ CV_TRI_D10 = 20,
+ CV_TRI_D11 = 21,
+ CV_TRI_D12 = 22,
+ CV_TRI_D13 = 23,
+ CV_TRI_D14 = 24,
+ CV_TRI_D15 = 25,
+
+ // General Purpose Address Registers
+
+ CV_TRI_A0 = 26,
+ CV_TRI_A1 = 27,
+ CV_TRI_A2 = 28,
+ CV_TRI_A3 = 29,
+ CV_TRI_A4 = 30,
+ CV_TRI_A5 = 31,
+ CV_TRI_A6 = 32,
+ CV_TRI_A7 = 33,
+ CV_TRI_A8 = 34,
+ CV_TRI_A9 = 35,
+ CV_TRI_A10 = 36,
+ CV_TRI_A11 = 37,
+ CV_TRI_A12 = 38,
+ CV_TRI_A13 = 39,
+ CV_TRI_A14 = 40,
+ CV_TRI_A15 = 41,
+
+ // Extended (64-bit) data registers
+
+ CV_TRI_E0 = 42,
+ CV_TRI_E2 = 43,
+ CV_TRI_E4 = 44,
+ CV_TRI_E6 = 45,
+ CV_TRI_E8 = 46,
+ CV_TRI_E10 = 47,
+ CV_TRI_E12 = 48,
+ CV_TRI_E14 = 49,
+
+ // Extended (64-bit) address registers
+
+ CV_TRI_EA0 = 50,
+ CV_TRI_EA2 = 51,
+ CV_TRI_EA4 = 52,
+ CV_TRI_EA6 = 53,
+ CV_TRI_EA8 = 54,
+ CV_TRI_EA10 = 55,
+ CV_TRI_EA12 = 56,
+ CV_TRI_EA14 = 57,
+
+ CV_TRI_PSW = 58,
+ CV_TRI_PCXI = 59,
+ CV_TRI_PC = 60,
+ CV_TRI_FCX = 61,
+ CV_TRI_LCX = 62,
+ CV_TRI_ISP = 63,
+ CV_TRI_ICR = 64,
+ CV_TRI_BIV = 65,
+ CV_TRI_BTV = 66,
+ CV_TRI_SYSCON = 67,
+ CV_TRI_DPRx_0 = 68,
+ CV_TRI_DPRx_1 = 69,
+ CV_TRI_DPRx_2 = 70,
+ CV_TRI_DPRx_3 = 71,
+ CV_TRI_CPRx_0 = 68,
+ CV_TRI_CPRx_1 = 69,
+ CV_TRI_CPRx_2 = 70,
+ CV_TRI_CPRx_3 = 71,
+ CV_TRI_DPMx_0 = 68,
+ CV_TRI_DPMx_1 = 69,
+ CV_TRI_DPMx_2 = 70,
+ CV_TRI_DPMx_3 = 71,
+ CV_TRI_CPMx_0 = 68,
+ CV_TRI_CPMx_1 = 69,
+ CV_TRI_CPMx_2 = 70,
+ CV_TRI_CPMx_3 = 71,
+ CV_TRI_DBGSSR = 72,
+ CV_TRI_EXEVT = 73,
+ CV_TRI_SWEVT = 74,
+ CV_TRI_CREVT = 75,
+ CV_TRI_TRnEVT = 76,
+ CV_TRI_MMUCON = 77,
+ CV_TRI_ASI = 78,
+ CV_TRI_TVA = 79,
+ CV_TRI_TPA = 80,
+ CV_TRI_TPX = 81,
+ CV_TRI_TFA = 82,
+
+ //
+ // Register set for the AM33 and related processors.
+ //
+
+ CV_AM33_NOREG = CV_REG_NONE,
+
+ // "Extended" (general purpose integer) registers
+ CV_AM33_E0 = 10,
+ CV_AM33_E1 = 11,
+ CV_AM33_E2 = 12,
+ CV_AM33_E3 = 13,
+ CV_AM33_E4 = 14,
+ CV_AM33_E5 = 15,
+ CV_AM33_E6 = 16,
+ CV_AM33_E7 = 17,
+
+ // Address registers
+ CV_AM33_A0 = 20,
+ CV_AM33_A1 = 21,
+ CV_AM33_A2 = 22,
+ CV_AM33_A3 = 23,
+
+ // Integer data registers
+ CV_AM33_D0 = 30,
+ CV_AM33_D1 = 31,
+ CV_AM33_D2 = 32,
+ CV_AM33_D3 = 33,
+
+ // (Single-precision) floating-point registers
+ CV_AM33_FS0 = 40,
+ CV_AM33_FS1 = 41,
+ CV_AM33_FS2 = 42,
+ CV_AM33_FS3 = 43,
+ CV_AM33_FS4 = 44,
+ CV_AM33_FS5 = 45,
+ CV_AM33_FS6 = 46,
+ CV_AM33_FS7 = 47,
+ CV_AM33_FS8 = 48,
+ CV_AM33_FS9 = 49,
+ CV_AM33_FS10 = 50,
+ CV_AM33_FS11 = 51,
+ CV_AM33_FS12 = 52,
+ CV_AM33_FS13 = 53,
+ CV_AM33_FS14 = 54,
+ CV_AM33_FS15 = 55,
+ CV_AM33_FS16 = 56,
+ CV_AM33_FS17 = 57,
+ CV_AM33_FS18 = 58,
+ CV_AM33_FS19 = 59,
+ CV_AM33_FS20 = 60,
+ CV_AM33_FS21 = 61,
+ CV_AM33_FS22 = 62,
+ CV_AM33_FS23 = 63,
+ CV_AM33_FS24 = 64,
+ CV_AM33_FS25 = 65,
+ CV_AM33_FS26 = 66,
+ CV_AM33_FS27 = 67,
+ CV_AM33_FS28 = 68,
+ CV_AM33_FS29 = 69,
+ CV_AM33_FS30 = 70,
+ CV_AM33_FS31 = 71,
+
+ // Special purpose registers
+
+ // Stack pointer
+ CV_AM33_SP = 80,
+
+ // Program counter
+ CV_AM33_PC = 81,
+
+ // Multiply-divide/accumulate registers
+ CV_AM33_MDR = 82,
+ CV_AM33_MDRQ = 83,
+ CV_AM33_MCRH = 84,
+ CV_AM33_MCRL = 85,
+ CV_AM33_MCVF = 86,
+
+ // CPU status words
+ CV_AM33_EPSW = 87,
+ CV_AM33_FPCR = 88,
+
+ // Loop buffer registers
+ CV_AM33_LIR = 89,
+ CV_AM33_LAR = 90,
+
+ //
+ // Register set for the Mitsubishi M32R
+ //
+
+ CV_M32R_NOREG = CV_REG_NONE,
+
+ CV_M32R_R0 = 10,
+ CV_M32R_R1 = 11,
+ CV_M32R_R2 = 12,
+ CV_M32R_R3 = 13,
+ CV_M32R_R4 = 14,
+ CV_M32R_R5 = 15,
+ CV_M32R_R6 = 16,
+ CV_M32R_R7 = 17,
+ CV_M32R_R8 = 18,
+ CV_M32R_R9 = 19,
+ CV_M32R_R10 = 20,
+ CV_M32R_R11 = 21,
+ CV_M32R_R12 = 22, // Gloabal Pointer, if used
+ CV_M32R_R13 = 23, // Frame Pointer, if allocated
+ CV_M32R_R14 = 24, // Link Register
+ CV_M32R_R15 = 25, // Stack Pointer
+ CV_M32R_PSW = 26, // Preocessor Status Register
+ CV_M32R_CBR = 27, // Condition Bit Register
+ CV_M32R_SPI = 28, // Interrupt Stack Pointer
+ CV_M32R_SPU = 29, // User Stack Pointer
+ CV_M32R_SPO = 30, // OS Stack Pointer
+ CV_M32R_BPC = 31, // Backup Program Counter
+ CV_M32R_ACHI = 32, // Accumulator High
+ CV_M32R_ACLO = 33, // Accumulator Low
+ CV_M32R_PC = 34, // Program Counter
+
+}
+
+} // Namespace Dia
diff --git a/src/ToolBox/SOS/DacTableGen/diautil.cs b/src/ToolBox/SOS/DacTableGen/diautil.cs
new file mode 100644
index 0000000000..d0e9c811a7
--- /dev/null
+++ b/src/ToolBox/SOS/DacTableGen/diautil.cs
@@ -0,0 +1,809 @@
+// 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.Text;
+using System.IO;
+using Dia;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+/***************************************************************************************
+ *
+ ***************************************************************************************/
+
+namespace Dia.Util
+{
+
+/***************************************************************************************
+ *
+ ***************************************************************************************/
+
+class Util
+{
+ public static IDiaSymbol FindSymbol(String symName, IDiaSymbol parent, SymTagEnum symTag)
+ {
+ IDiaEnumSymbols e;
+ parent.findChildren(symTag,
+ symName,
+ (uint)(NameSearchOptions.nsfCaseSensitive),
+ out e);
+
+ IDiaSymbol s;
+ uint celt;
+
+ if (e == null || e.count == 0)
+ return null;
+
+ e.Next(1, out s, out celt);
+
+ if (e.count > 1)
+ {
+ for (int i = 1; i < e.count; i++)
+ {
+ IDiaSymbol s2;
+ e.Next(1, out s2, out celt);
+
+ // Diasym reader returns multiple symbols with same RVA in some cases. Issue the warning only
+ // if the returned symbols actually differ.
+ if (s.virtualAddress != s2.virtualAddress)
+ {
+ Shell.Error("Symbol " + symName + " has " + e.count + " matches. Taking first.");
+ break;
+ }
+ }
+ }
+
+ return s;
+ }
+
+ public static IDiaSymbol FindClassSymbol(String name, IDiaSymbol sym, SymTagEnum tag)
+ {
+ IDiaSymbol res = null;
+ //Console.WriteLine("Looking for " + name + " in " + sym.name);
+
+ res = Util.FindSymbol(name, sym, tag);
+
+ if (res == null)
+ {
+ IDiaEnumSymbols e;
+
+ sym.findChildren(
+ SymTagEnum.SymTagBaseClass,
+ null,
+ (uint)NameSearchOptions.nsNone,
+ out e);
+
+ if (e == null || e.count == 0)
+ return null;
+
+ for (int i = 0; i < e.count && res == null; i++)
+ {
+ UInt32 celt;
+ IDiaSymbol s;
+ e.Next(1, out s, out celt);
+ res = FindClassSymbol(name, s.type, tag);
+ }
+ }
+
+ return res;
+ }
+}
+
+/***************************************************************************************
+ *
+ ***************************************************************************************/
+
+public class DiaFile
+{
+ DiaSourceClass m_dsc;
+ IDiaSession m_session;
+ DiaSymbol m_global;
+ IDiaEnumSymbols m_publicsEnum;
+ UInt32 m_debugTimestamp;
+ String m_loadedPdbPath;
+
+ public DiaFile(String pdbFile, String dllFile)
+ {
+ m_dsc = new DiaSourceClass();
+ string pdbPath = System.IO.Path.GetDirectoryName(pdbFile);
+
+ // Open the PDB file, validating it matches the supplied DLL file
+ DiaLoadCallback loadCallback = new DiaLoadCallback();
+ try
+ {
+ m_dsc.loadDataForExe(dllFile, pdbPath, loadCallback);
+ }
+ catch (System.Exception diaEx)
+ {
+ // Provide additional diagnostics context and rethrow
+ string msg = "ERROR from DIA loading PDB for specified DLL";
+ COMException comEx = diaEx as COMException;
+ if (comEx != null)
+ {
+ if (Enum.IsDefined(typeof(DiaHResults), comEx.ErrorCode))
+ {
+ // This is a DIA-specific error code,
+ DiaHResults hr = (DiaHResults)comEx.ErrorCode;
+ msg += ": " + hr.ToString();
+
+ // Additional clarification for the common case of the DLL not matching the PDB
+ if (hr == DiaHResults.E_PDB_NOT_FOUND)
+ {
+ msg += " - The specified PDB file does not match the specified DLL file";
+ }
+ }
+ }
+ throw new ApplicationException(msg, diaEx);
+ }
+
+ // Save the path of the PDB file actually loaded
+ Debug.Assert(loadCallback.LoadedPdbPath != null, "Didn't get PDB load callback");
+ m_loadedPdbPath = loadCallback.LoadedPdbPath;
+
+ // Also use DIA to get the debug directory entry in the DLL referring
+ // to the PDB, and save it's timestamp comparison at runtime.
+ m_debugTimestamp = loadCallback.DebugTimeDateStamp;
+ Debug.Assert(m_debugTimestamp != 0, "Didn't find debug directory entry");
+
+ m_dsc.openSession(out m_session);
+ m_global = new DiaSymbol(m_session.globalScope);
+ m_publicsEnum = null;
+ }
+
+ public DiaSymbol GlobalSymbol
+ {
+ get
+ { return m_global; }
+ }
+
+ /// <summary>
+ /// Path of the PDB file actually loaded (must be non-null)
+ /// </summary>
+ public String LoadedPdbPath
+ {
+ get { return m_loadedPdbPath; }
+ }
+
+ /// <summary>
+ /// Timestamp in the debug directory of the DLL corresponding to the PDB loaded (always set non-zero).
+ /// </summary>
+ public UInt32 DebugTimestamp
+ {
+ get { return m_debugTimestamp; }
+ }
+
+ private void GetPublicsEnum()
+ {
+ if (m_publicsEnum != null)
+ return;
+
+ m_session.findChildren(m_global.IDiaSymbol, SymTagEnum.SymTagPublicSymbol,
+ null, (UInt32) NameSearchOptions.nsNone, out m_publicsEnum);
+ }
+
+ public IDiaEnumSymbols PublicSymbols
+ {
+ get
+ {
+ GetPublicsEnum();
+
+ if (m_publicsEnum != null)
+ {
+ IDiaEnumSymbols en;
+ m_publicsEnum.Clone(out en);
+ return en;
+ }
+ else
+ return null;
+ }
+ }
+
+ public IDiaEnumSymbols FindPublicSymbols(String name)
+ {
+ IDiaEnumSymbols se;
+ m_session.findChildren(m_global.IDiaSymbol, SymTagEnum.SymTagPublicSymbol,
+ name, (UInt32) NameSearchOptions.nsNone, out se);
+
+ return se;
+ }
+
+ // Use only the path we supply, don't look for PDBs anywhere else
+ private class DiaLoadCallback : IDiaLoadCallback2
+ {
+ /// <summary>
+ /// The path from with the PDB file was actually loaded, or null if none yet.
+ /// </summary>
+ public string LoadedPdbPath
+ {
+ get { return m_loadedPdbPath; }
+ }
+
+ /// <summary>
+ /// The time stamp in the debug directory corresponding to the PDB that was loaded
+ /// </summary>
+ public UInt32 DebugTimeDateStamp
+ {
+ get { return m_debugTimeDateStamp; }
+ }
+
+ private string m_loadedPdbPath = null;
+ private UInt32 m_debugTimeDateStamp = 0;
+
+ public void NotifyDebugDir(int fExecutable, uint cbData, ref IMAGE_DEBUG_DIRECTORY pbData)
+ {
+ Debug.Assert(cbData == Marshal.SizeOf(typeof(IMAGE_DEBUG_DIRECTORY)), "Got unexpected size for IMAGE_DEBUG_DIRECTORY");
+ // There may be mutliple calls, or calls with no timestamp, but only one entry should be
+ // for the code-view record describing the PDB file.
+ if (pbData.Type == ImageDebugType.IMAGE_DEBUG_TYPE_CODEVIEW)
+ {
+ Debug.Assert(fExecutable == 1, "Got debug directory that wasn't read from an executable");
+ Debug.Assert(m_debugTimeDateStamp == 0, "Got unexpected duplicate NotifyDebugDir callback");
+ Debug.Assert(pbData.TimeDateStamp != 0, "Got unexpected 0 debug timestamp in DLL");
+ m_debugTimeDateStamp = pbData.TimeDateStamp;
+ }
+ }
+
+ public void NotifyOpenDBG(string dbgPath, int resultCode)
+ {
+ Debug.Assert(false, "Unexpected DBG opening: " + dbgPath);
+ }
+
+ public void NotifyOpenPDB(string pdbPath, int resultCode)
+ {
+ // Keep track of the path from which DIA loaded the PDB.
+ // If resultCode is non-zero, it means DIA is not loading this PDB (eg. just probing paths
+ // that may not exist).
+ // The DIA SDK docs say the caller will generally ignore any error HResult we return, so we
+ // don't get the chance to reject any non-matching paths.
+ if (resultCode >= 0)
+ {
+ Debug.Assert(m_loadedPdbPath == null, "DIA indicated it was loading more than one PDB file!");
+ m_loadedPdbPath = pdbPath;
+ }
+ }
+
+ public int RestrictRegistryAccess()
+ {
+ return 1; // don't query the registry
+ }
+
+ public int RestrictSymbolServerAccess()
+ {
+ return 1; // don't use the symbol server
+ }
+
+ public int RestrictDBGAccess()
+ {
+ return 1; // don't look for DBG files
+ }
+
+ public int RestrictOriginalPathAccess()
+ {
+ return 1; // don't look in the full path specified in the debug directory
+ }
+
+ public int RestrictReferencePathAccess()
+ {
+ return 1; // Don't look in the directory next to the DLL
+ }
+
+ public int RestrictSystemRootAccess()
+ {
+ return 1; // Don't look in the system directory
+ }
+ }
+
+ // From WinNt.h
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ private struct IMAGE_DEBUG_DIRECTORY
+ {
+ public UInt32 Characteristics;
+ public UInt32 TimeDateStamp;
+ public UInt16 MajorVersion;
+ public UInt16 MinorVersion;
+ public ImageDebugType Type;
+ public UInt32 SizeOfData;
+ public UInt32 AddressOfRawData;
+ public UInt32 PointerToRawData;
+ }
+ private enum ImageDebugType : uint
+ {
+ IMAGE_DEBUG_TYPE_UNKNOWN = 0,
+ IMAGE_DEBUG_TYPE_COFF = 1,
+ IMAGE_DEBUG_TYPE_CODEVIEW = 2,
+ IMAGE_DEBUG_TYPE_FPO = 3,
+ IMAGE_DEBUG_TYPE_MISC = 4,
+ IMAGE_DEBUG_TYPE_EXCEPTION = 5,
+ IMAGE_DEBUG_TYPE_FIXUP = 6,
+ IMAGE_DEBUG_TYPE_BORLAND = 9
+ }
+
+ // DIA Callback interface definitions
+ // These should be in the interop assembly, but due to a bug in dia2.idl, they're missing.
+ // dia2.idl should include these interfaces in the library section to get them into the TLB.
+ // However, it's handy to be able to define them ourselves, because it lets us easily
+ // set the interface that's most convenient (specifically PreserveSig, and explicit use
+ // of IMAGE_DEBUG_DIRECTORY)
+ [ComImport,
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
+ Guid("C32ADB82-73F4-421B-95D5-A4706EDF5DBE"),
+ System.Security.SuppressUnmanagedCodeSecurity]
+ private interface IDiaLoadCallback
+ {
+ void NotifyDebugDir(int fExecutable, uint cbData, [In] ref IMAGE_DEBUG_DIRECTORY pbData);
+
+ void NotifyOpenDBG([In, MarshalAs(UnmanagedType.LPWStr)] string dbgPath, int resultCode);
+
+ void NotifyOpenPDB([In, MarshalAs(UnmanagedType.LPWStr)] string pdbPath, int resultCode);
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol path lookup from registry
+ int RestrictRegistryAccess();
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol server lookup
+ int RestrictSymbolServerAccess();
+ }
+
+ [ComImport,
+ Guid("4688A074-5A4D-4486-AEA8-7B90711D9F7C"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
+ System.Security.SuppressUnmanagedCodeSecurity]
+ private interface IDiaLoadCallback2 : IDiaLoadCallback
+ {
+ new void NotifyDebugDir(int fExecutable, uint cbData, [In] ref IMAGE_DEBUG_DIRECTORY pbData);
+
+ new void NotifyOpenDBG([In, MarshalAs(UnmanagedType.LPWStr)] string dbgPath, int resultCode);
+
+ new void NotifyOpenPDB([In, MarshalAs(UnmanagedType.LPWStr)] string pdbPath, int resultCode);
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol path lookup from registry
+ new int RestrictRegistryAccess();
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol server lookup
+ new int RestrictSymbolServerAccess();
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol path lookup from registry
+ int RestrictDBGAccess();
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol path lookup from registry
+ int RestrictOriginalPathAccess();
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol path lookup from registry
+ int RestrictReferencePathAccess();
+
+ [PreserveSig]
+ // return S_OK (0) to allow symbol lookup in the system directory
+ int RestrictSystemRootAccess();
+ }
+
+ // DIA specific HResults from dia2.idl, for providing better diagnostics messages
+ private enum DiaHResults : int
+ {
+ E_PDB_OK = unchecked((int)0x806d0001),
+ E_PDB_USAGE ,
+ E_PDB_OUT_OF_MEMORY , // not used, use E_OUTOFMEMORY
+ E_PDB_FILE_SYSTEM ,
+ E_PDB_NOT_FOUND ,
+ E_PDB_INVALID_SIG ,
+ E_PDB_INVALID_AGE ,
+ E_PDB_PRECOMP_REQUIRED ,
+ E_PDB_OUT_OF_TI ,
+ E_PDB_NOT_IMPLEMENTED , // use E_NOTIMPL
+ E_PDB_V1_PDB ,
+ E_PDB_FORMAT ,
+ E_PDB_LIMIT ,
+ E_PDB_CORRUPT ,
+ E_PDB_TI16 ,
+ E_PDB_ACCESS_DENIED , // use E_ACCESSDENIED
+ E_PDB_ILLEGAL_TYPE_EDIT ,
+ E_PDB_INVALID_EXECUTABLE ,
+ E_PDB_DBG_NOT_FOUND ,
+ E_PDB_NO_DEBUG_INFO ,
+ E_PDB_INVALID_EXE_TIMESTAMP ,
+ E_PDB_RESERVED ,
+ E_PDB_DEBUG_INFO_NOT_IN_PDB ,
+ E_PDB_SYMSRV_BAD_CACHE_PATH ,
+ E_PDB_SYMSRV_CACHE_FULL ,
+ E_PDB_MAX
+ }
+}
+
+/***************************************************************************************
+ *
+ ***************************************************************************************/
+
+public class DiaSymbol
+{
+ protected IDiaSymbol m_symbol;
+
+ public DiaSymbol(IDiaSymbol symbol)
+ {
+ if (symbol == null)
+ throw new ArgumentNullException();
+
+ m_symbol = symbol;
+ }
+
+ public DiaSymbol FindSymbol(String name, SymTagEnum tag)
+ {
+ DiaSymbol res = null;
+ IDiaSymbol sym = Util.FindSymbol(name, m_symbol, tag);
+
+ if (sym != null)
+ {
+ if ((SymTagEnum)sym.symTag == SymTagEnum.SymTagData)
+ res = new DiaDataSymbol(sym);
+ else
+ res = new DiaSymbol(sym);
+ }
+
+ return res;
+ }
+
+ public DiaSymbol FindSymbol(String name)
+ {
+ return FindSymbol(name, SymTagEnum.SymTagNull);
+ }
+
+ public DiaSymbol FindClassSymbol(String name, SymTagEnum tag)
+ {
+ DiaSymbol res = null;
+ IDiaSymbol sym = Util.FindClassSymbol(name, m_symbol, tag);
+
+ if (sym != null)
+ {
+ if ((SymTagEnum)sym.symTag == SymTagEnum.SymTagData)
+ res = new DiaDataSymbol(sym);
+ else
+ res = new DiaSymbol(sym);
+ }
+
+ return res;
+ }
+
+ public DiaSymbol FindClassSymbol(String name)
+ {
+ return FindClassSymbol(name, SymTagEnum.SymTagNull);
+ }
+
+ public DiaSymbol FindUDTSymbol(String name)
+ {
+ DiaSymbol res = null;
+ IDiaSymbol sym = Util.FindSymbol(name, m_symbol, SymTagEnum.SymTagUDT);
+
+ if (sym == null)
+ {
+ sym = Util.FindSymbol(name, m_symbol, SymTagEnum.SymTagTypedef);
+
+ if (sym != null)
+ sym = sym.type;
+ }
+
+ if (sym != null)
+ res = new DiaSymbol(sym);
+
+ return res;
+ }
+
+ private String GetVariantString(Object o)
+ {
+ /*
+ switch( v.vt )
+ {
+ //* LONGLONG VT_I8
+ case VT_I8:
+ printf( "%ld", v.llVal );
+ break;
+ //* LONG VT_I4
+ case VT_I4:
+ printf( "%d", v.lVal );
+ break;
+ //* BYTE VT_UI1
+ case VT_UI1:
+ printf( "%d", v.bVal);
+ break;
+ //* SHORT VT_I2
+ case VT_I2:
+ printf( "%d", v.iVal);
+ break;
+ //* CHAR VT_I1
+ case VT_I1:
+ printf( "%d", v.cVal);
+ break;
+ //* USHORT VT_UI2
+ case VT_UI2:
+ printf( "%d", v.uiVal);
+ break;
+ //* ULONG VT_UI4
+ case VT_UI4:
+ printf( "%d", v.ulVal);
+ break;
+ //* ULONGLONG VT_UI8
+ case VT_UI8:
+ printf( "%ld", v.ullVal);
+ break;
+ //* INT VT_INT
+ case VT_INT:
+ printf( "%d", v.intVal);
+ break;
+ //* UINT VT_UINT
+ case VT_UINT:
+ printf( "%d", v.uintVal);
+ break;
+ default:
+ printf( "<Not implemented>" );
+ break;
+ }
+ */
+ return "VARIANT";
+ }
+
+ private String GetBoundString(IDiaSymbol bound)
+ {
+ SymTagEnum tag = (SymTagEnum) bound.symTag;
+ LocationType kind = (LocationType) bound.locationType;
+
+ System.Diagnostics.Debugger.Break();
+ if (tag == SymTagEnum.SymTagData && kind == LocationType.LocIsConstant)
+ {
+ return GetVariantString(bound.value);
+ }
+
+ return bound.name;
+ }
+
+ public String GetTypeString()
+ {
+ return GetTypeString(m_symbol);
+ }
+
+ private String GetTypeString(IDiaSymbol s)
+ {
+ SymTagEnum tag = (SymTagEnum) s.symTag;
+
+ if (tag == SymTagEnum.SymTagData || tag == SymTagEnum.SymTagTypedef)
+ {
+ s = s.type;
+ tag = (SymTagEnum) s.symTag;
+ }
+
+ StringBuilder str = new StringBuilder();
+
+ if (s.name != null)
+ {
+ str.Append(s.name);
+ }
+ else if (tag == SymTagEnum.SymTagPointerType)
+ {
+ str.Append(GetTypeString(s.type));
+ str.Append("*");
+ }
+ else if (tag == SymTagEnum.SymTagBaseType)
+ {
+ BasicType bt = (BasicType) s.baseType;
+ str.AppendFormat("(base type={0}, len={1:d})", bt.ToString(), s.length);
+ }
+ else if (tag == SymTagEnum.SymTagArrayType)
+ {
+ str.Append(GetTypeString(s.type));
+
+ bool succ = true;
+ int i;
+
+ try
+ {
+ UInt32 rank = s.rank;
+
+ IDiaEnumSymbols e;
+ s.findChildren(SymTagEnum.SymTagDimension, null, (UInt32) NameSearchOptions.nsNone, out e);
+
+ for (i = 0; i < e.count; i++)
+ {
+ IDiaSymbol ds;
+ UInt32 celt;
+ e.Next(1, out ds, out celt);
+
+ str.Append("[" + GetBoundString(ds.lowerBound) + ".." + GetBoundString(ds.upperBound) + "]");
+ }
+ }
+ catch (Exception)
+ {
+ succ = false;
+ }
+
+ if (succ == false)
+ {
+ try
+ {
+ succ = true;
+ IDiaEnumSymbols e;
+ s.findChildren(SymTagEnum.SymTagCustomType, null, (UInt32) NameSearchOptions.nsNone, out e);
+
+ for (i = 0; i < e.count; i++)
+ {
+ IDiaSymbol ds;
+ UInt32 celt;
+ e.Next(1, out ds, out celt);
+
+ str.Append("[" + GetTypeString(ds) + "]");
+ }
+ }
+ catch (Exception)
+ {
+ succ = false;
+ }
+ }
+
+ if (succ == false)
+ {
+ try
+ {
+ succ = true;
+ str.AppendFormat("[{0:d}]", s.length/s.type.length );
+ }
+ catch (Exception)
+ {
+ succ = false;
+ }
+ }
+ }
+ else if (tag == SymTagEnum.SymTagFunctionType)
+ {
+ str.Append("Function Type");
+ }
+ else if (tag == SymTagEnum.SymTagCustomType)
+ {
+ throw new Exception("NYI");
+ /*
+ str.Append("Custom Type: ");
+ try
+ {
+ str.Append(s.guid.ToString());
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ str.AppendFormat("{0:x}:{0:x}", s.oemId, s.oemSymbolId);
+ }
+ catch (Exception)
+ {
+ }
+ }
+ DWORD len = 0;
+ if ( s.get_types( 0, &len, NULL ) == S_OK && len > 0 ) {
+ IDiaSymbol** psyms = new IDiaSymbol*[ len ];
+ s.get_types( len, &len, psyms );
+ for ( DWORD i = 0; i < len; ++i ) {
+ printf( " <" );
+ printType( psyms[i] );
+ printf( ">" );
+ psyms[i]->Release();
+ }
+ delete [] psyms;
+ }
+ len = 0;
+ if ( s.get_dataBytes( 0, &len, NULL ) == S_OK && len > 0 ) {
+ BYTE* pdata = new BYTE[ len ];
+ s.get_dataBytes( len, &len, pdata );
+ printf( "<data" );
+ for ( DWORD i = 0; i < len; ++i ) {
+ printf( " %02x", pdata[i] );
+ }
+ printf( " data>" );
+ delete [] pdata;
+ }
+ */
+ } else {
+ str.Append( "No Type.");
+ }
+
+ return str.ToString();
+ }
+
+ public string Name
+ {
+ get
+ { return m_symbol.name; }
+ }
+
+ public UInt64 Address
+ {
+ get
+ { return m_symbol.virtualAddress; }
+ }
+
+ public UInt32 Offset
+ {
+ get
+ { return (UInt32) m_symbol.offset; }
+ }
+
+ public UInt64 Size
+ {
+ get
+ {
+ IDiaSymbol symbol = m_symbol;
+ UInt64 size = symbol.length;
+ if (size != 0)
+ return size;
+
+ SymTagEnum tag = (SymTagEnum) symbol.symTag;
+
+ if (tag == SymTagEnum.SymTagData || tag == SymTagEnum.SymTagTypedef)
+ {
+ symbol = symbol.type;
+ tag = (SymTagEnum) symbol.symTag;
+
+ size = symbol.length;
+ if (size != 0)
+ return size;
+ }
+
+ if (tag == SymTagEnum.SymTagPointerType)
+ {
+ // @TODO: find a way of determining native word size
+ return 4;
+ }
+ else
+ {
+ throw new Exception("Unknown length.");
+ }
+ }
+ }
+
+ public IDiaSymbol IDiaSymbol
+ {
+ get
+ { return m_symbol; }
+ }
+}
+
+public class DiaDataSymbol : DiaSymbol
+{
+ public DiaDataSymbol(IDiaSymbol symbol) : base(symbol)
+ {
+ if ((SymTagEnum)symbol.symTag != SymTagEnum.SymTagData)
+ throw new Exception("Not a data symbol.");
+ }
+
+ public DataKind DataKind
+ {
+ get
+ { return ((DataKind) m_symbol.dataKind); }
+ }
+
+ public bool IsGlobal
+ {
+ get
+ { return (DataKind == DataKind.DataIsGlobal); }
+ }
+
+ public bool IsFileStatic
+ {
+ get
+ { return (DataKind == DataKind.DataIsFileStatic); }
+ }
+
+ public bool IsMember
+ {
+ get
+ { return (DataKind == DataKind.DataIsMember); }
+ }
+
+ public bool IsStaticMember
+ {
+ get
+ { return (DataKind == DataKind.DataIsStaticMember); }
+ }
+}
+
+} // Namespace Dia.Util
diff --git a/src/ToolBox/SOS/DacTableGen/main.cs b/src/ToolBox/SOS/DacTableGen/main.cs
new file mode 100644
index 0000000000..03d1bfde89
--- /dev/null
+++ b/src/ToolBox/SOS/DacTableGen/main.cs
@@ -0,0 +1,656 @@
+// 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.IO;
+using System.Collections;
+using System.Collections.Generic;
+
+#if !FEATURE_PAL
+using Dia;
+using Dia.Util;
+#endif // !FEATURE_PAL
+using System.Globalization;
+
+/******************************************************************************
+ *
+ *****************************************************************************/
+
+public abstract class SymbolProvider
+{
+ public enum SymType
+ {
+ GlobalData,
+ GlobalFunction,
+ };
+
+ public abstract UInt32 GetGlobalRVA(String symbolName,
+ SymType symType);
+ public abstract UInt32 GetVTableRVA(String symbolName,
+ String keyBaseName);
+}
+
+#if !FEATURE_PAL
+public class PdbSymbolProvider : SymbolProvider
+{
+ public PdbSymbolProvider(String symbolFilename, String dllFilename)
+ {
+ fPDB = new FileInfo(symbolFilename);
+ df = new DiaFile(fPDB.FullName, dllFilename);
+ }
+
+ public UInt32 DebugTimestamp
+ {
+ get { return df.DebugTimestamp; }
+ }
+
+ public string LoadedPdbPath
+ {
+ get { return df.LoadedPdbPath; }
+ }
+
+ private UInt32 GetSymbolRva(DiaSymbol sy,
+ String symbolName,
+ String typeName)
+ {
+ if (sy == null)
+ {
+ // Ideally this would throw an exception and
+ // cause the whole process to fail but
+ // currently it's too complicated to get
+ // all the ifdef'ing right for all the
+ // mix of debug/checked/free multiplied by
+ // x86/AMD64/IA64/etc.
+ return UInt32.MaxValue;
+ }
+ if (sy.Address > UInt32.MaxValue)
+ {
+ throw new InvalidOperationException(typeName +
+ " symbol " +
+ symbolName +
+ " overflows UInt32");
+ }
+
+ return (UInt32)sy.Address;
+ }
+
+ private DiaSymbol GetValidPublicSymbolEntry(String name)
+ {
+ IDiaEnumSymbols e = df.FindPublicSymbols(name);
+
+ if (e.count != 1)
+ {
+ return null;
+ }
+ else
+ {
+ IDiaSymbol s;
+ UInt32 celt;
+
+ e.Next(1, out s, out celt);
+ return new DiaSymbol(s);
+ }
+ }
+
+ public override UInt32 GetGlobalRVA(String symbolName,
+ SymType symType)
+ {
+ DiaSymbol sy = df.GlobalSymbol.FindSymbol(symbolName);
+ if (sy == null && symType == SymType.GlobalFunction)
+ {
+ // Try looking for the symbol in public symbols,
+ // as assembly routines do not have normal
+ // global symbol table entries. We don't know
+ // how many parameters to use, so just guess
+ // at a few sizes.
+ for (int i = 0; i <= 16; i += 4)
+ {
+ // Non-fastcall.
+ sy = GetValidPublicSymbolEntry("_" + symbolName + "@" + i);
+ if (sy != null)
+ {
+ break;
+ }
+ // Fastcall.
+ sy = GetValidPublicSymbolEntry("@" + symbolName + "@" + i);
+ if (sy != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return GetSymbolRva(sy, symbolName, "Symbol");
+ }
+
+ public override UInt32 GetVTableRVA(String symbolName,
+ String keyBaseName)
+ {
+ String mangledName;
+
+ // Single-inheritance vtable symbols have different
+ // mangling from multiple-inheritance so form the
+ // proper name based on which case this is.
+ mangledName = "??_7" + symbolName + "@@6B";
+ if (keyBaseName != null)
+ {
+ mangledName += keyBaseName + "@@@";
+ }
+ else
+ {
+ mangledName += "@";
+ }
+
+ return GetSymbolRva(GetValidPublicSymbolEntry(mangledName),
+ symbolName, "VTable");
+ }
+
+ FileInfo fPDB = null;
+ DiaFile df = null;
+}
+#endif // !FEATURE_PAL
+
+public class Shell
+{
+ const String dacSwitch = "/dac:";
+ const String pdbSwitch = "/pdb:";
+ const String mapSwitch = "/map:";
+ const String binSwitch = "/bin:";
+ const String dllSwitch = "/dll:";
+ const String ignoreErrorsSwitch = "/ignoreerrors";
+
+ public static void Help()
+ {
+ HelpHdr();
+ Console.WriteLine();
+ HelpBody();
+ }
+
+ public static void HelpHdr()
+ {
+String helpHdr =
+
+////////////
+@"Microsoft (R) CLR External Data Access Data Table Generator Version 0.3
+Copyright (C) Microsoft Corp. All rights reserved.";
+////////////
+
+ Console.WriteLine(helpHdr);
+ }
+
+ public static void HelpBody()
+ {
+
+String helpMsg =
+
+////////////
+@"Usage:
+ DacTableGen /dac:<file> [/pdb:<file> /dll:<file>] [/map:<file>] /bin:<file> [/ignoreerrors]
+
+Required:
+ /dac: The data access header file containing items to be added.
+ /pdb: The PDB file from which to get details.
+ /map: The MAP file from which to get details.
+ In Windows, this file is created by providing /MAP in link.exe.
+ In UNIX, this file is created by the nm utility.
+ OBSOLETE - Use DacTableGen.pl instead for UNIX systems
+ /dll: The DLL which matches the specified PDB or MAP file.
+ /bin: The binary output file.
+ /ignoreerrors: Turn errors into warnings. The produced binary may hit
+ runtime failures
+";
+////////////
+
+ Console.WriteLine(helpMsg);
+ }
+
+ public static bool MatchArg(String arg, String cmd)
+ {
+ if (arg.Length >= cmd.Length &&
+ arg.Substring(0, cmd.Length).ToLower(CultureInfo.InvariantCulture).Equals(cmd.ToLower(CultureInfo.InvariantCulture)))
+ return true;
+
+ return false;
+ }
+
+ public static int DoMain(String[] args)
+ {
+ String dacFile = null;
+ String pdbFile = null;
+ String mapFile = null;
+ String binFile = null;
+ String dllFile = null;
+
+ for (int i = 0; i < args.Length; i++)
+ {
+ if (MatchArg(args[i], dacSwitch))
+ {
+ dacFile = args[i].Substring(dacSwitch.Length);
+ }
+ else if (MatchArg(args[i], pdbSwitch))
+ {
+ pdbFile = args[i].Substring(pdbSwitch.Length);
+ }
+ else if (MatchArg(args[i], mapSwitch))
+ {
+ mapFile = args[i].Substring(mapSwitch.Length);
+ }
+ else if (MatchArg(args[i], binSwitch))
+ {
+ binFile = args[i].Substring(binSwitch.Length);
+ }
+ else if (MatchArg(args[i], dllSwitch))
+ {
+ dllFile = args[i].Substring(dllSwitch.Length);
+ }
+ else if (MatchArg(args[i], ignoreErrorsSwitch))
+ {
+ s_ignoreErrors = true;
+ }
+ else
+ {
+ Help();
+ return 1;
+ }
+ }
+
+ if (dacFile == null ||
+ (pdbFile == null && mapFile == null) ||
+ (dllFile == null && pdbFile != null) ||
+ binFile == null)
+ {
+ HelpHdr();
+ Console.WriteLine();
+ Console.WriteLine("Required option missing.");
+ // Provide some extra help if just the new dllFile option is missing
+ if ((dllFile == null) && (dacFile != null) && (binFile != null) && (pdbFile != null))
+ {
+ Console.WriteLine("NOTE that /dll is a new required argument which must point to mscorwks.dll.");
+ Console.WriteLine("Ideally all uses of DacTableGen.exe should use the build logic in ndp/clr/src/DacUpdateDll.");
+ }
+ Console.WriteLine();
+ HelpBody();
+
+ return 1;
+ }
+
+ // Validate the specified files exist
+ string[] inputFiles = new string[] { dacFile, pdbFile, mapFile, dllFile };
+ foreach (string file in inputFiles)
+ {
+ if (file != null && !File.Exists(file))
+ {
+ Console.WriteLine("ERROR, file does not exist: " + file);
+ return 1;
+ }
+ }
+
+ HelpHdr();
+ Console.WriteLine();
+
+ List<UInt32> rvaArray = new List<UInt32>();
+ UInt32 numGlobals;
+ UInt32 debugTimestamp = 0;
+
+ if (pdbFile != null)
+ {
+#if FEATURE_PAL
+ throw new InvalidOperationException("Not supported in Rotor.");
+#else
+ PdbSymbolProvider pdbSymProvider = new PdbSymbolProvider(pdbFile, dllFile);
+
+ // Read the mscorwks debug directory timestamp
+ debugTimestamp = pdbSymProvider.DebugTimestamp;
+ if (debugTimestamp == 0)
+ {
+ throw new System.ApplicationException("Didn't get debug directory timestamp from DIA");
+ }
+ DateTime dt = new DateTime(1970, 01, 01, 0, 0, 0, DateTimeKind.Utc);
+ dt = dt.AddSeconds(debugTimestamp).ToLocalTime();
+
+ // Output information about the PDB loaded
+ Console.WriteLine("Processing DLL with PDB timestamp: {0}", dt.ToString("F"));
+ Console.WriteLine("Loaded PDB file: " + pdbSymProvider.LoadedPdbPath);
+ if (Path.GetFullPath(pdbSymProvider.LoadedPdbPath).ToLowerInvariant() != Path.GetFullPath(pdbFile).ToLowerInvariant())
+ {
+ // DIA loaded a PDB oter than the one the user asked for. This could possibly happen if the PDB
+ // also exists in a sub-directory that DIA automatically probes for ("retail" etc.). There doesn't
+ // appear to be any mechanism for turning this sub-directory probing off, but all other searching mechanisms
+ // should be turned off by the DiaLoadCallback. This could also happen if the user specified an incorrect
+ // (but still existing) filename in a path containing the real PDB. Since DIA loaded it, it must match the DLL,
+ // and so should only be an exact copy of the requested PDB (if the requested PDB actuall matches the DLL). So
+ // go ahead and use it anyway with a warning. To be less confusing, we could update the command-line syntax
+ // to take a PDB search path instead of a filename, but that inconsistent with the map path, and probably not
+ // worth changing semantics for. In practice this warning will probably never be hit.
+ Shell.Error("Loaded PDB path differs from requested path: " + pdbFile);
+ }
+ Console.WriteLine();
+
+ ScanDacFile(dacFile,
+ pdbSymProvider,
+ rvaArray,
+ out numGlobals);
+
+ if (mapFile != null)
+ {
+ List<UInt32> mapRvaArray = new List<UInt32>();
+ UInt32 mapNumGlobals;
+
+ // check that both map file and pdb file produce same output to avoid breakages on Rotor
+ ScanDacFile(dacFile,
+ new MapSymbolProvider(mapFile),
+ mapRvaArray,
+ out mapNumGlobals);
+
+ // Produce a nice message to include with any errors. For some reason, binplace will silently fail
+ // when a PDB can't be updated due to file locking. This means that problems of this nature usually
+ // occur when mscorwks.pdb was locked when mscorwks.dll was last rebuilt.
+ string diagMsg = String.Format(". This is usually caused by mscorwks.pdb and mscorwks.map being out of sync. " +
+ "Was {0} (last modified {1}) in-use and locked when {2} was built (last modified {3})? " +
+ "Both should have been created when {4} was last rebuilt (last modified {5}).",
+ pdbFile, File.GetLastWriteTime(pdbFile),
+ mapFile, File.GetLastWriteTime(mapFile),
+ dllFile, File.GetLastWriteTime(dllFile));
+
+ if (rvaArray.Count != mapRvaArray.Count)
+ throw new InvalidOperationException("Number of RVAs differes between pdb file and map file: " +
+ numGlobals + " " + mapNumGlobals + diagMsg);
+
+ for (int i = 0; i < rvaArray.Count; i++)
+ {
+ if (rvaArray[i] != mapRvaArray[i]
+ // it is ok if we find more stuff in the MAP file
+ && rvaArray[i] != UInt32.MaxValue)
+ {
+ throw new InvalidOperationException("RVAs differ between pdb file and map file: " +
+ ToHexNB(rvaArray[i]) + " " + ToHexNB(mapRvaArray[i]) + diagMsg);
+ }
+ }
+
+ if (numGlobals != mapNumGlobals)
+ throw new InvalidOperationException("Number of globals differes between pdb file and map file: " +
+ numGlobals + " " + mapNumGlobals + diagMsg);
+ }
+#endif
+ }
+ else
+ {
+ ScanDacFile(dacFile,
+ new MapSymbolProvider(mapFile),
+ rvaArray,
+ out numGlobals);
+ }
+
+ if (s_errors && !s_ignoreErrors)
+ {
+ Console.Error.WriteLine(
+ "DacTableGen : fatal error : Failing due to above validation errors. " +
+ "Do you have an #ifdef (or name) mismatch between the symbol definition and the entry specified? " +
+ "Or perhaps the symbol referenced was optimized away as unused? " +
+ "If you're stuck, send e-mail to 'ClrDac'. Worst case, these errors can be temporarily ignored by passing the /ignoreerrors switch - but you may cause runtime failures instead.");
+ return 1;
+ }
+
+ UInt32 numVptrs;
+ numVptrs = (UInt32)rvaArray.Count - numGlobals;
+
+ FileStream outFile = new FileStream(binFile, FileMode.Create,
+ FileAccess.Write);
+ BinaryWriter binWrite = new BinaryWriter(outFile);
+
+ // Write header information
+ binWrite.Write(numGlobals);
+ binWrite.Write(numVptrs);
+ binWrite.Write(debugTimestamp);
+ binWrite.Write(0); // On Windows we only need a 4-byte timestamp, but on Mac we use
+ binWrite.Write(0); // a 16-byte UUID. We need to be consistent here.
+ binWrite.Write(0);
+
+ // Write out the table of RVAs
+ for (int i = 0; i < numGlobals + numVptrs; i++)
+ {
+ binWrite.Write(rvaArray[i]);
+ }
+
+ binWrite.Close();
+ return 0;
+ }
+
+ public static int Main(string[] args)
+ {
+ // Don't catch exceptions if a debugger is attached - makes debugging easier
+ if (System.Diagnostics.Debugger.IsAttached)
+ {
+ return DoMain(args);
+ }
+
+ int exitCode;
+ try
+ {
+ exitCode = DoMain(args);
+ }
+ catch(Exception e)
+ {
+ Console.WriteLine("BUILDMSG: " + e.ToString());
+ exitCode = 1;
+ }
+ return exitCode;
+ }
+
+ private static void ScanDacFile(String file,
+ SymbolProvider sf,
+ List<UInt32> rvaArray,
+ out UInt32 numGlobals)
+ {
+ StreamReader strm =
+ new StreamReader(file, System.Text.Encoding.ASCII);
+ String line;
+ Hashtable vtables = new Hashtable(); // hashtable to guarantee uniqueness of entries
+
+ //
+ // Scan through the data access header file looking
+ // for the globals structure.
+ //
+
+ for (;;)
+ {
+ line = strm.ReadLine();
+ if (line == null)
+ {
+ throw new
+ InvalidOperationException("Invalid dac header format");
+ }
+ else if (line == "typedef struct _DacGlobals")
+ {
+ break;
+ }
+ }
+
+ if (strm.ReadLine() != "{")
+ {
+ throw new InvalidOperationException("Invalid dac header format");
+ }
+
+ //
+ // All the globals come first so pick up each line that
+ // begins with ULONG.
+ //
+
+ bool fFoundVptrs = false;
+ numGlobals = 0;
+
+ for (;;)
+ {
+ line = strm.ReadLine().Trim();
+
+ if ( line.Equals("union {")
+ || line.Equals("struct {")
+ || line.Equals("};")
+ || line.StartsWith("#line ")
+ || line.StartsWith("# "))
+ {
+ // Ignore.
+ }
+ else if (line.StartsWith("ULONG "))
+ {
+ UInt32 rva = 0;
+
+ line = line.Remove(0, 6);
+ line = line.TrimEnd(";".ToCharArray());
+
+ string vptrSuffixSingle = "__vtAddr";
+ string vptrSuffixMulti = "__mvtAddr";
+ string vptrSuffix = null;
+
+ if (line.EndsWith(vptrSuffixSingle))
+ {
+ vptrSuffix = vptrSuffixSingle;
+ }
+ else if (line.EndsWith(vptrSuffixMulti))
+ {
+ vptrSuffix = vptrSuffixMulti;
+ }
+
+ if (vptrSuffix != null)
+ {
+ if (!fFoundVptrs)
+ {
+ numGlobals = (UInt32)rvaArray.Count;
+ fFoundVptrs = true;
+ }
+
+ line = line.Remove(line.Length - vptrSuffix.Length,
+ vptrSuffix.Length);
+
+ string keyBaseName = null;
+ string descTail = null;
+
+ if (vptrSuffix == vptrSuffixMulti)
+ {
+ // line now has the form <class>__<base>, so
+ // split off the base.
+ int basePrefix = line.LastIndexOf("__");
+ if (basePrefix < 0)
+ {
+ throw new InvalidOperationException("VPTR_MULTI_CLASS has no keyBase.");
+ }
+ keyBaseName = line.Substring(basePrefix + 2);
+ line = line.Remove(basePrefix);
+ descTail = " for " + keyBaseName;
+ }
+
+ rva = sf.GetVTableRVA(line, keyBaseName);
+
+ if (rva == UInt32.MaxValue)
+ {
+ Console.WriteLine(" " + ToHexNB(rva));
+ Shell.Error("Invalid vtable " + line + descTail);
+ }
+ else
+ {
+ String existing = (String)vtables[rva];
+ if (existing != null)
+ {
+ throw new InvalidOperationException(existing + " and " + line + " are at the same offsets." +
+ " Add VPTR_UNIQUE(<a random unique number here>) to the offending classes to make their vtables unique.");
+ }
+ vtables[rva] = line;
+
+ Console.WriteLine(" " + ToHexNB(rva) +
+ ", // vtable " + line + descTail);
+ }
+ }
+ else
+ {
+ SymbolProvider.SymType symType;
+
+ if (fFoundVptrs)
+ throw new InvalidOperationException("Invalid dac header format. Vtable pointers must be last.");
+
+ if (line.StartsWith("dac__"))
+ {
+ // Global variables, use the prefix.
+ line = line.Remove(0, 5);
+ symType = SymbolProvider.SymType.GlobalData;
+ }
+ else if (line.StartsWith("fn__"))
+ {
+ // Global or static functions, use the prefix.
+ line = line.Remove(0, 4);
+ line = line.Replace("__", "::");
+ symType = SymbolProvider.SymType.GlobalFunction;
+ }
+ else
+ {
+ // Static member variable, use the full name with
+ // namespace replacement.
+ line = line.Replace("__", "::");
+ symType = SymbolProvider.SymType.GlobalData;
+ }
+
+ if (0 == rva)
+ {
+ rva = sf.GetGlobalRVA(line, symType);
+
+ if (rva == UInt32.MaxValue)
+ {
+ Console.WriteLine(" " + ToHexNB(rva));
+ Shell.Error("Invalid symbol " + line);
+ }
+ else
+ {
+ Console.WriteLine(" " + ToHexNB(rva) + ", // " +
+ line);
+ }
+ }
+ }
+
+ rvaArray.Add(rva);
+
+ }
+ else if (line == "")
+ {
+ // Skip blanks.
+ }
+ else
+ {
+ // We hit a non-global so we're done.
+ if (!line.Equals("} DacGlobals;"))
+ {
+ throw new
+ InvalidOperationException("Invalid dac header format at \"" + line + "\"");
+ }
+ break;
+ }
+ }
+
+ if (!fFoundVptrs)
+ throw new InvalidOperationException("Invalid dac header format. Vtable pointers not found.");
+ }
+
+ private static String ToHex(Object o)
+ {
+ if (o is UInt32 || o is Int32)
+ return String.Format("0x{0:x8}", o);
+ else if (o is UInt64 || o is Int64)
+ return String.Format("0x{0:x16}", o);
+ else
+ return null;
+ }
+
+ private static String ToHexNB(Object o)
+ {
+ return String.Format("0x{0:x}", o);
+ }
+
+ public static void Error(string message)
+ {
+ Console.Error.WriteLine((s_ignoreErrors ? "WARNING: " : "ERROR: ") + message);
+ s_errors = true;
+ }
+
+ // Try to tolerate errors (as we've always done in the past), which may result in failures at run-time instead.
+ private static bool s_ignoreErrors = false;
+ private static bool s_errors = false;
+}
diff --git a/src/ToolBox/SOS/NETCore/.gitmirror b/src/ToolBox/SOS/NETCore/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/NETCore/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj b/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj
new file mode 100644
index 0000000000..bd9d2395f8
--- /dev/null
+++ b/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+
+ <PropertyGroup>
+ <AssemblyName>SOS.NETCore</AssemblyName>
+ <AssemblyVersion>1.0.0.0</AssemblyVersion>
+ <ProjectGuid>{20513BA2-A156-4A17-4C70-5AC2DBD4F833}</ProjectGuid>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <OutputType>Library</OutputType>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <NoStdLib>true</NoStdLib>
+ <NoCompilerStandardLib>true</NoCompilerStandardLib>
+ <UseOpenKey Condition="'$(UseOpenKey)'==''">true</UseOpenKey>
+ </PropertyGroup>
+
+ <!-- Default configurations to help VS understand the options -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+
+ <!-- Configuration specific properties -->
+ <PropertyGroup Condition="'$(Configuration)' == 'Debug' or '$(Configuration)' == 'Checked'">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <DefineConstants>_DEBUG;DEBUG;TRACE;$(DefineConstants)</DefineConstants>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(OsEnvironment)' == 'Unix'">
+ <DebugSymbols>false</DebugSymbols>
+ <DebugType>none</DebugType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="SymbolReader.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Include="project.json" />
+ </ItemGroup>
+
+ <Target Name="CopyItemsToDirectory" AfterTargets="Build">
+ <Copy
+ SourceFiles="$(OutputPath)$(AssemblyName).dll"
+ DestinationFolder="$(BinDir)"
+ SkipUnchangedFiles="false"
+ OverwriteReadOnlyFiles="false"
+ UseHardlinksIfPossible="false">
+ </Copy>
+
+ <Copy
+ Condition="'$(OsEnvironment)' != 'Unix'"
+ SourceFiles="$(OutputPath)$(AssemblyName).pdb"
+ DestinationFolder="$(BinDir)\PDB"
+ SkipUnchangedFiles="false"
+ OverwriteReadOnlyFiles="false"
+ UseHardlinksIfPossible="false">
+ </Copy>
+ </Target>
+
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/ToolBox/SOS/NETCore/SOS.NETCore.sln b/src/ToolBox/SOS/NETCore/SOS.NETCore.sln
new file mode 100644
index 0000000000..0106883b9c
--- /dev/null
+++ b/src/ToolBox/SOS/NETCore/SOS.NETCore.sln
@@ -0,0 +1,22 @@
+
+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/ToolBox/SOS/NETCore/SymbolReader.cs b/src/ToolBox/SOS/NETCore/SymbolReader.cs
new file mode 100644
index 0000000000..f4a3ce30d8
--- /dev/null
+++ b/src/ToolBox/SOS/NETCore/SymbolReader.cs
@@ -0,0 +1,694 @@
+// 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)]
+ internal struct MethodDebugInfo
+ {
+ public IntPtr points;
+ public int size;
+ }
+
+ /// <summary>
+ /// Read memory callback
+ /// </summary>
+ /// <returns>number of bytes read or 0 for error</returns>
+ internal unsafe delegate int ReadMemoryDelegate(IntPtr 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 IntPtr _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(IntPtr 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(new IntPtr(_address.ToInt64() + 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>
+ /// 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, IntPtr loadedPeAddress, int loadedPeSize,
+ IntPtr inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory)
+ {
+ try
+ {
+ TargetStream peStream = null;
+ if (assemblyPath == null && loadedPeAddress != IntPtr.Zero)
+ {
+ peStream = new TargetStream(loadedPeAddress, loadedPeSize, readMemory);
+ }
+ TargetStream pdbStream = null;
+ if (inMemoryPdbAddress != IntPtr.Zero)
+ {
+ 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 = Path.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 && Path.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();
+ 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;
+ }
+
+ /// <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;
+
+ if (!GetDebugInfoForMethod(assemblyPath, methodToken, out points))
+ {
+ 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);
+ }
+ 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)
+ {
+ if (point.StartLine == 0 || point.StartLine == SequencePoint.HiddenLine)
+ continue;
+
+ 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), Path.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/ToolBox/SOS/NETCore/project.json b/src/ToolBox/SOS/NETCore/project.json
new file mode 100644
index 0000000000..e9e4f0999f
--- /dev/null
+++ b/src/ToolBox/SOS/NETCore/project.json
@@ -0,0 +1,20 @@
+{
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.IO.FileSystem": "4.0.1",
+ "System.Runtime.InteropServices": "4.1.0",
+ "System.Reflection.Metadata": "1.4.1-beta-24417-02"
+ },
+ "frameworks": {
+ "netcoreapp1.0": {}
+ },
+ "runtimes": {
+ "win7-x86": {},
+ "win7-x64": {},
+ "ubuntu.14.04-x64": {},
+ "osx.10.10-x64": {},
+ "centos.7-x64": {},
+ "rhel.7-x64": {},
+ "debian.8-x64": {}
+ }
+}
diff --git a/src/ToolBox/SOS/SOSAndICorDebug.md b/src/ToolBox/SOS/SOSAndICorDebug.md
new file mode 100644
index 0000000000..725d01881c
--- /dev/null
+++ b/src/ToolBox/SOS/SOSAndICorDebug.md
@@ -0,0 +1,81 @@
+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 sucks.
+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/ToolBox/SOS/Strike/.gitmirror b/src/ToolBox/SOS/Strike/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/ApolloNative.rc b/src/ToolBox/SOS/Strike/ApolloNative.rc
new file mode 100644
index 0000000000..ba320af911
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ApolloNative.rc
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft NTSD extension for .NET Runtime\0"
+
+#include <fxver.h>
+#include <fxver.rc>
+
+DOCUMENTATION TEXT DISCARDABLE "apollososdocs.txt"
diff --git a/src/ToolBox/SOS/Strike/CMakeLists.txt b/src/ToolBox/SOS/Strike/CMakeLists.txt
new file mode 100644
index 0000000000..5d25865780
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/CMakeLists.txt
@@ -0,0 +1,201 @@
+# Set the RPATH of sos so that it can find dependencies without needing to set LD_LIBRARY_PATH
+# For more information: http://www.cmake.org/Wiki/CMake_RPATH_handling.
+if (CORECLR_SET_RPATH)
+ set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+ if(CLR_CMAKE_PLATFORM_DARWIN)
+ set(CMAKE_INSTALL_RPATH "@loader_path")
+ else()
+ set(CMAKE_INSTALL_RPATH "\$ORIGIN")
+ endif(CLR_CMAKE_PLATFORM_DARWIN)
+endif (CORECLR_SET_RPATH)
+
+if(CLR_CMAKE_PLATFORM_ARCH_AMD64)
+ add_definitions(-DSOS_TARGET_AMD64=1)
+ add_definitions(-D_TARGET_WIN64_=1)
+ add_definitions(-DDBG_TARGET_64BIT)
+ add_definitions(-DDBG_TARGET_WIN64=1)
+ if(WIN32)
+ add_definitions(-DSOS_TARGET_ARM64=1)
+ endif(WIN32)
+ remove_definitions(-D_TARGET_ARM64_=1)
+ add_definitions(-D_TARGET_AMD64_)
+ add_definitions(-DDBG_TARGET_AMD64)
+elseif(CLR_CMAKE_PLATFORM_ARCH_I386)
+ add_definitions(-DSOS_TARGET_X86=1)
+ add_definitions(-D_TARGET_X86_=1)
+ add_definitions(-DDBG_TARGET_32BIT)
+ add_definitions(-DSOS_TARGET_ARM=1)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM)
+ add_definitions(-DSOS_TARGET_ARM=1)
+ add_definitions(-D_TARGET_WIN32_=1)
+ add_definitions(-D_TARGET_ARM_=1)
+ add_definitions(-DDBG_TARGET_32BIT)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64)
+ add_definitions(-DSOS_TARGET_ARM64=1)
+ add_definitions(-D_TARGET_WIN64_=1)
+ add_definitions(-DDBG_TARGET_64BIT)
+ add_definitions(-DDBG_TARGET_WIN64=1)
+endif()
+add_definitions(-DSTRIKE)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+include_directories(BEFORE ${VM_DIR})
+include_directories(${CLR_DIR}/src/gcdump)
+include_directories(${CLR_DIR}/src/debug/shim)
+include_directories(${CLR_DIR}/src/coreclr/hosts/unixcoreruncommon)
+include_directories(${CLR_DIR}/src/coreclr/hosts/inc)
+
+if(WIN32)
+ include_directories(inc)
+ include_directories("$ENV{VSInstallDir}/DIA SDK/include")
+
+ add_definitions(-DUSE_STL)
+
+ #use static crt
+ add_definitions(-MT)
+
+ set(SOS_SOURCES
+ disasm.cpp
+ dllsext.cpp
+ eeheap.cpp
+ EventCallbacks.cpp
+ ExpressionNode.cpp
+ exts.cpp
+ gchist.cpp
+ gcroot.cpp
+ metadata.cpp
+ sildasm.cpp
+ sos.cpp
+ stressLogDump.cpp
+ strike.cpp
+ util.cpp
+ vm.cpp
+ WatchCmd.cpp
+ Native.rc
+ )
+
+ add_definitions(-DFX_VER_INTERNALNAME_STR=SOS.dll)
+
+ #Preprocess exports definition file
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/sos.def ${CMAKE_CURRENT_BINARY_DIR}/sos.def)
+ list(APPEND SOS_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/sos.def)
+
+ set(SOS_LIBRARY
+ corguids
+ debugshim
+ dbgutil
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ ${STATIC_MT_VCRT_LIB}
+ kernel32.lib
+ user32.lib
+ ole32.lib
+ oleaut32.lib
+ dbghelp.lib
+ uuid.lib
+ version.lib
+ dbgeng.lib
+ advapi32.lib
+ psapi.lib
+ ntdll.lib
+ )
+else(WIN32)
+ add_definitions(-DFEATURE_ENABLE_HARDWARE_EXCEPTIONS)
+ add_definitions(-DPAL_STDCPP_COMPAT=1)
+ add_compile_options(-Wno-null-arithmetic)
+ add_compile_options(-Wno-format)
+
+ include_directories(BEFORE xplat)
+ include_directories(BEFORE ../lldbplugin/inc)
+
+ add_compile_options(-fPIC)
+
+ set(SOS_SOURCES
+ disasm.cpp
+ datatarget.cpp
+ eeheap.cpp
+ exts.cpp
+ gchist.cpp
+ gcroot.cpp
+ metadata.cpp
+ sildasm.cpp
+ stressLogDump.cpp
+ strike.cpp
+ sos.cpp
+ util.cpp
+ ../../../coreclr/hosts/unixcoreruncommon/coreruncommon.cpp
+ )
+
+ set(SOS_LIBRARY
+ corguids
+ debugshim
+ dbgutil
+ # share the PAL in the dac module
+ mscordaccore
+ palrt
+ )
+
+ set(DEF_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sos_unixexports.src)
+ set(EXPORTS_FILE ${CMAKE_CURRENT_BINARY_DIR}/sos.exports)
+ generate_exports_file(${DEF_SOURCES} ${EXPORTS_FILE})
+endif(WIN32)
+
+if(CLR_CMAKE_PLATFORM_ARCH_AMD64)
+ set(SOS_SOURCES_ARCH
+ disasmX86.cpp
+ )
+ if(WIN32)
+ list(APPEND
+ SOS_SOURCES_ARCH
+ disasmARM64.cpp
+ )
+ endif(WIN32)
+elseif(CLR_CMAKE_PLATFORM_ARCH_I386)
+ set(SOS_SOURCES_ARCH
+ disasmX86.cpp
+ disasmARM.cpp
+ )
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM)
+ set(SOS_SOURCES_ARCH
+ disasmARM.cpp
+ )
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64)
+ set(SOS_SOURCES_ARCH
+ disasmARM64.cpp
+ )
+endif()
+
+list(APPEND SOS_SOURCES ${SOS_SOURCES_ARCH})
+
+if(CLR_CMAKE_PLATFORM_LINUX OR CLR_CMAKE_PLATFORM_FREEBSD OR CLR_CMAKE_PLATFORM_NETBSD)
+ # Add linker exports file option
+ set(EXPORTS_LINKER_OPTION -Wl,--version-script=${EXPORTS_FILE})
+endif(CLR_CMAKE_PLATFORM_LINUX OR CLR_CMAKE_PLATFORM_FREEBSD OR CLR_CMAKE_PLATFORM_NETBSD)
+
+if(CLR_CMAKE_PLATFORM_DARWIN)
+ # Add linker exports file option
+ set(EXPORTS_LINKER_OPTION -Wl,-exported_symbols_list,${EXPORTS_FILE})
+endif(CLR_CMAKE_PLATFORM_DARWIN)
+
+add_library_clr(sos SHARED ${SOS_SOURCES})
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_custom_target(sos_exports DEPENDS ${EXPORTS_FILE})
+ add_dependencies(sos sos_exports)
+
+ set_property(TARGET sos APPEND_STRING PROPERTY LINK_FLAGS ${EXPORTS_LINKER_OPTION})
+ set_property(TARGET sos APPEND_STRING PROPERTY LINK_DEPENDS ${EXPORTS_FILE})
+
+ add_dependencies(sos mscordaccore)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+target_link_libraries(sos ${SOS_LIBRARY})
+
+# add the install targets
+install_clr(sos)
+
+if(NOT WIN32)
+ install(FILES sosdocsunix.txt DESTINATION .)
+endif(NOT WIN32)
diff --git a/src/ToolBox/SOS/Strike/EventCallbacks.cpp b/src/ToolBox/SOS/Strike/EventCallbacks.cpp
new file mode 100644
index 0000000000..0066dfa1e8
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/EventCallbacks.cpp
@@ -0,0 +1,160 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "EventCallbacks.h"
+
+EventCallbacks::EventCallbacks(IDebugClient* pDebugClient) : m_refCount(1), m_pDebugClient(pDebugClient)
+{
+}
+
+EventCallbacks::~EventCallbacks()
+{
+ if(m_pDebugClient != NULL)
+ m_pDebugClient->Release();
+}
+
+ // IUnknown implementation
+HRESULT __stdcall EventCallbacks::QueryInterface(REFIID riid, VOID** ppInterface)
+{
+ if(riid == __uuidof(IDebugEventCallbacks))
+ {
+ *ppInterface = static_cast<IDebugEventCallbacks*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else if(riid == __uuidof(IUnknown))
+ {
+ *ppInterface = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall EventCallbacks::AddRef()
+{
+ return InterlockedIncrement((volatile LONG *) &m_refCount);
+}
+
+ULONG __stdcall EventCallbacks::Release()
+{
+ ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
+ if(count == 0)
+ {
+ delete this;
+ }
+ return count;
+}
+
+// IDebugEventCallbacks implementation
+HRESULT __stdcall EventCallbacks::Breakpoint(PDEBUG_BREAKPOINT bp)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ChangeDebuggeeState(ULONG Flags, ULONG64 Argument)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ChangeEngineState(ULONG Flags, ULONG64 Argument)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+HRESULT __stdcall EventCallbacks::ChangeSymbolState(ULONG Flags, ULONG64 Argument)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+HRESULT __stdcall EventCallbacks::CreateProcess(ULONG64 ImageFileHandle,
+ ULONG64 Handle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp,
+ ULONG64 InitialThreadHandle,
+ ULONG64 ThreadDataOffset,
+ ULONG64 StartOffset)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::CreateThread(ULONG64 Handle,
+ ULONG64 DataOffset,
+ ULONG64 StartOffset)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::Exception(PEXCEPTION_RECORD64 Exception, ULONG FirstChance)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ExitProcess(ULONG ExitCode)
+{
+ UninitCorDebugInterface();
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ExitThread(ULONG ExitCode)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::GetInterestMask(PULONG Mask)
+{
+ *Mask = DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_EXIT_PROCESS;
+ return S_OK;
+}
+
+extern BOOL g_fAllowJitOptimization;
+
+HRESULT __stdcall EventCallbacks::LoadModule(ULONG64 ImageFileHandle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp)
+{
+ HRESULT handleEventStatus = DEBUG_STATUS_NO_CHANGE;
+ ExtQuery(m_pDebugClient);
+
+ if (ModuleName != NULL && _stricmp(ModuleName, MAIN_CLR_MODULE_NAME_A) == 0)
+ {
+ // if we don't want the JIT to optimize, we should also disable optimized NGEN images
+ if(!g_fAllowJitOptimization)
+ {
+ // if we aren't succesful SetNGENCompilerFlags will print relevant error messages
+ // and then we need to stop the debugger so the user can intervene if desired
+ if(FAILED(SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
+ {
+ handleEventStatus = DEBUG_STATUS_BREAK;
+ }
+ }
+ }
+
+ ExtRelease();
+ return handleEventStatus;
+}
+
+HRESULT __stdcall EventCallbacks::SessionStatus(ULONG Status)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::SystemError(ULONG Error, ULONG Level)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::UnloadModule(PCSTR ImageBaseName, ULONG64 BaseOffset)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
diff --git a/src/ToolBox/SOS/Strike/EventCallbacks.h b/src/ToolBox/SOS/Strike/EventCallbacks.h
new file mode 100644
index 0000000000..acd5e412d1
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/EventCallbacks.h
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EventCallbacks__
+#define __EventCallbacks__
+
+#include "exts.h"
+
+// A set of callbacks that are registered with windbg whenever SOS is loaded
+// Right now these callbacks only act on the module load event for CLR, but
+// feel free to add other event hooks as needed
+//
+// TODO: we should probably be using these callbacks to hook clrnotify exceptions
+// rather than attaching a user handler on the clrn event. That handler is both
+// visible to the user and could be accidentally erased by them.
+class EventCallbacks : IDebugEventCallbacks
+{
+public:
+ EventCallbacks(IDebugClient* pDebugClient);
+ ~EventCallbacks();
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ // IDebugEventCallbacks implementation
+ HRESULT __stdcall Breakpoint(PDEBUG_BREAKPOINT bp);
+ HRESULT __stdcall ChangeDebuggeeState(ULONG Flags, ULONG64 Argument);
+ HRESULT __stdcall ChangeEngineState(ULONG Flags, ULONG64 Argument);
+ HRESULT __stdcall ChangeSymbolState(ULONG Flags, ULONG64 Argument);
+ HRESULT __stdcall CreateProcess(ULONG64 ImageFileHandle,
+ ULONG64 Handle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp,
+ ULONG64 InitialThreadHandle,
+ ULONG64 ThreadDataOffset,
+ ULONG64 StartOffset);
+ HRESULT __stdcall CreateThread(ULONG64 Handle,
+ ULONG64 DataOffset,
+ ULONG64 StartOffset);
+ HRESULT __stdcall Exception(PEXCEPTION_RECORD64 Exception, ULONG FirstChance);
+ HRESULT __stdcall ExitProcess(ULONG ExitCode);
+ HRESULT __stdcall ExitThread(ULONG ExitCode);
+ HRESULT __stdcall GetInterestMask(PULONG Mask);
+ HRESULT __stdcall LoadModule(ULONG64 ImageFileHandle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp);
+ HRESULT __stdcall SessionStatus(ULONG Status);
+ HRESULT __stdcall SystemError(ULONG Error, ULONG Level);
+ HRESULT __stdcall UnloadModule(PCSTR ImageBaseName, ULONG64 BaseOffset);
+
+
+private:
+ volatile ULONG m_refCount;
+ IDebugClient* m_pDebugClient;
+};
+
+#endif
diff --git a/src/ToolBox/SOS/Strike/ExpressionNode.cpp b/src/ToolBox/SOS/Strike/ExpressionNode.cpp
new file mode 100644
index 0000000000..6516823aaa
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ExpressionNode.cpp
@@ -0,0 +1,2178 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "ExpressionNode.h"
+
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+// Returns the complete expression being evaluated to get the value for this node
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetAbsoluteExpression() { return pAbsoluteExpression; }
+
+// Returns the sub expression that logically indicates how the parent expression
+// was built upon to reach this node. This relative value has no purpose other
+// than an identifier and to convey UI meaning to the user. At present typical values
+// are the name of type, a local, a parameter, a field, an array index, or '<basetype>'
+// for a baseclass casting operation
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetRelativeExpression() { return pRelativeExpression; }
+
+// Returns a text representation of the type of value that this node refers to
+// It is possible this node doesn't evaluate to anything and therefore has no
+// type
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetTypeName() { PopulateType(); return pTypeName; }
+
+// Returns a text representation of the value for this node. It is possible that
+// this node doesn't evaluate to anything and therefore has no value text.
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetTextValue() { PopulateTextValue(); return pTextValue; }
+
+// If there is any error during the evaluation of this node's expression, it is
+// returned here.
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetErrorMessage() { return pErrorMessage; }
+
+// Factory function for creating the expression node at the root of a tree
+HRESULT ExpressionNode::CreateExpressionNode(__in_z WCHAR* pExpression, ExpressionNode** ppExpressionNode)
+{
+ *ppExpressionNode = NULL;
+ HRESULT Status = CreateExpressionNodeHelper(pExpression,
+ pExpression,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ ppExpressionNode);
+ if(FAILED(Status) && *ppExpressionNode == NULL)
+ {
+
+ WCHAR pErrorMessage[MAX_ERROR];
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Error 0x%x while parsing expression", Status);
+ *ppExpressionNode = new ExpressionNode(pExpression, pErrorMessage);
+ Status = S_OK;
+ if(*ppExpressionNode == NULL)
+ Status = E_OUTOFMEMORY;
+ }
+ return Status;
+}
+
+// Performs recursive expansion within the tree for nodes that are along the path to varToExpand.
+// Expansion involves calulating a set of child expressions from the current expression via
+// field dereferencing, array index dereferencing, or casting to a base type.
+// For example if a tree was rooted with expression 'foo.bar' and varToExpand is '(Baz)foo.bar[9]'
+// then 'foo.bar', 'foo.bar[9]', and '(Baz)foo.bar[9]' nodes would all be expanded.
+HRESULT ExpressionNode::Expand(__in_z WCHAR* varToExpand)
+{
+ if(!ShouldExpandVariable(varToExpand))
+ return S_FALSE;
+ if(pValue == NULL && pTypeCast == NULL)
+ return S_OK;
+
+ // if the node evaluates to a type, then the children are static fields of the type
+ if(pValue == NULL)
+ return ExpandFields(NULL, varToExpand);
+
+ // If the value is a null reference there is nothing to expand
+ HRESULT Status = S_OK;
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pInnerValue;
+ IfFailRet(DereferenceAndUnboxValue(pValue, &pInnerValue, &isNull));
+ if(isNull)
+ {
+ return S_OK;
+ }
+
+
+ CorElementType corElemType;
+ IfFailRet(pValue->GetType(&corElemType));
+ if (corElemType == ELEMENT_TYPE_SZARRAY)
+ {
+ //If its an array, add children representing the indexed elements
+ return ExpandSzArray(pInnerValue, varToExpand);
+ }
+ else if(corElemType == ELEMENT_TYPE_CLASS || corElemType == ELEMENT_TYPE_VALUETYPE)
+ {
+ // If its a class or struct (not counting string, array, or object) then add children representing
+ // the fields.
+ return ExpandFields(pInnerValue, varToExpand);
+ }
+ else
+ {
+ // nothing else expands
+ return S_OK;
+ }
+}
+
+// Standard depth first search tree traversal pattern with a callback
+VOID ExpressionNode::DFSVisit(ExpressionNodeVisitorCallback pFunc, VOID* pUserData, int depth)
+{
+ pFunc(this, depth, pUserData);
+ ExpressionNode* pCurChild = pChild;
+ while(pCurChild != NULL)
+ {
+ pCurChild->DFSVisit(pFunc, pUserData, depth+1);
+ pCurChild = pCurChild->pNextSibling;
+ }
+}
+
+
+// Creates a new expression with a given debuggee value and frame
+ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame)
+{
+ Init(pValue, NULL, pFrame);
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+ _snwprintf_s(pRelativeExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+}
+
+// Creates a new expression that has an error and no value
+ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, __in_z WCHAR* pErrorMessage)
+{
+ Init(NULL, NULL, NULL);
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+ _snwprintf_s(pRelativeExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+ _snwprintf_s(this->pErrorMessage, MAX_ERROR, _TRUNCATE, L"%s", pErrorMessage);
+}
+
+// Creates a new child expression
+ExpressionNode::ExpressionNode(__in_z WCHAR* pParentExpression, ChildKind ck, __in_z WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue, ULONG cchDefaultValue)
+{
+ Init(pValue, pType, pFrame);
+ if(ck == ChildKind_BaseClass)
+ {
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pParentExpression);
+ }
+ else
+ {
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, ck == ChildKind_Field ? L"%s.%s" : L"%s[%s]", pParentExpression, pRelativeExpression);
+ }
+ _snwprintf_s(this->pRelativeExpression, MAX_EXPRESSION, _TRUNCATE, ck == ChildKind_Index ? L"[%s]" : L"%s", pRelativeExpression);
+ this->pDefaultValue = pDefaultValue;
+ this->cchDefaultValue = cchDefaultValue;
+}
+
+// Common member initialization for the constructors
+VOID ExpressionNode::Init(ICorDebugValue* pValue, ICorDebugType* pTypeCast, ICorDebugILFrame* pFrame)
+{
+ this->pValue = pValue;
+ this->pTypeCast = pTypeCast;
+ this->pILFrame = pFrame;
+ pChild = NULL;
+ pNextSibling = NULL;
+ pTextValue[0] = 0;
+ pErrorMessage[0] = 0;
+ pAbsoluteExpression[0] = 0;
+ pRelativeExpression[0] = 0;
+ pTypeName[0] = 0;
+
+ pDefaultValue = NULL;
+ cchDefaultValue = 0;
+
+ // The ToRelease holders don't automatically AddRef
+ if(pILFrame != NULL)
+ pILFrame->AddRef();
+ if(pTypeCast != NULL)
+ pTypeCast->AddRef();
+ if(pValue != NULL)
+ {
+ pValue->AddRef();
+ PopulateMetaDataImport();
+ }
+}
+
+// Retreves the correct IMetaDataImport for the type represented in this node and stores it
+// in pMD.
+HRESULT ExpressionNode::PopulateMetaDataImport()
+{
+ if(pMD != NULL)
+ return S_OK;
+
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugType> pType;
+ if(pTypeCast != NULL)
+ {
+ pType = pTypeCast;
+ pType->AddRef();
+ }
+ else
+ {
+ BOOL isNull;
+ ToRelease<ICorDebugValue> pInnerValue;
+ IfFailRet(DereferenceAndUnboxValue(pValue, &pInnerValue, &isNull));
+ ToRelease<ICorDebugValue2> pValue2;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+
+ IfFailRet(pValue2->GetExactType(&pType));
+
+ // for array, pointer, and byref types we can't directly get a class, we must unwrap first
+ CorElementType et;
+ IfFailRet(pType->GetType(&et));
+ while(et == ELEMENT_TYPE_ARRAY || et == ELEMENT_TYPE_SZARRAY || et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_PTR)
+ {
+ pType->GetFirstTypeParameter(&pType);
+ IfFailRet(pType->GetType(&et));
+ }
+ }
+ ToRelease<ICorDebugClass> pClass;
+ IfFailRet(pType->GetClass(&pClass));
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+ ToRelease<IUnknown> pMDUnknown;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+ return Status;
+}
+
+// Determines the string representation of pType and stores it in typeName
+HRESULT ExpressionNode::CalculateTypeName(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen)
+{
+ HRESULT Status = S_OK;
+
+ CorElementType corElemType;
+ IfFailRet(pType->GetType(&corElemType));
+
+ switch (corElemType)
+ {
+ //List of unsupported CorElementTypes:
+ //ELEMENT_TYPE_END = 0x0,
+ //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1>
+ //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type
+ //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1>
+ //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle>
+ //ELEMENT_TYPE_MAX = 0x22, // first invalid element type
+ //ELEMENT_TYPE_MODIFIER = 0x40,
+ //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
+ //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER,
+ default:
+ swprintf_s(typeName, typeNameLen, L"(Unhandled CorElementType: 0x%x)\0", corElemType);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ {
+ //Defaults in case we fail...
+ if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, L"struct\0");
+ else swprintf_s(typeName, typeNameLen, L"class\0");
+
+ mdTypeDef typeDef;
+ ToRelease<ICorDebugClass> pClass;
+ if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
+ {
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
+ swprintf_s(typeName, typeNameLen, L"%s\0", g_mdName);
+ }
+ AddGenericArgs(pType, typeName, typeNameLen);
+ }
+ break;
+ case ELEMENT_TYPE_VOID:
+ swprintf_s(typeName, typeNameLen, L"void\0");
+ break;
+ case ELEMENT_TYPE_BOOLEAN:
+ swprintf_s(typeName, typeNameLen, L"bool\0");
+ break;
+ case ELEMENT_TYPE_CHAR:
+ swprintf_s(typeName, typeNameLen, L"char\0");
+ break;
+ case ELEMENT_TYPE_I1:
+ swprintf_s(typeName, typeNameLen, L"signed byte\0");
+ break;
+ case ELEMENT_TYPE_U1:
+ swprintf_s(typeName, typeNameLen, L"byte\0");
+ break;
+ case ELEMENT_TYPE_I2:
+ swprintf_s(typeName, typeNameLen, L"short\0");
+ break;
+ case ELEMENT_TYPE_U2:
+ swprintf_s(typeName, typeNameLen, L"unsigned short\0");
+ break;
+ case ELEMENT_TYPE_I4:
+ swprintf_s(typeName, typeNameLen, L"int\0");
+ break;
+ case ELEMENT_TYPE_U4:
+ swprintf_s(typeName, typeNameLen, L"unsigned int\0");
+ break;
+ case ELEMENT_TYPE_I8:
+ swprintf_s(typeName, typeNameLen, L"long\0");
+ break;
+ case ELEMENT_TYPE_U8:
+ swprintf_s(typeName, typeNameLen, L"unsigned long\0");
+ break;
+ case ELEMENT_TYPE_R4:
+ swprintf_s(typeName, typeNameLen, L"float\0");
+ break;
+ case ELEMENT_TYPE_R8:
+ swprintf_s(typeName, typeNameLen, L"double\0");
+ break;
+ case ELEMENT_TYPE_OBJECT:
+ swprintf_s(typeName, typeNameLen, L"object\0");
+ break;
+ case ELEMENT_TYPE_STRING:
+ swprintf_s(typeName, typeNameLen, L"string\0");
+ break;
+ case ELEMENT_TYPE_I:
+ swprintf_s(typeName, typeNameLen, L"IntPtr\0");
+ break;
+ case ELEMENT_TYPE_U:
+ swprintf_s(typeName, typeNameLen, L"UIntPtr\0");
+ break;
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ {
+ // get a name for the type we are building from
+ ToRelease<ICorDebugType> pFirstParameter;
+ if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
+ CalculateTypeName(pFirstParameter, typeName, typeNameLen);
+ else
+ swprintf_s(typeName, typeNameLen, L"<unknown>\0");
+
+ // append the appropriate [], *, &
+ switch(corElemType)
+ {
+ case ELEMENT_TYPE_SZARRAY:
+ wcsncat_s(typeName, typeNameLen, L"[]", typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_ARRAY:
+ {
+ ULONG32 rank = 0;
+ pType->GetRank(&rank);
+ wcsncat_s(typeName, typeNameLen, L"[", typeNameLen);
+ for(ULONG32 i = 0; i < rank - 1; i++)
+ {
+ // todo- could we print out exact boundaries?
+ wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
+ }
+ wcsncat_s(typeName, typeNameLen, L"]", typeNameLen);
+ }
+ return S_OK;
+ case ELEMENT_TYPE_BYREF:
+ wcsncat_s(typeName, typeNameLen, L"&", typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_PTR:
+ wcsncat_s(typeName, typeNameLen, L"*", typeNameLen);
+ return S_OK;
+ }
+ }
+ break;
+ case ELEMENT_TYPE_FNPTR:
+ swprintf_s(typeName, typeNameLen, L"*(...)");
+ break;
+ case ELEMENT_TYPE_TYPEDBYREF:
+ swprintf_s(typeName, typeNameLen, L"typedbyref");
+ break;
+ }
+ return S_OK;
+}
+
+
+// Appends angle brackets and the generic argument list to a type name
+HRESULT ExpressionNode::AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen)
+{
+ bool isFirst = true;
+ ToRelease<ICorDebugTypeEnum> pTypeEnum;
+ if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
+ {
+ ULONG numTypes = 0;
+ ToRelease<ICorDebugType> pCurrentTypeParam;
+
+ while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
+ {
+ if(numTypes == 0) break;
+
+ if(isFirst)
+ {
+ isFirst = false;
+ wcsncat_s(typeName, typeNameLen, L"<", typeNameLen);
+ }
+ else wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
+
+ WCHAR typeParamName[mdNameLen];
+ typeParamName[0] = L'\0';
+ CalculateTypeName(pCurrentTypeParam, typeParamName, mdNameLen);
+ wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
+ }
+ if(!isFirst)
+ wcsncat_s(typeName, typeNameLen, L">", typeNameLen);
+ }
+
+ return S_OK;
+}
+
+// Determines the text name for the type of this node and caches it
+HRESULT ExpressionNode::PopulateType()
+{
+ HRESULT Status = S_OK;
+ if(pTypeName[0] != 0)
+ return S_OK;
+
+ //default value
+ swprintf_s(pTypeName, MAX_EXPRESSION, L"<unknown>");
+
+ // if we are displaying this type as a specific sub-type, use that
+ if(pTypeCast != NULL)
+ return CalculateTypeName(pTypeCast, pTypeName, MAX_EXPRESSION);
+
+ // if there is no value then either we succesfully already determined the type
+ // name, or this node has no value or type and thus no type name.
+ if(pValue == NULL)
+ return S_OK;
+
+ // get the type from the value and then calculate a name based on that
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugValue2> pValue2;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
+ return CalculateTypeName(pType, pTypeName, MAX_EXPRESSION);
+
+ return S_OK;
+}
+
+// Node expansion helpers
+
+// Inserts a new child at the end of the linked list of children
+// PERF: This has O(N) insert time but these lists should never be large
+VOID ExpressionNode::AddChild(ExpressionNode* pNewChild)
+{
+ if(pChild == NULL)
+ pChild = pNewChild;
+ else
+ {
+ ExpressionNode* pCursor = pChild;
+ while(pCursor->pNextSibling != NULL)
+ pCursor = pCursor->pNextSibling;
+ pCursor->pNextSibling = pNewChild;
+ }
+}
+
+// Helper that determines if the current node is on the path of nodes represented by
+// expression varToExpand
+BOOL ExpressionNode::ShouldExpandVariable(__in_z WCHAR* varToExpand)
+{
+ if(pAbsoluteExpression == NULL || varToExpand == NULL) return FALSE;
+
+ // if there is a cast operation, move past it
+ WCHAR* pEndCast = _wcschr(varToExpand, L')');
+ varToExpand = (pEndCast == NULL) ? varToExpand : pEndCast+1;
+
+ size_t varToExpandLen = _wcslen(varToExpand);
+ size_t currentExpansionLen = _wcslen(pAbsoluteExpression);
+ if(currentExpansionLen > varToExpandLen) return FALSE;
+ if(currentExpansionLen < varToExpandLen &&
+ varToExpand[currentExpansionLen] != L'.' &&
+ varToExpand[currentExpansionLen] != L'[')
+ return FALSE;
+ if(_wcsncmp(pAbsoluteExpression, varToExpand, currentExpansionLen) != 0) return FALSE;
+
+ return TRUE;
+}
+
+// Expands this array node by creating child nodes with expressions refering to individual array elements
+HRESULT ExpressionNode::ExpandSzArray(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Multi-dimensional arrays NYI");
+ return E_UNEXPECTED;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+
+ //TODO: do we really want all the elements? This could be huge!
+ for (ULONG32 i=0; i < cElements; i++)
+ {
+ WCHAR index[20];
+ swprintf_s(index, 20, L"%d", i);
+
+ ToRelease<ICorDebugValue> pElementValue;
+ IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
+ ExpressionNode* pExpr = new ExpressionNode(pAbsoluteExpression, ChildKind_Index, index, pElementValue, NULL, pILFrame);
+ AddChild(pExpr);
+ pExpr->Expand(varToExpand);
+ }
+ return S_OK;
+}
+
+// Expands this struct/class node by creating child nodes with expressions refering to individual field values
+// and one node for the basetype value
+HRESULT ExpressionNode::ExpandFields(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand)
+{
+ HRESULT Status = S_OK;
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ if(pTypeCast == NULL)
+ {
+ ToRelease<ICorDebugValue2> pValue2;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ }
+ else
+ {
+ pType = pTypeCast;
+ pType->AddRef();
+ }
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ // If the current type has a base type that isn't object, enum, or ValueType then add a node for the base type
+ WCHAR baseTypeName[mdNameLen] = L"\0";
+ ToRelease<ICorDebugType> pBaseType;
+ ExpressionNode* pBaseTypeNode = NULL;
+ if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(CalculateTypeName(pBaseType, baseTypeName, mdNameLen)))
+ {
+ if(_wcsncmp(baseTypeName, L"System.Enum", 11) == 0)
+ return S_OK;
+ else if(_wcsncmp(baseTypeName, L"System.Object", 13) != 0 && _wcsncmp(baseTypeName, L"System.ValueType", 16) != 0)
+ {
+ pBaseTypeNode = new ExpressionNode(pAbsoluteExpression, ChildKind_BaseClass, L"<baseclass>", pInnerValue, pBaseType, pILFrame);
+ AddChild(pBaseTypeNode);
+ }
+ }
+
+ // add nodes for all the fields in this object
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ BOOL fieldExpanded = FALSE;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ mdTypeDef classDef = 0;
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ CorElementType fieldDefaultValueEt;
+ UVCP_CONSTANT pDefaultValue;
+ ULONG cchDefaultValue;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, &classDef, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, (DWORD*)&fieldDefaultValueEt, &pDefaultValue, &cchDefaultValue)))
+ {
+ ToRelease<ICorDebugType> pFieldType;
+ ToRelease<ICorDebugValue> pFieldVal;
+
+ // static fields (of any kind - AppDomain, thread, context, RVA)
+ if (fieldAttr & fdStatic)
+ {
+ pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
+ }
+ // non-static fields on an object instance
+ else if(pInnerValue != NULL)
+ {
+ ToRelease<ICorDebugObjectValue> pObjValue;
+ if (SUCCEEDED(pInnerValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
+ pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
+ }
+ // skip over non-static fields on static types
+ else
+ {
+ continue;
+ }
+
+ // we didn't get a value yet and there is default value available
+ // need to calculate the type because there won't be a ICorDebugValue to derive it from
+ if(pFieldVal == NULL && pDefaultValue != NULL)
+ {
+ FindTypeFromElementType(fieldDefaultValueEt, &pFieldType);
+ }
+
+ ExpressionNode* pNewChildNode = new ExpressionNode(pAbsoluteExpression, ChildKind_Field, mdName, pFieldVal, pFieldType, pILFrame, pDefaultValue, cchDefaultValue);
+ AddChild(pNewChildNode);
+ if(pNewChildNode->Expand(varToExpand) != S_FALSE)
+ fieldExpanded = TRUE;
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+ // Only recurse to expand the base type if all of these hold:
+ // 1) base type exists
+ // 2) no field was expanded
+ // 3) the non-casting portion of the varToExpand doesn't match the current expression
+ // OR the cast exists and doesn't match
+
+ if(pBaseTypeNode == NULL) return Status;
+ if(fieldExpanded) return Status;
+
+ WCHAR* pEndCast = _wcschr(varToExpand, L')');
+ WCHAR* pNonCast = (pEndCast == NULL) ? varToExpand : pEndCast+1;
+ if(_wcscmp(pNonCast, pAbsoluteExpression) != 0)
+ {
+ pBaseTypeNode->Expand(varToExpand);
+ return Status;
+ }
+
+ if(varToExpand[0] == L'(' && pEndCast != NULL)
+ {
+ int cchCastTypeName = ((int)(pEndCast-1)-(int)varToExpand)/2;
+ PopulateType();
+ if(_wcslen(pTypeName) != (cchCastTypeName) ||
+ _wcsncmp(varToExpand+1, pTypeName, cchCastTypeName) != 0)
+ {
+ pBaseTypeNode->Expand(varToExpand);
+ return Status;
+ }
+ }
+
+ return Status;
+}
+
+
+// Value Population functions
+
+//Helper for unwrapping values
+HRESULT ExpressionNode::DereferenceAndUnboxValue(ICorDebugValue * pInputValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull)
+{
+ HRESULT Status = S_OK;
+ *ppOutputValue = NULL;
+ if(pIsNull != NULL) *pIsNull = FALSE;
+
+ ToRelease<ICorDebugReferenceValue> pReferenceValue;
+ Status = pInputValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
+ if (SUCCEEDED(Status))
+ {
+ BOOL isNull = FALSE;
+ IfFailRet(pReferenceValue->IsNull(&isNull));
+ if(!isNull)
+ {
+ ToRelease<ICorDebugValue> pDereferencedValue;
+ IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
+ return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
+ }
+ else
+ {
+ if(pIsNull != NULL) *pIsNull = TRUE;
+ *ppOutputValue = pInputValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+ }
+ }
+
+ ToRelease<ICorDebugBoxValue> pBoxedValue;
+ Status = pInputValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
+ if (SUCCEEDED(Status))
+ {
+ ToRelease<ICorDebugObjectValue> pUnboxedValue;
+ IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
+ return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
+ }
+ *ppOutputValue = pInputValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+}
+
+// Returns TRUE if the value derives from System.Enum
+BOOL ExpressionNode::IsEnum(ICorDebugValue * pInputValue)
+{
+ ToRelease<ICorDebugValue> pValue;
+ if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
+
+ WCHAR baseTypeName[mdNameLen];
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugType> pBaseType;
+
+ if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
+ if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
+ if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
+ if(FAILED(CalculateTypeName(pBaseType, baseTypeName, mdNameLen))) return FALSE;
+
+ return (_wcsncmp(baseTypeName, L"System.Enum", 11) == 0);
+}
+
+// Calculates the value text for nodes that have enum values
+HRESULT ExpressionNode::PopulateEnumValue(ICorDebugValue* pEnumValue, BYTE* enumValue)
+{
+ HRESULT Status = S_OK;
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pEnumValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+
+ //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
+ //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ DWORD fieldAttr = 0;
+ PCCOR_SIGNATURE pSignatureBlob = NULL;
+ ULONG sigBlobLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
+ {
+ if((fieldAttr & fdStatic) == 0)
+ {
+ CorSigUncompressCallingConv(pSignatureBlob);
+ enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
+ break;
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+
+ //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
+ fEnum = NULL;
+ bool isFirst = true;
+ ULONG64 remainingValue = *((ULONG64*)enumValue);
+ WCHAR* pTextValueCursor = pTextValue;
+ DWORD cchTextValueCursor = MAX_EXPRESSION;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ UVCP_CONSTANT pRawValue = NULL;
+ ULONG rawValueLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
+ {
+ DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
+ if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
+ continue;
+
+ ULONG64 currentConstValue = 0;
+ switch (enumUnderlyingType)
+ {
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U1:
+ currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I2:
+ currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U2:
+ currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I4:
+ currentConstValue = (ULONG64)(*((INT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U4:
+ currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I8:
+ currentConstValue = (ULONG64)(*((LONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U8:
+ currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I:
+ currentConstValue = (ULONG64)(*((int*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
+ default:
+ currentConstValue = 0;
+ }
+
+ if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
+ {
+ remainingValue &= ~currentConstValue;
+ DWORD charsCopied = 0;
+ if(isFirst)
+ {
+ charsCopied = _snwprintf_s(pTextValueCursor, cchTextValueCursor, _TRUNCATE, L"= %s", mdName);
+ isFirst = false;
+ }
+ else
+ charsCopied = _snwprintf_s(pTextValueCursor, cchTextValueCursor, _TRUNCATE, L" | %s", mdName);
+
+ // if an error or truncation occurred, stop copying
+ if(charsCopied == -1)
+ {
+ cchTextValueCursor = 0;
+ pTextValueCursor = NULL;
+ }
+ else
+ {
+ // charsCopied is the number of characters copied, not counting the terminating null
+ // this advances the cursor to point right at the terminating null so that future copies
+ // will concatenate the string
+ pTextValueCursor += charsCopied;
+ cchTextValueCursor -= charsCopied;
+ }
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+ return Status;
+}
+
+// Helper that caches the textual value for nodes that evaluate to a string object
+HRESULT ExpressionNode::GetDebuggeeStringValue(ICorDebugValue* pInputValue, __inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer)
+{
+ HRESULT Status;
+
+ ToRelease<ICorDebugStringValue> pStringValue;
+ IfFailRet(pInputValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
+
+ ULONG32 cchValueReturned;
+ IfFailRet(pStringValue->GetString(cchBuffer, &cchValueReturned, wszBuffer));
+
+ return S_OK;
+}
+
+// Retrieves the string value for a constant
+HRESULT ExpressionNode::GetConstantStringValue(__inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer)
+{
+ // The string encoded in metadata isn't null-terminated
+ // so we need to copy it to a null terminated buffer
+ DWORD copyLen = cchDefaultValue;
+ if(copyLen > cchBuffer-1)
+ copyLen = cchDefaultValue;
+
+ wcsncpy_s(wszBuffer, cchBuffer, (WCHAR*)pDefaultValue, copyLen);
+ return S_OK;
+}
+
+// Helper that caches the textual value for nodes that evaluate to array objects
+HRESULT ExpressionNode::PopulateSzArrayValue(ICorDebugValue* pInputValue)
+{
+ HRESULT Status = S_OK;
+
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ IfFailRet(pInputValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ _snwprintf_s(pErrorMessage, MAX_EXPRESSION, _TRUNCATE, L"Multi-dimensional arrays NYI");
+ return E_UNEXPECTED;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+
+ if (cElements == 0)
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"(empty)");
+ else if (cElements == 1)
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"(1 element)");
+ else
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"(%d elements)", cElements);
+
+ return S_OK;
+}
+
+// Helper that caches the textual value for nodes of any type
+HRESULT ExpressionNode::PopulateTextValueHelper()
+{
+ HRESULT Status = S_OK;
+
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pInnerValue;
+ CorElementType corElemType;
+ ULONG32 cbSize = 0;
+ if(pValue != NULL)
+ {
+ IfFailRet(DereferenceAndUnboxValue(pValue, &pInnerValue, &isNull));
+
+ if(isNull)
+ {
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= null");
+ return S_OK;
+ }
+ IfFailRet(pInnerValue->GetSize(&cbSize));
+ IfFailRet(pInnerValue->GetType(&corElemType));
+ }
+ else if(pDefaultValue != NULL)
+ {
+ if(pTypeCast == NULL)
+ {
+ // this shouldn't happen, but just print nothing if it does
+ return S_OK;
+ }
+ // This works around an irritating issue in ICorDebug. For default values
+ // we have to construct the ICorDebugType ourselves, however ICorDebug
+ // doesn't allow type construction using the correct element types. The
+ // caller must past CLASS or VALUETYPE even when a more specific short
+ // form element type is applicable. That means that later, here, we get
+ // back the wrong answer. To work around this we format the type as a
+ // string, and check it against all the known types. That allows us determine
+ // everything except VALUETYPE/CLASS. Thankfully that distinction is the
+ // one piece of data ICorDebugType will tell us if needed.
+ if(FAILED(GetCanonicalElementTypeForTypeName(GetTypeName(), &corElemType)))
+ {
+ pTypeCast->GetType(&corElemType);
+ }
+
+ switch(corElemType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ cbSize = 1;
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ cbSize = 2;
+ break;
+
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ cbSize = 4;
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ cbSize = 8;
+ break;
+ }
+ }
+
+ if (corElemType == ELEMENT_TYPE_STRING)
+ {
+ WCHAR buffer[MAX_EXPRESSION];
+ buffer[0] = L'\0';
+ if(pInnerValue != NULL)
+ GetDebuggeeStringValue(pInnerValue, buffer, MAX_EXPRESSION);
+ else
+ GetConstantStringValue(buffer, MAX_EXPRESSION);
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= \"%s\"", buffer);
+ }
+ else if (corElemType == ELEMENT_TYPE_SZARRAY)
+ {
+ return PopulateSzArrayValue(pInnerValue);
+ }
+
+
+ ArrayHolder<BYTE> rgbValue = new BYTE[cbSize];
+ memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
+ if(pInnerValue != NULL)
+ {
+ ToRelease<ICorDebugGenericValue> pGenericValue;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
+ IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
+ }
+ else
+ {
+ memcpy((LPVOID) &(rgbValue[0]), pDefaultValue, cbSize);
+ }
+
+ //TODO: this should really be calculated from the type
+ if(pInnerValue != NULL && IsEnum(pInnerValue))
+ {
+ Status = PopulateEnumValue(pInnerValue, rgbValue);
+ return Status;
+ }
+
+ switch (corElemType)
+ {
+ default:
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Unhandled CorElementType: 0x%x", corElemType);
+ Status = E_FAIL;
+ break;
+
+ case ELEMENT_TYPE_PTR:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"<pointer>");
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ {
+ CORDB_ADDRESS addr = 0;
+ ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
+ if(SUCCEEDED(pInnerValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
+ pReferenceValue->GetValue(&addr);
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"<function pointer 0x%x>", addr);
+ }
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_OBJECT:
+ ULONG64 pointer;
+ if(pInnerValue != NULL && SUCCEEDED(pInnerValue->GetAddress(&pointer)))
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"@ 0x%p", (void *) pointer);
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %s", rgbValue[0] == 0 ? L"false" : L"true");
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= '%C'", *(WCHAR *) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I1:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U1:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(unsigned char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I2:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %hd", *(short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U2:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %hu", *(unsigned short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %u", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I4:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U4:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %u", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I8:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %I64d", *(__int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U8:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %I64u", *(unsigned __int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R4:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %f", (double) *(float*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R8:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"%f", *(double*) &(rgbValue[0]));
+ break;
+
+ // TODO: The following corElementTypes are not yet implemented here. Array
+ // might be interesting to add, though the others may be of rather limited use:
+ // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
+ //
+ // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ }
+
+ return Status;
+}
+
+// Caches the textual value of this node
+HRESULT ExpressionNode::PopulateTextValue()
+{
+ if(pErrorMessage[0] != 0)
+ return E_UNEXPECTED;
+ if(pValue == NULL && pDefaultValue == NULL)
+ return S_OK;
+ HRESULT Status = PopulateTextValueHelper();
+ if(FAILED(Status) && pErrorMessage[0] == 0)
+ {
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Error in PopulateTextValueHelper: 0x%x", Status);
+ }
+ return Status;
+}
+
+
+// Expression parsing and search
+
+//Callback that searches a frame to determine if it contains a local variable or parameter of a given name
+VOID ExpressionNode::EvaluateExpressionFrameScanCallback(ICorDebugFrame* pFrame, VOID* pUserData)
+{
+ EvaluateExpressionFrameScanData* pData = (EvaluateExpressionFrameScanData*)pUserData;
+
+ // we already found what we were looking for, just continue
+ if(pData->pFoundValue != NULL)
+ return;
+
+ // if any of these fail we just continue on
+ // querying for ILFrame will frequently fail because many frames aren't IL
+ ToRelease<ICorDebugILFrame> pILFrame;
+ HRESULT Status = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ // we need to save off the first frame we find regardless of whether we find the
+ // local or not. We might need this frame later for static field lookup.
+ if(pData->pFirstFrame == NULL)
+ {
+ pData->pFirstFrame = pILFrame;
+ pData->pFirstFrame->AddRef();
+ }
+ // not all IL frames map to an assembly (ex. LCG)
+ ToRelease<ICorDebugFunction> pFunction;
+ Status = pFrame->GetFunction(&pFunction);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ // from here down shouldn't generally fail, but just in case
+ mdMethodDef methodDef;
+ Status = pFunction->GetToken(&methodDef);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ ToRelease<ICorDebugModule> pModule;
+ Status = pFunction->GetModule(&pModule);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ ToRelease<IUnknown> pMDUnknown;
+ Status = pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ ToRelease<IMetaDataImport> pMD;
+ Status = pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD);
+ if (FAILED(Status))
+ {
+ return;
+ }
+
+ pData->pFoundFrame = pILFrame;
+ pData->pFoundFrame->AddRef();
+ // Enumerate all the parameters
+ EnumerateParameters(pMD, methodDef, pILFrame, EvaluateExpressionVariableScanCallback, pUserData);
+ // Enumerate all the locals
+ EnumerateLocals(pMD, methodDef, pILFrame, EvaluateExpressionVariableScanCallback, pUserData);
+
+ // if we didn't find it in this frame then clear the frame back out
+ if(pData->pFoundValue == NULL)
+ {
+ pData->pFoundFrame = NULL;
+ }
+
+ return;
+}
+
+//Callback checks to see if a given local/parameter has name pName
+VOID ExpressionNode::EvaluateExpressionVariableScanCallback(ICorDebugValue* pValue, __in_z WCHAR* pName, __out_z WCHAR* pErrorMessage, VOID* pUserData)
+{
+ EvaluateExpressionFrameScanData* pData = (EvaluateExpressionFrameScanData*)pUserData;
+ if(_wcscmp(pName, pData->pIdentifier) == 0)
+ {
+ // found it
+ pData->pFoundValue = pValue;
+ pValue->AddRef();
+ }
+ return;
+}
+
+//Factory method that recursively parses pExpression and create an ExpressionNode
+// pExpression - the entire expression being parsed
+// pExpressionRemainder - the portion of the expression that remains to be parsed in this
+// recursive invocation
+// charactersParsed - the number of characters that have been parsed from pExpression
+// so far (todo: this is basically the difference between remainder and
+// full expression, do we need it?)
+// pParsedValue - A debuggee value that should be used as the context for interpreting
+// pExpressionRemainder
+// pParsedType - A debuggee type that should be used as the context for interpreting
+// pExpressionRemainder.
+// pParsedDefaultValue - A fixed value from metadata that should be used as context for
+// interpretting pExpressionRemainder
+// cchParsedDefaultValue- Size of pParsedDefaultValue
+// pFrame - A debuggee IL frame that disambiguates the thread and context needed
+// to evaluate a thread-static or context-static value
+// ppExpressionNode - OUT - the resulting expression node
+//
+//
+// Valid combinations of state comming into this method:
+// The expression up to charactersParsed isn't recognized yet:
+// pParsedValue = pParsedType = pParsedDefaultValue = NULL
+// cchParsedDefaultValue = 0
+// The expression up to charactersParsed is a recognized type:
+// pParsedType = <parsed type>
+// pParsedValue = pParsedDefaultValue = NULL
+// cchParsedDefaultValue = 0
+// The expression up to charactersParsed is a recognized value in the debuggee:
+// pParsedValue = <parsed value>
+// pParsedType = pParsedDefaultValue = NULL
+// cchParsedDefaultValue = 0
+// The expression up to charactersParsed is a recognized default value stored in metadata:
+// pParsedValue = NULL
+// pParsedType = <type calculated from metadata>
+// pParsedDefaultValue = <value from metadata>
+// cchParsedDefaultValue = <size of metadata value>
+//
+//
+// REFACTORING NOTE: This method is very similar (but not identical) to the expansion logic
+// in ExpressionNode. The primary difference is that the nodes expand all
+// fields/indices whereas this function only expands along a precise route.
+// If the ExpressionNode code where enhanced to support expanding precisely
+// large portions of this function could be disposed of. As soon as the function
+// matched the initial name it could create an ExpressionNode and then use the
+// ExpressionNode expansion functions to drill down to the actual node required.
+// Also need to make sure the nodes manage lifetime ok when a parent is destroyed
+// but a child node is still referenced.
+HRESULT ExpressionNode::CreateExpressionNodeHelper(__in_z WCHAR* pExpression,
+ __in_z WCHAR* pExpressionParseRemainder,
+ DWORD charactersParsed,
+ ICorDebugValue* pParsedValue,
+ ICorDebugType* pParsedType,
+ UVCP_CONSTANT pParsedDefaultValue,
+ ULONG cchParsedDefaultValue,
+ ICorDebugILFrame* pFrame,
+ ExpressionNode** ppExpressionNode)
+{
+ HRESULT Status = S_OK;
+ WCHAR* pExpressionCursor = pExpressionParseRemainder;
+ DWORD currentCharsParsed = charactersParsed;
+ WCHAR pIdentifier[mdNameLen];
+ pIdentifier[0] = 0;
+ BOOL isArray = FALSE;
+ WCHAR pResultBuffer[MAX_EXPRESSION];
+
+ // Get the next name from the expression string
+ if(FAILED(Status = ParseNextIdentifier(&pExpressionCursor, pIdentifier, mdNameLen, pResultBuffer, MAX_EXPRESSION, &currentCharsParsed, &isArray)))
+ {
+ *ppExpressionNode = new ExpressionNode(pExpression, pResultBuffer);
+ if(*ppExpressionNode == NULL)
+ return E_OUTOFMEMORY;
+ else
+ return S_OK;
+ }
+
+ // we've gone as far as we need, nothing left to parse
+ if(Status == S_FALSE)
+ {
+ ToRelease<ICorDebugValue> pValue;
+ *ppExpressionNode = new ExpressionNode(pExpression, ChildKind_BaseClass, pExpression, pParsedValue, pParsedType, pFrame, pParsedDefaultValue, cchParsedDefaultValue);
+ if(*ppExpressionNode == NULL)
+ return E_OUTOFMEMORY;
+ else
+ return S_OK;
+ }
+ // if we are just starting and have no context then we need to search locals/parameters/type names
+ else if(pParsedValue == NULL && pParsedType == NULL)
+ {
+ // the first identifier must be a name, not an indexing expression
+ if(isArray)
+ {
+ *ppExpressionNode = new ExpressionNode(pExpression, L"Expression must begin with a local variable, parameter, or fully qualified type name");
+ return S_OK;
+ }
+
+ // scan for root on stack
+ EvaluateExpressionFrameScanData data;
+ data.pIdentifier = pIdentifier;
+ data.pFoundValue = NULL;
+ data.pFoundFrame = NULL;
+ data.pFirstFrame = NULL;
+ data.pErrorMessage = pResultBuffer;
+ data.cchErrorMessage = MAX_EXPRESSION;
+ EnumerateFrames(EvaluateExpressionFrameScanCallback, (VOID*) &data);
+
+ if(data.pFoundValue != NULL)
+ {
+ // found the root, now recurse along the expression
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, data.pFoundValue, NULL, NULL, 0, data.pFoundFrame, ppExpressionNode);
+ }
+
+ // didn't find it - search the type table for a matching name
+ WCHAR pName[MAX_EXPRESSION];
+ while(true)
+ {
+ wcsncpy_s(pName, MAX_EXPRESSION, pExpression, currentCharsParsed);
+ ToRelease<ICorDebugType> pType;
+ if(SUCCEEDED(FindTypeByName(pName, &pType)))
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, NULL, pType, NULL, 0, data.pFirstFrame, ppExpressionNode);
+
+ if(FAILED(Status = ParseNextIdentifier(&pExpressionCursor, pIdentifier, mdNameLen, pResultBuffer, MAX_EXPRESSION, &currentCharsParsed, &isArray)))
+ {
+ *ppExpressionNode = new ExpressionNode(pExpression, pResultBuffer);
+ return S_OK;
+ }
+ else if(Status == S_FALSE)
+ {
+ break;
+ }
+ }
+
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"No expression prefix could not be matched to an existing type, parameter, or local");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ // we've got some context from an earlier portion of the search, now just need to continue
+ // by dereferencing and indexing until we reach the end of the expression
+
+ // Figure out the type, module, and metadata from our context information
+ ToRelease<ICorDebugType> pType;
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pInnerValue = NULL;
+ if(pParsedValue != NULL)
+ {
+ IfFailRet(DereferenceAndUnboxValue(pParsedValue, &pInnerValue, &isNull));
+
+ if(isNull)
+ {
+ WCHAR parsedExpression[MAX_EXPRESSION];
+ wcsncpy_s(parsedExpression, MAX_EXPRESSION, pExpression, charactersParsed);
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Dereferencing \'%s\' throws NullReferenceException", parsedExpression);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ToRelease<ICorDebugValue2> pValue2;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ CorElementType et;
+ IfFailRet(pType->GetType(&et));
+ while(et == ELEMENT_TYPE_ARRAY || et == ELEMENT_TYPE_SZARRAY || et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_PTR)
+ {
+ pType->GetFirstTypeParameter(&pType);
+ IfFailRet(pType->GetType(&et));
+ }
+ }
+ else
+ {
+ pType = pParsedType;
+ pType->AddRef();
+ }
+ ToRelease<ICorDebugClass> pClass;
+ IfFailRet(pType->GetClass(&pClass));
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+ mdTypeDef currentTypeDef;
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+
+ // if we are searching along and this is an array index dereference
+ if(isArray)
+ {
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ if(pInnerValue == NULL || FAILED(Status = pInnerValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue)))
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Index notation only supported for instances of an array type");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Multi-dimensional arrays NYI");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ int index = -1;
+ if(swscanf_s(pIdentifier, L"%d", &index) != 1)
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Failed to parse expression, missing or invalid index expression at character %d", charactersParsed+1);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+ if(index < 0 || (ULONG32)index >= cElements)
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Index is out of range for this array");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ToRelease<ICorDebugValue> pElementValue;
+ IfFailRet(pArrayValue->GetElementAtPosition(index, &pElementValue));
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, pElementValue, NULL, NULL, 0, pFrame, ppExpressionNode);
+ }
+ // if we are searching along and this is field dereference
+ else
+ {
+ ToRelease<ICorDebugType> pBaseType = pType;
+ pBaseType->AddRef();
+
+ while(pBaseType != NULL)
+ {
+ // get the current base type class/token/MD
+ ToRelease<ICorDebugClass> pBaseClass;
+ IfFailRet(pBaseType->GetClass(&pBaseClass));
+ ToRelease<ICorDebugModule> pBaseTypeModule;
+ IfFailRet(pBaseClass->GetModule(&pBaseTypeModule));
+ mdTypeDef baseTypeDef;
+ IfFailRet(pBaseClass->GetToken(&baseTypeDef));
+ ToRelease<IUnknown> pBaseTypeMDUnknown;
+ ToRelease<IMetaDataImport> pBaseTypeMD;
+ IfFailRet(pBaseTypeModule->GetMetaDataInterface(IID_IMetaDataImport, &pBaseTypeMDUnknown));
+ IfFailRet(pBaseTypeMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pBaseTypeMD));
+
+
+ // iterate through all fields at this level of the class hierarchy
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, baseTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ CorElementType fieldDefaultValueEt;
+ UVCP_CONSTANT pDefaultValue;
+ ULONG cchDefaultValue;
+ if(SUCCEEDED(pBaseTypeMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, (DWORD*)&fieldDefaultValueEt, &pDefaultValue, &cchDefaultValue)) &&
+ _wcscmp(mdName, pIdentifier) == 0)
+ {
+ ToRelease<ICorDebugType> pFieldValType = NULL;
+ ToRelease<ICorDebugValue> pFieldVal;
+ if (fieldAttr & fdStatic)
+ pBaseType->GetStaticFieldValue(fieldDef, pFrame, &pFieldVal);
+ else if(pInnerValue != NULL)
+ {
+ ToRelease<ICorDebugObjectValue> pObjValue;
+ if (SUCCEEDED(pInnerValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
+ pObjValue->GetFieldValue(pBaseClass, fieldDef, &pFieldVal);
+ }
+
+ // we didn't get a value yet and there is default value available
+ // need to calculate the type because there won't be a ICorDebugValue to derive it from
+ if(pFieldVal == NULL && pDefaultValue != NULL)
+ {
+ FindTypeFromElementType(fieldDefaultValueEt, &pFieldValType);
+ }
+ else
+ {
+ // if we aren't using default value, make sure it is cleared out
+ pDefaultValue = NULL;
+ cchDefaultValue = 0;
+ }
+
+ // if we still don't have a value, check if we are trying to get an instance field from a static type
+ if(pInnerValue == NULL && pFieldVal == NULL && pDefaultValue == NULL)
+ {
+ WCHAR pObjectTypeName[MAX_EXPRESSION];
+ CalculateTypeName(pBaseType, pObjectTypeName, MAX_EXPRESSION);
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Can not evaluate instance field \'%s\' from static type \'%s\'", pIdentifier, pObjectTypeName);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, pFieldVal, pFieldValType, pDefaultValue, cchDefaultValue, pFrame, ppExpressionNode);
+ }
+ }
+
+ //advance to next base type
+ ICorDebugType* pTemp = NULL;
+ pBaseType->GetBase(&pTemp);
+ pBaseType = pTemp;
+ }
+
+ WCHAR pObjectTypeName[MAX_EXPRESSION];
+ CalculateTypeName(pType, pObjectTypeName, MAX_EXPRESSION);
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Field \'%s\' does not exist in type \'%s\'", pIdentifier, pObjectTypeName);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ return Status;
+}
+
+// Splits apart a C#-like expression and determines the first identifier in the string and updates expression to point
+// at the remaining unparsed portion
+HRESULT ExpressionNode::ParseNextIdentifier(__in_z WCHAR** expression, __inout_ecount(cchIdentifierName) WCHAR* identifierName, DWORD cchIdentifierName, __inout_ecount(cchErrorMessage) WCHAR* errorMessage, DWORD cchErrorMessage, DWORD* charactersParsed, BOOL* isArrayIndex)
+{
+
+ // This algorithm is best understood as a two stage process. The first stage splits
+ // the expression into two chunks an identifier and a remaining expression. The second stage
+ // normalizes the identifier. The splitting algorithm doesn't care if identifiers are well-formed
+ // at all, we do some error checking in the 2nd stage though. For the splitting stage, an identifier is
+ // any first character, followed by as many characters as possible that aren't a '.' or a '['.
+ // In the 2nd stage any '.' character is removed from the front (the only place it could be)
+ // and enclosing braces are removed. An error is recorded if the identifier ends 0 length or the
+ // opening bracket isn't matched. Here is an example showing how we would parse an expression
+ // which is deliberately not very well formed. Each line is the result of calling this function once... it
+ // takes many calls to break the entire expression down.
+ //
+ // expression 1st stage identifier 2nd stage identifier
+ // foo.bar[18..f[1.][2][[
+ // .bar[18..f[1.][2][[ foo foo
+ // [18..f[1.][2][[ .bar bar
+ // ..f[1.][2][[ [18 error no ]
+ // .f[1.][2][[ . error 0-length
+ // [1.][2][[ .f f
+ // .][2][[ [1 error no ]
+ // [2][[ .] ] (we don't error check legal CLI identifier name characters)
+ // [[ [2] 2
+ // [ [ error no ]
+ // [ error no ]
+
+ // not an error, just the end of the expression
+ if(*expression == NULL || **expression == 0)
+ return S_FALSE;
+
+ WCHAR* expressionStart = *expression;
+ DWORD currentCharsParsed = *charactersParsed;
+ DWORD identifierLen = (DWORD) _wcscspn(expressionStart, L".[");
+ // if the first character was a . or [ skip over it. Note that we don't
+ // do this always in case the first WCHAR was part of a surrogate pair
+ if(identifierLen == 0)
+ {
+ identifierLen = (DWORD) _wcscspn(expressionStart+1, L".[") + 1;
+ }
+
+ *expression += identifierLen;
+ *charactersParsed += identifierLen;
+
+ // done with the first stage splitting, on to 2nd stage
+
+ // a . should be followed by field name
+ if(*expressionStart == L'.')
+ {
+ if(identifierLen == 1) // 0-length after .
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing name after character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen-1 >= cchIdentifierName)
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, name at character %d is too long", currentCharsParsed+2);
+ return E_FAIL;
+ }
+ *isArrayIndex = FALSE;
+ wcsncpy_s(identifierName, cchIdentifierName, expressionStart+1, identifierLen-1);
+ return S_OK;
+ }
+ // an open bracket should be followed by a decimal value and then a closing bracket
+ else if(*expressionStart == L'[')
+ {
+ if(*(expressionStart+identifierLen-1) != L']')
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing or invalid index expression at character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen <= 2) // 0-length between []
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing index after character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen-2 >= cchIdentifierName)
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, index at character %d is too large", currentCharsParsed+2);
+ return E_FAIL;
+ }
+ *isArrayIndex = TRUE;
+ wcsncpy_s(identifierName, cchIdentifierName, expressionStart+1, identifierLen-2);
+ return S_OK;
+ }
+ else // no '.' or '[', this is an initial name
+ {
+ if(identifierLen == 0) // 0-length
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing name after character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen >= cchIdentifierName)
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, name at character %d is too long", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ *isArrayIndex = FALSE;
+ wcsncpy_s(identifierName, cchIdentifierName, expressionStart, identifierLen);
+ return S_OK;
+ }
+}
+
+
+// Iterate through all parameters in the ILFrame calling the callback function for each of them
+HRESULT ExpressionNode::EnumerateParameters(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData)
+{
+ HRESULT Status = S_OK;
+
+ ULONG cParams = 0;
+ ToRelease<ICorDebugValueEnum> pParamEnum;
+ IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
+ IfFailRet(pParamEnum->GetCount(&cParams));
+ DWORD methAttr = 0;
+ IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
+ for (ULONG i=0; i < cParams; i++)
+ {
+ ULONG paramNameLen = 0;
+ mdParamDef paramDef;
+ WCHAR paramName[mdNameLen] = L"\0";
+
+ if(i == 0 && (methAttr & mdStatic) == 0)
+ swprintf_s(paramName, mdNameLen, L"this\0");
+ else
+ {
+ int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
+ if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, &paramDef)))
+ pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, &paramNameLen, NULL, NULL, NULL, NULL);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, L"param_%d\0", i);
+
+ ToRelease<ICorDebugValue> pValue;
+ ULONG cArgsFetched;
+ WCHAR pErrorMessage[MAX_ERROR] = L"\0";
+ HRESULT hr = pParamEnum->Next(1, &pValue, &cArgsFetched);
+ if (FAILED(hr))
+ {
+ swprintf_s(pErrorMessage, MAX_ERROR, L" + (Error 0x%x retrieving parameter '%S')\n", hr, paramName);
+ }
+ if (hr == S_FALSE)
+ {
+ break;
+ }
+ pCallback(pValue, paramName, pErrorMessage, pUserData);
+ }
+
+ return Status;
+}
+
+// Enumerate all locals in the given ILFrame, calling the callback method for each of them
+HRESULT ExpressionNode::EnumerateLocals(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData)
+{
+ HRESULT Status = S_OK;
+ ULONG cLocals = 0;
+ ToRelease<ICorDebugFunction> pFunction;
+ ToRelease<ICorDebugModule> pModule;
+ if(SUCCEEDED(pILFrame->GetFunction(&pFunction)))
+ {
+ IfFailRet(pFunction->GetModule(&pModule));
+ }
+ ToRelease<ICorDebugValueEnum> pLocalsEnum;
+ IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
+ IfFailRet(pLocalsEnum->GetCount(&cLocals));
+ if (cLocals > 0)
+ {
+ SymbolReader symReader;
+ bool symbolsAvailable = false;
+ if(pModule != NULL && SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
+ symbolsAvailable = true;
+
+ for (ULONG i=0; i < cLocals; i++)
+ {
+ ULONG paramNameLen = 0;
+ WCHAR paramName[mdNameLen] = L"\0";
+ WCHAR pErrorMessage[MAX_ERROR] = L"\0";
+ ToRelease<ICorDebugValue> pValue;
+ HRESULT hr = S_OK;
+ if(symbolsAvailable)
+ hr = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
+ else
+ {
+ ULONG cArgsFetched;
+ hr = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, L"local_%d\0", i);
+
+ if (FAILED(hr))
+ {
+ swprintf_s(pErrorMessage, MAX_ERROR, L" + (Error 0x%x retrieving local variable '%S')\n", hr, paramName);
+ }
+ else if (hr == S_FALSE)
+ {
+ break;
+ }
+ pCallback(pValue, paramName, pErrorMessage, pUserData);
+ }
+ }
+
+ return Status;
+}
+
+// Iterates over all frames on the current thread's stack, calling the callback function for each of them
+HRESULT ExpressionNode::EnumerateFrames(FrameEnumCallback pCallback, VOID* pUserData)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugThread> pThread;
+ ToRelease<ICorDebugThread3> pThread3;
+ ToRelease<ICorDebugStackWalk> pStackWalk;
+ ULONG ulThreadID = 0;
+ g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
+
+ IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
+ IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
+ IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
+
+ InternalFrameManager internalFrameManager;
+ IfFailRet(internalFrameManager.Init(pThread3));
+
+ int currentFrame = -1;
+
+ for (Status = S_OK; ; Status = pStackWalk->Next())
+ {
+ currentFrame++;
+
+ if (Status == CORDBG_S_AT_END_OF_STACK)
+ {
+ break;
+ }
+ IfFailRet(Status);
+
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ break;
+ }
+
+ CROSS_PLATFORM_CONTEXT context;
+ ULONG32 cbContextActual;
+ if ((Status=pStackWalk->GetContext(
+ DT_CONTEXT_FULL,
+ sizeof(context),
+ &cbContextActual,
+ (BYTE *)&context))!=S_OK)
+ {
+ ExtOut("GetFrameContext failed: %lx\n",Status);
+ break;
+ }
+
+ ToRelease<ICorDebugFrame> pFrame;
+ IfFailRet(pStackWalk->GetFrame(&pFrame));
+ if (Status == S_FALSE)
+ {
+ Status = S_OK;
+ continue;
+ }
+
+ pCallback(pFrame, pUserData);
+ }
+
+ return Status;
+}
+
+// Determines the corresponding ICorDebugType for a given primitive type
+HRESULT ExpressionNode::FindTypeFromElementType(CorElementType et, ICorDebugType** ppType)
+{
+ HRESULT Status;
+ switch (et)
+ {
+ default:
+ Status = E_FAIL;
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ Status = FindTypeByName(L"System.Boolean", ppType);
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ Status = FindTypeByName(L"System.Char", ppType);
+ break;
+
+ case ELEMENT_TYPE_I1:
+ Status = FindTypeByName(L"System.SByte", ppType);
+ break;
+
+ case ELEMENT_TYPE_U1:
+ Status = FindTypeByName(L"System.Byte", ppType);
+ break;
+
+ case ELEMENT_TYPE_I2:
+ Status = FindTypeByName(L"System.Short", ppType);
+ break;
+
+ case ELEMENT_TYPE_U2:
+ Status = FindTypeByName(L"System.UShort", ppType);
+ break;
+
+ case ELEMENT_TYPE_I:
+ Status = FindTypeByName(L"System.Int32", ppType);
+ break;
+
+ case ELEMENT_TYPE_U:
+ Status = FindTypeByName(L"System.UInt32", ppType);
+ break;
+
+ case ELEMENT_TYPE_I4:
+ Status = FindTypeByName(L"System.Int32", ppType);
+ break;
+
+ case ELEMENT_TYPE_U4:
+ Status = FindTypeByName(L"System.UInt32", ppType);
+ break;
+
+ case ELEMENT_TYPE_I8:
+ Status = FindTypeByName(L"System.Int64", ppType);
+ break;
+
+ case ELEMENT_TYPE_U8:
+ Status = FindTypeByName(L"System.UInt64", ppType);
+ break;
+
+ case ELEMENT_TYPE_R4:
+ Status = FindTypeByName(L"System.Single", ppType);
+ break;
+
+ case ELEMENT_TYPE_R8:
+ Status = FindTypeByName(L"System.Double", ppType);
+ break;
+
+ case ELEMENT_TYPE_OBJECT:
+ Status = FindTypeByName(L"System.Object", ppType);
+ break;
+
+ case ELEMENT_TYPE_STRING:
+ Status = FindTypeByName(L"System.String", ppType);
+ break;
+ }
+ return Status;
+}
+
+// Gets the appropriate element type encoding for well-known fully qualified type names
+// This doesn't work for arbitrary types, just types that have CorElementType short forms.
+HRESULT ExpressionNode::GetCanonicalElementTypeForTypeName(__in_z WCHAR* pTypeName, CorElementType *et)
+{
+ //Sadly ICorDebug deliberately prevents creating ICorDebugType instances
+ //that use canonical short form element types... seems like an issue to me.
+
+ if(_wcscmp(pTypeName, L"System.String")==0)
+ {
+ *et = ELEMENT_TYPE_STRING;
+ }
+ else if(_wcscmp(pTypeName, L"System.Object")==0)
+ {
+ *et = ELEMENT_TYPE_OBJECT;
+ }
+ else if(_wcscmp(pTypeName, L"System.Void")==0)
+ {
+ *et = ELEMENT_TYPE_VOID;
+ }
+ else if(_wcscmp(pTypeName, L"System.Boolean")==0)
+ {
+ *et = ELEMENT_TYPE_BOOLEAN;
+ }
+ else if(_wcscmp(pTypeName, L"System.Char")==0)
+ {
+ *et = ELEMENT_TYPE_CHAR;
+ }
+ else if(_wcscmp(pTypeName, L"System.Byte")==0)
+ {
+ *et = ELEMENT_TYPE_U1;
+ }
+ else if(_wcscmp(pTypeName, L"System.Sbyte")==0)
+ {
+ *et = ELEMENT_TYPE_I1;
+ }
+ else if(_wcscmp(pTypeName, L"System.Int16")==0)
+ {
+ *et = ELEMENT_TYPE_I2;
+ }
+ else if(_wcscmp(pTypeName, L"System.UInt16")==0)
+ {
+ *et = ELEMENT_TYPE_U2;
+ }
+ else if(_wcscmp(pTypeName, L"System.UInt32")==0)
+ {
+ *et = ELEMENT_TYPE_U4;
+ }
+ else if(_wcscmp(pTypeName, L"System.Int32")==0)
+ {
+ *et = ELEMENT_TYPE_I4;
+ }
+ else if(_wcscmp(pTypeName, L"System.UInt64")==0)
+ {
+ *et = ELEMENT_TYPE_U8;
+ }
+ else if(_wcscmp(pTypeName, L"System.Int64")==0)
+ {
+ *et = ELEMENT_TYPE_I8;
+ }
+ else if(_wcscmp(pTypeName, L"System.Single")==0)
+ {
+ *et = ELEMENT_TYPE_R4;
+ }
+ else if(_wcscmp(pTypeName, L"System.Double")==0)
+ {
+ *et = ELEMENT_TYPE_R8;
+ }
+ else if(_wcscmp(pTypeName, L"System.IntPtr")==0)
+ {
+ *et = ELEMENT_TYPE_U;
+ }
+ else if(_wcscmp(pTypeName, L"System.UIntPtr")==0)
+ {
+ *et = ELEMENT_TYPE_I;
+ }
+ else if(_wcscmp(pTypeName, L"System.TypedReference")==0)
+ {
+ *et = ELEMENT_TYPE_TYPEDBYREF;
+ }
+ else
+ {
+ return E_FAIL; // can't tell from a name whether it should be valuetype or class
+ }
+ return S_OK;
+}
+
+// Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+// This will search across all AppDomains and Assemblies
+HRESULT ExpressionNode::FindTypeByName(__in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugAppDomainEnum> pAppDomainEnum;
+ IfFailRet(g_pCorDebugProcess->EnumerateAppDomains(&pAppDomainEnum));
+ DWORD count;
+ IfFailRet(pAppDomainEnum->GetCount(&count));
+ for(DWORD i = 0; i < count; i++)
+ {
+ ToRelease<ICorDebugAppDomain> pAppDomain;
+ DWORD countFetched = 0;
+ IfFailRet(pAppDomainEnum->Next(1, &pAppDomain, &countFetched));
+ Status = FindTypeByName(pAppDomain, pTypeName, ppType);
+ if(SUCCEEDED(Status))
+ break;
+ }
+
+ return Status;
+}
+
+// Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+// This will search across all Assemblies in the given AppDomain
+HRESULT ExpressionNode::FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugAssemblyEnum> pAssemblyEnum;
+ IfFailRet(pAppDomain->EnumerateAssemblies(&pAssemblyEnum));
+ DWORD count;
+ IfFailRet(pAssemblyEnum->GetCount(&count));
+ for(DWORD i = 0; i < count; i++)
+ {
+ ToRelease<ICorDebugAssembly> pAssembly;
+ DWORD countFetched = 0;
+ IfFailRet(pAssemblyEnum->Next(1, &pAssembly, &countFetched));
+ Status = FindTypeByName(pAssembly, pTypeName, ppType);
+ if(SUCCEEDED(Status))
+ break;
+ }
+
+ return Status;
+}
+
+// Searches the assembly for any ICorDebugType that matches the given fully qualified name
+HRESULT ExpressionNode::FindTypeByName(ICorDebugAssembly* pAssembly, __in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugModuleEnum> pModuleEnum;
+ IfFailRet(pAssembly->EnumerateModules(&pModuleEnum));
+ DWORD count;
+ IfFailRet(pModuleEnum->GetCount(&count));
+ for(DWORD i = 0; i < count; i++)
+ {
+ ToRelease<ICorDebugModule> pModule;
+ DWORD countFetched = 0;
+ IfFailRet(pModuleEnum->Next(1, &pModule, &countFetched));
+ Status = FindTypeByName(pModule, pTypeName, ppType);
+ if(SUCCEEDED(Status))
+ break;
+ }
+
+ return Status;
+}
+
+// Searches a given module for any ICorDebugType that matches the given fully qualified type name
+HRESULT ExpressionNode::FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ // If the name contains a generic argument list, extract the type name from
+ // before the list
+ WCHAR rootName[mdNameLen];
+ WCHAR* pRootName = NULL;
+ int typeNameLen = (int) _wcslen(pTypeName);
+ int genericParamListStart = (int) _wcscspn(pTypeName, L"<");
+ if(genericParamListStart != typeNameLen)
+ {
+ if(pTypeName[typeNameLen-1] != L'>' || genericParamListStart > mdNameLen)
+ {
+ return E_FAIL; // mal-formed type name
+ }
+ else
+ {
+ wcsncpy_s(rootName, mdNameLen, pTypeName, genericParamListStart);
+ pRootName = rootName;
+ }
+ }
+ else
+ {
+ pRootName = pTypeName;
+ }
+
+ // Convert from name to token to ICorDebugClass
+ mdTypeDef typeDef;
+ IfFailRet(pMD->FindTypeDefByName(pRootName, NULL, &typeDef));
+ DWORD flags;
+ ULONG nameLen;
+ mdToken tkExtends;
+ IfFailRet(pMD->GetTypeDefProps(typeDef, NULL, 0, &nameLen, &flags, &tkExtends));
+ BOOL isValueType;
+ IfFailRet(IsTokenValueTypeOrEnum(tkExtends, pMD, &isValueType));
+ CorElementType et = isValueType ? ELEMENT_TYPE_VALUETYPE : ELEMENT_TYPE_CLASS;
+ ToRelease<ICorDebugClass> pClass;
+ IfFailRet(pModule->GetClassFromToken(typeDef, &pClass));
+ ToRelease<ICorDebugClass2> pClass2;
+ IfFailRet(pClass->QueryInterface(__uuidof(ICorDebugClass2), (void**)&pClass2));
+
+ // Convert from class to type - if generic then recursively resolve the generic
+ // parameter list
+ ArrayHolder<ToRelease<ICorDebugType>> typeParams = NULL;
+ int countTypeParams = 0;
+ if(genericParamListStart != typeNameLen)
+ {
+ ToRelease<ICorDebugAssembly> pAssembly;
+ IfFailRet(pModule->GetAssembly(&pAssembly));
+ ToRelease<ICorDebugAppDomain> pDomain;
+ IfFailRet(pAssembly->GetAppDomain(&pDomain));
+
+ countTypeParams = 1;
+ for(int i = genericParamListStart+1; i < typeNameLen; i++)
+ {
+ if(pTypeName[i] == L',') countTypeParams++;
+ }
+ typeParams = new ToRelease<ICorDebugType>[countTypeParams];
+
+ WCHAR* pCurName = pTypeName + genericParamListStart+1;
+ for(int i = 0; i < countTypeParams; i++)
+ {
+ WCHAR typeParamName[mdNameLen];
+ WCHAR* pNextComma = _wcschr(pCurName, L',');
+ int len = (pNextComma != NULL) ? (int)(pNextComma - pCurName) : (int)_wcslen(pCurName)-1;
+ if(len > mdNameLen)
+ return E_FAIL;
+ wcsncpy_s(typeParamName, mdNameLen, pCurName, len);
+ FindTypeByName(pDomain, typeParamName, &(typeParams[i]));
+ pCurName = pNextComma+1;
+ }
+ }
+ IfFailRet(pClass2->GetParameterizedType(et, countTypeParams, &(typeParams[0]), ppType));
+
+ return Status;
+}
+
+// Checks whether the given token is or refers to type System.ValueType or System.Enum
+HRESULT ExpressionNode::IsTokenValueTypeOrEnum(mdToken token, IMetaDataImport* pMetadata, BOOL* pResult)
+{
+ // This isn't a 100% correct check because we aren't verifying the module portion of the
+ // type identity. Arbitrary assemblies could define a type named System.ValueType or System.Enum.
+ // If that happens this code will get the answer wrong... we just assume that happens so rarely
+ // that it isn't worth doing all the overhead of assembly resolution to deal with
+
+ HRESULT Status = S_OK;
+ CorTokenType type = (CorTokenType)(token & 0xFF000000);
+
+ // only need enough space to hold either System.ValueType or System.Enum
+ //System.ValueType -> 16 characters
+ //System.Enum -> 11 characters
+ WCHAR nameBuffer[17];
+ nameBuffer[0] = L'\0';
+
+ if(type == mdtTypeRef)
+ {
+ ULONG chTypeDef;
+ pMetadata->GetTypeRefProps(token, NULL, NULL, 0, &chTypeDef);
+ if(chTypeDef > _countof(nameBuffer))
+ {
+ *pResult = FALSE;
+ return Status;
+ }
+ IfFailRet(pMetadata->GetTypeRefProps(token, NULL, nameBuffer, _countof(nameBuffer), &chTypeDef));
+ }
+ else if(type == mdtTypeDef)
+ {
+ ULONG chTypeDef;
+ pMetadata->GetTypeDefProps(token, NULL, 0, &chTypeDef, NULL, NULL);
+ if(chTypeDef > _countof(nameBuffer))
+ {
+ *pResult = FALSE;
+ return Status;
+ }
+ IfFailRet(pMetadata->GetTypeDefProps(token, nameBuffer, _countof(nameBuffer), &chTypeDef, NULL, NULL));
+ }
+
+ if(_wcscmp(nameBuffer, L"System.ValueType") == 0 ||
+ _wcscmp(nameBuffer, L"System.Enum") == 0)
+ {
+ *pResult = TRUE;
+ }
+ else
+ {
+ *pResult = FALSE;
+ }
+ return Status;
+}
diff --git a/src/ToolBox/SOS/Strike/ExpressionNode.h b/src/ToolBox/SOS/Strike/ExpressionNode.h
new file mode 100644
index 0000000000..507a8a53d3
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ExpressionNode.h
@@ -0,0 +1,307 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+#ifndef _EXPRESSION_NODE_
+#define _EXPRESSION_NODE_
+
+#ifdef FEATURE_PAL
+#error This file isn't designed to build in PAL
+#endif
+
+#include "strike.h"
+#include "sos.h"
+#include "util.h"
+
+#define MAX_EXPRESSION 500
+#define MAX_ERROR 500
+
+
+// Represents one node in a tree of expressions and sub-expressions
+// These nodes are used in the !watch expandable expression tree
+// Each node consists of a string based C#-like expression and its
+// evaluation within the current context of the debuggee.
+//
+// These nodes are also intended for eventual use in ClrStack -i expression tree
+// but ClrStack -i hasn't yet been refactored to use them
+//
+// Each node can evaluate to:
+// nothing - if an error occurs during expression parsing or the expression
+// names don't match to anything in the debuggee
+// a debuggee value - these are values that are backed in memory of the debuggee
+// (ICorDebugValue objects) or build time constants which are
+// stored in the assembly metadata.
+// a debuggee type - instead of refering to a particular instance of a type (the
+// value case above), nodes can directly refer to a type definition
+// represented by an ICorDebugType object
+class ExpressionNode
+{
+public:
+
+ typedef VOID (*ExpressionNodeVisitorCallback)(ExpressionNode* pExpressionNode, int depth, VOID* pUserData);
+
+ // Returns the complete expression being evaluated to get the value for this node
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetAbsoluteExpression();
+
+ // Returns the sub expression that logically indicates how the parent expression
+ // was built upon to reach this node. This relative value has no purpose other
+ // than an identifier and to convey UI meaning to the user. At present typical values
+ // are the name of type, a local, a parameter, a field, an array index, or '<basetype>'
+ // for a baseclass casting operation
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetRelativeExpression();
+
+ // Returns a text representation of the type of value that this node refers to
+ // It is possible this node doesn't evaluate to anything and therefore has no
+ // type
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetTypeName();
+
+ // Returns a text representation of the value for this node. It is possible that
+ // this node doesn't evaluate to anything and therefore has no value text.
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetTextValue();
+
+ // If there is any error during the evaluation of this node's expression, it is
+ // returned here.
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetErrorMessage();
+
+ // Factory function for creating the expression node at the root of a tree
+ static HRESULT CreateExpressionNode(__in_z WCHAR* pExpression, ExpressionNode** ppExpressionNode);
+
+ // Performs recursive expansion within the tree for nodes that are along the path to varToExpand.
+ // Expansion involves calulating a set of child expressions from the current expression via
+ // field dereferencing, array index dereferencing, or casting to a base type.
+ // For example if a tree was rooted with expression 'foo.bar' and varToExpand is '(Baz)foo.bar[9]'
+ // then 'foo.bar', 'foo.bar[9]', and '(Baz)foo.bar[9]' nodes would all be expanded.
+ HRESULT Expand(__in_z WCHAR* varToExpand);
+
+ // Standard depth first search tree traversal pattern with a callback
+ VOID DFSVisit(ExpressionNodeVisitorCallback pFunc, VOID* pUserData, int depth=0);
+
+private:
+ // for nodes that evaluate to a type, this is that type
+ // for nodes that evaluate to a debuggee value, this is the type of that
+ // value or one of its base types. It represents the type the value should
+ // displayed and expanded as.
+ ToRelease<ICorDebugType> pTypeCast;
+
+ // for nodes that evaluate to a memory backed debuggee value, this is that value
+ ToRelease<ICorDebugValue> pValue;
+
+ // if this node gets expanded and it has thread-static or context-static sub-fields,
+ // this frame disambiguates which thread and context to use.
+ ToRelease<ICorDebugILFrame> pILFrame;
+
+ // TODO: exactly which metadata is this supposed to be? try to get rid of this
+ ToRelease<IMetaDataImport> pMD;
+
+ // PERF: this could be a lot more memory efficient
+ WCHAR pTextValue[MAX_EXPRESSION];
+ WCHAR pErrorMessage[MAX_ERROR];
+ WCHAR pAbsoluteExpression[MAX_EXPRESSION];
+ WCHAR pRelativeExpression[MAX_EXPRESSION];
+ WCHAR pTypeName[MAX_EXPRESSION];
+
+ // if this value represents a build time constant debuggee value, this is a pointer
+ // to the value data stored in metadata and its size
+ UVCP_CONSTANT pDefaultValue;
+ ULONG cchDefaultValue;
+
+ // Pointer in a linked list of sibling nodes that all share the same parent
+ ExpressionNode* pNextSibling;
+ // Pointer to the first child node of this node, other children can be found
+ // by following the child's sibling list.
+ ExpressionNode* pChild;
+
+ typedef VOID (*VariableEnumCallback)(ICorDebugValue* pValue, WCHAR* pName, WCHAR* pErrorMessage, VOID* pUserData);
+ typedef VOID (*FrameEnumCallback)(ICorDebugFrame* pFrame, VOID* pUserData);
+
+ // Indicates how a child node was derived from its parent
+ enum ChildKind
+ {
+ ChildKind_Field,
+ ChildKind_Index,
+ ChildKind_BaseClass
+ };
+
+ // Creates a new expression with a given debuggee value and frame
+ ExpressionNode(__in_z WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame);
+
+ // Creates a new expression that has an error and no value
+ ExpressionNode(__in_z WCHAR* pExpression, __in_z WCHAR* pErrorMessage);
+
+ // Creates a new child expression
+ ExpressionNode(__in_z WCHAR* pParentExpression, ChildKind ck, __in_z WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue = NULL, ULONG cchDefaultValue = 0);
+
+ // Common member initialization for the constructors
+ VOID Init(ICorDebugValue* pValue, ICorDebugType* pTypeCast, ICorDebugILFrame* pFrame);
+
+ // Retreves the correct IMetaDataImport for the type represented in this node and stores it
+ // in pMD.
+ HRESULT PopulateMetaDataImport();
+
+ // Determines the string representation of pType and stores it in typeName
+ static HRESULT CalculateTypeName(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen);
+
+
+ // Appends angle brackets and the generic argument list to a type name
+ static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen);
+
+ // Determines the text name for the type of this node and caches it
+ HRESULT PopulateType();
+
+ // Node expansion helpers
+
+ // Inserts a new child at the end of the linked list of children
+ // PERF: This has O(N) insert time but these lists should never be large
+ VOID AddChild(ExpressionNode* pNewChild);
+
+ // Helper that determines if the current node is on the path of nodes represented by
+ // expression varToExpand
+ BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand);
+
+ // Expands this array node by creating child nodes with expressions refering to individual array elements
+ HRESULT ExpandSzArray(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand);
+
+ // Expands this struct/class node by creating child nodes with expressions refering to individual field values
+ // and one node for the basetype value
+ HRESULT ExpandFields(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand);
+
+ // Value Population functions
+
+ //Helper for unwrapping values
+ static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pInputValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL);
+
+ // Returns TRUE if the value derives from System.Enum
+ static BOOL IsEnum(ICorDebugValue * pInputValue);
+
+ // Calculates the value text for nodes that have enum values
+ HRESULT PopulateEnumValue(ICorDebugValue* pEnumValue, BYTE* enumValue);
+
+ // Helper that fetches the text value of a string ICorDebugValue
+ HRESULT GetDebuggeeStringValue(ICorDebugValue* pInputValue, __inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer);
+
+ // Helper that fetches the text value of a string build-time literal
+ HRESULT GetConstantStringValue(__inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer);
+
+ // Helper that caches the textual value for nodes that evaluate to array objects
+ HRESULT PopulateSzArrayValue(ICorDebugValue* pInputValue);
+
+ // Helper that caches the textual value for nodes of any type
+ HRESULT PopulateTextValueHelper();
+
+ // Caches the textual value of this node
+ HRESULT PopulateTextValue();
+
+
+ // Expression parsing and search
+
+ // In/Out parameters for the EvaluateExpressionFrameScanCallback
+ typedef struct _EvaluateExpressionFrameScanData
+ {
+ WCHAR* pIdentifier;
+ ToRelease<ICorDebugValue> pFoundValue;
+ ToRelease<ICorDebugILFrame> pFoundFrame;
+ ToRelease<ICorDebugILFrame> pFirstFrame;
+ WCHAR* pErrorMessage;
+ DWORD cchErrorMessage;
+ } EvaluateExpressionFrameScanData;
+
+ //Callback that searches a frame to determine if it contains a local variable or parameter of a given name
+ static VOID EvaluateExpressionFrameScanCallback(ICorDebugFrame* pFrame, VOID* pUserData);
+
+ //Callback checks to see if a given local/parameter has name pName
+ static VOID EvaluateExpressionVariableScanCallback(ICorDebugValue* pValue, __in_z WCHAR* pName, __out_z WCHAR* pErrorMessage, VOID* pUserData);
+
+ //Factory method that recursively parses pExpression and create an ExpressionNode
+ // pExpression - the entire expression being parsed
+ // pExpressionRemainder - the portion of the expression that remains to be parsed in this
+ // recursive invocation
+ // charactersParsed - the number of characters that have been parsed from pExpression
+ // so far (todo: this is basically the difference between remainder and
+ // full expression, do we need it?)
+ // pParsedValue - A debuggee value that should be used as the context for interpreting
+ // pExpressionRemainder
+ // pParsedType - A debuggee type that should be used as the context for interpreting
+ // pExpressionRemainder.
+ // pParsedDefaultValue - A fixed value from metadata that should be used as context for
+ // interpretting pExpressionRemainder
+ // cchParsedDefaultValue- Size of pParsedDefaultValue
+ // pFrame - A debuggee IL frame that disambiguates the thread and context needed
+ // to evaluate a thread-static or context-static value
+ // ppExpressionNode - OUT - the resulting expression node
+ //
+ //
+ static HRESULT CreateExpressionNodeHelper(__in_z WCHAR* pExpression,
+ __in_z WCHAR* pExpressionParseRemainder,
+ DWORD charactersParsed,
+ ICorDebugValue* pParsedValue,
+ ICorDebugType* pParsedType,
+ UVCP_CONSTANT pParsedDefaultValue,
+ ULONG cchParsedDefaultValue,
+ ICorDebugILFrame* pFrame,
+ ExpressionNode** ppExpressionNode);
+
+ // Splits apart a C#-like expression and determines the first identifier in the string and updates expression to point
+ // at the remaining unparsed portion
+ static HRESULT ParseNextIdentifier(__in_z WCHAR** expression,
+ __inout_ecount(cchIdentifierName) WCHAR* identifierName,
+ DWORD cchIdentifierName,
+ __inout_ecount(cchErrorMessage) WCHAR* errorMessage,
+ DWORD cchErrorMessage,
+ DWORD* charactersParsed,
+ BOOL* isArrayIndex);
+
+
+ // Iterate through all parameters in the ILFrame calling the callback function for each of them
+ static HRESULT EnumerateParameters(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData);
+
+ // Enumerate all locals in the given ILFrame, calling the callback method for each of them
+ static HRESULT EnumerateLocals(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData);
+
+ // Iterates over all frames on the current thread's stack, calling the callback function for each of them
+ static HRESULT EnumerateFrames(FrameEnumCallback pCallback, VOID* pUserData);
+
+ // Determines the corresponding ICorDebugType for a given primitive type
+ static HRESULT FindTypeFromElementType(CorElementType et, ICorDebugType** ppType);
+
+ // Gets the appropriate element type encoding for well-known fully qualified type names
+ // This doesn't work for arbitrary types, just types that have CorElementType short forms.
+ static HRESULT GetCanonicalElementTypeForTypeName(__in_z WCHAR* pTypeName, CorElementType *et);
+
+ // Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+ // This will search across all AppDomains and Assemblies
+ static HRESULT FindTypeByName(__in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+ // This will search across all Assemblies in the given AppDomain
+ static HRESULT FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Searches the assembly for any ICorDebugType that matches the given fully qualified name
+ static HRESULT FindTypeByName(ICorDebugAssembly* pAssembly, __in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Searches a given module for any ICorDebugType that matches the given fully qualified type name
+ static HRESULT FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Checks whether the given token is or refers to type System.ValueType or System.Enum
+ static HRESULT IsTokenValueTypeOrEnum(mdToken token, IMetaDataImport* pMetadata, BOOL* pResult);
+};
+
+#endif
diff --git a/src/ToolBox/SOS/Strike/Native.rc b/src/ToolBox/SOS/Strike/Native.rc
new file mode 100644
index 0000000000..179ddfd24a
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/Native.rc
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft NTSD extension for .NET Runtime\0"
+
+#include <fxver.h>
+#include <fxver.rc>
+
+DOCUMENTATION TEXT DISCARDABLE "sosdocs.txt"
diff --git a/src/ToolBox/SOS/Strike/SOS.nativeproj b/src/ToolBox/SOS/Strike/SOS.nativeproj
new file mode 100644
index 0000000000..4c0fc7616f
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/SOS.nativeproj
@@ -0,0 +1,7 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetHostLocal.props"/>
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\toolbox\sos\strike\sos.targets" />
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ </PropertyGroup>
+</Project>
diff --git a/src/ToolBox/SOS/Strike/SOS.sln b/src/ToolBox/SOS/Strike/SOS.sln
new file mode 100644
index 0000000000..08f4a64836
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/SOS.sln
@@ -0,0 +1,76 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SOS", "SOS.vcxproj", "{3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mscordacwks", "..\..\..\mscordacwks.vcproj", "{C5716445-C233-4491-85A4-31B75731DD95}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mscordbi", "..\..\..\mscordbi.vcproj", "{95A6AE03-EC45-4450-93DB-9B21890F79E7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ amd64chk|Win32 = amd64chk|Win32
+ amd64dbg|Win32 = amd64dbg|Win32
+ amd64ret|Win32 = amd64ret|Win32
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ x86chk|Win32 = x86chk|Win32
+ x86dbg|Win32 = x86dbg|Win32
+ x86ret|Win32 = x86ret|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64chk|Win32.ActiveCfg = amd64chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64chk|Win32.Build.0 = amd64chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64dbg|Win32.ActiveCfg = amd64dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64dbg|Win32.Build.0 = amd64dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64ret|Win32.ActiveCfg = amd64ret|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64ret|Win32.Build.0 = amd64ret|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Debug|Win32.ActiveCfg = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Debug|Win32.Build.0 = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Release|Win32.ActiveCfg = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Release|Win32.Build.0 = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86chk|Win32.ActiveCfg = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86chk|Win32.Build.0 = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86dbg|Win32.ActiveCfg = x86dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86dbg|Win32.Build.0 = x86dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86ret|Win32.ActiveCfg = x86ret|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86ret|Win32.Build.0 = x86ret|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64chk|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64chk|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64dbg|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64dbg|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64ret|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64ret|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Debug|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Release|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Release|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86chk|Win32.ActiveCfg = x86chk|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86chk|Win32.Build.0 = x86chk|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86dbg|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86dbg|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86ret|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86ret|Win32.Build.0 = Debug|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64chk|Win32.ActiveCfg = amd64chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64chk|Win32.Build.0 = amd64chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64dbg|Win32.ActiveCfg = amd64dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64dbg|Win32.Build.0 = amd64dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64ret|Win32.ActiveCfg = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64ret|Win32.Build.0 = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Debug|Win32.ActiveCfg = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Debug|Win32.Build.0 = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Release|Win32.ActiveCfg = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Release|Win32.Build.0 = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86chk|Win32.ActiveCfg = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86chk|Win32.Build.0 = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86dbg|Win32.ActiveCfg = x86dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86dbg|Win32.Build.0 = x86dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86ret|Win32.ActiveCfg = x86ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86ret|Win32.Build.0 = x86ret|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/ToolBox/SOS/Strike/SOS.vcproj b/src/ToolBox/SOS/Strike/SOS.vcproj
new file mode 100644
index 0000000000..aff5e7cc60
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/SOS.vcproj
@@ -0,0 +1,303 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="SOS"
+ ProjectGUID="{3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}"
+ Keyword="MakeFileProj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="x86chk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:x86/buildType:chk"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:x86/buildType:chk"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2c\i386\SOS.dll"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86dbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:x86/buildType:dbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:x86/buildType:dbg"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2d\i386\SOS.dll"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86ret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:x86/buildType:ret"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:x86/buildType:ret"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2r\i386\SOS.dll"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64chk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:amd64/buildType:chk"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:amd64/buildType:chk"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2c\amd64\SOS.dll"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1;_DEBUG=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64dbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:amd64/buildType:dbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:amd64/buildType:dbg"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2d\amd64\SOS.dll"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64ret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:amd64/buildType:ret"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:amd64/buildType:ret"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2r\amd64\SOS.dll"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\data.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\dbgeng.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\dbghelp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\disasm.h"
+ >
+ </File>
+ <File
+ RelativePath=".\exts.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ntinfo.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sos_md.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sos_stacktrace.h"
+ >
+ </File>
+ <File
+ RelativePath=".\strike.h"
+ >
+ </File>
+ <File
+ RelativePath=".\symbol.h"
+ >
+ </File>
+ <File
+ RelativePath=".\util.h"
+ >
+ </File>
+ <File
+ RelativePath=".\UtilCode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\wdbgexts.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\Native.rc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\disasm.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\disasmIA64.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\disasmX86.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\dllsext.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eeheap.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\exts.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\gchist.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\gcroot.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\metadata.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sildasm.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sos.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sos.def"
+ >
+ </File>
+ <File
+ RelativePath=".\sos.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stressLogDump.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\strike.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\util.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\utilIA64.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\utilX86.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\vm.cpp"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\sosdocs.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\sources"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/ToolBox/SOS/Strike/UtilCode.h b/src/ToolBox/SOS/Strike/UtilCode.h
new file mode 100644
index 0000000000..a002edc89e
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/UtilCode.h
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+// An empty file so that gcdump.cpp does not include the one from other
+// places.
diff --git a/src/ToolBox/SOS/Strike/WatchCmd.cpp b/src/ToolBox/SOS/Strike/WatchCmd.cpp
new file mode 100644
index 0000000000..443f1dd6ef
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/WatchCmd.cpp
@@ -0,0 +1,331 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "WatchCmd.h"
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+_PersistList::~_PersistList()
+{
+ PersistWatchExpression* pCur = pHeadExpr;
+ while(pCur != NULL)
+ {
+ PersistWatchExpression* toDelete = pCur;
+ pCur = pCur->pNext;
+ delete toDelete;
+ }
+}
+
+WatchCmd::WatchCmd() :
+pExpressionListHead(NULL)
+{ }
+WatchCmd::~WatchCmd()
+{
+ Clear();
+ PersistList* pCur = pPersistListHead;
+ while(pCur != NULL)
+ {
+ PersistList* toDelete = pCur;
+ pCur = pCur->pNext;
+ delete toDelete;
+ }
+}
+
+// Deletes all current watch expressions from the watch list
+// (does not delete persisted watch lists though)
+HRESULT WatchCmd::Clear()
+{
+ WatchExpression* pCurrent = pExpressionListHead;
+ while(pCurrent != NULL)
+ {
+ WatchExpression* toDelete = pCurrent;
+ pCurrent = pCurrent->pNext;
+ delete toDelete;
+ }
+ pExpressionListHead = NULL;
+ return S_OK;
+}
+
+// Adds a new expression to the active watch list
+HRESULT WatchCmd::Add(__in_z WCHAR* pExpression)
+{
+ WatchExpression* pExpr = new WatchExpression;
+ if(pExpr == NULL)
+ return E_OUTOFMEMORY;
+ wcsncpy_s(pExpr->pExpression, MAX_EXPRESSION, pExpression, _TRUNCATE);
+ pExpr->pNext = NULL;
+
+ WatchExpression** ppCurrent = &pExpressionListHead;
+ while(*ppCurrent != NULL)
+ ppCurrent = &((*ppCurrent)->pNext);
+ *ppCurrent = pExpr;
+ return S_OK;
+}
+
+// removes an expression at the given index in the active watch list
+HRESULT WatchCmd::Remove(int index)
+{
+ HRESULT Status = S_FALSE;
+ WatchExpression** ppCurrent = &pExpressionListHead;
+ for(int i=1; *ppCurrent != NULL; i++)
+ {
+ if(i == index)
+ {
+ WatchExpression* toDelete = *ppCurrent;
+ *ppCurrent = (*ppCurrent)->pNext;
+ delete toDelete;
+ Status = S_OK;
+ break;
+ }
+ ppCurrent = &((*ppCurrent)->pNext);
+
+ }
+ return Status;
+}
+
+// Evaluates and prints a tree version of the active watch list
+// The tree will be expanded along the nodes in expansionPath
+// Optionally the list is filtered to only show differences from pFilterName (the name of a persisted watch list)
+HRESULT WatchCmd::Print(int expansionIndex, __in_z WCHAR* expansionPath, __in_z WCHAR* pFilterName)
+{
+ HRESULT Status = S_OK;
+ INIT_API_EE();
+ INIT_API_DAC();
+ EnableDMLHolder dmlHolder(TRUE);
+ IfFailRet(InitCorDebugInterface());
+
+ PersistList* pFilterList = NULL;
+ if(pFilterName != NULL)
+ {
+ pFilterList = pPersistListHead;
+ while(pFilterList != NULL)
+ {
+ if(_wcscmp(pFilterList->pName, pFilterName)==0)
+ break;
+ pFilterList = pFilterList->pNext;
+ }
+ }
+
+ PersistWatchExpression* pHeadFilterExpr = (pFilterList != NULL) ? pFilterList->pHeadExpr : NULL;
+
+ WatchExpression* pExpression = pExpressionListHead;
+ int index = 1;
+ while(pExpression != NULL)
+ {
+ ExpressionNode* pResult = NULL;
+ if(FAILED(Status = ExpressionNode::CreateExpressionNode(pExpression->pExpression, &pResult)))
+ {
+ ExtOut(" %d) Error: HRESULT 0x%x while evaluating expression \'%S\'", index, Status, pExpression->pExpression);
+ }
+ else
+ {
+ //check for matching absolute expression
+ PersistWatchExpression* pCurFilterExpr = pHeadFilterExpr;
+ while(pCurFilterExpr != NULL)
+ {
+ if(_wcscmp(pCurFilterExpr->pExpression, pResult->GetAbsoluteExpression())==0)
+ break;
+ pCurFilterExpr = pCurFilterExpr->pNext;
+ }
+
+ // check for matching persist evaluation on the matching expression
+ BOOL print = TRUE;
+ if(pCurFilterExpr != NULL)
+ {
+ WCHAR pCurPersistResult[MAX_EXPRESSION];
+ FormatPersistResult(pCurPersistResult, MAX_EXPRESSION, pResult);
+ if(_wcscmp(pCurPersistResult, pCurFilterExpr->pPersistResult)==0)
+ {
+ print = FALSE;
+ }
+ }
+
+ //expand and print
+ if(print)
+ {
+ if(index == expansionIndex)
+ pResult->Expand(expansionPath);
+ PrintCallbackData data;
+ data.index = index;
+ WCHAR pCommand[MAX_EXPRESSION];
+ swprintf_s(pCommand, MAX_EXPRESSION, L"!watch -expand %d", index);
+ data.pCommand = pCommand;
+ pResult->DFSVisit(EvalPrintCallback, (VOID*)&data);
+ }
+ delete pResult;
+ }
+ pExpression = pExpression->pNext;
+ index++;
+ }
+ return Status;
+}
+
+// Deletes an persisted watch list by name
+HRESULT WatchCmd::RemoveList(__in_z WCHAR* pListName)
+{
+ PersistList** ppList = &pPersistListHead;
+ while(*ppList != NULL)
+ {
+ if(_wcscmp((*ppList)->pName, pListName) == 0)
+ {
+ PersistList* toDelete = *ppList;
+ *ppList = (*ppList)->pNext;
+ delete toDelete;
+ return S_OK;
+ }
+ ppList = &((*ppList)->pNext);
+ }
+ return S_FALSE;
+}
+
+// Renames a previously saved persisted watch list
+HRESULT WatchCmd::RenameList(__in_z WCHAR* pOldName, __in_z WCHAR* pNewName)
+{
+ if(_wcscmp(pOldName, pNewName)==0)
+ return S_OK;
+ PersistList** ppList = &pPersistListHead;
+ while(*ppList != NULL)
+ {
+ if(_wcscmp((*ppList)->pName, pOldName) == 0)
+ {
+ PersistList* pListToChangeName = *ppList;
+ RemoveList(pNewName);
+ wcsncpy_s(pListToChangeName->pName, MAX_EXPRESSION, pNewName, _TRUNCATE);
+ return S_OK;
+ }
+ ppList = &((*ppList)->pNext);
+ }
+ return S_FALSE;
+}
+
+// Saves the active watch list together with the current evaluations as
+// a new persisted watch list
+HRESULT WatchCmd::SaveList(__in_z WCHAR* pSaveName)
+{
+ HRESULT Status = S_OK;
+ INIT_API_EE();
+ INIT_API_DAC();
+ IfFailRet(InitCorDebugInterface());
+
+ RemoveList(pSaveName);
+ PersistList* pList = new PersistList();
+ wcsncpy_s(pList->pName, MAX_EXPRESSION, pSaveName, _TRUNCATE);
+ pList->pHeadExpr = NULL;
+ PersistCallbackData data;
+ data.ppNext = &(pList->pHeadExpr);
+ WatchExpression* pExpression = pExpressionListHead;
+ while(pExpression != NULL)
+ {
+ ExpressionNode* pResult = NULL;
+ if(SUCCEEDED(Status = ExpressionNode::CreateExpressionNode(pExpression->pExpression, &pResult)))
+ {
+ pResult->DFSVisit(PersistCallback, (VOID*)&data);
+ delete pResult;
+ }
+ pExpression = pExpression->pNext;
+ }
+
+ pList->pNext = pPersistListHead;
+ pPersistListHead = pList;
+ return Status;
+}
+
+// Saves the current watch list to file as a sequence of commands that will
+// recreate the list
+HRESULT WatchCmd::SaveListToFile(FILE* pFile)
+{
+ WatchExpression* pExpression = pExpressionListHead;
+ while(pExpression != NULL)
+ {
+ fprintf_s(pFile, "!watch -a %S\n", pExpression->pExpression);
+ pExpression = pExpression->pNext;
+ }
+ return S_OK;
+}
+
+// Escapes characters that would be interpretted as DML markup, namely angle brackets
+// that often appear in generic type names
+VOID WatchCmd::DmlEscape(__in_ecount(cchInput) WCHAR* pInput, int cchInput, __in_ecount(cchOutput) WCHAR* pEscapedOutput, int cchOutput)
+{
+ pEscapedOutput[0] = L'\0';
+ for(int i = 0; i < cchInput; i++)
+ {
+ if(pInput[i] == L'<')
+ {
+ if(0 != wcscat_s(pEscapedOutput, cchOutput, L"&lt;")) return;
+ pEscapedOutput += 4;
+ cchOutput -= 4;
+ }
+ else if(pInput[i] == L'>')
+ {
+ if(0 != wcscat_s(pEscapedOutput, cchOutput, L"&gt;")) return;
+ pEscapedOutput += 4;
+ cchOutput -= 4;
+ }
+ else if(cchOutput > 1)
+ {
+ pEscapedOutput[0] = pInput[i];
+ pEscapedOutput[1] = '\0';
+ pEscapedOutput++;
+ cchOutput--;
+ }
+ if(pInput[i] == L'\0' || cchOutput == 1) break;
+ }
+}
+
+// A DFS traversal callback for the expression node tree that prints it
+VOID WatchCmd::EvalPrintCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData)
+{
+ PrintCallbackData* pData = (PrintCallbackData*)pUserData;
+ for(int i = 0; i < depth; i++) ExtOut(" ");
+ if(depth == 0)
+ ExtOut(" %d) ", pData->index);
+ else
+ ExtOut(" |- ");
+ if(pExpressionNode->GetErrorMessage()[0] != 0)
+ {
+ ExtOut("%S (%S)\n", pExpressionNode->GetRelativeExpression(), pExpressionNode->GetErrorMessage());
+ }
+ else
+ {
+ // names can have '<' and '>' in them, need to escape
+ WCHAR pEscapedTypeName[MAX_EXPRESSION];
+ DmlEscape(pExpressionNode->GetTypeName(), (int)_wcslen(pExpressionNode->GetTypeName()), pEscapedTypeName, MAX_EXPRESSION);
+ WCHAR pRelativeExpression[MAX_EXPRESSION];
+ DmlEscape(pExpressionNode->GetRelativeExpression(), (int)_wcslen(pExpressionNode->GetRelativeExpression()), pRelativeExpression, MAX_EXPRESSION);
+ DMLOut("%S <exec cmd=\"%S (%S)%S\">%S</exec> %S\n", pEscapedTypeName, pData->pCommand, pEscapedTypeName, pExpressionNode->GetAbsoluteExpression(), pRelativeExpression, pExpressionNode->GetTextValue());
+ }
+}
+
+// A DFS traversal callback for the expression node tree that saves all the values into a new
+// persisted watch list
+VOID WatchCmd::PersistCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData)
+{
+ PersistCallbackData* pData = (PersistCallbackData*)pUserData;
+ if(depth != 0)
+ return;
+
+ PersistWatchExpression* pPersistExpr = new PersistWatchExpression();
+ wcsncpy_s(pPersistExpr->pExpression, MAX_EXPRESSION, pExpressionNode->GetAbsoluteExpression(), _TRUNCATE);
+ FormatPersistResult(pPersistExpr->pPersistResult, MAX_EXPRESSION, pExpressionNode);
+ pPersistExpr->pNext = NULL;
+ *(pData->ppNext) = pPersistExpr;
+ pData->ppNext = &(pPersistExpr->pNext);
+}
+
+// Determines how the value of an expression node is saved as a persisted result. This effectively determines
+// the definition of equality when determining if an expression has changed value
+VOID WatchCmd::FormatPersistResult(__inout_ecount(cchPersistResult) WCHAR* pPersistResult, DWORD cchPersistResult, ExpressionNode* pExpressionNode)
+{
+ if(pExpressionNode->GetErrorMessage()[0] != 0)
+ {
+ _snwprintf_s(pPersistResult, MAX_EXPRESSION, _TRUNCATE, L"%s (%s)\n", pExpressionNode->GetRelativeExpression(), pExpressionNode->GetErrorMessage());
+ }
+ else
+ {
+ _snwprintf_s(pPersistResult, MAX_EXPRESSION, _TRUNCATE, L"%s %s %s\n", pExpressionNode->GetTypeName(), pExpressionNode->GetRelativeExpression(), pExpressionNode->GetTextValue());
+ }
+}
diff --git a/src/ToolBox/SOS/Strike/WatchCmd.h b/src/ToolBox/SOS/Strike/WatchCmd.h
new file mode 100644
index 0000000000..a34e391b79
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/WatchCmd.h
@@ -0,0 +1,110 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef _WATCH_CMD_
+#define _WATCH_CMD_
+
+#ifdef FEATURE_PAL
+#error This file not designed for use with FEATURE_PAL
+#endif
+
+#include "ExpressionNode.h"
+#include "windows.h"
+
+// A linked list node for watch expressions
+typedef struct _WatchExpression
+{
+ WCHAR pExpression[MAX_EXPRESSION];
+ _WatchExpression* pNext;
+
+} WatchExpression;
+
+// A linked list node that stores both the watch expression and a persisted result
+// of the evaluation at some point in the past
+typedef struct _PersistWatchExpression
+{
+ WCHAR pExpression[MAX_EXPRESSION];
+ WCHAR pPersistResult[MAX_EXPRESSION];
+ _PersistWatchExpression* pNext;
+
+} PersistWatchExpression;
+
+// A named list of persisted watch expressions, each of which has an expression and
+// a saved value
+typedef struct _PersistList
+{
+ ~_PersistList();
+ WCHAR pName[MAX_EXPRESSION];
+ PersistWatchExpression* pHeadExpr;
+ _PersistList* pNext;
+} PersistList;
+
+// An API for the functionality in the !watch command
+class WatchCmd
+{
+public:
+ WatchCmd();
+ ~WatchCmd();
+
+ // Deletes all current watch expressions from the watch list
+ // (does not delete persisted watch lists though)
+ HRESULT Clear();
+
+ // Adds a new expression to the active watch list
+ HRESULT Add(__in_z WCHAR* pExpression);
+
+ // removes an expression at the given index in the active watch list
+ HRESULT Remove(int index);
+
+ // Evaluates and prints a tree version of the active watch list
+ // The tree will be expanded along the nodes in expansionPath
+ // Optionally the list is filtered to only show differences from pFilterName (the name of a persisted watch list)
+ HRESULT Print(int expansionIndex, __in_z WCHAR* expansionPath, __in_z WCHAR* pFilterName);
+
+ // Deletes an persisted watch list by name
+ HRESULT RemoveList(__in_z WCHAR* pListName);
+
+ // Renames a previously saved persisted watch list
+ HRESULT RenameList(__in_z WCHAR* pOldName, __in_z WCHAR* pNewName);
+
+ // Saves the active watch list together with the current evaluations as
+ // a new persisted watch list
+ HRESULT SaveList(__in_z WCHAR* pSaveName);
+
+ // Saves the current watch list to file as a sequence of commands that will
+ // recreate the list
+ HRESULT SaveListToFile(FILE* pFile);
+
+private:
+ WatchExpression* pExpressionListHead;
+ PersistList* pPersistListHead;
+
+ // Escapes characters that would be interpretted as DML markup, namely angle brackets
+ // that often appear in generic type names
+ static VOID DmlEscape(__in_z WCHAR* pInput, int cchInput, __inout_ecount(cchOutput) WCHAR* pEscapedOutput, int cchOutput);
+
+ typedef struct _PrintCallbackData
+ {
+ int index;
+ WCHAR* pCommand;
+ } PrintCallbackData;
+
+ // A DFS traversal callback for the expression node tree that prints it
+ static VOID EvalPrintCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData);
+
+ typedef struct _PersistCallbackData
+ {
+ PersistWatchExpression** ppNext;
+ } PersistCallbackData;
+
+ // A DFS traversal callback for the expression node tree that saves all the values into a new
+ // persisted watch list
+ static VOID PersistCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData);
+
+ // Determines how the value of an expression node is saved as a persisted result. This effectively determines
+ // the definition of equality when determining if an expression has changed value
+ static VOID FormatPersistResult(__inout_ecount(cchPersistResult) WCHAR* pPersistResult, DWORD cchPersistResult, ExpressionNode* pExpressionNode);
+};
+
+#endif
diff --git a/src/ToolBox/SOS/Strike/apollososdocs.txt b/src/ToolBox/SOS/Strike/apollososdocs.txt
new file mode 100644
index 0000000000..71fefcf4d7
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/apollososdocs.txt
@@ -0,0 +1,2727 @@
+-------------------------------------------------------------------------------
+NOTE: THIS FILE CONTAINS SOS DOCUMENTATION. THE FORMAT OF THE FILE IS:
+
+<optional comments>
+COMMAND: <cmd name, all lower case>
+<descriptive text of the command>
+\\ <these are two backslashes, immediately followed by a newline>
+
+<repeat the sequence above>
+
+The first command is "contents" which is the general help screen. The rest
+correspond to SOS command names. This file is embedded as a resource in the SOS
+binary. Be sure to list any new commands here.
+-------------------------------------------------------------------------------
+
+
+
+COMMAND: contents.
+SOS is a debugger extension DLL designed to aid in the debugging of managed
+programs. Functions are listed by category, then roughly in order of
+importance. Shortcut names for popular functions are listed in parenthesis.
+Type "!help <functionname>" for detailed info on that function.
+
+Object Inspection Examining code and stacks
+----------------------------- -----------------------------
+DumpObj (do) Threads
+DumpArray (da) ThreadState
+DumpStackObjects (dso) IP2MD
+DumpHeap U
+DumpVC DumpStack
+GCRoot EEStack
+ObjSize CLRStack
+FinalizeQueue GCInfo
+PrintException (pe) EHInfo
+TraverseHeap BPMD
+Watch COMState
+ StopOnCatch
+ SuppressJitOptimization
+
+Examining CLR data structures Diagnostic Utilities
+----------------------------- -----------------------------
+DumpDomain VerifyHeap
+EEHeap VerifyObj
+Name2EE FindRoots
+SyncBlk HeapStat
+DumpMT GCWhere
+DumpClass ListNearObj (lno)
+DumpMD GCHandles
+Token2EE GCHandleLeaks
+EEVersion FinalizeQueue (fq)
+DumpModule FindAppDomain
+ThreadPool SaveModule
+DumpAssembly ProcInfo
+DumpSigElem StopOnException (soe)
+DumpRuntimeTypes DumpLog
+DumpSig VMMap
+RCWCleanupList VMStat
+DumpIL MinidumpMode
+DumpRCW AnalyzeOOM (ao)
+DumpCCW
+
+Examining the GC history Other
+----------------------------- -----------------------------
+HistInit FAQ
+HistRoot SaveState
+HistObj
+HistObjFind
+HistClear
+\\
+
+COMMAND: faq.
+>> Where can I get the right version of SOS for my build?
+
+If you are running version 1.1 or 2.0 of the CLR, SOS.DLL is installed in the
+same directory as the main CLR dll (CLR.DLL). Newer versions of the
+Windows Debugger provide a command to make it easy to load the right copy of
+SOS.DLL:
+
+ ".loadby sos clr"
+
+That will load the SOS extension DLL from the same place that CLR.DLL is
+loaded in the process. You shouldn't attempt to use a version of SOS.DLL that
+doesn't match the version of CLR.DLL. You can find the version of
+CLR.DLL by running
+
+ "lmvm clr"
+
+in the debugger. Note that if you are running CoreCLR (e.g. Silverlight)
+then you should replace "clr" with "coreclr".
+
+If you are using a dump file created on another machine, it is a little bit
+more complex. You need to make sure the mscordacwks.dll file that came with
+that install is on your symbol path, and you need to load the corresponding
+version of sos.dll (typing .load <full path to sos.dll> rather than using the
+.loadby shortcut). Within the Microsoft corpnet, we keep tagged versions
+of mscordacwks.dll, with names like mscordacwks_<architecture>_<version>.dll
+that the Windows Debugger can load. If you have the correct symbol path to the
+binaries for that version of the Runtime, the Windows Debugger will load the
+correct mscordacwks.dll file.
+
+>> I have a chicken and egg problem. I want to use SOS commands, but the CLR
+ isn't loaded yet. What can I do?
+
+In the debugger at startup you can type:
+
+ "sxe clrn"
+
+Let the program run, and it will stop with the notice
+
+ "CLR notification: module 'mscorlib' loaded"
+
+At this time you can use SOS commands. To turn off spurious notifications,
+type:
+
+ "sxd clrn"
+
+>> I got the following error message. Now what?
+
+ 0:000> .loadby sos clr
+ 0:000> !DumpStackObjects
+ Failed to find runtime DLL (clr.dll), 0x80004005
+ Extension commands need clr.dll in order to have something to do.
+ 0:000>
+
+This means that the CLR is not loaded yet, or has been unloaded. You need to
+wait until your managed program is running in order to use these commands. If
+you have just started the program a good way to do this is to type
+
+ bp clr!EEStartup "g @$ra"
+
+in the debugger, and let it run. After the function EEStartup is finished,
+there will be a minimal managed environment for executing SOS commands.
+
+>> I have a partial memory minidump, and !DumpObj doesn't work. Why?
+
+In order to run SOS commands, many CLR data structures need to be traversed.
+When creating a minidump without full memory, special functions are called at
+dump creation time to bring those structures into the minidump, and allow a
+minimum set of SOS debugging commands to work. At this time, those commands
+that can provide full or partial output are:
+
+CLRStack
+Threads
+Help
+PrintException
+EEVersion
+
+For a minidump created with this minimal set of functionality in mind, you
+will get an error message when running any other commands. A full memory dump
+(obtained with ".dump /ma <filename>" in the Windows Debugger) is often the
+best way to debug a managed program at this level.
+
+>> What other tools can I use to find my bug?
+
+Turn on Managed Debugging Assistants. These enable additional runtime diagnostics,
+particularly in the area of PInvoke/Interop. Adam Nathan has written some great
+information about that:
+
+http://blogs.msdn.com/adam_nathan/
+
+>> Does SOS support DML?
+
+Yes. SOS respects the .prefer_dml option in the debugger. If this setting is
+turned on, then SOS will output DML by default. Alternatively, you may leave
+it off and add /D to the beginning of a command to get DML based output for it.
+Not all SOS commands support DML output.
+
+\\
+
+COMMAND: stoponexception.
+!StopOnException [-derived]
+ [-create | -create2]
+ <Exception>
+ [<Pseudo-register number>]
+
+!StopOnException helps when you want the Windows Debugger to stop on a
+particular managed exception, say a System.OutOfMemoryException, but continue
+running if other exceptions are thrown. The command can be used in two ways:
+
+1) When you just want to stop on one particular CLR exception
+
+ At the debugger prompt, anytime after loading SOS, type:
+
+ !StopOnException -create System.OutOfMemoryException 1
+
+ The pseudo-register number (1) indicates that SOS can use register $t1 for
+ maintaining the breakpoint. The -create parameter allows SOS to go ahead
+ and set up the breakpoint as a first-chance exception. -create2 would set
+ it up as a 2nd-chance exception.
+
+2) When you need more complex logic for stopping on a CLR exception
+
+ !StopOnException can be used purely as a predicate in a larger expression.
+ If you type:
+
+ !StopOnException System.OutOfMemoryException 3
+
+ then register $t3 will be set to 1 if the last thrown exception on the
+ current thread is a System.OutOfMemoryException. Otherwise, $t3 will be set
+ to 0. Using the Windows Debugger scripting language, you could chain
+ such calls together to stop on various exception types. You'll have to
+ manually create such predicates, for example:
+
+ sxe -c "!soe System.OutOfMemoryException 3;
+ !soe -derived System.IOException 4;
+ .if(@$t3==1 || @$t4==1) { .echo 'stop' } .else {g}"
+
+The -derived option will cause StopOnException to set the pseudo-register to
+1 even if the thrown exception type doesn't exactly match the exception type
+given, but merely derives from it. So, "-derived System.Exception" would catch
+every exception in the System.Exception heirarchy.
+
+The pseudo-register number is optional. If you don't pass a number, SOS will
+use pseudo-register $t1.
+
+Note that !PrintException with no parameters will print out the last thrown
+exception on the current thread (if any). You can use !soe as a shortcut for
+!StopOnException.
+\\
+
+COMMAND: minidumpmode.
+!MinidumpMode <0 or 1>
+
+Minidumps created with ".dump /m" or ".dump" have a very small set of
+CLR-specific data, just enough to run a subset of SOS commands correctly. You
+are able to run other SOS commands, but they may fail with unexpected errors
+because required areas of memory are not mapped in or only partially mapped
+in. At this time, SOS cannot reliably detect if a dump file is of this type
+(for one thing, custom dump commands can map in additional memory, but there
+is no facility to read meta-information about this memory). You can turn this
+option on to protect against running unsafe commands against small minidumps.
+
+By default, MinidumpMode is 0, so there is no restriction on commands that will
+run against a minidump.
+\\
+
+COMMAND: dumpobj.
+!DumpObj [-nofields] <object address>
+
+This command allows you to examine the fields of an object, as well as learn
+important properties of the object such as the EEClass, the MethodTable, and
+the size.
+
+You might find an object pointer by running !DumpStackObjects and choosing
+from the resultant list. Here is a simple object:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 009038ec 4000008 4 Customer 0 instance 00a79ce4 name
+ 009038ec 4000009 8 Bank 0 instance 00a79d2c bank
+
+Note that fields of type Customer and Bank are themselves objects, and you can
+run !DumpObj on them too. You could look at the field directly in memory using
+the offset given. "dd a79d40+8 l1" would allow you to look at the bank field
+directly. Be careful about using this to set memory breakpoints, since objects
+can move around in the garbage collected heap.
+
+What else can you do with an object? You might run !GCRoot, to determine what
+roots are keeping it alive. Or you can find all objects of that type with
+"!DumpHeap -type Customer".
+
+The column VT contains the value 1 if the field is a valuetype structure, and
+0 if the field contains a pointer to another object. For valuetypes, you can
+take the MethodTable pointer in the MT column, and the Value and pass them to
+the command !DumpVC.
+
+The abbreviation !do can be used for brevity.
+
+The arguments in detail:
+-nofields: do not print fields of the object, useful for objects like
+ String
+\\
+
+COMMAND: dumparray.
+!DumpArray
+ [-start <startIndex>]
+ [-length <length>]
+ [-details]
+ [-nofields]
+ <array object address>
+
+This command allows you to examine elements of an array object.
+The arguments in detail:
+ -start <startIndex>: optional, only supported for single dimension array.
+ Specify from which index the command shows the elements.
+ -length <length>: optional, only supported for single dimension array.
+ Specify how many elements to show.
+ -details: optional. Ask the command to print out details
+ of the element using !DumpObj and !DumpVC format.
+ -nofields: optional, only takes effect when -details is used. Do
+ not print fields of the elements. Useful for arrays of
+ objects like String
+
+ Example output:
+
+ 0:000> !dumparray -start 2 -length 3 -details 00ad28d0
+ Name: Value[]
+ MethodTable: 03e41044
+ EEClass: 03e40fc0
+ Size: 132(0x84) bytes
+ Array: Rank 1, Number of elements 10, Type VALUETYPE
+ Element Type: Value
+ [2] 00ad28f0
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 2 x
+ 5b9a628c 4000002 4 System.Int32 instance 4 y
+ 5b9a628c 4000003 8 System.Int32 instance 6 z
+ [3] 00ad28fc
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 3 x
+ 5b9a628c 4000002 4 System.Int32 instance 6 y
+ 5b9a628c 4000003 8 System.Int32 instance 9 z
+ [4] 00ad2908
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 4 x
+ 5b9a628c 4000002 4 System.Int32 instance 8 y
+ 5b9a628c 4000003 8 System.Int32 instance 12 z
+
+
+\\
+
+COMMAND: dumpstackobjects.
+!DumpStackObjects [-verify] [top stack [bottom stack]]
+
+This command will display any managed objects it finds within the bounds of
+the current stack. Combined with the stack tracing commands like K and
+!CLRStack, it is a good aid to determining the values of locals and
+parameters.
+
+If you use the -verify option, each non-static CLASS field of an object
+candidate is validated. This helps to eliminate false positives. It is not
+on by default because very often in a debugging scenario, you are
+interested in objects with invalid fields.
+
+The abbreviation !dso can be used for brevity.
+\\
+
+COMMAND: dumpheap.
+!DumpHeap [-stat]
+ [-strings]
+ [-short]
+ [-min <size>]
+ [-max <size>]
+ [-thinlock]
+ [-startAtLowerBound]
+ [-mt <MethodTable address>]
+ [-type <partial type name>]
+ [start [end]]
+
+!DumpHeap is a powerful command that traverses the garbage collected heap,
+collection statistics about objects. With it's various options, it can look for
+particular types, restrict to a range, or look for ThinLocks (see !SyncBlk
+documentation). Finally, it will provide a warning if it detects excessive
+fragmentation in the GC heap.
+
+When called without options, the output is first a list of objects in the heap,
+followed by a report listing all the types found, their size and number:
+
+ 0:000> !dumpheap
+ Address MT Size
+ 00a71000 0015cde8 12 Free
+ 00a7100c 0015cde8 12 Free
+ 00a71018 0015cde8 12 Free
+ 00a71024 5ba58328 68
+ 00a71068 5ba58380 68
+ 00a710ac 5ba58430 68
+ 00a710f0 5ba5dba4 68
+ ...
+ total 619 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource
+ 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag
+ 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer
+ ...
+ 0015cde8 6 10260 Free
+ 5ba57bf8 318 18136 System.String
+ ...
+
+"Free" objects are simply regions of space the garbage collector can use later.
+If 30% or more of the heap contains "Free" objects, the process may suffer from
+heap fragmentation. This is usually caused by pinning objects for a long time
+combined with a high rate of allocation. Here is example output where !DumpHeap
+provides a warning about fragmentation:
+
+ <After the Statistics section>
+ Fragmented blocks larger than 1MB:
+ Addr Size Followed by
+ 00a780c0 1.5MB 00bec800 System.Byte[]
+ 00da4e38 1.2MB 00ed2c00 System.Byte[]
+ 00f16df0 1.2MB 01044338 System.Byte[]
+
+The arguments in detail:
+
+-stat Restrict the output to the statistical type summary
+-strings Restrict the output to a statistical string value summary
+-short Limits output to just the address of each object. This allows you
+ to easily pipe output from the command to another debugger
+ command for automation.
+-min Ignore objects less than the size given in bytes
+-max Ignore objects larger than the size given in bytes
+-thinlock Report on any ThinLocks (an efficient locking scheme, see !SyncBlk
+ documentation for more info)
+-startAtLowerBound
+ Force heap walk to begin at lower bound of a supplied address range.
+ (During plan phase, the heap is often not walkable because objects
+ are being moved. In this case, DumpHeap may report spurious errors,
+ in particular bad objects. It may be possible to traverse more of
+ the heap after the reported bad object. Even if you specify an
+ address range, !DumpHeap will start its walk from the beginning of
+ the heap by default. If it finds a bad object before the specified
+ range, it will stop before displaying the part of the heap in which
+ you are interested. This switch will force !DumpHeap to begin its
+ walk at the specified lower bound. You must supply the address of a
+ good object as the lower bound for this to work. Display memory at
+ the address of the bad object to manually find the next method
+ table (use !dumpmt to verify). If the GC is currently in a call to
+ memcopy, You may also be able to find the next object's address by
+ adding the size to the start address given as parameters.)
+-mt List only those objects with the MethodTable given
+-type List only those objects whose type name is a substring match of the
+ string provided.
+start Begin listing from this address
+end Stop listing at this address
+
+A special note about -type: Often, you'd like to find not only Strings, but
+System.Object arrays that are constrained to contain Strings. ("new
+String[100]" actually creates a System.Object array, but it can only hold
+System.String object pointers). You can use -type in a special way to find
+these arrays. Just pass "-type System.String[]" and those Object arrays will
+be returned. More generally, "-type <Substring of interesting type>[]".
+
+The start/end parameters can be obtained from the output of !EEHeap -gc. For
+example, if you only want to list objects in the large heap segment:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00c32754
+ generation 1 starts at 0x00c32748
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 010443a8 005d33a8(6108072)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a75000 0x00004000(16384)
+ Total Size 0x5d73a8(6124456)
+ ------------------------------
+ GC Heap Size 0x5d73a8(6124456)
+
+ 0:000> !dumpheap 1a71000 1a75000
+ Address MT Size
+ 01a71000 5ba88bd8 2064
+ 01a71810 0019fe48 2032 Free
+ 01a72000 5ba88bd8 4096
+ 01a73000 0019fe48 4096 Free
+ 01a74000 5ba88bd8 4096
+ total 5 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 0019fe48 2 6128 Free
+ 5ba88bd8 3 10256 System.Object[]
+ Total 5 objects
+
+Finally, if GC heap corruption is present, you may see an error like this:
+
+ 0:000> !dumpheap -stat
+ object 00a73d24: does not have valid MT
+ curr_object : 00a73d24
+ Last good object: 00a73d14
+ ----------------
+
+That indicates a serious problem. See the help for !VerifyHeap for more
+information on diagnosing the cause.
+\\
+
+COMMAND: dumpvc.
+!DumpVC <MethodTable address> <Address>
+
+!DumpVC allows you to examine the fields of a value class. In C#, this is a
+struct, and lives on the stack or within an Object on the GC heap. You need
+to know the MethodTable address to tell SOS how to interpret the fields, as
+a value class is not a first-class object with it's own MethodTable as the
+first field. For example:
+
+ 0:000> !DumpObj a79d98
+ Name: Mainy
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype
+ 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep
+
+m_valuetype is a value type. The value in the MT column (0090320c) is the
+MethodTable for it, and the Value column provides the start address:
+
+ 0:000> !DumpVC 0090320c 00a79d9c
+ Name: Funny
+ MethodTable 0090320c
+ EEClass: 03ee14b8
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000001 0 CLASS instance 00a743d8 signature
+ 0090320c 4000002 8 System.Int32 instance 2345 m1
+ 0090320c 4000003 10 System.Boolean instance 1 b1
+ 0090320c 4000004 c System.Int32 instance 1234 m2
+ 0090320c 4000005 4 CLASS instance 00a79d98 backpointer
+
+!DumpVC is quite a specialized function. Some managed programs make heavy use
+of value classes, while others do not.
+\\
+
+COMMAND: gcroot.
+!GCRoot [-nostacks] <Object address>
+
+!GCRoot looks for references (or roots) to an object. These can exist in four
+places:
+
+ 1. On the stack
+ 2. Within a GC Handle
+ 3. In an object ready for finalization
+ 4. As a member of an object found in 1, 2 or 3 above.
+
+First, all stacks will be searched for roots, then handle tables, and finally
+the freachable queue of the finalizer. Some caution about the stack roots:
+!GCRoot doesn't attempt to determine if a stack root it encountered is valid
+or is old (discarded) data. You would have to use !CLRStack and !U to
+disassemble the frame that the local or argument value belongs to in order to
+determine if it is still in use.
+
+Because people often want to restrict the search to gc handles and freachable
+objects, there is a -nostacks option.
+\\
+
+COMMAND: objsize.
+!ObjSize [<Object address>] | [-aggregate] [-stat]
+
+With no parameters, !ObjSize lists the size of all objects found on managed
+threads. It also enumerates all GCHandles in the process, and totals the size
+of any objects pointed to by those handles. In calculating object size,
+!ObjSize includes the size of all child objects in addition to the parent.
+
+For example, !DumpObj lists a size of 20 bytes for this Customer object:
+
+ 0:000> !do a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+but !ObjSize lists 152 bytes:
+
+ 0:000> !ObjSize a79d40
+ sizeof(00a79d40) = 152 ( 0x98) bytes (Customer)
+
+This is because a Customer points to a Bank, has a name, and the Bank points to
+an Address string. You can use !ObjSize to identify any particularly large
+objects, such as a managed cache in a web server.
+
+While running ObjSize with no arguments may point to specific roots that hold
+onto large amounts of memory it does not provide information regarding the
+amount of managed memory that is still alive. This is due to the fact that a
+number of roots can share a common subgraph, and that part will be reported in
+the size of all the roots that reference the subgraph. The -aggregate argument
+is meant to answer this question:
+
+The -aggregate option can be used in conjunction with the -stat argument to get
+a detailed view of what are the types that are still rooted. Using !dumpheap
+-stat and !objsize -aggregate -stat one can determine what are the the objects
+that are not rooted any more and diagnose various memory issues.
+
+Sample output when using the -aggregate and -stat:
+
+ 0:003> !ObjSize -aggregate -stat
+ Scan Thread 0 OSTHread f70
+ Scan Thread 2 OSTHread ef8
+ Statistics:
+ MT Count TotalSize Class Name
+ 01e63768 1 12 Test+d
+ 01e63660 1 16 Test+c
+ 01e63548 1 16 Test+b
+ 01e632f8 1 16 Test+a
+ ...
+ 5b6c6d40 9 504 System.Collections.Hashtable
+ 5b6ebe28 3 552 System.Byte[]
+ 5b6ec0bc 9 1296 System.Collections.Hashtable+bucket[]
+ 5b6ec200 9 1436 System.Char[]
+ 5b6c447c 77 2468 System.String
+ 5b6ebd64 8 9020 System.Object[]
+ Total 203 objects
+ Total Memory Size 18332 (0x479c) bytes
+
+\\
+
+COMMAND: finalizequeue.
+!FinalizeQueue [-detail] | [-allReady] [-short]
+
+This command lists the objects registered for finalization. Here is output from
+a simple program:
+
+ 0:000> !finalizequeue
+ SyncBlocks to be cleaned up: 0
+ MTA Interfaces to be released: 0
+ STA Interfaces to be released: 1
+ generation 0 has 4 finalizable objects (0015bc90->0015bca0)
+ generation 1 has 0 finalizable objects (0015bc90->0015bc90)
+ generation 2 has 0 finalizable objects (0015bc90->0015bc90)
+ Ready for finalization 0 objects (0015bca0->0015bca0)
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba6cf78 1 24 Microsoft.Win32.SafeHandles.SafeFileHandle
+ 5ba5db04 1 68 System.Threading.Thread
+ 5ba73e28 2 112 System.IO.StreamWriter
+ Total 4 objects
+
+The GC heap is divided into generations, and objects are listed accordingly. We
+see that only generation 0 (the youngest generation) has any objects registered
+for finalization. The notation "(0015bc90->0015bca0)" means that if you look at
+memory in that range, you'll see the object pointers that are registered:
+
+0:000> dd 15bc90 15bca0-4
+0015bc90 00a743f4 00a79f00 00a7b3d8 00a7b47c
+
+You could run !DumpObj on any of those pointers to learn more. In this example,
+there are no objects ready for finalization, presumably because they still have
+roots (You can use !GCRoot to find out). The statistics section provides a
+higher-level summary of the objects registered for finalization. Note that
+objects ready for finalization are also included in the statistics (if any).
+
+Specifying -short will inhibit any display related to SyncBlocks or RCWs.
+
+The arguments in detail:
+
+-allReady Specifying this argument will allow for the display of all objects
+ that are ready for finalization, whether they are already marked by
+ the GC as such, or whether the next GC will. The objects that are
+ not in the "Ready for finalization" list are finalizable objects that
+ are no longer rooted. This option can be very expensive, as it
+ verifies whether all the objects in the finalizable queues are still
+ rooted or not.
+-short Limits the output to just the address of each object. If used in
+ conjunction with -allReady it enumerates all objects that have a
+ finalizer that are no longer rooted. If used independently it lists
+ all objects in the finalizable and "ready for finalization" queues.
+-detail Will display extra information on any SyncBlocks that need to be
+ cleaned up, and on any RuntimeCallableWrappers (RCWs) that await
+ cleanup. Both of these data structures are cached and cleaned up by
+ the finalizer thread when it gets a chance to run.
+\\
+
+COMMAND: printexception.
+!PrintException [-nested] [-lines] [<Exception object address>]
+
+This will format fields of any object derived from System.Exception. One of the
+more useful aspects is that it will format the _stackTrace field, which is a
+binary array. If _stackTraceString field is not filled in, that can be helpful
+for debugging. You can of course use !DumpObj on the same exception object to
+explore more fields.
+
+If called with no parameters, PrintException will look for the last outstanding
+exception on the current thread and print it. This will be the same exception
+that shows up in a run of !Threads.
+
+!PrintException will notify you if there are any nested exceptions on the
+current managed thread. (A nested exception occurs when you throw another
+exception within a catch handler already being called for another exception).
+If there are nested exceptions, you can re-run !PrintException with the
+"-nested" option to get full details on the nested exception objects. The
+!Threads command will also tell you which threads have nested exceptions.
+
+!PrintException can display source information if available, by specifying the
+-lines command line argument.
+
+The abbreviation !pe can be used for brevity.
+\\
+
+COMMAND: traverseheap.
+!TraverseHeap [-xml] [-verify] <filename>
+
+!TraverseHeap writes out a file in a format understood by the CLR Profiler.
+You can download the CLR Profiler from this link:
+
+http://www.microsoft.com/downloads/details.aspx?FamilyId=86CE6052-D7F4-4AEB-
+9B7A-94635BEEBDDA&displaylang=en
+
+It creates a graphical display of the GC heap to help you analyze the state of
+your application.
+
+If you pass the -verify option it will do more sanity checking of the heap
+as it dumps it. Use this option if heap corruption is suspected.
+
+If you pass the "-xml" flag, the file is instead written out in an easy to
+understand xml format:
+
+ <gcheap>
+ <types>
+ <type id="1" name="System.String">
+ ...
+ </types>
+ <roots>
+ <root kind="handle" address="0x00a73ff0"/>
+ <root kind="stack" address="0x0069f0e0"/>
+ ...
+ </roots>
+ <objects>
+ <object address="0x00b73030" typeid="1" size="300"/>
+ <object address="0x00b75054" typeid="5" size="20">
+ <member address="0x00b75088" />
+ ...
+ </object>
+ ...
+ </objects>
+ </gcheap>
+
+You can break into your process, load SOS, take a snapshot of your heap with
+this function, then continue.
+\\
+COMMAND: threadstate.
+!ThreadState value
+
+The !Threads command outputs, among other things, the state of the thread.
+This is a bit field which corresponds to various states the thread is in.
+To check the state of the thread, simply pass that bit field from the
+output of !Threads into !ThreadState.
+
+Example:
+ 0:003> !Threads
+ ThreadCount: 2
+ UnstartedThread: 0
+ BackgroundThread: 1
+ PendingThread: 0
+ DeadThread: 0
+ Hosted Runtime: no
+ PreEmptive GC Alloc Lock
+ ID OSID ThreadOBJ State GC Context Domain Count APT Exception
+ 0 1 250 0019b068 a020 Disabled 02349668:02349fe8 0015def0 0 MTA
+ 2 2 944 001a6020 b220 Enabled 00000000:00000000 0015def0 0 MTA (Finalizer)
+ 0:003> !ThreadState b220
+ Legal to Join
+ Background
+ CLR Owns
+ CoInitialized
+ In Multi Threaded Apartment
+
+Possible thread states:
+ Thread Abort Requested
+ GC Suspend Pending
+ User Suspend Pending
+ Debug Suspend Pending
+ GC On Transitions
+ Legal to Join
+ Yield Requested
+ Hijacked by the GC
+ Blocking GC for Stack Overflow
+ Background
+ Unstarted
+ Dead
+ CLR Owns
+ CoInitialized
+ In Single Threaded Apartment
+ In Multi Threaded Apartment
+ Reported Dead
+ Fully initialized
+ Task Reset
+ Sync Suspended
+ Debug Will Sync
+ Stack Crawl Needed
+ Suspend Unstarted
+ Aborted
+ Thread Pool Worker Thread
+ Interruptible
+ Interrupted
+ Completion Port Thread
+ Abort Initiated
+ Finalized
+ Failed to Start
+ Detached
+\\
+COMMAND: threads.
+!Threads [-live] [-special]
+
+!Threads lists all the mananaged threads in the process.
+
+-live: optional. Only print threads associated with a live thread.
+-special: optional. With this switch, the command will display all the special
+ threads created by CLR. Those threads might not be managed threads
+ so they might not be shown in the first part of the command's
+ output. Example of special threads include: GC threads (in
+ concurrent GC and server GC), Debugger helper threads, Finalizer
+ threads, AppDomain Unload threads, and Threadpool timer threads.
+
+Each thread has many attributes, many of which can be ignored. The important
+ones are discussed below:
+
+There are three ID columns:
+
+1) The debugger shorthand ID (When the runtime is hosted this column might
+ display the special string "<<<<" when this internal thread object is not
+ associated with any physical thread - this may happen when the host reuses
+ the runtime internal thread object)
+2) The CLR Thread ID
+3) The OS thread ID.
+
+If PreEmptiveGC is enabled for a thread, then a garbage collection
+can occur while that thread is running. For example, if you break in while
+a managed thread is making a PInvoke call to a Win32 function, that thread
+will be in PreEmptive GC mode.
+
+The Domain column indicates what AppDomain the thread is currently executing
+in. You can pass this value to !DumpDomain to find out more.
+
+The APT column gives the COM apartment mode.
+
+Exception will list the last thrown exception (if any) for the thread. More
+details can be obtained by passing the pointer value to !PrintException. If
+you get the notation "(nested exceptions)", you can get details on those
+exceptions by switching to the thread in question, and running
+"!PrintException -nested".
+\\
+
+COMMAND: clrstack.
+!CLRStack [-a] [-l] [-p] [-n]
+!CLRStack [-a] [-l] [-p] [-i] [variable name] [frame]
+
+CLRStack attempts to provide a true stack trace for managed code only. It is
+handy for clean, simple traces when debugging straightforward managed
+programs. The -p parameter will show arguments to the managed function. The
+-l parameter can be used to show information on local variables in a frame.
+SOS can't retrieve local names at this time, so the output for locals is in
+the format <local address> = <value>. The -a (all) parameter is a short-cut
+for -l and -p combined.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+When you see methods with the name "[Frame:...", that indicates a transition
+between managed and unmanaged code. You could run !IP2MD on the return
+addresses in the call stack to get more information on each managed method.
+
+On x64 platforms, Transition Frames are not displayed at this time. To avoid
+heavy optimization of parameters and locals one can request the JIT compiler
+to not optimize functions in the managed app by creating a file myapp.ini
+(if your program is myapp.exe) in the same directory. Put the following lines
+in myapp.ini and re-run:
+
+[.NET Framework Debugging Control]
+GenerateTrackingInfo=1
+AllowOptimize=0
+
+The -i option is a new EXPERIMENTAL addition to CLRStack and will use the ICorDebug
+interfaces to display the managed stack and variables. With this option you can also
+view and expand arrays and fields for managed variables. If a stack frame number is
+specified in the command line, CLRStack will show you the parameters and/or locals
+only for that frame (provided you specify -l or -p or -a of course). If a variable
+name and a stack frame number are specified in the command line, CLRStack will show
+you the parameters and/or locals for that frame, and will also show you the fields
+for that variable name you specified. Here are some examples:
+ !CLRStack -i -a : This will show you all parameters and locals for all frames
+ !CLRStack -i -a 3 : This will show you all parameters and locals, for frame 3
+ !CLRStack -i var1 0 : This will show you the fields of 'var1' for frame 0
+ !CLRStack -i var1.abc 2 : This will show you the fields of 'var1', and expand
+ 'var1.abc' to show you the fields of the 'abc' field,
+ for frame 2.
+ !CLRStack -i var1.[basetype] 0 : This will show you the fields of 'var1', and
+ expand the base type of 'var1' to show you its
+ fields.
+ !CLRStack -i var1.[6] 0 : If 'var1' is an array, this will show you the element
+ at index 6 in the array, along with its fields
+The -i options uses DML output for a better debugging experience, so typically you
+should only need to execute "!CLRStack -i", and from there, click on the DML
+hyperlinks to inspect the different managed stack frames and managed variables.
+\\
+
+COMMAND: ip2md.
+!IP2MD <Code address>
+
+Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc
+associated with it. For example, this output from K:
+
+ 0:000> K
+ ChildEBP RetAddr
+ 00a79c78 03ef02ab image00400000!Mainy.Top()+0xb
+ 00a79c78 03ef01a6 image00400000!Mainy.Level(Int32)+0xb
+ 00a79c78 5d3725a1 image00400000!Mainy.Main()+0xee
+ 0012ea04 5d512f59 clr!CallDescrWorkerInternal+0x30
+ 0012ee34 5d7946aa clr!CallDescrWorker+0x109
+
+ 0:000> !IP2MD 03ef01a6
+ MethodDesc: 00902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa38
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+ Transparency: Critical
+ Source file: c:\Code\prj.mini\exc.cs @ 39
+
+We have taken a return address into Mainy.Main, and discovered information
+about that method. You could run !U, !DumpMT, !DumpClass, !DumpMD, or
+!DumpModule on the fields listed to learn more.
+
+The "Source line" output will only be present if the debugger can find the
+symbols for the managed module containing the given <code address>, and if the
+debugger is configured to load line number information.
+\\
+
+COMMAND: u.
+!U [-gcinfo] [-ehinfo] [-n] <MethodDesc address> | <Code address>
+
+Presents an annotated disassembly of a managed method when given a MethodDesc
+pointer for the method, or a code address within the method body. Unlike the
+debugger "U" function, the entire method from start to finish is printed,
+with annotations that convert metadata tokens to names.
+
+ <example output>
+ ...
+ 03ef015d b901000000 mov ecx,0x1
+ 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713)
+ 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter)
+ 03ef016d 89442414 mov [esp+0x14],eax
+
+If you pass the -gcinfo flag, you'll get inline display of the GCInfo for
+the method. You can also obtain this information with the !GCInfo command.
+
+If you pass the -ehinfo flag, you'll get inline display of exception info
+for the method. (Beginning and end of try/finally/catch handlers, etc.).
+You can also obtain this information with the !EHInfo command.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), and if symbols are available for the managed
+module containing the method being examined, the output of the command will
+include the source file name and line number corresponding to the
+disassembly. The -n (No line numbers) flag can be specified to disable this
+behavior.
+
+ <example output>
+ ...
+ c:\Code\prj.mini\exc.cs @ 38:
+ 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type <Enter> to continue: ")
+ 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b)
+ 001b00bb 90 nop
+
+ c:\Code\prj.mini\exc.cs @ 39:
+ 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6)
+ >>> 001b00c1 90 nop
+ ...
+\\
+
+COMMAND: dumpstack.
+!DumpStack [-EE] [-n] [top stack [bottom stack]]
+
+[x86 and x64 documentation]
+
+This command provides a verbose stack trace obtained by "scraping." Therefore
+the output is very noisy and potentially confusing. The command is good for
+viewing the complete call stack when "kb" gets confused. For best results,
+make sure you have valid symbols.
+
+-EE will only show managed functions.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+You can also pass a stack range to limit the output. Use the debugger
+extension !teb to get the top and bottom stack values.
+
+\\
+
+COMMAND: eestack.
+!EEStack [-short] [-EE]
+
+This command runs !DumpStack on all threads in the process. The -EE option is
+passed directly to !DumpStack. The -short option tries to narrow down the
+output to "interesting" threads only, which is defined by
+
+1) The thread has taken a lock.
+2) The thread has been "hijacked" in order to allow a garbage collection.
+3) The thread is currently in managed code.
+
+See the documentation for !DumpStack for more info.
+\\
+
+COMMAND: ehinfo.
+!EHInfo (<MethodDesc address> | <Code address>)
+
+!EHInfo shows the exception handling blocks in a jitted method. For each
+handler, it shows the type, including code addresses and offsets for the clause
+block and the handler block. For a TYPED handler, this would be the "try" and
+"catch" blocks respectively.
+
+Sample output:
+
+ 0:000> !ehinfo 33bbd3a
+ MethodDesc: 03310f68
+ Method Name: MainClass.Main()
+ Class: 03571358
+ MethodTable: 0331121c
+ mdToken: 0600000b
+ Module: 001e2fd8
+ IsJitted: yes
+ CodeAddr: 033bbca0
+ Transparency: Critical
+
+ EHHandler 0: TYPED catch(System.IO.FileNotFoundException)
+ Clause: [033bbd2b, 033bbd3c] [8b, 9c]
+ Handler: [033bbd3c, 033bbd50] [9c, b0]
+
+ EHHandler 1: FINALLY
+ Clause: [033bbd83, 033bbda3] [e3, 103]
+ Handler: [033bbda3, 033bbdc5] [103, 125]
+
+ EHHandler 2: TYPED catch(System.Exception)
+ Clause: [033bbd7a, 033bbdc5] [da, 125]
+ Handler: [033bbdc5, 033bbdd6] [125, 136]
+
+\\
+
+COMMAND: gcinfo.
+!GCInfo (<MethodDesc address> | <Code address>)
+
+!GCInfo is especially useful for CLR Devs who are trying to determine if there
+is a bug in the JIT Compiler. It parses the GCEncoding for a method, which is a
+compressed stream of data indicating when registers or stack locations contain
+managed objects. It is important to keep track of this information, because if
+a garbage collection occurs, the collector needs to know where roots are so it
+can update them with new object pointer values.
+
+Here is sample output where you can see the change in register state. Normally
+you would print this output out and read it alongside a disassembly of the
+method. For example, the notation "reg EDI becoming live" at offset 0x11 of the
+method might correspond to a "mov edi,ecx" statement.
+
+ 0:000> !gcinfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method)
+ entry point 5b68dbb8
+ preJIT generated code
+ GC info 5b9f2f09
+ Method info block:
+ method size = 0036
+ prolog size = 19
+ epilog size = 8
+ epilog count = 1
+ epilog end = yes
+ saved reg. mask = 000B
+ ebp frame = yes
+ fully interruptible=yes
+ double align = no
+ security check = no
+ exception handlers = no
+ local alloc = no
+ edit & continue = no
+ varargs = no
+ argument count = 4
+ stack frame size = 1
+ untracked count = 5
+ var ptr tab count = 0
+ epilog at 002E
+ 36 D4 8C C7 AA |
+ 93 F3 40 05 |
+
+ Pointer table:
+ 14 | [EBP+14H] an untracked local
+ 10 | [EBP+10H] an untracked local
+ 0C | [EBP+0CH] an untracked local
+ 08 | [EBP+08H] an untracked local
+ 44 | [EBP-04H] an untracked local
+ F1 79 | 0011 reg EDI becoming live
+ 72 | 0013 reg ESI becoming live
+ 83 | 0016 push ptr 0
+ 8B | 0019 push ptr 1
+ 93 | 001C push ptr 2
+ 9B | 001F push ptr 3
+ 56 | 0025 reg EDX becoming live
+ 4A | 0027 reg ECX becoming live
+ 0E | 002D reg ECX becoming dead
+ 10 | 002D reg EDX becoming dead
+ E0 | 002D pop 4 ptrs
+ F0 31 | 0036 reg ESI becoming dead
+ 38 | 0036 reg EDI becoming dead
+ FF |
+
+This function is important for CLR Devs, but very difficult for anyone else to
+make sense of it. You would usually come to use it if you suspect a gc heap
+corruption bug caused by invalid GCEncoding for a particular method.
+\\
+
+COMMAND: comstate.
+!COMState
+
+!COMState lists the com apartment model for each thread, as well as a Context
+pointer if provided.
+\\
+
+COMMAND: bpmd.
+!BPMD [-nofuturemodule] <module name> <method name> [<il offset>]
+!BPMD <source file name>:<line number>
+!BPMD -md <MethodDesc>
+!BPMD -list
+!BPMD -clear <pending breakpoint number>
+!BPMD -clearall
+
+!BPMD provides managed breakpoint support. If it can resolve the method name
+to a loaded, jitted or ngen'd function it will create a breakpoint with "bp".
+If not then either the module that contains the method hasn't been loaded yet
+or the module is loaded, but the function is not jitted yet. In these cases,
+!bpmd asks the Windows Debugger to receive CLR Notifications, and waits to
+receive news of module loads and JITs, at which time it will try to resolve
+the function to a breakpoint. -nofuturemodule can be used to suppress
+creating a breakpoint against a module that has not yet been loaded.
+
+Management of the list of pending breakpoints can be done via !BPMD -list,
+!BPMD -clear, and !BPMD -clearall commands. !BPMD -list generates a list of
+all of the pending breakpoints. If the pending breakpoint has a non-zero
+module id, then that pending breakpoint is specific to function in that
+particular loaded module. If the pending breakpoint has a zero module id, then
+the breakpoint applies to modules that have not yet been loaded. Use
+!BPMD -clear or !BPMD -clearall to remove pending breakpoints from the list.
+
+This brings up a good question: "I want to set a breakpoint on the main
+method of my application. How can I do this?"
+
+ 1) If you know the full path to SOS, use this command and skip to step 6
+ .load <the full path to sos.dll>
+
+ 2) If you don't know the full path to sos, its usually next to clr.dll
+ You can wait for clr to load and then find it.
+ Start the debugger and type:
+ sxe -c "" clrn
+ 3) g
+ 4) You'll get the following notification from the debugger:
+ "CLR notification: module 'mscorlib' loaded"
+ 5) Now you can load SOS. Type
+ .loadby sos clr
+
+ 6) Add the breakpoint with command such as:
+ !bpmd myapp.exe MyApp.Main
+ 7) g
+ 8) You will stop at the start of MyApp.Main. If you type "bl" you will
+ see the breakpoint listed.
+
+You can specify breakpoints by file and line number if:
+ a) You have some version of .Net Framework installed on your machine. Any OS from
+ Vista onwards should have .Net Framework installed by default.
+ b) You have PDBs for the managed modules that need breakpoints, and your symbol
+ path points to those PDBs.
+This is often easier than module and method name syntax. For example:
+ !bpmd Demo.cs:15
+
+
+To correctly specify explicitly implemented methods make sure to retrieve the
+method name from the metadata, or from the output of the "!dumpmt -md" command.
+For example:
+
+ public interface I1
+ {
+ void M1();
+ }
+ public class ExplicitItfImpl : I1
+ {
+ ...
+ void I1.M1() // this method's name is 'I1.M1'
+ { ... }
+ }
+
+ !bpmd myapp.exe ExplicitItfImpl.I1.M1
+
+
+!BPMD works equally well with generic types. Adding a breakpoint on a generic
+type sets breakpoints on all already JIT-ted generic methods and sets a pending
+breakpoint for any instantiation that will be JIT-ted in the future.
+
+Example for generics:
+ Given the following two classes:
+
+ class G3<T1, T2, T3>
+ {
+ ...
+ public void F(T1 p1, T2 p2, T3 p3)
+ { ... }
+ }
+
+ public class G1<T> {
+ // static method
+ static public void G<W>(W w)
+ { ... }
+ }
+
+ One would issue the following commands to set breapoints on G3.F() and
+ G1.G():
+
+ !bpmd myapp.exe G3`3.F
+ !bpmd myapp.exe G1`1.G
+
+And for explicitly implemented methods on generic interfaces:
+ public interface IT1<T>
+ {
+ void M1(T t);
+ }
+
+ public class ExplicitItfImpl<U> : IT1<U>
+ {
+ ...
+ void IT1<U>.M1(U u) // this method's name is 'IT1<U>.M1'
+ { ... }
+ }
+
+ !bpmd bpmd.exe ExplicitItfImpl`1.IT1<U>.M1
+
+Additional examples:
+ If IT1 and ExplicitItfImpl are types declared inside another class,
+ Outer, the bpmd command would become:
+
+ !bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1<U>.M1
+
+ (note that the fully qualified type name for ExplicitItfImpl became
+ Outer+ExplicitItfImpl, using the '+' separator, while the method name
+ is Outer.IT1<U>.M1, using a '.' as the separator)
+
+ Furthermore, if the Outer class resides in a namespace, NS, the bpmd
+ command to use becomes:
+
+ !bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1<U>.M1
+
+!BPMD does not accept offsets nor parameters in the method name. You can add
+an IL offset as an optional parameter seperate from the name. If there are overloaded
+methods, !bpmd will set a breakpoint for all of them.
+
+In the case of hosted environments such as SQL, the module name may be
+complex, like 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
+For this case, just be sure to surround the module name with single quotes,
+like:
+
+!bpmd 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' Price.M2
+
+\\
+
+COMMAND: dumpdomain.
+!DumpDomain [<Domain address>]
+
+When called with no parameters, !DumpDomain will list all the AppDomains in the
+process. It enumerates each Assembly loaded into those AppDomains as well.
+In addition to your application domain, and any domains it might create, there
+are two special domains: the Shared Domain and the System Domain.
+
+Any Assembly pointer in the output can be passed to !DumpAssembly. Any Module
+pointer in the output can be passed to !DumpModule. Any AppDomain pointer can
+be passed to !DumpDomain to limit output only to that AppDomain. Other
+functions provide an AppDomain pointer as well, such as !Threads where it lists
+the current AppDomain for each thread.
+\\
+
+COMMAND: eeheap.
+!EEHeap [-gc] [-loader]
+
+!EEHeap enumerates process memory consumed by internal CLR data structures. You
+can limit the output by passing "-gc" or "-loader". All information will be
+displayed otherwise.
+
+The information for the Garbage Collector lists the ranges of each Segment in
+the managed heap. This can be useful if you believe you have an object pointer.
+If the pointer falls within a segment range given by "!EEHeap -gc", then you do
+have an object pointer, and can attempt to run "!DumpObj" on it.
+
+Here is output for a simple program:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00a71018
+ generation 1 starts at 0x00a7100c
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 00a7e01c 0000d01c(53276)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a76000 0x00005000(20480)
+ Total Size 0x1201c(73756)
+ ------------------------------
+ GC Heap Size 0x1201c(73756)
+
+So the total size of the GC Heap is only 72K. On a large web server, with
+multiple processors, you can expect to see a GC Heap of 400MB or more. The
+Garbage Collector attempts to collect and reclaim memory only when required to
+by memory pressure for better performance. You can also see the notion of
+"generations," wherein the youngest objects live in generation 0, and
+long-lived objects eventually get "promoted" to generation 2.
+
+The loader output lists various private heaps associated with AppDomains. It
+also lists heaps associated with the JIT compiler, and heaps associated with
+Modules. For example:
+
+ 0:000> !EEHeap -loader
+ Loader Heap:
+ --------------------------------------
+ System Domain: 5e0662a0
+ LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes.
+ HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x3000(12288)bytes
+ --------------------------------------
+ Shared Domain: 5e066970
+ LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x6000(24576)bytes
+ --------------------------------------
+ Domain 1: 14f000
+ LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes.
+ StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x8000(32768)bytes
+ --------------------------------------
+ Jit code heap:
+ Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Module Thunk heaps:
+ Module 5ba22410: Size: 0x00000000 bytes.
+ Module 001c1320: Size: 0x00000000 bytes.
+ Module 001c03f0: Size: 0x00000000 bytes.
+ Module 001caa38: Size: 0x00000000 bytes.
+ Total size: 0x0(0)bytes
+ --------------------------------------
+ Module Lookup Table heaps:
+ Module 5ba22410:Size: 0x00000000 bytes.
+ Module 001c1320:Size: 0x00000000 bytes.
+ Module 001c03f0:Size: 0x00000000 bytes.
+ Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Total LoaderHeap size: 0x15000(86016)bytes
+ =======================================
+
+By using !EEHeap to keep track of the growth of these private heaps, we are
+able to rule out or include them as a source of a memory leak.
+\\
+
+COMMAND: name2ee.
+!Name2EE <module name> <type or method name>
+!Name2EE <module name>!<type or method name>
+
+This function allows you to turn a class name into a MethodTable and EEClass.
+It turns a method name into a MethodDesc. Here is an example for a method:
+
+ 0:000> !name2ee unittest.exe MainClass.Main
+ Module: 001caa38
+ Token: 0x0600000d
+ MethodDesc: 00902f40
+ Name: MainClass.Main()
+ JITTED Code Address: 03ef00b8
+
+and for a class:
+
+ 0:000> !name2ee unittest!MainClass
+ Module: 001caa38
+ Token: 0x02000005
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Name: MainClass
+
+The module you are "browsing" with Name2EE needs to be loaded in the process.
+To get a type name exactly right, first browse the module with ILDASM. You
+can also pass * as the <module name> to search all loaded managed modules.
+<module name> can also be the debugger's name for a module, such as
+mscorlib or image00400000.
+
+The Windows Debugger syntax of <module>!<type> is also supported. You can
+use an asterisk on the left of the !, but the type on the right side needs
+to be fully qualified.
+
+If you are looking for a way to display a static field of a class (and you
+don't have an instance of the class, so !dumpobj won't help you), note that
+once you have the EEClass, you can run !DumpClass, which will display the
+value of all static fields.
+
+There is yet one more way to specify a module name. In the case of modules
+loaded from an assembly store (such as a SQL db) rather than disk, the
+module name will look like this:
+
+price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
+
+For this kind of module, simply use price as the module name:
+
+ 0:044> !name2ee price Price
+ Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
+ Token: 0x02000002
+ MethodTable: 11a47ae0
+ EEClass: 11a538c8
+ Name: Price
+
+Where are we getting these module names from? Run !DumpDomain to see a list of
+all loaded modules in all domains. And remember that you can browse all the
+types in a module with !DumpModule -mt <module pointer>.
+\\
+
+COMMAND: syncblk.
+!SyncBlk [-all | <syncblk number>]
+
+A SyncBlock is a holder for extra information that doesn't need to be created
+for every object. It can hold COM Interop data, HashCodes, and locking
+information for thread-safe operations.
+
+When called without arguments, !SyncBlk will print the list of SyncBlocks
+corresponding to objects that are owned by a thread. For example, a
+
+ lock(MyObject)
+ {
+ ....
+ }
+
+statement will set MyObject to be owned by the current thread. A SyncBlock will
+be created for MyObject, and the thread ownership information stored there
+(this is an oversimplification, see NOTE below). If another thread tries to
+execute the same code, they won't be able to enter the block until the first
+thread exits.
+
+This makes !SyncBlk useful for detecting managed deadlocks. Consider that the
+following code is executed by Threads A & B:
+
+ Resource r1 = new Resource();
+ Resource r2 = new Resource();
+
+ ...
+
+ lock(r1) lock(r2)
+ { {
+ lock(r2) lock(r1)
+ { {
+ ... ...
+ } }
+ } }
+
+This is a deadlock situation, as Thread A could take r1, and Thread B r2,
+leaving both threads with no option but to wait forever in the second lock
+statement. !SyncBlk will detect this with the following output:
+
+ 0:003> !syncblk
+ Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
+ 238 001e40ec 3 1 001e4e60 e04 3 00a7a194 Resource
+ 239 001e4124 3 1 001e5980 ab8 4 00a7a1a4 Resource
+
+It means that Thread e04 owns object 00a7a194, and Thread ab8 owns object
+00a7a1a4. Combine that information with the call stacks of the deadlock:
+
+(threads 3 and 4 have similar output)
+ 0:003> k
+ ChildEBP RetAddr
+ 0404ea04 77f5c524 SharedUserData!SystemCallStub+0x4
+ 0404ea08 77e75ee0 ntdll!NtWaitForMultipleObjects+0xc
+ 0404eaa4 5d9de9d6 KERNEL32!WaitForMultipleObjectsEx+0x12c
+ 0404eb38 5d9def80 clr!Thread::DoAppropriateAptStateWait+0x156
+ 0404ecc4 5d9dd8bb clr!Thread::DoAppropriateWaitWorker+0x360
+ 0404ed20 5da628dd clr!Thread::DoAppropriateWait+0xbb
+ 0404ede4 5da4e2e2 clr!CLREvent::Wait+0x29d
+ 0404ee70 5da4dd41 clr!AwareLock::EnterEpilog+0x132
+ 0404ef34 5da4efa3 clr!AwareLock::Enter+0x2c1
+ 0404f09c 5d767880 clr!AwareLock::Contention+0x483
+ 0404f1c4 03f00229 clr!JITutil_MonContention+0x2c0
+ 0404f1f4 5b6ef077 image00400000!Worker.Work()+0x79
+ ...
+
+By looking at the code corresponding to Worker.Work()+0x79 (run "!u 03f00229"),
+you can see that thread 3 is attempting to acquire the Resource 00a7a1a4, which
+is owned by thread 4.
+
+NOTE:
+It is not always the case that a SyncBlock will be created for every object
+that is locked by a thread. In version 2.0 of the CLR and above, a mechanism
+called a ThinLock will be used if there is not already a SyncBlock for the
+object in question. ThinLocks will not be reported by the !SyncBlk command.
+You can use "!DumpHeap -thinlock" to list objects locked in this way.
+\\
+
+COMMAND: dumpmt.
+!DumpMT [-MD] <MethodTable address>
+
+Examine a MethodTable. Each managed object has a MethodTable pointer at the
+start. If you pass the "-MD" flag, you'll also see a list of all the methods
+defined on the object.
+\\
+
+COMMAND: dumpclass.
+!DumpClass <EEClass address>
+
+The EEClass is a data structure associated with an object type. !DumpClass
+will show attributes, as well as list the fields of the type. The output is
+similar to !DumpObj. Although static field values will be displayed,
+non-static values won't because you need an instance of an object for that.
+
+You can get an EEClass to look at from !DumpMT, !DumpObj, !Name2EE, and
+!Token2EE among others.
+\\
+
+COMMAND: dumpmd.
+!DumpMD <MethodDesc address>
+
+This command lists information about a MethodDesc. You can use !IP2MD to turn
+a code address in a managed function into a MethodDesc:
+
+ 0:000> !dumpmd 902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa78
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+
+If IsJitted is "yes," you can run !U on the CodeAddr pointer to see a
+disassembly of the JITTED code. You can also call !DumpClass, !DumpMT,
+!DumpModule on the Class, MethodTable and Module fields above.
+\\
+
+COMMAND: token2ee.
+!Token2EE <module name> <token>
+
+This function allows you to turn a metadata token into a MethodTable or
+MethodDesc. Here is an example showing class tokens being resolved:
+
+ 0:000> !token2ee unittest.exe 02000003
+ Module: 001caa38
+ Token: 0x02000003
+ MethodTable: 0090375c
+ EEClass: 03ee1ae0
+ Name: Bank
+ 0:000> !token2ee image00400000 02000004
+ Module: 001caa38
+ Token: 0x02000004
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Name: Customer
+
+The module you are "browsing" with Token2EE needs to be loaded in the process.
+This function doesn't see much use, especially since a tool like ILDASM can
+show the mapping between metadata tokens and types/methods in a friendlier way.
+But it could be handy sometimes.
+
+You can pass "*" for <module name> to find what that token maps to in every
+loaded managed module. <module name> can also be the debugger's name for a
+module, such as mscorlib or image00400000.
+\\
+
+COMMAND: eeversion.
+!EEVersion
+
+This prints the Common Language Runtime version. It also tells you if the code
+is running in "Workstation" or "Server" mode, a distinction which affects the
+garbage collector. The most apparent difference in the debugger is that in
+"Server" mode there is one dedicated garbage collector thread per CPU.
+
+A handy supplement to this function is to also run "lm v m clr". That
+will provide more details about the CLR, including where clr.dll is
+loaded from.
+\\
+
+COMMAND: dumpmodule.
+!DumpModule [-mt] <Module address>
+
+You can get a Module address from !DumpDomain, !DumpAssembly and other
+functions. Here is sample output:
+
+ 0:000> !DumpModule 1caa50
+ Name: C:\pub\unittest.exe
+ Attributes: PEFile
+ Assembly: 001ca248
+ LoaderHeap: 001cab3c
+ TypeDefToMethodTableMap: 03ec0010
+ TypeRefToMethodTableMap: 03ec0024
+ MethodDefToDescMap: 03ec0064
+ FieldDefToDescMap: 03ec00a4
+ MemberRefToDescMap: 03ec00e8
+ FileReferencesMap: 03ec0128
+ AssemblyReferencesMap: 03ec012c
+ MetaData start address: 00402230 (1888 bytes)
+
+The Maps listed map metadata tokens to CLR data structures. Without going into
+too much detail, you can examine memory at those addresses to find the
+appropriate structures. For example, the TypeDefToMethodTableMap above can be
+examined:
+
+ 0:000> dd 3ec0010
+ 03ec0010 00000000 00000000 0090320c 0090375c
+ 03ec0020 009038ec ...
+
+This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You
+can run !DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token
+and maps it to a MethodDesc, which can be passed to !DumpMD.
+
+There is a new option "-mt", which will display the types defined in a module,
+and the types referenced by the module. For example:
+
+ 0:000> !dumpmodule -mt 1aa580
+ Name: C:\pub\unittest.exe
+ ...<etc>...
+ MetaData start address: 0040220c (1696 bytes)
+
+ Types defined in this module
+
+ MT TypeDef Name
+ --------------------------------------------------------------------------
+ 030d115c 0x02000002 Funny
+ 030d1228 0x02000003 Mainy
+
+ Types referenced in this module
+
+ MT TypeRef Name
+ --------------------------------------------------------------------------
+ 030b6420 0x01000001 System.ValueType
+ 030b5cb0 0x01000002 System.Object
+ 030fceb4 0x01000003 System.Exception
+ 0334e374 0x0100000c System.Console
+ 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle
+ 0336a048 0x0100000f System.GC
+
+\\
+
+COMMAND: threadpool.
+!ThreadPool
+
+This command lists basic information about the ThreadPool, including the number
+of work requests in the queue, number of completion port threads, and number of
+timers.
+\\
+
+COMMAND: dumpassembly.
+!DumpAssembly <Assembly address>
+
+Example output:
+
+ 0:000> !dumpassembly 1ca248
+ Parent Domain: 0014f000
+ Name: C:\pub\unittest.exe
+ ClassLoader: 001ca060
+ Module Name
+ 001caa50 C:\pub\unittest.exe
+
+An assembly can consist of multiple modules, and those will be listed. You can
+get an Assembly address from the output of !DumpDomain.
+\\
+
+COMMAND: dumpruntimetypes.
+!DumpRuntimeTypes
+
+!DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and
+prints the type name and MethodTable they refer too. Sample output:
+
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ a515f4 14a740 5baf8d28 System.TypedReference
+ a51608 14a740 5bb05764 System.Globalization.BaseInfoTable
+ a51958 14a740 5bb05b24 System.Globalization.CultureInfo
+ a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly
+ a51de0 14a740 5bb069c8 System.Globalization.TextInfo
+ a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource
+ a56bbc 14a740 5baf7248 System.Int32
+ a56bd0 14a740 5baf3fdc System.String
+ a56cfc 14a740 5baf36a4 System.ValueType
+ ...
+
+This command will print a "?" in the domain column if the type is loaded into multiple
+AppDomains. For example:
+
+ 0:000> !DumpRuntimeTypes
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ 28435a0 ? 3f6a8c System.TypedReference
+ 28435b4 ? 214d6c System.ValueType
+ 28435c8 ? 216314 System.Enum
+ 28435dc ? 2147cc System.Object
+ 284365c ? 3cd57c System.IntPtr
+ 2843670 ? 3feaac System.Byte
+ 2843684 ? 23a544c System.IEquatable`1[[System.IntPtr, mscorlib]]
+ 2843784 ? 3c999c System.Int32
+ 2843798 ? 3caa04 System.IEquatable`1[[System.Int32, mscorlib]]
+
+\\
+
+COMMAND: dumpsig.
+!DumpSig <sigaddr> <moduleaddr>
+
+This command dumps the signature of a method or field given by <sigaddr>. This is
+useful when you are debugging parts of the runtime which returns a raw PCCOR_SIGNATURE
+structure and need to know what its contents are.
+
+Sample output for a method:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+The first section of the output is the calling convention. This includes, but is not
+limited to, "[DEFAULT]", "[C]", "[STDCALL]", "[THISCALL]", and so on. The second
+portion of the output is either "[hasThis]" or "[explicit]" for whether the method
+is an instance method or a static method respectively. The third portion of the
+output is the return value (in this case a "void"). Finally, the method's arguments
+are printed as the final portion of the output.
+
+Sample output for a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+!DumpSig will also work with generics. Here is the output for the following
+function:
+ public A Test(IEnumerable<B> n)
+
+ 0:000> !dumpsig 00000000`00bc2437 000007ff00043178
+ [DEFAULT] [hasThis] __Canon (Class System.Collections.Generic.IEnumerable`1<__Canon>)
+
+\\
+
+COMMAND: dumpsigelem.
+!DumpSigElem <sigaddr> <moduleaddr>
+
+This command dumps a single element of a signature object. For most circumstances,
+you should use !DumpSig to look at individual signature objects, but if you find a
+signature that has been corrupted in some manner you can use !DumpSigElem to read out
+the valid portions of it.
+
+If we look at a valid signature object for a method we see the following:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+We can look at the individual elements of this object by adding the offsets into the
+object which correspond to the return value and parameters:
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+2 0x000007fe`eabd1000
+ Void
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+3 0x000007fe`eabd1000
+ Boolean
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+4 0x000007fe`eabd1000
+ String
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+5 0x000007fe`eabd1000
+ String
+
+We can do something similar for fields. Here is the full signature of a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+Using !DumpSigElem we can find the type of the field by adding the offset of it (1) to
+the address of the signature:
+ 0:000> !dumpsigelem 0x000007fe`eb7fd8cd+1 0x000007fe`eabd1000
+ ValueClass System.RuntimeTypeHandle
+
+!DumpSigElem will also work with generics. Let a function be defined as follows:
+ public A Test(IEnumerable<B> n)
+
+The elements of this signature can be obtained by adding offsets into the signature
+when calling !DumpSigElem:
+
+ 0:000> !dumpsigelem 00000000`00bc2437+2 000007ff00043178
+ __Canon
+ 0:000> !dumpsigelem 00000000`00bc2437+4 000007ff00043178
+ Class System.Collections.Generic.IEnumerable`1<__Canon>
+
+The actual offsets that you should add are determined by the contents of the
+signature itself. By trial and error you should be able to find various elements
+of the signature.
+
+\\
+
+COMMAND: rcwcleanuplist.
+!RCWCleanupList [address]
+
+A RuntimeCallableWrapper is an internal CLR structure used to host COM objects
+which are exposed to managed code. This is exposed to managed code through the
+System.__ComObject class, and when objects of this type are collected, and a
+reference to the underlying COM object is no longer needed, the corresponding
+RCW is cleaned up. If you are trying to debug an issue related to one of these
+RCWs, then you can use the !RCWCleanupList function to display which COM objects
+will be released the next time a cleanup occurs.
+
+If given an address, this function will display the RCWCleanupList at that address.
+If no address is specified, it displays the default cleanup list, printing the
+wrapper, the context, and the thread of the object.
+
+Example:
+ 0:002> !rcwcleanuplist 001c04d0
+ RuntimeCallableWrappers (RCW) to be cleaned:
+ RCW CONTEXT THREAD Apartment
+ 1d54e0 192008 181180 STA
+ 1d4140 192178 0 MTA
+ 1dff50 192178 0 MTA
+ MTA Interfaces to be released: 2
+ STA Interfaces to be released: 1
+
+Note that CLR keeps track of which RCWs are bound to which managed objects through
+the SyncBlock of the object. As such, you can see more information about RCW
+objects through the !SyncBlk command. You can find more information about RCW
+cleanup through the !FinalizeQueue command.
+
+\\
+
+COMMAND: dumpil.
+!DumpIL <Managed DynamicMethod object> |
+ <DynamicMethodDesc pointer> |
+ <MethodDesc pointer> |
+ /i <IL pointer>
+
+!DumpIL prints the IL code associated with a managed method. We added this
+function specifically to debug DynamicMethod code which was constructed on
+the fly. Happily it works for non-dynamic code as well.
+
+You can use it in four ways:
+
+ 1) If you have a System.Reflection.Emit.DynamicMethod object, just pass
+ the pointer as the first argument.
+ 2) If you have a DynamicMethodDesc pointer you can use that to print the
+ IL associated with the dynamic method.
+ 3) If you have an ordinary MethodDesc, you can see the IL for that as well,
+ just pass it as the first argument.
+ 4) If you have a pointer directly to the IL, specify /i followed by the
+ the IL address. This is useful for writers of profilers that instrument
+ IL.
+
+
+Note that dynamic IL is constructed a bit differently. Rather than referring
+to metadata tokens, the IL points to objects in a managed object array. Here
+is a simple example of the output for a dynamic method:
+
+ 0:000> !dumpil b741dc
+ This is dynamic IL. Exception info is not reported at this time.
+ If a token is unresolved, run "!do <addr>" on the addr given
+ in parenthesis. You can also look at the token table yourself, by
+ running "!DumpArray 00b77388".
+
+ IL_0000: ldstr 70000002 "Inside invoked method "
+ IL_0005: call 6000003 System.Console.WriteLine(System.String)
+ IL_000a: ldc.i4.1
+ IL_000b: newarr 2000004 "System.Int32"
+ IL_0010: stloc.0
+ IL_0011: ldloc.0
+ IL_0012: ret
+
+\\
+
+COMMAND: verifyheap.
+!VerifyHeap
+
+!VerifyHeap is a diagnostic tool that checks the garbage collected heap for
+signs of corruption. It walks objects one by one in a pattern like this:
+
+ o = firstobject;
+ while(o != endobject)
+ {
+ o.ValidateAllFields();
+ o = (Object *) o + o.Size();
+ }
+
+If an error is found, !VerifyHeap will report it. I'll take a perfectly good
+object and corrupt it:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+ 0:000> ed a79d40+4 01 (change the name field to the bogus pointer value 1)
+ 0:000> !VerifyHeap
+ object 01ee60dc: bad member 00000003 at 01EE6168
+ Last good object: 01EE60C4.
+
+If this gc heap corruption exists, there is a serious bug in your own code or
+in the CLR. In user code, an error in constructing PInvoke calls can cause
+this problem, and running with Managed Debugging Assistants is advised. If that
+possibility is eliminated, consider contacting Microsoft Product Support for
+help.
+
+\\
+
+COMMAND: verifyobj.
+!VerifyObj <object address>
+
+!VerifyObj is a diagnostic tool that checks the object that is passed as an
+argument for signs of corruption.
+
+ 0:002> !verifyobj 028000ec
+ object 0x28000ec does not have valid method table
+
+ 0:002> !verifyobj 0680017c
+ object 0x680017c: bad member 00000001 at 06800184
+
+\\
+
+COMMAND: findroots.
+!FindRoots -gen <N> | -gen any | <object address>
+
+The "-gen" form causes the debugger to break in the debuggee on the next
+collection of the specified generation. The effect is reset as soon as the
+break occurs, in other words, if you need to break on the next collection you
+would need to reissue the command.
+
+The last form of this command is meant to be used after the break caused by the
+other forms has occurred. Now the debuggee is in the right state for
+!FindRoots to be able to identify roots for objects from the current condemned
+generations.
+
+!FindRoots is a diagnostic command that is meant to answer the following
+question:
+
+"I see that GCs are happening, however my objects have still not been
+collected. Why? Who is holding onto them?"
+
+The process of answering the question would go something like this:
+
+1. Find out the generation of the object of interest using the !GCWhere
+command, say it is gen 1:
+ !GCWhere <object address>
+
+2. Instruct the runtime to stop the next time it collects that generation using
+the !FindRoots command:
+ !FindRoots -gen 1
+ g
+
+3. When the next GC starts, and has proceeded past the mark phase a CLR
+notification will cause a break in the debugger:
+ (fd0.ec4): CLR notification exception - code e0444143 (first chance)
+ CLR notification: GC - end of mark phase.
+ Condemned generation: 1.
+
+4. Now we can use the !FindRoots <object address> to find out the cross
+generational references to the object of interest. In other words, even if the
+object is not referenced by any "proper" root it may still be referenced by an
+older object (from an older generation), from a generation that has not yet been
+scheduled for collection. At this point !FindRoots will search those older
+generations too, and report those roots.
+ 0:002> !findroots 06808094
+ older generations::Root: 068012f8(AAA.Test+a)->
+ 06808094(AAA.Test+b)
+
+
+\\
+
+COMMAND: heapstat.
+!HeapStat [-inclUnrooted | -iu]
+
+This command shows the generation sizes for each heap and the total, how much free
+space there is in each generation on each heap. If the -inclUnrooted option is
+specified the report will include information about the managed objects from the
+GC heap that are not rooted anymore.
+
+Sample output:
+
+ 0:002> !heapstat
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ 0:002> !heapstat -inclUnrooted
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ Unrooted objects: Percentage
+ Heap0 152212 0 306196 0 SOH: 94% LOH: 0%
+ Heap1 155704 0 0 0 SOH: 97% LOH: 0%
+ Total 307916 0 306196 0
+
+The percentage column contains a breakout of free or unrooted bytes to total bytes.
+
+\\
+
+COMMAND: analyzeoom.
+!AnalyzeOOM
+
+!AnalyzeOOM displays the info of the last OOM occurred on an allocation request to
+the GC heap (in Server GC it displays OOM, if any, on each GC heap).
+
+To see the managed exception(s) use the !Threads command which will show you
+managed exception(s), if any, on each managed thread. If you do see an
+OutOfMemoryException exception you can use the !PrintException command on it.
+To get the full callstack use the "kb" command in the debugger for that thread.
+For example, to display thread 3's stack use ~3kb.
+
+OOM exceptions could be because of the following reasons:
+
+1) allocation request to GC heap
+ in which case you will see JIT_New* on the call stack because managed code called new.
+2) other runtime allocation failure
+ for example, failure to expand the finalize queue when GC.ReRegisterForFinalize is
+ called.
+3) some other code you use throws a managed OOM exception
+ for example, some .NET framework code converts a native OOM exception to managed
+ and throws it.
+
+The !AnalyzeOOM command aims to help you with investigating 1) which is the most
+difficult because it requires some internal info from GC. The only exception is
+we don't support allocating objects larger than 2GB on CLR v2.0 or prior. And this
+command will not display any managed OOM because we will throw OOM right away
+instead of even trying to allocate it on the GC heap.
+
+There are 2 legitimate scenarios where GC would return OOM to allocation requests -
+one is if the process is running out of VM space to reserve a segment; the other
+is if the system is running out physical memory (+ page file if you have one) so
+GC can not commit memory it needs. You can look at these scenarios by using performance
+counters or debugger commands. For example for the former scenario the "!address
+-summary" debugger command will show you the largest free region in the VM. For
+the latter scenario you can look at the "Memory\% Committed Bytes In Use" see
+if you are running low on commit space. One important thing to keep in mind is
+when you do this kind of memory analysis it could an aftereffect and doesn't
+completely agree with what this command tells you, in which case the command should
+be respected because it truly reflects what happened during GC.
+
+The other cases should be fairly obvious from the callstack.
+
+Sample output:
+
+0:011> !ao
+---------Heap 2 ---------
+Managed OOM occurred after GC #28 (Requested to allocate 1234 bytes)
+Reason: Didn't have enough memory to commit
+Detail: SOH: Didn't have enough memory to grow the internal GC datastructures (800000 bytes) -
+ on GC entry available commit space was 500 MB
+---------Heap 4 ---------
+Managed OOM occurred after GC #12 (Requested to allocate 100000 bytes)
+Reason: Didn't have enough memory to allocate an LOH segment
+Detail: LOH: Failed to reserve memory (16777216 bytes)
+
+\\
+
+COMMAND: gcwhere.
+!GCWhere <object address>
+
+!GCWhere displays the location in the GC heap of the argument passed in.
+
+ 0:002> !GCWhere 02800038
+ Address Gen Heap segment begin allocated size
+ 02800038 2 0 02800000 02800038 0282b740 12
+
+When the argument lies in the managed heap, but is not a valid *object* address
+the "size" is displayed as 0:
+
+ 0:002> !GCWhere 0280003c
+ Address Gen Heap segment begin allocated size
+ 0280003c 2 0 02800000 02800038 0282b740 0
+
+\\
+
+COMMAND: listnearobj.
+!ListNearObj <object address>
+
+!ListNearObj is a diagnostic tool that displays the object preceeding and
+succeeding the address passed in:
+
+The command looks for the address in the GC heap that looks like a valid
+beginning of a managed object (based on a valid method table) and the object
+following the argument address.
+
+ 0:002> !ListNearObj 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+ 0:002> !ListNearObj 028000f0
+ Before: 0x28000ec 72 (0x48 ) System.ExecutionEngineException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+The command considers the heap as "locally consistent" if:
+ prev_obj_addr + prev_obj_size = arg_addr && arg_obj + arg_size = next_obj_addr
+OR
+ prev_obj_addr + prev_obj_size = next_obj_addr
+
+When the condition is not satisfied:
+
+ 0:002> !lno 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency not confirmed.
+
+\\
+
+COMMAND: dumplog.
+!DumpLog [-addr <addressOfStressLog>] [<Filename>]
+
+To aid in diagnosing hard-to-reproduce stress failures, the CLR team added an
+in-memory log capability. The idea was to avoid using locks or I/O which could
+disturb a fragile repro environment. The !DumpLog function allows you to write
+that log out to a file. If no Filename is specified, the file "Stresslog.txt"
+in the current directory is created.
+
+The optional argument addr allows one to specify a stress log other then the
+default one.
+
+ 0:000> !DumpLog
+ Attempting to dump Stress log to file 'StressLog.txt'
+ .................
+ SUCCESS: Stress log dumped
+
+To turn on the stress log, set the following registry keys under
+HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework:
+
+
+(DWORD) StressLog = 1
+(DWORD) LogFacility = 0xffffffbf (this is a bit mask, almost all logging is on.
+ This is also the default value if the key
+ isn't specified)
+(DWORD) StressLogSize = 65536 (this is the default value if the key isn't
+ specified)
+(DWORD) LogLevel = 6 (this is the default value if the key isn't
+ specified. The higher the number the more
+ detailed logs are generated. The maximum
+ value is decimal 10)
+
+StressLogSize is the size in bytes of the in-memory log allocated for each
+thread in the process. In the case above, each thread gets a 64K log. You
+could increase this to get more logging, but more memory will be required for
+this log in the process. For example, 20 threads with 524288 bytes per thread
+has a memory demand of 10 Megabytes. The stress log is circular so new entries
+will replace older ones on threads which have reached their buffer limit.
+
+The log facilities are defined as follows:
+ GC 0x00000001
+ GCINFO 0x00000002
+ STUBS 0x00000004
+ JIT 0x00000008
+ LOADER 0x00000010
+ METADATA 0x00000020
+ SYNC 0x00000040
+ EEMEM 0x00000080
+ GCALLOC 0x00000100
+ CORDB 0x00000200
+ CLASSLOADER 0x00000400
+ CORPROF 0x00000800
+ REMOTING 0x00001000
+ DBGALLOC 0x00002000
+ EH 0x00004000
+ ENC 0x00008000
+ ASSERT 0x00010000
+ VERIFIER 0x00020000
+ THREADPOOL 0x00040000
+ GCROOTS 0x00080000
+ INTEROP 0x00100000
+ MARSHALER 0x00200000
+ IJW 0x00400000
+ ZAP 0x00800000
+ STARTUP 0x01000000
+ APPDOMAIN 0x02000000
+ CODESHARING 0x04000000
+ STORE 0x08000000
+ SECURITY 0x10000000
+ LOCKS 0x20000000
+ BCL 0x40000000
+
+Here is some sample output:
+
+ 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering.
+ Thread state = a030
+
+ 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2,
+ collect_classes = 0) ==========={
+
+ 3560 9.981125826 : `GC` Segment mem 00C61000 alloc
+ = 00D071F0 used 00D09254 committed 00D17000
+
+ 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000
+ ] cur = 00000000
+
+ 3560 9.981124963 : `GC` GC Heap 00000000
+
+ 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0)
+
+The first column is the OS thread ID for the thread appending to the log,
+the second column is the timestamp, the third is the facility category for the
+log entry, and the fourth contains the log message. The facility field is
+expressed as `facility1`facility2`facility3`. This facilitates the creation of
+filters for displaying only specific message categories. To make sense of this
+log, you would probably want the Shared Source CLI to find out exactly where
+the log comes from.
+\\
+
+COMMAND: findappdomain.
+!FindAppDomain <Object address>
+
+!FindAppDomain will attempt to resolve the AppDomain of an object. For example,
+using an Object Pointer from the output of !DumpStackObjects:
+
+ 0:000> !findappdomain 00a79d98
+ AppDomain: 0014f000
+ Name: unittest.exe
+ ID: 1
+
+You can find out more about the AppDomain with the !DumpDomain command. Not
+every object has enough clues about it's origin to determine the AppDomain.
+Objects with Finalizers are the easiest case, as the CLR needs to be able to
+call those when an AppDomain shuts down.
+\\
+
+COMMAND: savemodule.
+!SaveModule <Base address> <Filename>
+
+This command allows you to take a image loaded in memory and write it to a
+file. This is especially useful if you are debugging a full memory dump, and
+don't have the original DLLs or EXEs. This is most often used to save a managed
+binary to a file, so you can disassemble the code and browse types with ILDASM.
+
+The base address of an image can be found with the "LM" debugger command:
+
+ 0:000> lm
+ start end module name
+ 00400000 00408000 image00400000 (deferred)
+ 10200000 102ac000 MSVCR80D (deferred)
+ 5a000000 5a0b1000 mscoree (deferred)
+ 5a140000 5a29e000 clrjit (deferred)
+ 5b660000 5c440000 mscorlib_dll (deferred)
+ 5d1d0000 5e13c000 clr (deferred)
+ ...
+
+If I wanted to save a copy of clr.dll, I could run:
+
+ 0:000> !SaveModule 5d1d0000 c:\pub\out.tmp
+ 4 sections in file
+ section 0 - VA=1000, VASize=e82da9, FileAddr=400, FileSize=e82e00
+ section 1 - VA=e84000, VASize=24d24, FileAddr=e83200, FileSize=ec00
+ section 2 - VA=ea9000, VASize=5a8, FileAddr=e91e00, FileSize=600
+ section 3 - VA=eaa000, VASize=c183c, FileAddr=e92400, FileSize=c1a00
+
+The diagnostic output indicates that the operation was successful. If
+c:\pub\out.tmp already exists, it will be overwritten.
+\\
+
+COMMAND: gchandles.
+!GCHandles [-type handletype] [-stat] [-perdomain]
+
+!GCHandles provides statistics about GCHandles in the process.
+
+Paremeters:
+ stat - Only display the statistics and not the list of handles and
+ what they point to.
+ perdomain - Break down the statistics by the app domain in which
+ the handles reside.
+ type - A type of handle to filter it by. The handle types are:
+ Pinned
+ RefCounted
+ WeakShort
+ WeakLong
+ Strong
+ Variable
+ AsyncPinned
+ SizedRef
+
+Sometimes the source of a memory leak is a GCHandle leak. For example, code
+might keep a 50 Megabyte array alive because a strong GCHandle points to it,
+and the handle was discarded without freeing it.
+
+The most common handles are "Strong Handles," which keep the object they point
+to alive until the handle is explicitly freed. "Pinned Handles" are used to
+prevent the garbage collector from moving an object during collection. These
+should be used sparingly, and for short periods of time. If you don't follow
+that precept, the gc heap can become very fragmented.
+
+Here is sample output from a very simple program. Note that the "RefCount"
+field only applies to RefCount Handles, and this field will contain the
+reference count:
+
+ 0:000> !GCHandles
+ Handle Type Object Size RefCount Type
+ 001611c0 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c4 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c8 Strong 01d1b48c 40 System.Diagnostics.LogSwitch
+ 001611d0 Strong 01cfd2c0 36 System.Security.PermissionSet
+ 001611d4 Strong 01cf7484 56 System.Object[]
+ 001611d8 Strong 01cf1238 32 System.SharedStatics
+ 001611dc Strong 01cf11c8 84 System.Threading.ThreadAbortException
+ 001611e0 Strong 01cf1174 84 System.Threading.ThreadAbortException
+ 001611e4 Strong 01cf1120 84 System.ExecutionEngineException
+ 001611e8 Strong 01cf10cc 84 System.StackOverflowException
+ 001611ec Strong 01cf1078 84 System.OutOfMemoryException
+ 001611f0 Strong 01cf1024 84 System.Exception
+ 001611f8 Strong 01cf2068 48 System.Threading.Thread
+ 001611fc Strong 01cf1328 112 System.AppDomain
+ 001613ec Pinned 02cf3268 8176 System.Object[]
+ 001613f0 Pinned 02cf2258 4096 System.Object[]
+ 001613f4 Pinned 02cf2038 528 System.Object[]
+ 001613f8 Pinned 01cf121c 12 System.Object
+ 001613fc Pinned 02cf1010 4116 System.Object[]
+
+ Statistics:
+ MT Count TotalSize Class Name
+ 563266dc 1 12 System.Object
+ 56329708 1 32 System.SharedStatics
+ 5632bc38 1 36 System.Security.PermissionSet
+ 5635f934 1 40 System.Diagnostics.LogSwitch
+ 5632759c 1 48 System.Threading.Thread
+ 5632735c 1 84 System.ExecutionEngineException
+ 56327304 1 84 System.StackOverflowException
+ 563272ac 1 84 System.OutOfMemoryException
+ 563270c4 1 84 System.Exception
+ 56328914 1 112 System.AppDomain
+ 56335f78 2 168 System.IndexOutOfRangeException
+ 563273b4 2 168 System.Threading.ThreadAbortException
+ 563208d0 5 16972 System.Object[]
+ Total 19 objects
+
+ Handles:
+ Strong Handles: 14
+ Pinned Handles: 5
+\\
+
+COMMAND: gchandleleaks.
+!GCHandleLeaks
+
+This command is an aid in tracking down GCHandle leaks. It searches all of
+memory for any references to the Strong and Pinned GCHandles in the process,
+and reports what it found. If a handle is found, you'll see the address of the
+reference. This might be a stack address or a field within an object, for
+example. If a handle is not found in memory, you'll get notification of that
+too.
+
+The command has diagnostic output which doesn't need to be repeated here. One
+thing to keep in mind is that anytime you search all of memory for a value, you
+can get false positives because even though the value was found, it might be
+garbage in that no code knows about the address. You can also get false
+negatives because a user is free to pass that GCHandle to unmanaged code that
+might store the handle in a strange way (shifting bits, for example).
+
+For example, a GCHandle valuetype is stored on the stack with the low bit set
+if it points to a Pinned handle. So !GCHandleLeaks ignores the low bit in it's
+searches.
+
+That said, if a serious leak is going on, you'll get a ever-growing set of
+handle addresses that couldn't be found.
+\\
+
+COMMAND: vmmap.
+!VMMap
+
+!VMMap traverses the virtual address space and lists the type of protection
+applied to each region. Sample output:
+
+ 0:000> !VMMap
+ Start Stop Length AllocProtect Protect State Type
+ 00000000-0000ffff 00010000 NA Free
+ 00010000-00011fff 00002000 RdWr RdWr Commit Private
+ 00012000-0001ffff 0000e000 NA Free
+ 00020000-00020fff 00001000 RdWr RdWr Commit Private
+ 00021000-0002ffff 0000f000 NA Free
+ 00030000-00030fff 00001000 RdWr Reserve Private
+ ...
+\\
+
+COMMAND: vmstat.
+!VMStat
+
+Provides a summary view of the virtual address space, ordered by each type of
+protection applied to that memory (free, reserved, committed, private, mapped,
+image). The TOTAL column is (AVERAGE * BLK COUNT). Sample output below:
+
+ 0:000> !VMStat
+ ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~ ~~~~~
+ TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL
+ Free:
+ Small 4,096 65,536 48,393 27 1,306,611
+ Medium 139,264 528,384 337,920 4 1,351,680
+ Large 6,303,744 974,778,368 169,089,706 12 2,029,076,472
+ Summary 4,096 974,778,368 47,249,646 43 2,031,734,778
+
+ Reserve:
+ Small 4,096 65,536 43,957 41 1,802,237
+ Medium 249,856 1,019,904 521,557 6 3,129,342
+ Large 2,461,696 16,703,488 11,956,224 3 35,868,672
+ Summary 4,096 16,703,488 816,005 50 40,800,250
+
+\\
+
+COMMAND: procinfo.
+!ProcInfo [-env] [-time] [-mem]
+
+!ProcInfo lists the environment variables for the process, kernel CPU time, as
+well as memory usage statistics.
+\\
+
+COMMAND: histinit.
+!HistInit
+
+Before running any of the Hist - family commands you need to initialize the SOS
+structures from the stress log saved in the debuggee. This is achieved by the
+HistInit command.
+
+Sample output:
+
+ 0:001> !HistInit
+ Attempting to read Stress log
+ STRESS LOG:
+ facilitiesToLog = 0xffffffff
+ levelToLog = 6
+ MaxLogSizePerThread = 0x10000 (65536)
+ MaxTotalLogSize = 0x1000000 (16777216)
+ CurrentTotalLogChunk = 9
+ ThreadsWithLogs = 3
+ Clock frequency = 3.392 GHz
+ Start time 15:26:31
+ Last message time 15:26:56
+ Total elapsed time 25.077 sec
+ .....................................
+ ---------------------------- 2407 total entries -----------------------------
+
+
+ SUCCESS: GCHist structures initialized
+
+\\
+
+COMMAND: histobjfind.
+!HistObjFind <obj_address>
+
+To examine log entries related to an object whose present address is known one
+would use this command. The output of this command contains all entries that
+reference the object:
+
+ 0:003> !HistObjFind 028970d4
+ GCCount Object Message
+ ---------------------------------------------------------
+ 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2296 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2296 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+ 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2295 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2295 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+
+\\
+
+COMMAND: histroot.
+!HistRoot <root>
+
+The root value obtained from !HistObjFind can be used to track the movement of
+an object through the GCs.
+
+HistRoot provides information related to both promotions and relocations of the
+root specified as the argument.
+
+ 0:003> !HistRoot 01e411b8
+ GCCount Value MT Promoted? Notes
+ ---------------------------------------------------------
+ 2296 028970d4 5b6c5cd8 yes
+ 2295 028970d4 5b6c5cd8 yes
+ 2294 028970d4 5b6c5cd8 yes
+ 2293 028970d4 5b6c5cd8 yes
+ 2292 028970d4 5b6c5cd8 yes
+ 2291 028970d4 5b6c5cd8 yes
+ 2290 028970d4 5b6c5cd8 yes
+ 2289 028970d4 5b6c5cd8 yes
+ 2288 028970d4 5b6c5cd8 yes
+ 2287 028970d4 5b6c5cd8 yes
+ 2286 028970d4 5b6c5cd8 yes
+ 2285 028970d4 5b6c5cd8 yes
+ 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs
+ ...
+
+\\
+
+COMMAND: histobj.
+!HistObj <obj_address>
+
+This command examines all stress log relocation records and displays the chain
+of GC relocations that may have led to the address passed in as an argument.
+Conceptually the output is:
+
+ GenN obj_address root1, root2, root3,
+ GenN-1 prev_obj_addr root1, root2,
+ GenN-2 prev_prev_oa root1, root4,
+ ...
+
+Sample output:
+ 0:003> !HistObj 028970d4
+ GCCount Object Roots
+ ---------------------------------------------------------
+ 2296 028970d4 00223fc4, 01e411b8,
+ 2295 028970d4 00223fc4, 01e411b8,
+ 2294 028970d4 00223fc4, 01e411b8,
+ 2293 028970d4 00223fc4, 01e411b8,
+ 2292 028970d4 00223fc4, 01e411b8,
+ 2291 028970d4 00223fc4, 01e411b8,
+ 2290 028970d4 00223fc4, 01e411b8,
+ 2289 028970d4 00223fc4, 01e411b8,
+ 2288 028970d4 00223fc4, 01e411b8,
+ 2287 028970d4 00223fc4, 01e411b8,
+ 2286 028970d4 00223fc4, 01e411b8,
+ 2285 028970d4 00223fc4, 01e411b8,
+ 322 028970d4 01e411b8,
+ 0 028970d4
+
+\\
+
+COMMAND: histclear.
+!HistClear
+
+This command releases any resources used by the Hist-family of commands.
+Generally there's no need to call this explicitly, as each HistInit will first
+cleanup the previous resources.
+
+ 0:003> !HistClear
+ Completed successfully.
+
+\\
+
+COMMAND: dumprcw.
+!DumpRCW <RCW address>
+
+This command lists information about a Runtime Callable Wrapper. You can use
+!DumpObj to obtain the RCW address corresponding to a managed object.
+
+The output contains all COM interface pointers that the RCW holds on to, which
+is useful for investigating lifetime issues of interop-heavy applications.
+\\
+
+COMMAND: dumpccw.
+!DumpCCW <CCW address or COM IP>
+
+This command lists information about a COM Callable Wrapper. You can use
+!DumpObj to obtain the CCW address corresponding to a managed object or pass
+a COM interface pointer to which the object has been marshaled.
+
+The output contains the COM reference count of the CCW, which is useful for
+investigating lifetime issues of interop-heavy applications.
+\\
+
+COMMAND: suppressjitoptimization.
+!SuppressJitOptimization [on|off]
+
+!SuppressJitOptimization helps when you want the CLR to generate more debuggable
+jitted code. This will turn off inlining, enregistering of local variables,
+optimizing across sequence point boundaries, and precise variable lifetimes.
+The resulting code should step more cleanly and make local variables more
+accesible to inspection. The cost is that code quality is lower so your
+application may execute a little slower.
+
+Once you execute !SuppressJitOptimization on, all managed modules that load
+from that point onwards will not be optimized. The setting is only checked once
+per module, at the time that module loads. Changing the setting later will only
+affect modules that are loaded later. The recommendation is to set this once at
+app startup and let the same setting apply for all managed modules.
+\\
+
+COMMAND: stoponcatch.
+!StopOnCatch
+
+This commands sets a one-time breakpoint on the first managed catch clause
+entered by managed code. This will cause the debugger to stop on the first line
+of the catch handler. This is useful step from from the point of an exception
+throw to the catch handler.
+\\
+
+
+COMMAND: savestate.
+!SaveState <file_path>
+
+!SaveState serializes SOS managed breakpoints and watch values into a script file
+that can be executed on a future windbg session to restore them. Use the windbg
+command <$ <file_path> to execute the saved script.
+\\
+
+COMMAND: watch.
+!Watch
+!Watch -add <expression>
+!Watch -remove <index>
+!Watch -save <watch_list_name>
+!Watch -rename <old_watch_list_name> <new_watch_list_name>
+!Watch [-filter <watch_list_name>] [-expand <index> [type_cast]<expression>]
+
+!Watch displays the value of managed expressions, and manages the list of
+expressions. A quick example...
+
+DEBUGGEE CODE:
+ static int Main(string[] args)
+ {
+ int p14 = -123;
+ MyStruct ms = new MyStruct();
+ ms.a = p14;
+ ms.b = true;
+ MyStruct.c = "Foo";
+ ...
+
+COMMAND WINDOW:
+0:000> !Watch -add ms
+0:000> !Watch -add p14
+0:000> !Watch
+ 1) MyStruct ms @ 0x0093D1CC
+ 2) int p14 = -123
+
+ms in the command window will be a DML link, if you click on it then you see:
+
+COMMAND WINDOW:
+0:000> !watch -expand 1 (MyStruct)ms
+ 1) MyStruct ms @ 0x0093D1CC
+ |- int a = -123
+ |- bool b = true
+ |- string c = null
+ 2) int p14 = -123
+
+
+
+Watch is capable of parsing a variety of different expressions. Expressions
+can start with the name of a local variable or parameter in any stack frame.
+To access fields of classes and structures simply add a '.' and then the field
+name. To access array elements use standard [<index>] notation. In the above
+code example these would be valid expressions:
+args[19]
+p14
+ms.a
+
+Additionally 'this' is available in the leafmost frame to access fields.
+You can not use unqualified field names. For example if there is a class:
+class C
+{
+ int m_foo;
+ public void Hello() { Console.WriteLine(m_foo); }
+}
+
+this.m_foo is a legal expression when executing inside C.Hello(),
+m_foo would not work.
+
+Expressions also allow fully qualified type names and dereferencing static
+fields from those types using the same '.' syntax. For example:
+System.Int32
+System.Type.Missing
+
+Generic types can be used, though don't forget to use the CLI type
+name which adds `<number_of_generic_params> to the type name you would see
+in VB or C#. For example:
+System.Collections.Generic.Dictionary`2<System.String,System.Type>
+
+Dereferencing fields or array indices can recurse as you might expect. A
+made up example:
+foo.bar.baz[19][12].m_field[4]
+
+Property values and method invocations can not be used in expressions as
+the !Watch command will never execute any managed code to evaluate the
+expressions.
+
+To remove entries in the list use !Watch -remove <index> where index is
+the number printed next to the expression in the viewing list. In our
+initial example !Watch -remove 2 would remove the 'p14' expression from
+the list.
+
+Saving/Renaming/Filtering
+Sometimes it can be helpful to see only those values which have changed
+since some point in the past. As an example assume that MyStruct.c changes
+values during the go:
+
+0:000> !Watch -save myOldValues
+0:000> g
+...
+0:000> !Watch -filter myOldValues
+ 3) string MyStruct.c = "Foo"
+
+When saving a list of expressions, the string that would be displayed in a
+!Watch command is also saved. It is this string that is compared to determine
+if the value has changed. Some changes might not alter the console string
+representation. In the first example changing the value of ms.a would not
+cause the 'ms' expression to be different.
+
+Renaming can be useful in scripts. It allows the name of a saved watch list to
+change after saving it. For example:
+0:000> !Watch -rename myCurrentValues myOldValues
+0:000> !Watch -save myCurrentValues
+0:000> !Watch -filter myOldValues
+ 3) string MyStruct.c = "Foo"
+
+Placing that pattern inside a script shows the changed watch values
+since the last time the script was run.
+\\
+
diff --git a/src/ToolBox/SOS/Strike/data.h b/src/ToolBox/SOS/Strike/data.h
new file mode 100644
index 0000000000..9989038653
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/data.h
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __data_h__
+#define __data_h__
+
+#include "cor.h"
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+
+BOOL FileExist (const char *filename);
+BOOL FileExist (const WCHAR *filename);
+
+// We use global variables
+// because move returns void if it fails
+//typedef DWORD DWORD_PTR;
+//typedef ULONG ULONG_PTR;
+
+// Max length in WCHAR for a buffer to store metadata name
+const int mdNameLen = 2048;
+extern WCHAR g_mdName[mdNameLen];
+
+const int nMDIMPORT = 128;
+struct MDIMPORT
+{
+ enum MDType {InMemory, InFile, Dynamic};
+ WCHAR *name;
+ size_t base; // base of the PE module
+ size_t mdBase; // base of the metadata
+ char *metaData;
+ ULONG metaDataSize;
+ MDType type;
+ IMetaDataImport *pImport;
+
+ MDIMPORT *left;
+ MDIMPORT *right;
+};
+
+class Module;
+
+extern "C" BOOL ControlC;
+extern IMetaDataDispenserEx *pDisp;
+
+#endif // __data_h__
diff --git a/src/ToolBox/SOS/Strike/datatarget.cpp b/src/ToolBox/SOS/Strike/datatarget.cpp
new file mode 100644
index 0000000000..8f24b7d841
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/datatarget.cpp
@@ -0,0 +1,215 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "sos.h"
+#include "datatarget.h"
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+#include "sospriv.h"
+#include "corerror.h"
+
+#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
+
+DataTarget::DataTarget(void) :
+ m_ref(0)
+{
+}
+
+STDMETHODIMP
+DataTarget::QueryInterface(
+ THIS_
+ ___in REFIID InterfaceId,
+ ___out PVOID* Interface
+ )
+{
+ if (InterfaceId == IID_IUnknown ||
+ InterfaceId == IID_ICLRDataTarget)
+ {
+ *Interface = (ICLRDataTarget*)this;
+ AddRef();
+ return S_OK;
+ }
+ else if (InterfaceId == IID_ICorDebugDataTarget4)
+ {
+ *Interface = (ICorDebugDataTarget4*)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+DataTarget::AddRef(
+ THIS
+ )
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+STDMETHODIMP_(ULONG)
+DataTarget::Release(
+ THIS
+ )
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetMachineType(
+ /* [out] */ ULONG32 *machine)
+{
+ if (g_ExtControl == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtControl->GetExecutingProcessorType(machine);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetPointerSize(
+ /* [out] */ ULONG32 *size)
+{
+#if defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64)
+ *size = 8;
+#elif defined(SOS_TARGET_ARM)
+ *size = 4;
+#elif
+ #error Unsupported architecture
+#endif
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetImageBase(
+ /* [string][in] */ LPCWSTR name,
+ /* [out] */ CLRDATA_ADDRESS *base)
+{
+ if (g_ExtSymbols == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ CHAR lpstr[MAX_LONGPATH];
+ int name_length = WideCharToMultiByte(CP_ACP, 0, name, -1, lpstr, MAX_LONGPATH, NULL, NULL);
+ if (name_length == 0)
+ {
+ return E_FAIL;
+ }
+ return g_ExtSymbols->GetModuleByModuleName(lpstr, 0, NULL, base);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->ReadVirtual(address, (PVOID)buffer, request, done);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->WriteVirtual(address, (PVOID)buffer, request, done);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID)
+{
+ if (g_ExtSystem == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtSystem->GetCurrentThreadSystemId(threadID);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ if (g_ExtSystem == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtSystem->GetThreadContextById(threadID, contextFlags, contextSize, context);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::VirtualUnwind(
+ /* [in] */ DWORD threadId,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, out, size_is(contextSize)] */ PBYTE context)
+{
+ if (g_ExtServices == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtServices->VirtualUnwind(threadId, contextSize, context);
+}
diff --git a/src/ToolBox/SOS/Strike/datatarget.h b/src/ToolBox/SOS/Strike/datatarget.h
new file mode 100644
index 0000000000..0293bc668b
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/datatarget.h
@@ -0,0 +1,90 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+class DataTarget : public ICLRDataTarget, ICorDebugDataTarget4
+{
+private:
+ LONG m_ref; // Reference count.
+
+public:
+ DataTarget(void);
+ virtual ~DataTarget() {}
+
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ ___in REFIID InterfaceId,
+ ___out PVOID* Interface
+ );
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ );
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ );
+
+ //
+ // ICLRDataTarget.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE GetMachineType(
+ /* [out] */ ULONG32 *machine);
+
+ virtual HRESULT STDMETHODCALLTYPE GetPointerSize(
+ /* [out] */ ULONG32 *size);
+
+ virtual HRESULT STDMETHODCALLTYPE GetImageBase(
+ /* [string][in] */ LPCWSTR name,
+ /* [out] */ CLRDATA_ADDRESS *base);
+
+ virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done);
+
+ virtual HRESULT STDMETHODCALLTYPE WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done);
+
+ virtual HRESULT STDMETHODCALLTYPE GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value);
+
+ virtual HRESULT STDMETHODCALLTYPE SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value);
+
+ virtual HRESULT STDMETHODCALLTYPE GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID);
+
+ virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context);
+
+ virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, size_is(contextSize)] */ PBYTE context);
+
+ virtual HRESULT STDMETHODCALLTYPE Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer);
+
+ // ICorDebugDataTarget4
+
+ virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(
+ /* [in] */ DWORD threadId,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, out, size_is(contextSize)] */ PBYTE context);
+}; \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/dirs.proj b/src/ToolBox/SOS/Strike/dirs.proj
new file mode 100644
index 0000000000..1e391307aa
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/dirs.proj
@@ -0,0 +1,20 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Condition="'$(BuildForCoreSystem)'!='true'" Include="sos.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'arm'" Include="sosx86\sosx86.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'i386' and '$(BuildForCoreSystem)' == 'true'" Include="sosx86\sosx86.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'amd64' and '$(BuildForCoreSystem)' == 'true'" Include="sosx64\sosx64.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'arm64' and '$(BuildForCoreSystem)' == 'true'" Include="sosx64\sosx64.nativeproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/ToolBox/SOS/Strike/disasm.cpp b/src/ToolBox/SOS/Strike/disasm.cpp
new file mode 100644
index 0000000000..e141f8038f
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasm.cpp
@@ -0,0 +1,1142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#include "strike.h"
+#include "gcinfo.h"
+#include "util.h"
+#include <dbghelp.h>
+#include <limits.h>
+
+#include "sos_md.h"
+
+#ifdef SOS_TARGET_X86
+namespace X86GCDump
+{
+#include "gcdump.h"
+#undef assert
+#define assert(a)
+#define CONTRACTL
+#define DAC_ARG(x)
+#define CONTRACTL_END
+#define LIMITED_METHOD_CONTRACT
+#define NOTHROW
+#define GC_NOTRIGGER
+#define SUPPORTS_DAC
+#define LIMITED_METHOD_DAC_CONTRACT
+#include "gcdecoder.cpp"
+#undef CONTRACTL
+#undef CONTRACTL_END
+#undef LIMITED_METHOD_CONTRACT
+#undef NOTHROW
+#undef GC_NOTRIGGER
+#undef _ASSERTE
+#define _ASSERTE(a) do {} while (0)
+
+#include "gcdump.cpp"
+#include "i386/gcdumpx86.cpp"
+}
+#endif // SOS_TARGET_X86
+
+#ifdef SOS_TARGET_AMD64
+#include "gcdump.h"
+#define DAC_ARG(x)
+#define SUPPORTS_DAC
+#define LIMITED_METHOD_DAC_CONTRACT
+#undef LIMITED_METHOD_CONTRACT
+#undef PREGDISPLAY
+ #ifdef LOG
+ #undef LOG
+ #endif
+ #define LOG(x) ((void)0)
+ #ifdef LOG_PIPTR
+ #undef LOG_PIPTR
+ #endif
+ #define LOG_PIPTR(pObjRef, gcFlags, hCallBack) ((void)0)
+#include "gcdumpnonx86.cpp"
+#endif // SOS_TARGET_AMD64
+
+#include "disasm.h"
+
+#ifndef ERANGE
+#define ERANGE 34
+#endif
+
+PVOID
+GenOpenMapping(
+ PCSTR FilePath,
+ PULONG Size
+ )
+{
+#ifndef FEATURE_PAL
+ HANDLE hFile;
+ HANDLE hMappedFile;
+ PVOID MappedFile;
+
+ hFile = CreateFileA(
+ FilePath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+#if 0
+ if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
+
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
+
+ // We're on an OS that doesn't support Unicode
+ // file operations. Convert to ANSI and see if
+ // that helps.
+
+ CHAR FilePathA [ MAX_LONGPATH + 10 ];
+
+ if (WideCharToMultiByte (CP_ACP,
+ 0,
+ FilePath,
+ -1,
+ FilePathA,
+ sizeof (FilePathA),
+ 0,
+ 0
+ ) > 0) {
+
+ hFile = CreateFileA(FilePathA,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ }
+ }
+
+ if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
+ return NULL;
+ }
+ }
+#endif
+
+ *Size = GetFileSize(hFile, NULL);
+ if (*Size == ULONG_MAX) {
+ CloseHandle( hFile );
+ return NULL;
+ }
+
+ hMappedFile = CreateFileMapping (
+ hFile,
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 0,
+ NULL
+ );
+
+ if ( !hMappedFile ) {
+ CloseHandle ( hFile );
+ return NULL;
+ }
+
+ MappedFile = MapViewOfFile (
+ hMappedFile,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 0
+ );
+
+ CloseHandle (hMappedFile);
+ CloseHandle (hFile);
+
+ return MappedFile;
+#else // FEATURE_PAL
+ return NULL;
+#endif // FEATURE_PAL
+}
+
+char* PrintOneLine (__in_z char *begin, __in_z char *limit)
+{
+ if (begin == NULL || begin >= limit) {
+ return NULL;
+ }
+ char line[128];
+ size_t length;
+ char *end;
+ while (1) {
+ if (IsInterrupt())
+ return NULL;
+ length = strlen (begin);
+ end = strstr (begin, "\r\xa");
+ if (end == NULL) {
+ ExtOut ("%s", begin);
+ end = begin+length+1;
+ if (end >= limit) {
+ return NULL;
+ }
+ }
+ else {
+ end += 2;
+ length = end-begin;
+ while (length) {
+ if (IsInterrupt())
+ return NULL;
+ size_t n = length;
+ if (n > 127) {
+ n = 127;
+ }
+ strncpy_s (line,_countof(line), begin, n);
+ line[n] = '\0';
+ ExtOut ("%s", line);
+ begin += n;
+ length -= n;
+ }
+ return end;
+ }
+ }
+}
+
+void UnassemblyUnmanaged(DWORD_PTR IP, BOOL bSuppressLines)
+{
+ char filename[MAX_PATH_FNAME+1];
+ char line[256];
+ int lcount = 10;
+
+ ULONG linenum = 0;
+ ULONG64 Displacement = 0;
+ BOOL fLineAvailable = FALSE;
+ ULONG64 vIP = 0;
+
+ if (!bSuppressLines)
+ {
+ ReloadSymbolWithLineInfo();
+ fLineAvailable = SUCCEEDED (g_ExtSymbols->GetLineByOffset(TO_CDADDR(IP),
+ &linenum,
+ filename,
+ MAX_PATH_FNAME+1,
+ NULL,
+ &Displacement));
+ }
+ ULONG FileLines = 0;
+ ArrayHolder<ULONG64> Buffer = NULL;
+
+ if (fLineAvailable)
+ {
+ g_ExtSymbols->GetSourceFileLineOffsets(filename, NULL, 0, &FileLines);
+ if (FileLines == 0xFFFFFFFF || FileLines == 0)
+ fLineAvailable = FALSE;
+ }
+
+ if (fLineAvailable)
+ {
+ Buffer = new ULONG64[FileLines];
+ if (Buffer == NULL)
+ fLineAvailable = FALSE;
+ }
+
+ if (!fLineAvailable)
+ {
+ vIP = TO_CDADDR(IP);
+ // There is no line info. Just disasm the code.
+ while (lcount-- > 0)
+ {
+ if (IsInterrupt())
+ return;
+ g_ExtControl->Disassemble (vIP, 0, line, 256, NULL, &vIP);
+ ExtOut (line);
+ }
+ return;
+ }
+
+ g_ExtSymbols->GetSourceFileLineOffsets(filename, Buffer, FileLines, NULL);
+
+ int beginLine = 0;
+ int endLine = 0;
+ int lastLine;
+ linenum --;
+ for (lastLine = linenum; lastLine >= 0; lastLine --) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ g_ExtSymbols->GetNameByOffset(Buffer[lastLine], NULL, 0, NULL, &Displacement);
+ if (Displacement == 0) {
+ beginLine = lastLine;
+ break;
+ }
+ }
+ }
+ if (lastLine < 0) {
+ int n = lcount / 2;
+ lastLine = linenum-1;
+ beginLine = lastLine;
+ while (lastLine >= 0) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ beginLine = lastLine;
+ n --;
+ if (n == 0) {
+ break;
+ }
+ }
+ lastLine --;
+ }
+ }
+ while (beginLine > 0 && Buffer[beginLine-1] == DEBUG_INVALID_OFFSET) {
+ if (IsInterrupt())
+ return;
+ beginLine --;
+ }
+ int endOfFunc = 0;
+ for (lastLine = linenum+1; (ULONG)lastLine < FileLines; lastLine ++) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ g_ExtSymbols->GetNameByOffset(Buffer[lastLine], NULL, 0, NULL, &Displacement);
+ if (Displacement == 0) {
+ endLine = lastLine;
+ break;
+ }
+ endOfFunc = lastLine;
+ }
+ }
+ if ((ULONG)lastLine == FileLines) {
+ int n = lcount / 2;
+ lastLine = linenum+1;
+ endLine = lastLine;
+ while ((ULONG)lastLine < FileLines) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ endLine = lastLine;
+ n --;
+ if (n == 0) {
+ break;
+ }
+ }
+ lastLine ++;
+ }
+ }
+
+ PVOID MappedBase = NULL;
+ ULONG MappedSize = 0;
+
+ class ToUnmap
+ {
+ PVOID *m_Base;
+ public:
+ ToUnmap (PVOID *base)
+ :m_Base(base)
+ {}
+ ~ToUnmap ()
+ {
+ if (*m_Base) {
+ UnmapViewOfFile (*m_Base);
+ *m_Base = NULL;
+ }
+ }
+ };
+ ToUnmap toUnmap(&MappedBase);
+
+#define MAX_SOURCE_PATH 1024
+ char Found[MAX_SOURCE_PATH];
+ char *pFile;
+ if (g_ExtSymbols->FindSourceFile(0,
+ filename,
+ DEBUG_FIND_SOURCE_BEST_MATCH | DEBUG_FIND_SOURCE_FULL_PATH,
+ NULL,
+ Found,
+ sizeof(Found),
+ NULL) != S_OK)
+ {
+ pFile = filename;
+ }
+ else
+ {
+ MappedBase = GenOpenMapping(Found, &MappedSize);
+ pFile = Found;
+ }
+
+ lastLine = beginLine;
+ char *pFileCh = (char*)MappedBase;
+ if (MappedBase) {
+ ExtOut ("%s\n", pFile);
+ int n = beginLine;
+ while (n > 0) {
+ while (!(pFileCh[0] == '\r' && pFileCh[1] == 0xa)) {
+ if (IsInterrupt())
+ return;
+ pFileCh ++;
+ }
+ pFileCh += 2;
+ n --;
+ }
+ }
+
+ char filename1[MAX_PATH_FNAME+1];
+ for (lastLine = beginLine; lastLine < endLine; lastLine ++) {
+ if (IsInterrupt())
+ return;
+ if (MappedBase) {
+ ExtOut("%4d ", lastLine+1);
+ pFileCh = PrintOneLine(pFileCh, (char*)MappedBase+MappedSize);
+ }
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ if (MappedBase == 0) {
+ ExtOut (">>> %s:%d\n", pFile, lastLine+1);
+ }
+ vIP = Buffer[lastLine];
+ ULONG64 vNextLineIP;
+ int i;
+ for (i = lastLine + 1; (ULONG)i < FileLines && Buffer[i] == DEBUG_INVALID_OFFSET; i ++) {
+ if (IsInterrupt())
+ return;
+ }
+ if ((ULONG)i == FileLines) {
+ vNextLineIP = 0;
+ }
+ else
+ vNextLineIP = Buffer[i];
+ while (1) {
+ if (IsInterrupt())
+ return;
+ g_ExtControl->Disassemble(vIP, 0, line, 256, NULL, &vIP);
+ ExtOut (line);
+ if (vIP > vNextLineIP || vNextLineIP - vIP > 40) {
+ if (FAILED (g_ExtSymbols->GetLineByOffset(vIP, &linenum,
+ filename1,
+ MAX_PATH_FNAME+1,
+ NULL,
+ &Displacement))) {
+ if (lastLine != endOfFunc) {
+ break;
+ }
+ if (strstr (line, "ret") || strstr (line, "jmp")) {
+ break;
+ }
+ }
+
+ if (linenum != (ULONG)lastLine+1 || strcmp (filename, filename1)) {
+ break;
+ }
+ }
+ else if (vIP == vNextLineIP) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void DisasmAndClean (DWORD_PTR &IP, __out_ecount_opt(length) char *line, ULONG length)
+{
+ ULONG64 vIP = TO_CDADDR(IP);
+ g_ExtControl->Disassemble (vIP, 0, line, length, NULL, &vIP);
+ IP = (DWORD_PTR)vIP;
+ // remove the ending '\n'
+ char *ptr = strrchr (line, '\n');
+ if (ptr != NULL)
+ ptr[0] = '\0';
+}
+
+// If byref, move to pass the byref prefix
+BOOL IsByRef (__deref_inout_z char *& ptr)
+{
+ BOOL bByRef = FALSE;
+ const char* qindirCh = "qword ptr [";
+ const char* dindirCh = "dword ptr [";
+ const char* qindirDsCh = "qword ptr ds:[";
+ const char* dindirDsCh = "dword ptr ds:[";
+ if (ptr[0] == '[')
+ {
+ bByRef = TRUE;
+ ptr ++;
+ }
+ else if (!IsDbgTargetArm() && !strncmp (ptr, IsDbgTargetWin64() ? qindirCh : dindirCh, 11))
+ {
+ bByRef = TRUE;
+ ptr += 11;
+ }
+ // The new disassembly engine for windbg formats indirect calls
+ // slightly differently:
+ else if (!IsDbgTargetArm() && !strncmp (ptr, IsDbgTargetWin64() ? qindirDsCh : dindirDsCh, 14))
+ {
+ bByRef = TRUE;
+ ptr += 14;
+ }
+ return bByRef;
+}
+
+BOOL IsTermSep (char ch)
+{
+ return (ch == '\0' || isspace (ch) || ch == ',' || ch == '\n');
+}
+
+// Find next term. A term is seperated by space or ,
+void NextTerm (__deref_inout_z char *& ptr)
+{
+ // If we have a byref, skip to ']'
+ if (IsByRef (ptr))
+ {
+ while (ptr[0] != ']' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return;
+ ptr ++;
+ }
+ if (ptr[0] == ']')
+ ptr ++;
+ }
+
+ while (!IsTermSep (ptr[0]))
+ {
+ if (IsInterrupt())
+ return;
+ ptr ++;
+ }
+
+ while (IsTermSep(ptr[0]) && (*ptr != '\0'))
+ {
+ if (IsInterrupt())
+ return;
+ ptr ++;
+ }
+}
+
+
+// Parses something like 6e24d310, 0x6e24d310, or 6e24d310h.
+// On 64-bit, also parses things like 000006fb`f9b70f50 and
+// 000006fbf9b70f50 (as well as their 0x-prefix, -h suffix variations).
+INT_PTR ParseHexNumber (__in_z char *ptr, ___out char **endptr)
+{
+ char *endptr1;
+ INT_PTR value1 = strtoul(ptr, &endptr1, 16);
+
+#ifdef _TARGET_WIN64_
+ if ('`' == endptr1[0] && isxdigit(endptr1[1]))
+ {
+ char *endptr2;
+ INT_PTR value2 = strtoul(endptr1+1, &endptr2, 16);
+
+ value1 = (value1 << 32) | value2;
+ endptr1 = endptr2;
+ }
+ // if the hex number was specified as 000006fbf9b70f50, an overflow occurred
+ else if (ULONG_MAX == value1 && errno == ERANGE)
+ {
+ if (!strncmp(ptr, "0x", 2))
+ ptr += 2;
+
+ char savedigit = ptr[8];
+ ptr[8] = '\0';
+
+ value1 = strtoul(ptr, &endptr1, 16);
+
+ ptr[8] = savedigit;
+
+ char *endptr2;
+ INT_PTR value2 = strtoul(ptr+8, &endptr2, 16);
+
+ size_t ndigits2 = endptr2 - (ptr+8);
+
+ value1 = (value1 << (ndigits2*4)) | value2;
+ endptr1 = endptr2;
+ }
+#endif // _TARGET_WIN64_
+
+ // account for the possible 'h' suffix
+ if ((*endptr1 == 'h') || (*endptr1 == 'H'))
+ {
+ ++endptr1;
+ }
+
+ *endptr = endptr1;
+ return value1;
+}
+
+
+// only handle pure value, or memory address
+INT_PTR GetValueFromExpr(__in_z char *ptr, INT_PTR &value)
+{
+ BOOL bNegative = FALSE;
+ value = 0;
+ char *myPtr = ptr;
+ BOOL bByRef = IsByRef (myPtr);
+
+ // ARM disassembly contains '#' prefixes for hex constants
+ if (*myPtr == '#')
+ ++myPtr;
+
+ if (myPtr[0] == '-')
+ {
+ myPtr ++;
+ bNegative = TRUE;
+ }
+ if (!strncmp (myPtr, "0x", 2) || isxdigit (myPtr[0]))
+ {
+ char *endptr;
+ value = ParseHexNumber(myPtr, &endptr);
+ if ((!bByRef && IsTermSep(endptr[0])) || (bByRef && endptr[0] == ']'))
+ {
+ if (bNegative)
+ value = -value;
+ ptr = endptr;
+ if (bByRef)
+ {
+ ptr += 1;
+ SafeReadMemory (TO_TADDR(value), &value, 4, NULL);
+ }
+ return ptr - myPtr;
+ }
+ }
+
+ // handle mscorlib+0xed310 (6e24d310)
+ if (!bByRef)
+ {
+ ptr = myPtr;
+ // handle 'offset ' before the expression:
+ if (strncmp(ptr, "offset ", 7) == 0)
+ {
+ ptr += 7;
+ }
+ while (ptr[0] != ' ' && ptr[0] != '+' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return 0;
+ ptr ++;
+ }
+ if (ptr[0] == '+')
+ {
+ NextTerm (ptr);
+ if (ptr[0] == '(')
+ {
+ ptr ++;
+ char *endptr;
+ value = ParseHexNumber(ptr, &endptr);
+ if (endptr[0] == ')')
+ {
+ ptr ++;
+ return ptr - myPtr;
+ }
+ }
+ }
+ }
+ if (bByRef)
+ {
+ // handle dword [mscorlib+0x2bd788 (02ead788)]
+ ptr = myPtr;
+ // handle 'offset ' before the expression:
+ if (strncmp(ptr, "offset ", 7) == 0)
+ {
+ ptr += 7;
+ }
+ while (ptr[0] != '(' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return 0;
+ ptr ++;
+ }
+ if (ptr[0] == '(')
+ {
+ ptr ++;
+ char *endptr;
+ value = ParseHexNumber(ptr, &endptr);
+ if (endptr[0] == ')' && endptr[1] == ']')
+ {
+ ptr = endptr + 2;
+ SafeReadMemory (TO_TADDR(value), &value, 4, NULL);
+ return ptr - myPtr;
+ }
+ }
+ }
+
+#ifdef _TARGET_WIN64_
+ // handle CLRStub@7fffc8601cc (000007fffc8601cc)
+ if (!bByRef && !strncmp(myPtr, "CLRStub[", 8))
+ {
+ ptr = myPtr;
+ while (ptr[0] != '(' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return 0;
+ ptr ++;
+ }
+ if (ptr[0] == '(')
+ {
+ ptr ++;
+ char *endptr;
+ value = ParseHexNumber(ptr, &endptr);
+ if (endptr[0] == ')')
+ {
+ ptr ++;
+ return ptr - myPtr;
+ }
+ }
+ }
+#endif // _TARGET_WIN64_
+
+ return 0;
+}
+
+
+const char * HelperFuncName (size_t IP)
+{
+ static char s_szHelperName[100];
+ if (S_OK == g_sos->GetJitHelperFunctionName(IP, sizeof(s_szHelperName), &s_szHelperName[0], NULL))
+ return &s_szHelperName[0];
+ else
+ return NULL;
+}
+
+
+// Returns:
+// NULL if the EHInfo passed in does not refer to a Typed clause
+// "..." if pEHInfo->isCatchAllHandler is TRUE
+// "TypeName" if pEHInfo is a DACEHInfo*.
+// Note:
+// The return is a pointer to a global buffer, therefore this value must
+// be consumed as soon as possible after a call to this function.
+LPCWSTR EHTypedClauseTypeName(___in const DACEHInfo* pEHInfo)
+{
+ _ASSERTE(pEHInfo != NULL);
+ if ((pEHInfo->clauseType == EHTyped) && pEHInfo->isCatchAllHandler)
+ {
+ return W("...");
+ }
+
+ // is there a method table or a token to look at?
+ if (pEHInfo->clauseType == EHTyped)
+ {
+ TADDR mt;
+ if (pEHInfo->moduleAddr == 0)
+ {
+ mt = TO_TADDR(pEHInfo->mtCatch);
+ NameForMT_s(mt, g_mdName, mdNameLen);
+ } else {
+ PrettyPrintClassFromToken(TO_TADDR(pEHInfo->moduleAddr), pEHInfo->tokCatch, g_mdName, mdNameLen, FormatCSharp);
+ }
+ return g_mdName;
+ }
+
+ return NULL;
+}
+
+BOOL IsClonedFinally(DACEHInfo *pEHInfo)
+{
+ // This maybe should be determined in the VM and passed in the DACEHInfo struct.
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ return ((pEHInfo->tryStartOffset == pEHInfo->tryEndOffset) &&
+ (pEHInfo->tryStartOffset == pEHInfo->handlerStartOffset) &&
+ (pEHInfo->clauseType == EHFinally) &&
+ pEHInfo->isDuplicateClause);
+#else
+ return FALSE;
+#endif
+}
+
+
+void SOSEHInfo::FormatForDisassembly(CLRDATA_ADDRESS offSet)
+{
+ LPCWSTR typeName = NULL;
+ // the order of printing and iterating will matter on the boundaries
+
+ // Print END tags in forward order (most nested to least nested). However, cloned
+ // finally clauses are always at the end, but they should be considered most nested,
+ // so have a separate loop to output them first.
+ for (UINT i=0; i < EHCount; i++)
+ {
+ DACEHInfo *pCur = &m_pInfos[i];
+
+ if (IsClonedFinally(pCur) &&
+ (offSet == pCur->handlerEndOffset))
+ {
+ ExtOut ("EHHandler %d: CLONED FINALLY END\n", i);
+ }
+ }
+
+ for (UINT i=0; i < EHCount; i++)
+ {
+ DACEHInfo *pCur = &m_pInfos[i];
+
+ if (pCur->isDuplicateClause)
+ {
+ // Don't print anything for duplicate clauses
+ continue;
+ }
+
+ if (offSet == pCur->tryEndOffset)
+ {
+ ExtOut ("EHHandler %d: %s CLAUSE END\n", i, EHTypeName(pCur->clauseType));
+ }
+
+ if (offSet == pCur->handlerEndOffset)
+ {
+ ExtOut ("EHHandler %d: %s HANDLER END\n", i, EHTypeName(pCur->clauseType));
+ }
+ }
+
+ // Print BEGIN tags in reverse order (least nested to most nested).
+ for (UINT i=EHCount-1; i != (UINT)-1; --i)
+ {
+ DACEHInfo *pCur = &m_pInfos[i];
+
+ // Must do this before the isDuplicatedClause check, since these are marked as duplicated clauses.
+ if (IsClonedFinally(pCur) &&
+ (offSet == pCur->handlerStartOffset))
+ {
+ ExtOut ("EHHandler %d: CLONED FINALLY BEGIN\n", i);
+ }
+
+ if (pCur->isDuplicateClause)
+ {
+ // Don't print anything for duplicate clauses
+ continue;
+ }
+
+ if (offSet == pCur->tryStartOffset)
+ {
+ ExtOut ("EHHandler %d: %s CLAUSE BEGIN", i, EHTypeName(pCur->clauseType));
+ typeName = EHTypedClauseTypeName(pCur);
+ if (typeName != NULL)
+ {
+ ExtOut(" catch(%S) ", typeName);
+ }
+ ExtOut ("\n");
+ }
+
+ if (offSet == pCur->handlerStartOffset)
+ {
+ ExtOut ("EHHandler %d: %s HANDLER BEGIN", i, EHTypeName(pCur->clauseType));
+ typeName = EHTypedClauseTypeName(pCur);
+ if (typeName != NULL)
+ {
+ ExtOut(" catch(%S) ", typeName);
+ }
+ ExtOut ("\n");
+ }
+
+ if ((pCur->clauseType == EHFilter) &&
+ (offSet == pCur->filterOffset))
+ {
+ ExtOut ("EHHandler %d: %s FILTER BEGIN\n",i, EHTypeName(pCur->clauseType));
+ }
+ }
+}
+
+
+//
+// Implementation shared by X86, ARM, and X64
+// Any cross platform code should resolve through g_targetMachine or should
+// use the IS_DBG_TARGET_XYZ macro.
+//
+
+void PrintNativeStack(DWORD_PTR ip, BOOL bSuppressLines)
+{
+ char filename[MAX_PATH_FNAME + 1];
+ char symbol[1024];
+ ULONG64 displacement;
+
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
+ if (SUCCEEDED(hr) && symbol[0] != '\0')
+ {
+ ExtOut("%s", symbol);
+
+ if (displacement)
+ {
+ ExtOut(" + %#x", displacement);
+ }
+
+ if (!bSuppressLines)
+ {
+ ULONG line;
+ hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
+ if (SUCCEEDED(hr))
+ {
+ ExtOut(" [%s:%d]", filename, line);
+ }
+ }
+ }
+ else
+ {
+ DMLOut(DMLIP(ip));
+ }
+}
+
+// Return TRUE if we have printed something.
+BOOL PrintCallInfo(DWORD_PTR vEBP, DWORD_PTR IP, DumpStackFlag& DSFlag, BOOL bSymbolOnly)
+{
+ char Symbol[1024];
+ char filename[MAX_PATH_FNAME+1];
+ ULONG64 Displacement;
+ BOOL bOutput = FALSE;
+
+ // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
+ DWORD_PTR methodDesc = 0;
+ if (!g_bDacBroken)
+ {
+ methodDesc = FunctionType (IP);
+ }
+
+ if (methodDesc > 1)
+ {
+ bOutput = TRUE;
+ if (!bSymbolOnly)
+ DMLOut("%p %s ", SOS_PTR(vEBP), DMLIP(IP));
+ DMLOut("(MethodDesc %s ", DMLMethodDesc(methodDesc));
+
+ // TODO: Microsoft, more checks to make sure method is not eeimpl, etc. Add this field to MethodDesc
+
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(g_sos, TO_CDADDR(IP)) == S_OK)
+ {
+ DWORD_PTR IPBegin = (DWORD_PTR) codeHeaderData.MethodStart;
+ methodDesc = (DWORD_PTR) codeHeaderData.MethodDescPtr;
+ Displacement = IP - IPBegin;
+ if (IP >= IPBegin && Displacement <= codeHeaderData.MethodSize)
+ ExtOut ("+ %#x ", Displacement);
+ }
+ if (NameForMD_s(methodDesc, g_mdName, mdNameLen))
+ {
+ ExtOut("%S)", g_mdName);
+ }
+ else
+ {
+ ExtOut("%s)", DMLIP(IP));
+ }
+ }
+ else
+ {
+ if (!DSFlag.fEEonly)
+ {
+ bOutput = TRUE;
+ const char *name;
+ if (!bSymbolOnly)
+ DMLOut("%p %s ", SOS_PTR(vEBP), DMLIP(IP));
+
+ // if AMD64 ever becomes a cross platform target this must be resolved through
+ // virtual dispatch rather than conditional compilation
+#ifdef _TARGET_AMD64_
+ // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
+ eTargetType ett = ettUnk;
+ if (!g_bDacBroken)
+ {
+ DWORD_PTR finalMDorIP = 0;
+ ett = GetFinalTarget(IP, &finalMDorIP);
+ if (ett == ettNative || ett==ettJitHelp)
+ {
+ methodDesc = 0;
+ IP = finalMDorIP;
+ }
+ else
+ {
+ methodDesc = finalMDorIP;
+ }
+ }
+#endif // _TARGET_AMD64_
+ if (methodDesc == 0)
+ {
+ PrintNativeStack(IP, DSFlag.fSuppressSrcInfo);
+ }
+ else if (g_bDacBroken)
+ {
+ // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
+ DMLOut(DMLIP(IP));
+ }
+ else if (IsMethodDesc (IP))
+ {
+ NameForMD_s(IP, g_mdName, mdNameLen);
+ ExtOut(" (stub for %S)", g_mdName);
+ }
+ else if (IsMethodDesc(IP+5)) {
+ NameForMD_s((DWORD_PTR)(IP+5), g_mdName, mdNameLen);
+ DMLOut("%s (MethodDesc %s %S)", DMLIP(IP), DMLMethodDesc(IP+5), g_mdName);
+ }
+ else if ((name = HelperFuncName(IP)) != NULL) {
+ ExtOut(" (JitHelp: %s)", name);
+ }
+#ifdef _TARGET_AMD64_
+ else if (ett == ettMD || ett == ettStub)
+ {
+ NameForMD_s(methodDesc, g_mdName,mdNameLen);
+ DMLOut("%s (stub for %S)", DMLIP(IP), g_mdName);
+ // fallthrough to return
+ }
+#endif // _TARGET_AMD64_
+ else
+ {
+ DMLOut(DMLIP(IP));
+ }
+ }
+ }
+ return bOutput;
+}
+
+void DumpStackWorker (DumpStackFlag &DSFlag)
+{
+ DWORD_PTR eip;
+ ULONG64 Offset;
+ g_ExtRegisters->GetInstructionOffset(&Offset);
+ eip = (DWORD_PTR)Offset;
+
+ ExtOut("Current frame: ");
+ PrintCallInfo (0, eip, DSFlag, TRUE);
+ ExtOut ("\n");
+
+ // make certain dword/qword aligned
+ DWORD_PTR ptr = DSFlag.top & (~ALIGNCONST);
+
+ ExtOut (g_targetMachine->GetDumpStackHeading());
+ while (ptr < DSFlag.end)
+ {
+ if (IsInterrupt())
+ return;
+ DWORD_PTR retAddr;
+ DWORD_PTR whereCalled;
+ move_xp(retAddr, ptr);
+ g_targetMachine->IsReturnAddress(retAddr, &whereCalled);
+ if (whereCalled)
+ {
+ BOOL bOutput = PrintCallInfo(ptr-sizeof(TADDR), retAddr, DSFlag, FALSE);
+ if (!DSFlag.fEEonly)
+ {
+ if (whereCalled != 0xFFFFFFFF)
+ {
+ ExtOut (", calling ");
+ PrintCallInfo (0, whereCalled, DSFlag, TRUE);
+ }
+ }
+ if (bOutput)
+ ExtOut ("\n");
+
+ DWORD_PTR cxrAddr;
+ CROSS_PLATFORM_CONTEXT cxr;
+ DWORD_PTR exrAddr;
+ EXCEPTION_RECORD exr;
+
+ if (g_targetMachine->GetExceptionContext(ptr,retAddr,&cxrAddr,&cxr,&exrAddr,&exr))
+ {
+ TADDR sp = g_targetMachine->GetSP(cxr);
+ TADDR ip = g_targetMachine->GetIP(cxr);
+ bOutput = PrintCallInfo(sp, ip, DSFlag, FALSE);
+ if (bOutput)
+ {
+ ExtOut(" ====> Exception ");
+ if (exrAddr)
+ ExtOut("Code %x ", exr.ExceptionCode);
+ ExtOut ("cxr@%p", SOS_PTR(cxrAddr));
+ if (exrAddr)
+ ExtOut(" exr@%p", SOS_PTR(exrAddr));
+ ExtOut("\n");
+ }
+ }
+ }
+ ptr += sizeof (DWORD_PTR);
+ }
+}
+
+#ifdef SOS_TARGET_X86
+///
+/// X86Machine implementation
+///
+LPCSTR X86Machine::s_DumpStackHeading = "ChildEBP RetAddr Caller, Callee\n";
+LPCSTR X86Machine::s_DSOHeading = "ESP/REG Object Name\n";
+LPCSTR X86Machine::s_GCRegs[7] = {"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp"};
+LPCSTR X86Machine::s_SPName = "ESP";
+
+void PrintNothing (const char *fmt, ...)
+{
+ // Do nothing.
+}
+
+///
+/// Dump X86 GCInfo header and table
+///
+void X86Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+ X86GCDump::InfoHdr header;
+ X86GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ BYTE* pTable = dac_cast<PTR_BYTE>(gcInfoToken.Info);
+ if (bPrintHeader)
+ {
+ gcDump.gcPrintf = gcPrintf;
+ gcPrintf("Method info block:\n");
+ }
+ else
+ {
+ gcDump.gcPrintf = PrintNothing;
+ }
+ pTable += gcDump.DumpInfoHdr(pTable, &header, &methodSize, 0);
+ if (bPrintHeader)
+ {
+ gcPrintf("\n");
+ gcPrintf("Pointer table:\n");
+ }
+ gcDump.gcPrintf = gcPrintf;
+ gcDump.DumpGCTable(pTable, header, methodSize, 0);
+}
+#endif // SOS_TARGET_X86
+
+#ifdef SOS_TARGET_ARM
+///
+/// ARMMachine implementation
+///
+LPCSTR ARMMachine::s_DumpStackHeading = "ChildFP RetAddr Caller, Callee\n";
+LPCSTR ARMMachine::s_DSOHeading = "SP/REG Object Name\n";
+LPCSTR ARMMachine::s_GCRegs[14] = {"r0", "r1", "r2", "r3", "r4", "r5", "r6",
+ "r7", "r8", "r9", "r10", "r11", "r12", "lr"};
+LPCSTR ARMMachine::s_SPName = "sp";
+
+#endif // SOS_TARGET_ARM
+
+#ifdef SOS_TARGET_AMD64
+///
+/// AMD64Machine implementation
+///
+LPCSTR AMD64Machine::s_DumpStackHeading = "Child-SP RetAddr Caller, Callee\n";
+LPCSTR AMD64Machine::s_DSOHeading = "RSP/REG Object Name\n";
+LPCSTR AMD64Machine::s_GCRegs[15] = {"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
+LPCSTR AMD64Machine::s_SPName = "RSP";
+
+///
+/// Dump AMD64 GCInfo table
+///
+void AMD64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+ if (bPrintHeader)
+ {
+ ExtOut("Pointer table:\n");
+ }
+
+ GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ gcDump.gcPrintf = gcPrintf;
+
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
+}
+
+#endif // SOS_TARGET_AMD64
+
+#ifdef SOS_TARGET_ARM64
+///
+/// ARM64Machine implementation
+///
+LPCSTR ARM64Machine::s_DumpStackHeading = "ChildFP RetAddr Caller, Callee\n";
+LPCSTR ARM64Machine::s_DSOHeading = "SP/REG Object Name\n";
+// excluding x18, fp & lr as these will not contain object references
+LPCSTR ARM64Machine::s_GCRegs[28] = {"x0", "x1", "x2", "x3", "x4", "x5", "x6",
+ "x7", "x8", "x9", "x10", "x11", "x12", "x13",
+ "x14", "x15", "x16", "x17", "x19", "x20","x21",
+ "x22", "x23", "x24", "x25", "x26", "x27", "x28"};
+LPCSTR ARM64Machine::s_SPName = "sp";
+
+#endif // SOS_TARGET_ARM64
+
+
diff --git a/src/ToolBox/SOS/Strike/disasm.h b/src/ToolBox/SOS/Strike/disasm.h
new file mode 100644
index 0000000000..59fc168a6e
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasm.h
@@ -0,0 +1,453 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __disasm_h__
+#define __disasm_h__
+
+#include "sos_stacktrace.h"
+
+struct InfoHdr;
+class GCDump;
+
+
+struct DumpStackFlag
+{
+ BOOL fEEonly;
+ BOOL fSuppressSrcInfo;
+ DWORD_PTR top;
+ DWORD_PTR end;
+};
+
+struct GCEncodingInfo
+{
+ LPVOID pvMainFiber;
+ LPVOID pvGCTableFiber;
+
+ BYTE *table;
+ unsigned int methodSize;
+
+ char buf[1000];
+ int cch;
+
+ SIZE_T ofs;
+
+ // When decoding a cold region, set this to the size of the hot region to keep offset
+ // calculations working.
+ SIZE_T hotSizeToAdd;
+ bool fDoneDecoding;
+};
+
+// Returns:
+// NULL if the EHInfo passed in does not refer to a Typed clause
+// "..." if pEHInfo->isCatchAllHandler is TRUE
+// "TypeName" if pEHInfo is a DACEHInfo* that references type "TypeName".
+// Note:
+// The return is a pointer to a global buffer, therefore this value must
+// be consumed as soon as possible after a call to this function.
+LPCWSTR EHTypedClauseTypeName(const DACEHInfo* pEHInfo);
+
+struct SOSEHInfo
+{
+#ifndef FEATURE_CORECLR
+ __field_ecount(EHCount)
+#endif
+ DACEHInfo *m_pInfos;
+ UINT EHCount;
+ CLRDATA_ADDRESS methodStart;
+
+ SOSEHInfo() { ZeroMemory(this, sizeof(SOSEHInfo)); }
+ ~SOSEHInfo() { if (m_pInfos) { delete [] m_pInfos; } }
+
+ void FormatForDisassembly(CLRDATA_ADDRESS offSet);
+};
+
+BOOL IsClonedFinally(DACEHInfo *pEHInfo);
+
+void DumpStackWorker (DumpStackFlag &DSFlag);
+
+void UnassemblyUnmanaged (DWORD_PTR IP, BOOL bSuppressLines);
+
+HRESULT CheckEEDll ();
+
+BOOL GetCalleeSite (DWORD_PTR IP, DWORD_PTR &IPCallee);
+
+void DisasmAndClean (DWORD_PTR &IP, __out_ecount_opt(length) char *line, ULONG length);
+
+INT_PTR GetValueFromExpr(___in __in_z char *ptr, INT_PTR &value);
+
+void NextTerm (__deref_inout_z char *& ptr);
+
+BOOL IsByRef (__deref_inout_z char *& ptr);
+
+BOOL IsTermSep (char ch);
+
+const char * HelperFuncName (size_t IP);
+
+enum eTargetType { ettUnk = 0, ettNative = 1, ettJitHelp = 2, ettStub = 3, ettMD = 4 };
+
+// GetFinalTarget is based on HandleCall, but avoids printing anything to the output.
+// This is currently only called on x64
+eTargetType GetFinalTarget(DWORD_PTR callee, DWORD_PTR* finalMDorIP);
+
+#ifdef _MSC_VER
+// SOS is essentially single-threaded. ignore "construction of local static object is not thread-safe"
+#pragma warning(push)
+#pragma warning(disable:4640)
+#endif // _MSC_VER
+
+//-----------------------------------------------------------------------------------------
+//
+// Implementations for the supported target platforms
+//
+//-----------------------------------------------------------------------------------------
+
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
+
+#ifdef SOS_TARGET_X86
+
+/// X86 Machine specific code
+class X86Machine : public IMachine
+{
+public:
+ typedef X86_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { static X86Machine s_X86MachineInstance; return &s_X86MachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_I386; }
+ ULONG GetContextSize() const { return sizeof(X86_CONTEXT); }
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo * pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR * exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.X86Context.Esp; }
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.X86Context.Ebp; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.X86Context.Eip; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ X86Machine() {}
+ ~X86Machine() {}
+ X86Machine(const X86Machine& machine); // undefined
+ X86Machine & operator=(const X86Machine&); // undefined
+
+private:
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[7];
+ static LPCSTR s_SPName;
+}; // class X86Machine
+
+#endif // SOS_TARGET_X86
+
+
+#ifdef SOS_TARGET_ARM
+
+/// ARM Machine specific code
+class ARMMachine : public IMachine
+{
+public:
+ typedef ARM_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { return &s_ARMMachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_ARMNT; }
+ ULONG GetContextSize() const { return sizeof(ARM_CONTEXT); }
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.ArmContext.Sp; }
+ // @ARMTODO: frame pointer
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return 0; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.ArmContext.Pc; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ ARMMachine() {}
+ ~ARMMachine() {}
+ ARMMachine(const ARMMachine& machine); // undefined
+ ARMMachine & operator=(const ARMMachine&); // undefined
+
+private:
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[14];
+ static LPCSTR s_SPName;
+ static ARMMachine s_ARMMachineInstance;
+}; // class ARMMachine
+
+#endif // SOS_TARGET_ARM
+
+#ifdef SOS_TARGET_AMD64
+
+/// AMD64 Machine specific code
+class AMD64Machine : public IMachine
+{
+public:
+ typedef AMD64_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { static AMD64Machine s_AMD64MachineInstance; return &s_AMD64MachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_AMD64; }
+ ULONG GetContextSize() const { return sizeof(AMD64_CONTEXT); }
+
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Amd64Context.Rsp; }
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Amd64Context.Rbp; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Amd64Context.Rip; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ AMD64Machine() {}
+ ~AMD64Machine() {}
+ AMD64Machine(const AMD64Machine& machine); // undefined
+ AMD64Machine & operator=(const AMD64Machine&); // undefined
+
+private:
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[15];
+ static LPCSTR s_SPName;
+}; // class AMD64Machine
+
+#endif // SOS_TARGET_AMD64
+
+#ifdef SOS_TARGET_ARM64
+
+/// ARM64 Machine specific code
+class ARM64Machine : public IMachine
+{
+public:
+ typedef ARM64_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { static ARM64Machine s_ARM64MachineInstance; return &s_ARM64MachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_ARM64; }
+ ULONG GetContextSize() const { return sizeof(ARM64_CONTEXT); }
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Arm64Context.Sp; }
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Arm64Context.Fp; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Arm64Context.Pc; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs);}
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ ARM64Machine() {}
+ ~ARM64Machine() {}
+ ARM64Machine(const ARM64Machine& machine); // undefined
+ ARM64Machine & operator=(const ARM64Machine&); // undefined
+
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[28];
+ static LPCSTR s_SPName;
+
+}; // class ARM64Machine
+
+#endif // SOS_TARGET_ARM64
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+
+//
+// Inline methods
+//
+
+
+#ifdef SOS_TARGET_X86
+inline void X86Machine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Esp;
+ dest->FrameOffset = src.Ebp;
+ dest->InstructionOffset = src.Eip;
+}
+
+inline void X86Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_X86
+
+
+#ifdef SOS_TARGET_ARM
+inline void ARMMachine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Sp;
+ // @ARMTODO: frame pointer - keep in sync with ARMMachine::GetBP
+ dest->FrameOffset = 0;
+ dest->InstructionOffset = src.Pc;
+}
+
+inline void ARMMachine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_ARM
+
+
+#ifdef SOS_TARGET_AMD64
+inline void AMD64Machine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Rsp;
+ dest->FrameOffset = src.Rbp;
+ dest->InstructionOffset = src.Rip;
+}
+
+inline void AMD64Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_AMD64
+
+#ifdef SOS_TARGET_ARM64
+inline void ARM64Machine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Sp;
+ dest->FrameOffset = src.Fp;
+ dest->InstructionOffset = src.Pc;
+}
+
+inline void ARM64Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_ARM64
+
+#endif // __disasm_h__
diff --git a/src/ToolBox/SOS/Strike/disasmARM.cpp b/src/ToolBox/SOS/Strike/disasmARM.cpp
new file mode 100644
index 0000000000..82173558fd
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasmARM.cpp
@@ -0,0 +1,626 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#ifndef _TARGET_ARM_
+#define _TARGET_ARM_
+#endif
+
+
+#include "strike.h"
+#include "util.h"
+#include <dbghelp.h>
+
+
+#include "disasm.h"
+
+#include "../../../inc/corhdr.h"
+#include "../../../inc/cor.h"
+#include "../../../inc/dacprivate.h"
+
+#ifndef FEATURE_PAL
+namespace ARMGCDump
+{
+#undef _TARGET_X86_
+#undef LIMITED_METHOD_CONTRACT
+#define LIMITED_METHOD_DAC_CONTRACT
+#define SUPPORTS_DAC
+#define LF_GCROOTS
+#define LL_INFO1000
+#define LOG(x)
+#define LOG_PIPTR(pObjRef, gcFlags, hCallBack)
+#define DAC_ARG(x)
+#include "gcdumpnonx86.cpp"
+}
+#endif // !FEATURE_PAL
+
+#if defined(_TARGET_WIN64_)
+#error This file does not support SOS targeting ARM from a 64-bit debugger
+#endif
+
+#if !defined(SOS_TARGET_ARM)
+#error This file should be used to support SOS targeting ARM debuggees
+#endif
+
+#ifdef SOS_TARGET_ARM
+ARMMachine ARMMachine::s_ARMMachineInstance;
+
+// Decodes the target label of the immediate form of bl and blx instructions. The PC given is that of the
+// start of the instruction.
+static TADDR DecodeCallTarget(TADDR PC, WORD rgInstr[2])
+{
+ // Displacement is spread across several bitfields in the two words of the instruction. Using the same
+ // bitfield names as the ARM Architecture Reference Manual.
+ DWORD S = (rgInstr[0] & 0x0400) >> 10;
+ DWORD imm10 = rgInstr[0] & 0x03ff;
+ DWORD J1 = (rgInstr[1] & 0x2000) >> 13;
+ DWORD J2 = (rgInstr[1] & 0x0800) >> 11;
+ DWORD imm11 = rgInstr[1] & 0x07ff;
+
+ // For reasons that escape me the I1 and I2 fields are computed by XOR'ing J1 and J2 with S.
+ DWORD I1 = (~J1 ^ S) & 0x1;
+ DWORD I2 = (~J2 ^ S) & 0x1;
+
+ // The final displacement is put together as: SignExtend(S:I1:I2:imm10:imm11:0)
+ DWORD highByte = S ? 0xff000000 : 0x00000000;
+ DWORD disp = highByte | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
+
+ // The displacement is relative to the PC but the PC for a given instruction reads as the PC for the
+ // beginning of the instruction plus 4.
+ return PC + 4 + disp;
+}
+
+// Validate that a potential call target points to readable memory. If so, and the code appears to be one of
+// our standard jump thunks we'll deference through that and return the real target. Returns 0 if any checks
+// fail.
+static TADDR GetRealCallTarget(TADDR PC)
+{
+ WORD instr[2];
+
+ // Read the minimum (a WORD) first in case we're calling to a single WORD method at the end of a page
+ // (e.g. BLX <reg>).
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC), &instr[0], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // All the jump thunks we handle start with the literal form of LDR (i.e. LDR <reg>, [PC +/- <imm>]). It's
+ // always the two word form since we're either loading R12 or PC. We never use the decrement version of
+ // the instruction (U == 0).
+ // If it's not an instruction of that form we can return immediately.
+ if (instr[0] != 0xf8df)
+ return PC;
+
+ // The first instruction is defintely a LDR of the form we expect so it's OK to read the second half of
+ // the encoding.
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC + 2), &instr[1], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // Determine which register we're loading. There are three cases:
+ // 1) PC: we're jumping, perform final calculation of the jump target
+ // 2) R12: we're possibly setting up a special argument to the jump target. Ignore this instruction and
+ // check for a LDR PC in the next instruction
+ // 3) Any other register: we don't recognize this instruction sequence, just return the PC we have
+ WORD reg = (instr[1] & 0xf000) >> 12;
+ if (reg == 12)
+ {
+ // Possibly a LDR R12, [...]; LDR PC, [...] thunk. Overwrite the current instruction with the next and
+ // then fall through into the common LDR PC, [...] handling below. If we fail to read the next word
+ // we're not really looking at valid code. But we need to be more careful reading the second word of
+ // the potential instruction since there are valid sequences that would terminate with a single word
+ // at the end of page.
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC + 4), &instr[0], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // Following instruction is not a LDR <literal>. Return this PC as the real target.
+ if (instr[0] != 0xf8df)
+ return PC;
+
+ // Read second half of the LDR instruction.
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC + 6), &instr[1], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // Determine the target register. If it's not the PC then return this PC as the real target.
+ reg = (instr[1] & 0xf000) >> 12;
+ if (reg != 12)
+ return PC;
+
+ // Fall through to process this LDR PC, [...] instruction. Update the input PC because it figures into
+ // the calculation below.
+ PC += 4;
+ }
+ else if (reg == 15)
+ {
+ // First instruction was a LDR PC, [...] Just fall through to common handling below.
+ }
+ else
+ {
+ // Any other target register is unrecognized. Just return what we have as the final target.
+ return PC;
+ }
+
+ // Decode the LDR PC, [PC + <imm>] to find the jump target.
+ // The displacement is in the low order 12 bits of the second instruction word.
+ DWORD disp = instr[1] & 0x0fff;
+
+ // The PC used for the effective address calculation is the PC from the start of the instruction rounded
+ // down to 4-byte alignment then incremented by 4.
+ TADDR targetAddress = (PC & ~3) + 4 + disp;
+
+ // Read the target address from this routine.
+ TADDR target;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(targetAddress), &target, sizeof(target), NULL) != S_OK)
+ return 0;
+
+ // Clear the low-bit in the target used to indicate a Thumb mode destination. If this is not set we can't
+ // be looking at one of our jump thunks (in fact ARM mode code is illegal under CoreARM so this would
+ // indicate an issue).
+ _ASSERTE((target & 1) == 1);
+ target &= ~1;
+
+ // Recursively call ourselves on this target in case we have any double jump thunks.
+ return GetRealCallTarget(target);
+}
+
+// Determine (heuristically, basically a best effort guess) whether an address on the stack represents a
+// return address. This is achieved by looking at the memory prior to the potential return address and
+// disassembling it to see whether it looks like a potential call. If possible the target of the callsite is
+// also returned.
+//
+// Result is returned in whereCalled:
+// 0 : retAddr doesn't look like a return address
+// 0xffffffff : retAddr looks like a return address but we couldn't tell where the call site was targeted
+// <other> : retAddr looks like a return address, *whereCalled set to target address
+void ARMMachine::IsReturnAddress(TADDR retAddr, TADDR* whereCalled) const
+{
+ *whereCalled = 0;
+
+ // If retAddr doesn't have the low-order bit set (indicating a return to Thumb code) then it can't be a
+ // legal return address.
+ if ((retAddr & 1) == 0)
+ return;
+ retAddr &= ~1;
+
+ // Potential calling instructions may have been one or two WORDs in length.
+ WORD rgPrevious[2];
+ move_xp(rgPrevious, retAddr - sizeof(rgPrevious));
+
+ // Check two-word variants first.
+ if (((rgPrevious[0] & 0xf800) == 0xf000) &&
+ ((rgPrevious[1] & 0xd000) == 0xd000))
+ {
+ // BL <label>
+
+ // Decode and validate PC-relative call target. Dereference through any jump thunks and return the
+ // call target.
+ TADDR target = GetRealCallTarget(DecodeCallTarget(retAddr - 4, rgPrevious));
+ if (target)
+ {
+ *whereCalled = target;
+ return;
+ }
+ }
+ else if (((rgPrevious[0] & 0xf800) == 0xf000) &&
+ ((rgPrevious[1] & 0xd001) == 0xc000))
+ {
+ // BLX <label>
+
+ // Decode and validate PC-relative call target. Dereference through any jump thunks and return the
+ // call target.
+ TADDR target = GetRealCallTarget(DecodeCallTarget(retAddr - 4, rgPrevious));
+ if (target)
+ {
+ *whereCalled = target;
+ return;
+ }
+ }
+ else if (((rgPrevious[0] & 0xfff0) == 0xf8d0) &&
+ ((rgPrevious[1] & 0xf000) == 0xf000))
+ {
+ // LDR PC, [<reg> + #<imm>]
+ *whereCalled = 0xffffffff;
+ return;
+ }
+ else if (((rgPrevious[0] & 0xff7f) == 0xf85f) &&
+ ((rgPrevious[1] & 0xf000) == 0xf000))
+ {
+ // LDR PC, [PC + #<imm>]
+ *whereCalled = 0xffffffff;
+ return;
+ }
+ else if (((rgPrevious[0] & 0xfff0) == 0xf850) &&
+ ((rgPrevious[1] & 0xffc0) == 0xf000))
+ {
+ // LDR PC, [<reg> + <reg>, LSL #<imm>]
+ *whereCalled = 0xffffffff;
+ return;
+ }
+
+ // Fall through any failures to decode as a two-word instruction to the one word cases below...
+
+ // BLX <register>
+ if ((rgPrevious[1] & 0xff87) == 0x4780)
+ {
+ *whereCalled = 0xffffffff;
+ return;
+ }
+}
+
+
+// Return 0 for non-managed call. Otherwise return MD address.
+static TADDR MDForCall (TADDR callee)
+{
+ // call managed code?
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR PC = callee;
+ TADDR gcinfoAddr;
+
+ PC = GetRealCallTarget(callee);
+ if (!PC)
+ return 0;
+
+ IP2MethodDesc (PC, methodDesc, jitType, gcinfoAddr);
+ return methodDesc;
+}
+
+// Determine if a value is MT/MD/Obj
+static void HandleValue(TADDR value)
+{
+#ifndef FEATURE_PAL
+ // remove the thumb bit (if set)
+ value = value & ~1;
+#else
+ // set the thumb bit (if not set)
+ value = value | 1;
+#endif //!FEATURE_PAL
+
+ // A MethodTable?
+ if (IsMethodTable(value))
+ {
+ NameForMT_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MT: %S)", g_mdName);
+ return;
+ }
+
+ // A Managed Object?
+ TADDR dwMTAddr;
+ move_xp (dwMTAddr, value);
+ if (IsStringObject(value))
+ {
+ ExtOut (" (\"");
+ StringObjectContent (value, TRUE);
+ ExtOut ("\")");
+ return;
+ }
+ else if (IsMethodTable(dwMTAddr))
+ {
+ NameForMT_s (dwMTAddr, g_mdName,mdNameLen);
+ ExtOut (" (Object: %S)", g_mdName);
+ return;
+ }
+
+ // A MethodDesc?
+ if (IsMethodDesc(value))
+ {
+ NameForMD_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MD: %S)", g_mdName);
+ return;
+ }
+
+ // A JitHelper?
+ const char* name = HelperFuncName(value);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+
+ // A call to managed code?
+ TADDR methodDesc = MDForCall(value);
+ if (methodDesc)
+ {
+ NameForMD_s (methodDesc, g_mdName,mdNameLen);
+ ExtOut (" (code for MD: %S)", g_mdName);
+ return;
+ }
+
+ // Random symbol.
+ char Symbol[1024];
+ if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(value), Symbol, 1024,
+ NULL, NULL)))
+ {
+ if (Symbol[0] != '\0')
+ {
+ ExtOut (" (%s)", Symbol);
+ return;
+ }
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Unassembly a managed code. Translating managed object, *
+* call. *
+* *
+\**********************************************************************/
+void ARMMachine::Unassembly (
+ TADDR PCBegin,
+ TADDR PCEnd,
+ TADDR PCAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const
+{
+ ULONG_PTR PC = PCBegin;
+ char line[1024];
+ char *ptr;
+ char *valueptr;
+ bool fLastWasMovW = false;
+ INT_PTR lowbits = 0;
+ ULONG curLine = -1;
+ WCHAR filename[MAX_LONGPATH];
+ ULONG linenum;
+
+ while (PC < PCEnd)
+ {
+ if (IsInterrupt())
+ return;
+
+ // Print out line numbers if needed
+ if (!bSuppressLines
+ && SUCCEEDED(GetLineByOffset(TO_CDADDR(PC), &linenum, filename, MAX_LONGPATH)))
+ {
+ if (linenum != curLine)
+ {
+ curLine = linenum;
+ ExtOut("\n%S @ %d:\n", filename, linenum);
+ }
+ }
+
+#ifndef FEATURE_PAL
+ //
+ // Print out any GC information corresponding to the current instruction offset.
+ //
+ if (pGCEncodingInfo)
+ {
+ SIZE_T curOffset = (PC - PCBegin) + pGCEncodingInfo->hotSizeToAdd;
+ while ( !pGCEncodingInfo->fDoneDecoding
+ && pGCEncodingInfo->ofs <= curOffset)
+ {
+ ExtOut(pGCEncodingInfo->buf);
+ ExtOut("\n");
+ SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
+ }
+ }
+#endif //!FEATURE_PAL
+ //
+ // Print out any EH info corresponding to the current offset
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(PC - PCBegin);
+ }
+
+ if ((PC & ~1) == (PCAskedFor & ~1))
+ {
+ ExtOut (">>> ");
+ }
+
+ //
+ // Print offsets, in addition to actual address.
+ //
+ if (bDisplayOffsets)
+ {
+ ExtOut("%04x ", PC - PCBegin);
+ }
+
+ ULONG_PTR prevPC = PC;
+ DisasmAndClean (PC, line, _countof(line));
+
+ // look at the disassembled bytes
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // If there is gcstress info for this method, and this is a 'hlt'
+ // instruction, then gcstress probably put the 'hlt' there. Look
+ // up the original instruction and print it instead.
+ //
+
+
+ if ( GCStressCodeCopy
+ && ( !strncmp (ptr, "de00 ", 5)
+ || !strncmp (ptr, "de01 ", 5)
+ || !strncmp (ptr, "de02 ", 5)
+ || !strncmp (ptr, "f7f0a001", 8)
+ || !strncmp (ptr, "f7f0a002", 8)
+ || !strncmp (ptr, "f7f0a003", 8)
+ ))
+ {
+ ULONG_PTR InstrAddr = prevPC;
+
+ //
+ // Compute address into saved copy of the code, and
+ // disassemble the original instruction
+ //
+
+ ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - PCBegin);
+ ULONG_PTR OrigPC = OrigInstrAddr;
+
+ DisasmAndClean(OrigPC, line, _countof(line));
+
+ //
+ // Increment the real PC based on the size of the unmodifed
+ // instruction
+ //
+
+ PC = InstrAddr + (OrigPC - OrigInstrAddr);
+
+ //
+ // Print out real code address in place of the copy address
+ //
+
+ ExtOut("%08x ", (ULONG)InstrAddr);
+
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // Print out everything after the code address, and skip the
+ // instruction bytes
+ //
+
+ ExtOut(ptr);
+
+ //
+ // Add an indicator that this address has not executed yet
+ //
+
+ ExtOut(" (gcstress)");
+ }
+ else
+ {
+ ExtOut (line);
+ }
+
+ // Now advance to the opcode
+ NextTerm (ptr);
+
+ if (!strncmp (ptr, "movw ", 5) || !strncmp (ptr, "mov ", 4))
+ {
+ // Possibly the loading the low-order 16-bits of a 32-bit constant. Cache the value in case the
+ // next instruction is a movt with the high-order bits.
+ if ((valueptr = strchr(ptr, '#')) != NULL)
+ {
+ GetValueFromExpr(valueptr, lowbits);
+ fLastWasMovW = true;
+ }
+ }
+ else
+ {
+ if (!strncmp (ptr, "movt ", 5) && fLastWasMovW)
+ {
+ // A movt following a movw (if we were being really careful we'd check that the destination
+ // register was the same in both cases). Assemble the two 16-bit immediate values from both
+ // instructions and see if the resultant constant is interesting.
+ if ((valueptr = strchr(ptr, '#')) != NULL)
+ {
+ INT_PTR highbits;
+ GetValueFromExpr(valueptr, highbits);
+ HandleValue((highbits << 16) | lowbits);
+ }
+ }
+ else if ((valueptr = strchr(ptr, '=')) != NULL)
+ {
+ // Some instruction fetched a PC-relative constant which the disassembler nicely decoded for
+ // us using the ARM convention =<constant>. Retrieve this value and see if it's interesting.
+ INT_PTR value;
+ GetValueFromExpr(valueptr, value);
+ HandleValue(value);
+ }
+
+ fLastWasMovW = false;
+ }
+
+ ExtOut ("\n");
+ }
+}
+
+#if 0 // @ARMTODO: Figure out how to extract this information under CoreARM
+static void ExpFuncStateInit (TADDR *PCRetAddr)
+{
+ ULONG64 offset;
+ if (FAILED(g_ExtSymbols->GetOffsetByName("ntdll!KiUserExceptionDispatcher", &offset))) {
+ return;
+ }
+ char line[256];
+ int i = 0;
+ while (i < 3) {
+ g_ExtControl->Disassemble (offset, 0, line, 256, NULL, &offset);
+ if (strstr (line, "call")) {
+ PCRetAddr[i++] = (TADDR)offset;
+ }
+ }
+}
+#endif // 0
+
+
+// @ARMTODO: Figure out how to extract this information under CoreARM
+BOOL ARMMachine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR * exrAddr, PEXCEPTION_RECORD exr) const
+{
+ return FALSE;
+#if 0 // @ARMTODO: Figure out how to extract this information under CoreARM
+ static TADDR PCRetAddr[3] = {0,0,0};
+
+ if (PCRetAddr[0] == 0) {
+ ExpFuncStateInit (PCRetAddr);
+ }
+ *cxrAddr = 0;
+ *exrAddr = 0;
+ if (PC == PCRetAddr[0]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else if (PC == PCRetAddr[1]) {
+ *cxrAddr = stack + sizeof(TADDR);
+ }
+ else if (PC == PCRetAddr[2]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else
+ return FALSE;
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), &stack, sizeof(stack), NULL)))
+ return FALSE;
+ *cxrAddr = stack;
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), cxr, sizeof(DT_CONTEXT), NULL))) {
+ return FALSE;
+ }
+
+ if (*exrAddr) {
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), &stack, sizeof(stack), NULL)))
+ {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ *exrAddr = stack;
+ size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), exr, erSize, NULL))) {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ }
+ return TRUE;
+#endif // 0
+}
+
+
+///
+/// Dump ARM GCInfo table
+///
+void ARMMachine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+#ifndef FEATURE_PAL
+ if (bPrintHeader)
+ {
+ ExtOut("Pointer table:\n");
+ }
+
+ ARMGCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ gcDump.gcPrintf = gcPrintf;
+
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
+#endif // !FEATURE_PAL
+}
+
+#endif // SOS_TARGET_ARM
diff --git a/src/ToolBox/SOS/Strike/disasmARM64.cpp b/src/ToolBox/SOS/Strike/disasmARM64.cpp
new file mode 100644
index 0000000000..6a19fc9377
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasmARM64.cpp
@@ -0,0 +1,392 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#ifndef _TARGET_ARM64_
+#define _TARGET_ARM64_
+#endif
+
+#ifdef _TARGET_AMD64_
+#undef _TARGET_AMD64_
+#endif
+
+#include "strike.h"
+#include "util.h"
+#include <dbghelp.h>
+
+
+#include "disasm.h"
+
+#include "../../../inc/corhdr.h"
+#include "../../../inc/cor.h"
+#include "../../../inc/dacprivate.h"
+
+namespace ARM64GCDump
+{
+#undef _TARGET_X86_
+#undef LIMITED_METHOD_CONTRACT
+#define LIMITED_METHOD_DAC_CONTRACT
+#define SUPPORTS_DAC
+#define LF_GCROOTS
+#define LL_INFO1000
+#define LOG(x)
+#define LOG_PIPTR(pObjRef, gcFlags, hCallBack)
+#define DAC_ARG(x)
+#include "gcdumpnonx86.cpp"
+}
+
+#ifdef FEATURE_PAL
+void SwitchToFiber(void*)
+{
+ // TODO: Fix for linux
+ assert(false);
+}
+#endif
+
+#if !defined(_TARGET_WIN64_)
+#error This file only supports SOS targeting ARM64 from a 64-bit debugger
+#endif
+
+#if !defined(SOS_TARGET_ARM64)
+#error This file should be used to support SOS targeting ARM64 debuggees
+#endif
+
+
+void ARM64Machine::IsReturnAddress(TADDR retAddr, TADDR* whereCalled) const
+{
+ *whereCalled = 0;
+
+ DWORD previousInstr;
+ move_xp(previousInstr, retAddr - sizeof(previousInstr));
+
+ // ARM64TODO: needs to be implemented for jump stubs for ngen case
+
+ if ((previousInstr & 0xfffffc1f) == 0xd63f0000)
+ {
+ // BLR <reg>
+ *whereCalled = 0xffffffff;
+ }
+ else if ((previousInstr & 0xfc000000) == 0x94000000)
+ {
+ // BL <label>
+ DWORD imm26 = previousInstr & 0x03ffffff;
+ // offset = SignExtend(imm26:'00', 64);
+ INT64 offset = ((INT64)imm26 << 38) >> 36;
+ *whereCalled = retAddr - 4 + offset;
+ }
+}
+
+// Determine if a value is MT/MD/Obj
+static void HandleValue(TADDR value)
+{
+ // A MethodTable?
+ if (IsMethodTable(value))
+ {
+ NameForMT_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MT: %S)", g_mdName);
+ return;
+ }
+
+ // A Managed Object?
+ TADDR dwMTAddr;
+ move_xp (dwMTAddr, value);
+ if (IsStringObject(value))
+ {
+ ExtOut (" (\"");
+ StringObjectContent (value, TRUE);
+ ExtOut ("\")");
+ return;
+ }
+ else if (IsMethodTable(dwMTAddr))
+ {
+ NameForMT_s (dwMTAddr, g_mdName,mdNameLen);
+ ExtOut (" (Object: %S)", g_mdName);
+ return;
+ }
+
+ // A MethodDesc?
+ if (IsMethodDesc(value))
+ {
+ NameForMD_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MD: %S)", g_mdName);
+ return;
+ }
+
+ // A JitHelper?
+ const char* name = HelperFuncName(value);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+
+ // A call to managed code?
+ // ARM64TODO: not (yet) implemented. perhaps we don't need it at all.
+
+ // Random symbol.
+ char Symbol[1024];
+ if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(value), Symbol, 1024,
+ NULL, NULL)))
+ {
+ if (Symbol[0] != '\0')
+ {
+ ExtOut (" (%s)", Symbol);
+ return;
+ }
+ }
+
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Unassembly a managed code. Translating managed object, *
+* call. *
+* *
+\**********************************************************************/
+void ARM64Machine::Unassembly (
+ TADDR PCBegin,
+ TADDR PCEnd,
+ TADDR PCAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const
+{
+ TADDR PC = PCBegin;
+ char line[1024];
+ ULONG lineNum;
+ ULONG curLine = -1;
+ WCHAR fileName[MAX_LONGPATH];
+ char *ptr;
+ INT_PTR accumulatedConstant = 0;
+ BOOL loBitsSet = FALSE;
+ BOOL hiBitsSet = FALSE;
+ char *szConstant = NULL;
+
+
+ while(PC < PCEnd)
+ {
+ ULONG_PTR currentPC = PC;
+ DisasmAndClean (PC, line, _countof(line));
+
+ // This is the closing of the previous run.
+ // Check the next instruction. if it's not a the last movk, handle the accumulated value
+ // else simply print a new line.
+ if (loBitsSet && hiBitsSet)
+ {
+ ptr = line;
+ // Advance to the instruction encoding
+ NextTerm(ptr);
+ // Advance to the opcode
+ NextTerm(ptr);
+ // if it's not movk, handle the accumulated value
+ // otherwise simply print the new line. The constant in this expression will be
+ // accumulated below.
+ if (strncmp(ptr, "movk ", 5))
+ {
+ HandleValue(accumulatedConstant);
+ accumulatedConstant = 0;
+ }
+ ExtOut ("\n");
+ }
+ else if (currentPC != PCBegin)
+ {
+ ExtOut ("\n");
+ }
+
+ // This is the new instruction
+
+ if (IsInterrupt())
+ return;
+ //
+ // Print out line numbers if needed
+ //
+ if (!bSuppressLines &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(currentPC), &lineNum, fileName, MAX_LONGPATH)))
+ {
+ if (lineNum != curLine)
+ {
+ curLine = lineNum;
+ ExtOut("\n%S @ %d:\n", fileName, lineNum);
+ }
+ }
+
+ //
+ // Print out any GC information corresponding to the current instruction offset.
+ //
+ if (pGCEncodingInfo)
+ {
+ SIZE_T curOffset = (currentPC - PCBegin) + pGCEncodingInfo->hotSizeToAdd;
+ while ( !pGCEncodingInfo->fDoneDecoding
+ && pGCEncodingInfo->ofs <= curOffset)
+ {
+ ExtOut(pGCEncodingInfo->buf);
+ ExtOut("\n");
+ SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
+ }
+ }
+
+ //
+ // Print out any EH info corresponding to the current offset
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(currentPC - PCBegin);
+ }
+
+ if (currentPC == PCAskedFor)
+ {
+ ExtOut (">>> ");
+ }
+
+ //
+ // Print offsets, in addition to actual address.
+ //
+ if (bDisplayOffsets)
+ {
+ ExtOut("%04x ", currentPC - PCBegin);
+ }
+
+ // look at the disassembled bytes
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // If there is gcstress info for this method, and this is a 'hlt'
+ // instruction, then gcstress probably put the 'hlt' there. Look
+ // up the original instruction and print it instead.
+ //
+
+
+ if ( GCStressCodeCopy
+ && ( !strncmp (ptr, "badc0de0", 8)
+ || !strncmp (ptr, "badc0de1", 8)
+ || !strncmp (ptr, "badc0de2", 8)
+ ))
+ {
+ ULONG_PTR InstrAddr = currentPC;
+
+ //
+ // Compute address into saved copy of the code, and
+ // disassemble the original instruction
+ //
+
+ ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - PCBegin);
+ ULONG_PTR OrigPC = OrigInstrAddr;
+
+ DisasmAndClean(OrigPC, line, _countof(line));
+
+ //
+ // Increment the real PC based on the size of the unmodifed
+ // instruction
+ //
+
+ PC = InstrAddr + (OrigPC - OrigInstrAddr);
+
+ //
+ // Print out real code address in place of the copy address
+ //
+
+ ExtOut("%08x`%08x ", (ULONG)(InstrAddr >> 32), (ULONG)InstrAddr);
+
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // Print out everything after the code address, and skip the
+ // instruction bytes
+ //
+
+ ExtOut(ptr);
+
+ //
+ // Add an indicator that this address has not executed yet
+ //
+
+ ExtOut(" (gcstress)");
+ }
+ else
+ {
+ ExtOut (line);
+ }
+
+ // Now advance to the opcode
+ NextTerm (ptr);
+
+ if (!strncmp(ptr, "mov ", 4))
+ {
+ if ((szConstant = strchr(ptr, '#')) != NULL)
+ {
+ GetValueFromExpr(szConstant, accumulatedConstant);
+ loBitsSet = TRUE;
+ }
+ }
+ else if (!strncmp(ptr, "movk ", 5))
+ {
+ char *szShiftAmount = NULL;
+ INT_PTR shiftAmount = 0;
+ INT_PTR constant = 0;
+ if (((szShiftAmount = strrchr(ptr, '#')) != NULL) &&
+ ((szConstant = strchr(ptr, '#')) != NULL) &&
+ (szShiftAmount != szConstant) &&
+ (accumulatedConstant > 0)) // Misses when movk is succeeding mov reg, #0x0, which I don't think makes any sense
+ {
+ GetValueFromExpr(szShiftAmount, shiftAmount);
+ GetValueFromExpr(szConstant, constant);
+ accumulatedConstant += (constant<<shiftAmount);
+ hiBitsSet = TRUE;
+ }
+ }
+ else
+ {
+ accumulatedConstant = 0;
+ loBitsSet = hiBitsSet = FALSE;
+ if ((szConstant = strchr(ptr, '=')) != NULL)
+ {
+ // Some instruction fetched a PC-relative constant which the disassembler nicely decoded for
+ // us using the ARM convention =<constant>. Retrieve this value and see if it's interesting.
+ INT_PTR value;
+ GetValueFromExpr(szConstant, value);
+ HandleValue(value);
+ }
+
+
+ // ARM64TODO: we could possibly handle adr(p)/ldr pair too.
+ }
+
+ }
+ ExtOut ("\n");
+}
+
+
+// @ARMTODO: Figure out how to extract this information under CoreARM
+BOOL ARM64Machine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR * exrAddr, PEXCEPTION_RECORD exr) const
+{
+ _ASSERTE("ARM64:NYI");
+ return FALSE;
+}
+
+///
+/// Dump ARM GCInfo table
+///
+void ARM64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+ if (bPrintHeader)
+ {
+ ExtOut("Pointer table:\n");
+ }
+
+ ARM64GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ gcDump.gcPrintf = gcPrintf;
+
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
+}
+
diff --git a/src/ToolBox/SOS/Strike/disasmX86.cpp b/src/ToolBox/SOS/Strike/disasmX86.cpp
new file mode 100644
index 0000000000..36a08d20a3
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasmX86.cpp
@@ -0,0 +1,1707 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#include "strike.h"
+#include "util.h"
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "../../../inc/corhdr.h"
+#include "../../../inc/cor.h"
+#include "../../../inc/dacprivate.h"
+
+
+#if defined(SOS_TARGET_X86) && defined(SOS_TARGET_AMD64)
+#error This file does not support SOS targeting both X86 and AMD64 debuggees
+#endif
+
+#if !defined(SOS_TARGET_X86) && !defined(SOS_TARGET_AMD64)
+#error This file should be used to support SOS targeting either X86 or AMD64 debuggees
+#endif
+
+
+// These must be in the same order as they are used in the instruction
+// encodings/same as the CONTEXT field order.
+enum RegIndex
+{
+ EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
+
+#ifdef _TARGET_AMD64_
+ R8, R9, R10, R11, R12, R13, R14, R15,
+#endif // _TARGET_AMD64_
+
+ EIP, NONE
+};
+
+const int NumReg = NONE;
+struct Register
+{
+ TADDR value;
+ BOOL bValid;
+ TADDR stack;
+ BOOL bOnStack;
+};
+
+// Find the index for a register name
+inline RegIndex FindReg (___in __in_z char *ptr, __out_opt int *plen = NULL, __out_opt int *psize = NULL)
+{
+ struct RegName
+ {
+ RegIndex index;
+ PCSTR pszName;
+ int cchName;
+ int size;
+ };
+
+ static RegName rgRegNames[] = {
+
+#define REG(index, reg, size) { index, #reg, sizeof(#reg)-1, size }
+#define REG8(index, reg) REG(index, reg, 1)
+#define REG16(index, reg) REG(index, reg, 2)
+#define REG32(index, reg) REG(index, reg, 4)
+#define REG64(index, reg) REG(index, reg, 8)
+
+ REG8(EAX, al),
+ REG8(EAX, ah),
+ REG8(EBX, bl),
+ REG8(EBX, bh),
+ REG8(ECX, cl),
+ REG8(ECX, ch),
+ REG8(EDX, dl),
+ REG8(EDX, dh),
+
+ REG16(EAX, ax),
+ REG16(EBX, bx),
+ REG16(ECX, cx),
+ REG16(EDX, dx),
+ REG16(ESI, si),
+ REG16(EDI, di),
+ REG16(EBP, bp),
+ REG16(ESP, sp),
+
+ REG32(EAX, eax),
+ REG32(EBX, ebx),
+ REG32(ECX, ecx),
+ REG32(EDX, edx),
+ REG32(ESI, esi),
+ REG32(EDI, edi),
+ REG32(EBP, ebp),
+ REG32(ESP, esp),
+
+#ifdef _TARGET_AMD64_
+
+ REG8(R8, r8b),
+ REG8(R9, r9b),
+ REG8(R10, r10b),
+ REG8(R11, r11b),
+ REG8(R12, r12b),
+ REG8(R13, r13b),
+ REG8(R14, r14b),
+ REG8(R15, r15b),
+
+ REG16(R8, r8w),
+ REG16(R9, r9w),
+ REG16(R10, r10w),
+ REG16(R11, r11w),
+ REG16(R12, r12w),
+ REG16(R13, r13w),
+ REG16(R14, r14w),
+ REG16(R15, r15w),
+
+ REG32(R8, r8d),
+ REG32(R9, r9d),
+ REG32(R10, r10d),
+ REG32(R11, r11d),
+ REG32(R12, r12d),
+ REG32(R13, r13d),
+ REG32(R14, r14d),
+ REG32(R15, r15d),
+
+ REG64(EAX, rax),
+ REG64(EBX, rbx),
+ REG64(ECX, rcx),
+ REG64(EDX, rdx),
+ REG64(ESI, rsi),
+ REG64(EDI, rdi),
+ REG64(EBP, rbp),
+ REG64(ESP, rsp),
+ REG64(R8, r8),
+ REG64(R9, r9),
+ REG64(R10, r10),
+ REG64(R11, r11),
+ REG64(R12, r12),
+ REG64(R13, r13),
+ REG64(R14, r14),
+ REG64(R15, r15),
+
+#endif // _TARGET_AMD64_
+
+#undef REG
+#undef REG8
+#undef REG16
+#undef REG32
+#undef REG64
+
+ };
+
+ for (size_t i = 0; i < sizeof(rgRegNames)/sizeof(rgRegNames[0]); i++)
+ {
+ if (!strncmp(ptr, rgRegNames[i].pszName, rgRegNames[i].cchName))
+ {
+ if (psize)
+ *psize = rgRegNames[i].size;
+
+ if (plen)
+ *plen = rgRegNames[i].cchName;
+
+ return rgRegNames[i].index;
+ }
+ }
+
+ return NONE;
+}
+
+// Find the value of an expression.
+inline BOOL FindSrc (__in_z char *ptr, ___in Register *reg, INT_PTR &value, BOOL &bDigit)
+{
+ if (GetValueFromExpr (ptr, value))
+ {
+ bDigit = TRUE;
+ return TRUE;
+ }
+
+ BOOL bValid = FALSE;
+ BOOL bByRef = IsByRef (ptr);
+ bDigit = FALSE;
+
+ int regnamelen;
+ RegIndex index = FindReg (ptr, &regnamelen);
+ if (index != NONE)
+ {
+ if (reg[index].bValid)
+ {
+ value = reg[index].value;
+ ptr += regnamelen;
+ // TODO: consider ecx+edi*4+0x4
+ if ((IsTermSep (ptr[0]) && !bByRef)
+ || (ptr[0] == ']' && bByRef))
+ {
+ bValid = TRUE;
+ if (bByRef)
+ SafeReadMemory (TO_TADDR(value), &value, sizeof(void*), NULL);
+ }
+ }
+ }
+ return bValid;
+}
+
+enum ADDRESSMODE {REG, DATA, INDIRECT, NODATA, BAD};
+
+struct RegState
+{
+ RegIndex reg;
+ BOOL bFullReg;
+ char scale;
+ int namelen;
+};
+
+struct InstData
+{
+ ADDRESSMODE mode;
+ RegState reg[2];
+ INT_PTR value;
+};
+
+void FindMainReg (___in __in_z char *ptr, RegState &reg)
+{
+ int size = 0;
+
+ reg.reg = FindReg(ptr, &reg.namelen, &size);
+
+ reg.bFullReg = (reg.reg!=NONE && sizeof(void*)==size) ? TRUE : FALSE;
+}
+
+static void DecodeAddressIndirect (___in __in_z char *term, InstData& arg)
+{
+ arg.mode = BAD;
+ arg.value = 0;
+ arg.reg[0].scale = 0;
+ arg.reg[1].scale = 0;
+
+ if (!IsByRef (term))
+ {
+ return;
+ }
+
+ // first part must be a reg
+ arg.reg[0].scale = 1;
+ if (term[0] == '+')
+ term ++;
+ else if (term[0] == '-')
+ {
+ term ++;
+ arg.reg[0].scale = -1;
+ }
+ if (isdigit(term[0]))
+ {
+ arg.reg[0].scale *= term[0]-'0';
+ term ++;
+ }
+
+ FindMainReg (term, arg.reg[0]);
+ if (arg.reg[0].reg == NONE)
+ return;
+ term += arg.reg[0].namelen;
+
+ if (term[0] == ']')
+ {
+ // It is [reg]
+ arg.mode = INDIRECT;
+ arg.value = 0;
+ return;
+ }
+
+ char sign = (char)((term[0] == '+')?1:-1);
+ term ++;
+ FindMainReg (term, arg.reg[1]);
+ if (arg.reg[1].reg != NONE)
+ {
+ // It is either [reg+reg*c] or [reg+reg*c+c]
+
+ term += arg.reg[1].namelen;
+
+ if (term[0] == '*')
+ {
+ term ++;
+ arg.reg[1].scale = sign*(term[0]-'0');
+ term ++;
+ }
+ else
+ arg.reg[1].scale = sign;
+
+ if (term[0] == ']')
+ {
+ // It is [reg+reg*c]
+ arg.mode = INDIRECT;
+ arg.value = 0;
+ return;
+ }
+ sign = (char)((term[0] == '+')?1:-1);
+ term ++;
+ }
+
+ char *endptr;
+ arg.value = strtoul(term, &endptr, 16);
+ if (endptr[0] == ']')
+ {
+ // It is [reg+reg*c+c]
+ arg.value *= sign;
+ arg.mode = INDIRECT;
+ }
+}
+
+void DecodeAddressTerm (___in __in_z char *term, InstData& arg)
+{
+ arg.mode = BAD;
+ arg.reg[0].scale = 0;
+ arg.reg[1].scale = 0;
+ arg.value = 0;
+ INT_PTR value;
+
+ if (GetValueFromExpr (term, value))
+ {
+ arg.value = value;
+ arg.mode = DATA;
+ }
+ else
+ {
+ FindMainReg (term, arg.reg[0]);
+ if (arg.reg[0].reg != NONE)
+ {
+ arg.mode = REG;
+ }
+ else
+ {
+ DecodeAddressIndirect (term, arg);
+ }
+ }
+}
+
+// Return 0 for non-managed call. Otherwise return MD address.
+TADDR MDForCall (TADDR callee)
+{
+ // call managed code?
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR IP = callee;
+ TADDR gcinfoAddr;
+
+ if (!GetCalleeSite (callee, IP))
+ return 0;
+
+ IP2MethodDesc (IP, methodDesc, jitType, gcinfoAddr);
+ if (methodDesc)
+ {
+ return methodDesc;
+ }
+
+ // jmp stub
+ char line[256];
+ DisasmAndClean (IP, line, 256);
+ char *ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "jmp ", 4))
+ {
+ // jump thunk
+ NextTerm (ptr);
+ INT_PTR value;
+ methodDesc = 0;
+ if (GetValueFromExpr (ptr, value))
+ {
+ IP2MethodDesc (value, methodDesc, jitType, gcinfoAddr);
+ }
+ return methodDesc;
+ }
+ return 0;
+}
+
+// Handle a call instruction.
+void HandleCall(TADDR callee, Register *reg)
+{
+ // call managed code?
+ TADDR methodDesc = MDForCall (callee);
+ if (methodDesc)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ NameForMD_s(methodDesc, g_mdName,mdNameLen);
+ ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken));
+ return;
+ }
+ }
+
+#ifdef _TARGET_AMD64_
+ // A jump thunk?
+
+ CONTEXT ctx = {0};
+
+ ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER);
+
+ for (unsigned ireg = 0; ireg < 16; ireg++)
+ {
+ if (reg[ireg].bValid)
+ {
+ *(&ctx.Rax + ireg) = reg[ireg].value;
+ }
+ }
+
+ ctx.Rip = callee;
+
+ CLRDATA_ADDRESS ip = 0, md = 0;
+ if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md))
+ {
+ if (md)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, md) == S_OK)
+ {
+ NameForMD_s(md, g_mdName,mdNameLen);
+ ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken));
+ return;
+ }
+ }
+
+ if (ip != callee)
+ {
+ return HandleCall(ip, reg);
+ }
+ }
+#endif // _TARGET_AMD64_
+
+ // A JitHelper?
+ const char* name = HelperFuncName(callee);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+
+ // call unmanaged code?
+ char Symbol[1024];
+ if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(callee), Symbol, 1024,
+ NULL, NULL)))
+ {
+ if (Symbol[0] != '\0')
+ {
+ ExtOut (" (%s)", Symbol);
+ return;
+ }
+ }
+}
+
+// Determine if a value is MT/MD/Obj
+void HandleValue(TADDR value)
+{
+ // A MethodTable?
+ if (IsMethodTable(value))
+ {
+ NameForMT_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MT: %S)", g_mdName);
+ return;
+ }
+
+ // A Managed Object?
+ TADDR dwMTAddr;
+ move_xp (dwMTAddr, value);
+ if (IsStringObject(value))
+ {
+ ExtOut (" (\"");
+ StringObjectContent (value, TRUE);
+ ExtOut ("\")");
+ return;
+ }
+ else if (IsMethodTable(dwMTAddr))
+ {
+ NameForMT_s (dwMTAddr, g_mdName,mdNameLen);
+ ExtOut (" (Object: %S)", g_mdName);
+ return;
+ }
+
+ // A MethodDesc?
+ if (IsMethodDesc(value))
+ {
+ NameForMD_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MD: %S)", g_mdName);
+ return;
+ }
+
+ // A JitHelper?
+ const char* name = HelperFuncName(value);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Unassembly a managed code. Translating managed object, *
+* call. *
+* *
+\**********************************************************************/
+void
+#ifdef _TARGET_X86_
+ X86Machine::Unassembly
+#elif defined(_TARGET_AMD64_)
+ AMD64Machine::Unassembly
+#endif
+ (TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const
+{
+ ULONG_PTR IP = IPBegin;
+ char line[1024];
+ Register reg [NumReg];
+ ZeroMemory (reg, sizeof(reg));
+ RegIndex dest;
+ INT_PTR value;
+ BOOL bDigit;
+ char *ptr;
+
+ ULONG curLine = -1;
+ WCHAR filename[MAX_LONGPATH];
+ ULONG linenum;
+
+ while (IP < IPEnd)
+ {
+ if (IsInterrupt())
+ return;
+
+ // Print out line numbers if needed
+ if (!bSuppressLines
+ && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, MAX_LONGPATH)))
+ {
+ if (linenum != curLine)
+ {
+ curLine = linenum;
+ ExtOut("\n%S @ %d:\n", filename, linenum);
+ }
+ }
+
+ //
+ // Print out any GC information corresponding to the current instruction offset.
+ //
+
+#ifndef FEATURE_PAL
+ if (pGCEncodingInfo)
+ {
+ SIZE_T curOffset = (IP - IPBegin) + pGCEncodingInfo->hotSizeToAdd;
+ while ( !pGCEncodingInfo->fDoneDecoding
+ && pGCEncodingInfo->ofs <= curOffset)
+ {
+ ExtOut(pGCEncodingInfo->buf);
+ ExtOut("\n");
+ SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
+ }
+ }
+#endif // FEATURE_PAL
+
+ ULONG_PTR InstrAddr = IP;
+
+ //
+ // Print out any EH info corresponding to the current offset
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(IP - IPBegin);
+ }
+
+ if (IP == IPAskedFor)
+ {
+ ExtOut (">>> ");
+ }
+
+ //
+ // Print offsets, in addition to actual address.
+ //
+ if (bDisplayOffsets)
+ {
+ ExtOut("%04x ", IP - IPBegin);
+ }
+
+ DisasmAndClean (IP, line, _countof(line));
+
+ // look at key word
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+
+ //
+ // If there is gcstress info for this method, and this is a 'hlt'
+ // instruction, then gcstress probably put the 'hlt' there. Look
+ // up the original instruction and print it instead.
+ //
+
+ SSIZE_T cbIPOffset = 0;
+
+ if ( GCStressCodeCopy
+ && ( !strncmp (ptr, "hlt", 3)
+ || !strncmp (ptr, "cli", 3)
+ || !strncmp (ptr, "sti", 3)))
+ {
+ //
+ // Compute address into saved copy of the code, and
+ // disassemble the original instruction
+ //
+
+ ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - IPBegin);
+ ULONG_PTR OrigIP = OrigInstrAddr;
+
+ DisasmAndClean(OrigIP, line, _countof(line));
+
+ //
+ // Increment the real IP based on the size of the unmodifed
+ // instruction
+ //
+
+ IP = InstrAddr + (OrigIP - OrigInstrAddr);
+
+ cbIPOffset = IP - OrigIP;
+
+ //
+ // Print out real code address in place of the copy address
+ //
+
+#ifdef _WIN64
+ ExtOut("%08x`%08x ", (ULONG)(InstrAddr >> 32), (ULONG)InstrAddr);
+#else
+ ExtOut("%08x ", (ULONG)InstrAddr);
+#endif
+
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // Print out everything after the code address, and skip the
+ // instruction bytes
+ //
+
+ ExtOut(ptr);
+
+ NextTerm (ptr);
+
+ //
+ // Add an indicator that this address has not executed yet
+ //
+
+ ExtOut(" (gcstress)");
+ }
+ else
+ {
+ ExtOut (line);
+ }
+
+ if (!strncmp (ptr, "mov ", 4))
+ {
+ NextTerm (ptr);
+
+ dest = FindReg(ptr);
+ if (dest != NONE)
+ {
+ NextTerm (ptr);
+
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ reg[dest].bValid = TRUE;
+ reg[dest].value = value;
+ // Is it a managed obj
+ if (bDigit)
+ HandleValue (reg[dest].value);
+ }
+ else
+ {
+ reg[dest].bValid = FALSE;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5))
+ {
+ NextTerm (ptr);
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ if (bDigit)
+ value += cbIPOffset;
+
+ HandleCall (value, reg);
+ }
+
+ // trash EAX, ECX, EDX
+ reg[EAX].bValid = FALSE;
+ reg[ECX].bValid = FALSE;
+ reg[EDX].bValid = FALSE;
+
+#ifdef _TARGET_AMD64_
+ reg[R8].bValid = FALSE;
+ reg[R9].bValid = FALSE;
+ reg[R10].bValid = FALSE;
+ reg[R11].bValid = FALSE;
+#endif // _TARGET_AMD64_
+ }
+ else if (!strncmp (ptr, "lea ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest != NONE)
+ {
+ NextTerm (ptr);
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ reg[dest].bValid = TRUE;
+ reg[dest].value = value;
+ }
+ else
+ {
+ reg[dest].bValid = FALSE;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "push ", 5))
+ {
+ // do not do anything
+ NextTerm (ptr);
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ if (bDigit)
+ {
+ HandleValue (value);
+ }
+ }
+ }
+ else
+ {
+ // assume this instruction will trash dest reg
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest != NONE)
+ reg[dest].bValid = FALSE;
+ }
+ ExtOut ("\n");
+ }
+
+ //
+ // Print out any "end" EH info (where the end address is the byte immediately following the last instruction)
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(IP - IPBegin);
+ }
+}
+
+// Find the real callee site. Handle JMP instruction.
+// Return TRUE if we get the address, FALSE if not.
+BOOL GetCalleeSite (TADDR IP, TADDR &IPCallee)
+{
+ while (TRUE) {
+ unsigned char inst[2];
+ if (g_ExtData->ReadVirtual(TO_CDADDR(IP), inst, sizeof(inst), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ if (inst[0] == 0xEB) {
+ IP += 2+(char)inst[1];
+ }
+ else if (inst[0] == 0xE9) {
+ int displace;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(IP+1), &displace, sizeof(displace), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ else
+ {
+ IP += 5+displace;
+ }
+ }
+ else if (inst[0] == 0xFF && (inst[1] & 070) == 040) {
+ if (inst[1] == 0x25) {
+ DWORD displace;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(IP+2), &displace, sizeof(displace), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ if (g_ExtData->ReadVirtual(TO_CDADDR(displace), &displace, sizeof(displace), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ else
+ {
+ IP = displace;
+ }
+ }
+ else
+ // Target for jmp is determined from register values.
+ return FALSE;
+ }
+ else
+ {
+ IPCallee = IP;
+ return TRUE;
+ }
+ }
+}
+
+// GetFinalTarget is based on HandleCall, but avoids printing anything to the output.
+// This is currently only called on x64
+eTargetType GetFinalTarget(TADDR callee, TADDR* finalMDorIP)
+{
+ // call managed code?
+ TADDR methodDesc = MDForCall (callee);
+ if (methodDesc)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ *finalMDorIP = methodDesc;
+ return ettMD;
+ }
+ }
+
+#ifdef _TARGET_AMD64_
+ // A jump thunk?
+
+ CONTEXT ctx = {0};
+ ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER);
+ ctx.Rip = callee;
+
+ CLRDATA_ADDRESS ip = 0, md = 0;
+ if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md))
+ {
+ if (md)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, md) == S_OK)
+ {
+ *finalMDorIP = md;
+ return ettStub;
+ }
+ }
+
+ if (ip != callee)
+ {
+ return GetFinalTarget(ip, finalMDorIP);
+ }
+ }
+#endif // _TARGET_AMD64_
+
+ // A JitHelper?
+ const char* name = HelperFuncName(callee);
+ if (name) {
+ *finalMDorIP = callee;
+ return ettJitHelp;
+ }
+
+ // call unmanaged code?
+ *finalMDorIP = callee;
+ return ettNative;
+}
+
+#ifndef FEATURE_PAL
+
+void ExpFuncStateInit (TADDR *IPRetAddr)
+{
+ ULONG64 offset;
+ if (FAILED(g_ExtSymbols->GetOffsetByName("ntdll!KiUserExceptionDispatcher", &offset))) {
+ return;
+ }
+
+ // test if we have a minidump for which the image is not cached anymore. this avoids
+ // the having the while loop below spin forever (or a very long time)...
+ // (Watson backend hit this a few times, and they had to institute a timeout policy
+ // to work around this)
+ SIZE_T instrs;
+ if (FAILED(g_ExtData->ReadVirtual(offset, &instrs, sizeof(instrs), NULL)) || instrs == 0) {
+ return;
+ }
+
+ char line[256];
+ int i = 0;
+ int cnt = 0;
+#ifdef SOS_TARGET_X86
+ // On x86 and x64 the last 3 "call" instructions in ntdll!KiUserExceptionDispatcher
+ // are making calls to OS APIs that take as argument the context record (and some
+ // of them the exception record as well)
+ const int cCallInstrs = 3;
+#elif defined(SOS_TARGET_AMD64)
+ // On x64 the first "call" instruction should be considered, as well
+ const int cCallInstrs = 4;
+#endif
+
+ while (i < cCallInstrs) {
+ g_ExtControl->Disassemble (offset, 0, line, 256, NULL, &offset);
+ if (strstr (line, "call")) {
+ IPRetAddr[i++] = (TADDR)offset;
+ }
+ // if we didn't find at least one "call" in the first 500 instructions give up...
+ if (++cnt >= 500 && IPRetAddr[0] == 0)
+ break;
+ }
+}
+
+#endif // FEATURE_PAL
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to fill in a cross platform context *
+* struct by looking on the stack for return addresses into *
+* KiUserExceptionDispatcher *
+* *
+\**********************************************************************/
+BOOL
+#ifdef SOS_TARGET_X86
+ X86Machine::GetExceptionContext
+#elif defined(SOS_TARGET_AMD64)
+ AMD64Machine::GetExceptionContext
+#endif
+ (TADDR stack,
+ TADDR IP,
+ TADDR * cxrAddr,
+ CROSS_PLATFORM_CONTEXT * pcxr,
+ TADDR * exrAddr,
+ PEXCEPTION_RECORD exr) const
+{
+#ifndef FEATURE_PAL
+#ifdef SOS_TARGET_X86
+ X86_CONTEXT * cxr = &pcxr->X86Context;
+ size_t contextSize = offsetof(CONTEXT, ExtendedRegisters);
+#elif defined(SOS_TARGET_AMD64)
+ AMD64_CONTEXT * cxr = &pcxr->Amd64Context;
+ size_t contextSize = offsetof(CONTEXT, FltSave);
+#endif
+
+ static TADDR IPRetAddr[4] = {0, 0, 0, 0};
+
+ if (IPRetAddr[0] == 0) {
+ ExpFuncStateInit (IPRetAddr);
+ }
+ *cxrAddr = 0;
+ *exrAddr = 0;
+
+#ifdef SOS_TARGET_X86
+
+ if (IP == IPRetAddr[0]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else if (IP == IPRetAddr[1]) {
+ *cxrAddr = stack + sizeof(TADDR);
+ }
+ else if (IP == IPRetAddr[2]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else
+ return FALSE;
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), &stack, sizeof(stack), NULL)))
+ return FALSE;
+ *cxrAddr = stack;
+
+ //if ((pContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
+ // contextSize += sizeof(pContext->ExtendedRegisters);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), cxr, (ULONG)contextSize, NULL))) {
+ return FALSE;
+ }
+
+ if (*exrAddr) {
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), &stack, sizeof(stack), NULL)))
+ {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ *exrAddr = stack;
+ size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), exr, (ULONG)erSize, NULL))) {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ }
+
+#elif defined(SOS_TARGET_AMD64)
+
+ if (IP == IPRetAddr[0] || IP == IPRetAddr[1] || IP == IPRetAddr[3]) {
+ *exrAddr = stack + sizeof(TADDR) + 0x4F0;
+ *cxrAddr = stack + sizeof(TADDR);
+ } else if (IP == IPRetAddr[2]) {
+ *cxrAddr = stack + sizeof(TADDR);
+ }
+ else {
+ return FALSE;
+ }
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), cxr, (ULONG)contextSize, NULL))) {
+ return FALSE;
+ }
+
+ if (*exrAddr) {
+ size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), exr, (ULONG)erSize, NULL))) {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ }
+
+#endif
+ return TRUE;
+#else
+ return FALSE;
+#endif // FEATURE_PAL
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to determine if a DWORD on the stack is *
+* a return address. *
+* It does this by checking several bytes before the DWORD to see if *
+* there is a call instruction. *
+* *
+\**********************************************************************/
+
+void
+#ifdef _TARGET_X86_
+ X86Machine::IsReturnAddress
+#elif defined(_TARGET_AMD64_)
+ AMD64Machine::IsReturnAddress
+#endif
+ (TADDR retAddr, TADDR* whereCalled) const
+{
+ *whereCalled = 0;
+
+ unsigned char spotend[6];
+ move_xp (spotend, retAddr-6);
+ unsigned char *spot = spotend+6;
+ TADDR addr;
+
+ // Note this is possible to be spoofed, but pretty unlikely
+ // call XXXXXXXX
+ if (spot[-5] == 0xE8) {
+ DWORD offs = 0;
+ move_xp (offs, retAddr-4);
+ *whereCalled = retAddr + (ULONG64)(LONG)(offs);
+ //*whereCalled = *((int*) (retAddr-4)) + retAddr;
+ // on WOW64 the range valid for code is almost the whole 4GB adddress space
+ if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK)
+ {
+ TADDR callee;
+ if (GetCalleeSite(*whereCalled, callee)) {
+ *whereCalled = callee;
+ }
+ return;
+ }
+ else
+ *whereCalled = 0;
+ }
+
+ // call [XXXXXXXX]
+ if (spot[-6] == 0xFF && (spot[-5] == 025)) {
+ DWORD offs = 0;
+ move_xp (offs, retAddr-4);
+#ifdef _TARGET_AMD64_
+ // on x64 this 32-bit is an RIP offset
+ addr = retAddr + (ULONG64)(LONG)(offs);
+#elif defined (_TARGET_X86_)
+ addr = offs;
+#endif
+ if (g_ExtData->ReadVirtual(TO_CDADDR(addr), whereCalled, sizeof(*whereCalled), NULL) == S_OK) {
+ move_xp (*whereCalled, addr);
+ //*whereCalled = **((unsigned**) (retAddr-4));
+ // on WOW64 the range valid for code is almost the whole 4GB adddress space
+ if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK)
+ {
+ TADDR callee;
+ if (GetCalleeSite(*whereCalled,callee)) {
+ *whereCalled = callee;
+ }
+ return;
+ }
+ else
+ *whereCalled = 0;
+ }
+ else
+ *whereCalled = 0;
+ }
+
+ // call [REG+XX]
+ if (spot[-3] == 0xFF && (spot[-2] & ~7) == 0120 && (spot[-2] & 7) != 4)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+ if (spot[-4] == 0xFF && spot[-3] == 0124)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // call [REG+XXXX]
+ if (spot[-6] == 0xFF && (spot[-5] & ~7) == 0220 && (spot[-5] & 7) != 4)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+ if (spot[-7] == 0xFF && spot[-6] == 0224)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // call [REG]
+ if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0020 && (spot[-1] & 7) != 4 && (spot[-1] & 7) != 5)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // call REG
+ if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0320 && (spot[-1] & 7) != 4)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // There are other cases, but I don't believe they are used.
+ return;
+}
+
+
+#ifdef _X86_
+
+///
+/// This is dead code, not called from anywhere, not linked in the final product.
+///
+static BOOL DecodeLine (___in __in_z char *line, ___in __in_z const char *const inst, InstData& arg1, InstData& arg2)
+{
+ char *ptr = line;
+ if (inst[0] == '*' || !strncmp (ptr, inst, strlen (inst)))
+ {
+ arg1.mode = BAD;
+ arg2.mode = BAD;
+ NextTerm (ptr);
+ if (*ptr == '\0')
+ {
+ arg1.mode = NODATA;
+ return TRUE;
+ }
+
+ DecodeAddressTerm (ptr, arg1);
+ NextTerm (ptr);
+ if (*ptr == '\0')
+ {
+ return TRUE;
+ }
+ DecodeAddressTerm (ptr, arg2);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+void PrintReg (Register *reg)
+{
+ ExtOut ("[EBX=%08x ESI=%08x EDI=%08x EBP=%08x ESP=%08x]\n",
+ reg[EBX].value, reg[ESI].value, reg[EDI].value, reg[EBP].value,
+ reg[ESP].value);
+}
+
+
+struct CallInfo
+{
+ DWORD_PTR stackPos;
+ DWORD_PTR retAddr;
+ DWORD_PTR whereCalled;
+};
+
+// Search for a Return address on stack.
+BOOL GetNextRetAddr (DWORD_PTR stackBegin, DWORD_PTR stackEnd,
+ CallInfo &callInfo)
+{
+ for (callInfo.stackPos = stackBegin;
+ callInfo.stackPos <= stackEnd;
+ callInfo.stackPos += 4)
+ {
+ if (!SafeReadMemory (callInfo.stackPos, &callInfo.retAddr, 4, NULL))
+ continue;
+
+ g_targetMachine->IsReturnAddress(callInfo.retAddr, &callInfo.whereCalled);
+ if (callInfo.whereCalled)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+struct FrameInfo
+{
+ DWORD_PTR IPStart;
+ DWORD_PTR Prolog;
+ DWORD_PTR FrameBase; // The value of ESP at the entry.
+ DWORD_PTR StackEnd;
+ DWORD_PTR argCount;
+ BOOL bEBPFrame;
+};
+
+// if a EBP frame, return TRUE if EBP has been setup
+void GetFrameBaseHelper (DWORD_PTR IPBegin, DWORD_PTR IPEnd,
+ INT_PTR &StackChange)
+{
+ char line[256];
+ char *ptr;
+ InstData arg1;
+ InstData arg2;
+ DWORD_PTR IP = IPBegin;
+ StackChange = 0;
+ while (IP < IPEnd)
+ {
+ DisasmAndClean (IP, line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (DecodeLine (ptr, "push ", arg1, arg2))
+ {
+ StackChange += 4;
+ }
+ else if (DecodeLine (ptr, "pop ", arg1, arg2))
+ {
+ StackChange -= 4;
+ }
+ else if (DecodeLine (ptr, "sub ", arg1, arg2))
+ {
+ if (arg1.mode == REG && arg1.reg[0].reg == ESP)
+ {
+ if (arg2.mode == DATA)
+ StackChange -= arg2.value;
+ }
+ }
+ else if (DecodeLine (ptr, "add ", arg1, arg2))
+ {
+ if (arg1.mode == REG && arg1.reg[0].reg == ESP)
+ {
+ if (arg2.mode == DATA)
+ StackChange += arg2.value;
+ }
+ }
+ else if (!strncmp (ptr, "ret", 3)) {
+ return;
+ }
+ }
+}
+
+enum IPSTATE {IPPROLOG1 /*Before EBP set*/, IPPROLOG2 /*After EBP set*/, IPCODE, IPEPILOG, IPEND};
+
+IPSTATE GetIpState (DWORD_PTR IP, FrameInfo* pFrame)
+{
+ char line[256];
+ char *ptr;
+
+ if (IP >= pFrame->IPStart && IP < pFrame->IPStart + pFrame->Prolog)
+ {
+ if (pFrame->bEBPFrame) {
+ DWORD_PTR pIP = pFrame->IPStart;
+ while (pIP < IP) {
+ DisasmAndClean (IP,line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "mov ", 4)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "ebp", 3)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "esp", 3)) {
+ return IPPROLOG2;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5)) {
+ NextTerm (ptr);
+ if (strstr (ptr, "__EH_prolog")) {
+ return IPPROLOG2;
+ }
+ }
+ }
+ pIP = IP;
+ while (pIP < pFrame->IPStart + pFrame->Prolog) {
+ DisasmAndClean (IP,line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "mov ", 4)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "ebp", 3)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "esp", 3)) {
+ return IPPROLOG1;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5)) {
+ NextTerm (ptr);
+ if (strstr (ptr, "__EH_prolog")) {
+ return IPPROLOG1;
+ }
+ }
+ }
+
+ ExtOut ("Fail to find where EBP is saved\n");
+ return IPPROLOG2;
+ }
+ else
+ {
+ return IPPROLOG1;
+ }
+ }
+
+ int nline = 0;
+ while (1) {
+ DisasmAndClean (IP,line, 256);
+ nline ++;
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "ret", 3)) {
+ return (nline==1)?IPEND:IPEPILOG;
+ }
+ else if (!strncmp (ptr, "leave", 5)) {
+ return IPEPILOG;
+ }
+ else if (!strncmp (ptr, "call", 4)) {
+ return IPCODE;
+ }
+ else if (ptr[0] == 'j') {
+ return IPCODE;
+ }
+ }
+}
+
+// FrameBase is the ESP value at the entry of a function.
+BOOL GetFrameBase (Register callee[], FrameInfo* pFrame)
+{
+ //char line[256];
+ //char *ptr;
+ INT_PTR dwpushed = 0;
+ //DWORD_PTR IP;
+
+ IPSTATE IpState = GetIpState (callee[EIP].value, pFrame);
+
+ if (pFrame->bEBPFrame)
+ {
+ if (IpState == IPEND || IpState == IPPROLOG1) {
+ pFrame->FrameBase = callee[ESP].value;
+ }
+ else
+ {
+ pFrame->FrameBase = callee[EBP].value+4;
+ }
+ return TRUE;
+ }
+ else
+ {
+ if (IpState == IPEND) {
+ pFrame->FrameBase = callee[ESP].value;
+ return TRUE;
+ }
+
+ DWORD_PTR IPBegin, IPEnd;
+ if (IpState == IPEPILOG) {
+ IPBegin = callee[EIP].value;
+ IPEnd = ~0ul;
+ }
+ else if (IpState == IPPROLOG1) {
+ IPBegin = pFrame->IPStart;
+ IPEnd = callee[EIP].value;
+ }
+ else
+ {
+ IPBegin = pFrame->IPStart;
+ IPEnd = IPBegin + pFrame->Prolog;
+ }
+ GetFrameBaseHelper (IPBegin, IPEnd, dwpushed);
+
+ if (IpState == IPEPILOG) {
+ ExtOut ("stack %d\n", dwpushed);
+ pFrame->FrameBase = callee[ESP].value - dwpushed;
+ return TRUE;
+ }
+
+ CallInfo callInfo;
+ if (GetNextRetAddr (callee[ESP].value + dwpushed,
+ pFrame->StackEnd, callInfo))
+ {
+ pFrame->FrameBase = callInfo.stackPos;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+}
+
+// caller[ESP]: the ESP value when we return to caller.
+void RestoreCallerRegister (Register callee[], Register caller[],
+ FrameInfo *pFrame)
+{
+ if (pFrame->bEBPFrame)
+ {
+ if (callee[ESP].value < pFrame->FrameBase)
+ {
+ SafeReadMemory (pFrame->FrameBase-4, &caller[EBP].value, 4, NULL);
+ }
+ else
+ caller[EBP].value = callee[EBP].value;
+ }
+ else
+ caller[EBP].value = callee[EBP].value;
+
+ caller[EBP].bValid = TRUE;
+ caller[ESP].value = pFrame->FrameBase + 4 + pFrame->argCount;
+ callee[EBP].value = pFrame->FrameBase - sizeof(void*);
+ SafeReadMemory (pFrame->FrameBase, &caller[EIP].value, 4, NULL);
+}
+
+BOOL GetFrameInfoHelper (Register callee[], Register caller[],
+ FrameInfo *pFrame)
+{
+ if (GetFrameBase (callee, pFrame))
+ {
+ RestoreCallerRegister (callee, caller, pFrame);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+// Return TRUE if Frame Info is OK, otherwise FALSE.
+BOOL GetUnmanagedFrameInfo (Register callee[], Register caller[],
+ DumpStackFlag &DSFlag, PFPO_DATA data)
+{
+ FrameInfo Frame;
+ ULONG64 base;
+ g_ExtSymbols->GetModuleByOffset (callee[EIP].value, 0, NULL, &base);
+ Frame.IPStart = data->ulOffStart + (ULONG_PTR)base;
+ Frame.Prolog = data->cbProlog;
+ // Why do we have to do this to make it work?
+ if (Frame.Prolog == 1) {
+ Frame.Prolog = 0;
+ }
+ Frame.bEBPFrame = (data->cbFrame == FRAME_NONFPO);
+ Frame.StackEnd = DSFlag.end;
+ Frame.argCount = data->cdwParams*4;
+
+ return GetFrameInfoHelper (callee, caller, &Frame);
+}
+
+// offsetEBP: offset of stack position where EBP is saved.
+// If EBP is not saved, *offsetEBP = -1 (~0ul);
+BOOL IPReachable (DWORD_PTR IPBegin, DWORD_PTR IP, DWORD *offsetEBP)
+{
+ *offsetEBP = ~0ul;
+ return FALSE;
+}
+
+BOOL HandleEEStub (Register callee[], Register caller[],
+ DumpStackFlag &DSFlag)
+{
+ // EEStub can only be called by IP directory. Let's look for possible caller.
+ CallInfo callInfo;
+ DWORD_PTR stackPos = callee[ESP].value;
+ while (stackPos < DSFlag.end) {
+ if (GetNextRetAddr (stackPos,
+ DSFlag.end, callInfo))
+ {
+ if (callInfo.whereCalled != ~0ul) {
+ DWORD offsetEBP;
+ if (IPReachable (callInfo.whereCalled, callee[EIP].value, &offsetEBP)) {
+ caller[EIP].value = callInfo.retAddr;
+ // TODO: We may have saved EBP.
+ if (offsetEBP == ~0ul) {
+ caller[EBP].value = callee[EBP].value;
+ }
+ else
+ {
+ TADDR offs = TO_TADDR(callInfo.stackPos)-sizeof(PVOID)-offsetEBP;
+ SafeReadMemory (offs, &caller[EBP].value, sizeof(PVOID), NULL);
+ }
+ caller[ESP].value = callInfo.stackPos+sizeof(PVOID);
+ return TRUE;
+ }
+ }
+ stackPos = callInfo.stackPos+sizeof(PVOID);
+ }
+ else
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL HandleByEpilog (Register callee[], Register caller[],
+ DumpStackFlag &DSFlag)
+{
+ return FALSE;
+}
+
+#ifndef FEATURE_PAL
+void RestoreFrameUnmanaged (Register *reg, DWORD_PTR CurIP)
+{
+ char line[256];
+ char *ptr;
+ DWORD_PTR IP = CurIP;
+ INT_PTR value;
+ BOOL bDigit;
+ BOOL bGoodESP = true;
+ RegIndex dest;
+
+ ULONG64 base;
+ g_ExtSymbols->GetModuleByOffset (TO_CDADDR(CurIP), 0, NULL, &base);
+ ULONG64 handle;
+ g_ExtSystem->GetCurrentProcessHandle(&handle);
+ PFPO_DATA data =
+ (PFPO_DATA)SymFunctionTableAccess((HANDLE)handle, CurIP);
+ DWORD_PTR IPBegin = data->ulOffStart + (ULONG_PTR)base;
+
+ if (CurIP - IPBegin <= data->cbProlog)
+ {
+ // We are inside a prolog.
+ // See where we save the callee saved register.
+ // Also how many DWORD's we pushd
+ IP = IPBegin;
+ reg[ESP].stack = 0;
+ reg[ESP].bOnStack = FALSE;
+ reg[EBP].stack = 0;
+ reg[EBP].bOnStack = FALSE;
+ reg[ESI].stack = 0;
+ reg[ESI].bOnStack = FALSE;
+ reg[EDI].stack = 0;
+ reg[EDI].bOnStack = FALSE;
+ reg[EBX].stack = 0;
+ reg[EBX].bOnStack = FALSE;
+
+ while (IP < CurIP)
+ {
+ DisasmAndClean (IP, line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "push ", 5))
+ {
+ reg[ESP].stack += 4;
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == EBP || dest == EBX || dest == ESI || dest == EDI)
+ {
+ reg[dest].bOnStack = TRUE;
+ reg[dest].stack = reg[ESP].stack;
+ }
+ }
+ else if (!strncmp (ptr, "sub ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == ESP)
+ {
+ NextTerm (ptr);
+ char *endptr;
+ reg[ESP].stack += strtoul(ptr, &endptr, 16);;
+ }
+ }
+ }
+
+ DWORD_PTR baseESP = reg[ESP].value + reg[ESP].stack;
+ if (reg[EBP].bOnStack)
+ {
+ move_xp (reg[EBP].value, baseESP-reg[EBP].stack);
+ }
+ if (reg[EBX].bOnStack)
+ {
+ move_xp (reg[EBX].value, baseESP-reg[EBX].stack);
+ }
+ if (reg[ESI].bOnStack)
+ {
+ move_xp (reg[ESI].value, baseESP-reg[ESI].stack);
+ }
+ if (reg[EDI].bOnStack)
+ {
+ move_xp (reg[EDI].value, baseESP-reg[EDI].stack);
+ }
+ move_xp (reg[EIP].value, baseESP);
+ reg[ESP].value = baseESP + 4;
+ return;
+ }
+
+ if (data->cbFrame == FRAME_NONFPO)
+ {
+ // EBP Frame
+ }
+
+ // Look for epilog
+ while (1)
+ {
+ DisasmAndClean (IP, line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "mov ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == ESP)
+ {
+ NextTerm (ptr);
+ if (FindReg(ptr) == EBP)
+ {
+ // We have a EBP frame
+ bGoodESP = true;
+ reg[ESP].value = reg[EBP].value;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "ret", 3))
+ {
+ NextTerm (ptr);
+ // check the value on stack is a return address.
+ DWORD_PTR retAddr;
+ DWORD_PTR whereCalled;
+ move_xp (retAddr, reg[ESP].value);
+ int ESPAdjustCount = 0;
+ while (1)
+ {
+ g_targetMachine->IsReturnAddress(retAddr, &whereCalled);
+ if (whereCalled)
+ break;
+ ESPAdjustCount ++;
+ reg[ESP].value += 4;
+ move_xp (retAddr, reg[ESP].value);
+ }
+ reg[EIP].value = retAddr;
+ if (ESPAdjustCount)
+ {
+ ESPAdjustCount *= 4;
+ }
+ if (reg[EBX].bOnStack)
+ {
+ reg[EBX].stack += ESPAdjustCount;
+ move_xp (reg[EBX].value, reg[EBX].stack);
+ }
+ if (reg[ESI].bOnStack)
+ {
+ reg[ESI].stack += ESPAdjustCount;
+ move_xp (reg[ESI].value, reg[EBX].stack);
+ }
+ if (reg[EDI].bOnStack)
+ {
+ reg[EDI].stack += ESPAdjustCount;
+ move_xp (reg[EDI].value, reg[EBX].stack);
+ }
+
+ reg[ESP].value += 4;
+ if (ptr[0] != '\0')
+ {
+ FindSrc (ptr, reg, value, bDigit);
+ reg[ESP].value += value;
+ }
+ break;
+ }
+ else if (!strncmp (ptr, "pop ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == EBP || dest == EBX || dest == ESI || dest == EDI)
+ {
+ reg[dest].stack = reg[ESP].value;
+ reg[dest].bOnStack = TRUE;
+ }
+ reg[ESP].value += 4;
+ }
+ else if (!strncmp (ptr, "add ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == ESP)
+ {
+ NextTerm (ptr);
+ FindSrc (ptr, reg, value, bDigit);
+ reg[ESP].value += value;
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5))
+ {
+ // assume we do not have a good value on ESP.
+ // We could go into the call and find out number of pushed args.
+ bGoodESP = FALSE;
+ }
+ }
+
+ // Look for prolog
+}
+#endif // !FEATURE_PAL
+
+#elif defined(_AMD64_)
+
+
+#endif // !_X86_
diff --git a/src/ToolBox/SOS/Strike/dllsext.cpp b/src/ToolBox/SOS/Strike/dllsext.cpp
new file mode 100644
index 0000000000..757a04c91f
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/dllsext.cpp
@@ -0,0 +1,278 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#include "strike.h"
+#include "data.h"
+#include "util.h"
+#include "platformspecific.h"
+
+typedef struct _PRIVATE_LDR_DATA_TABLE_ENTRY {
+ LIST_ENTRY InLoadOrderLinks;
+ LIST_ENTRY InMemoryOrderLinks;
+ LIST_ENTRY InInitializationOrderLinks;
+ PVOID DllBase;
+ PVOID EntryPoint;
+ ULONG SizeOfImage;
+ UNICODE_STRING FullDllName;
+ UNICODE_STRING BaseDllName;
+ ULONG Flags;
+ USHORT LoadCount;
+ USHORT TlsIndex;
+ union _LDR_DATA_TABLE_ENTRY_UNION1 { //DevDiv LKG RC Changes: Added union name to avoid warning C4408
+ LIST_ENTRY HashLinks;
+ struct _LDR_DATA_TABLE_ENTRY_STRUCT1 { //DevDiv LKG RC Changes: Added struct name to avoid warning C4201
+ PVOID SectionPointer;
+ ULONG CheckSum;
+ };
+ };
+ union _LDR_DATA_TABLE_ENTRY_UNION2 { //DevDiv LKG RC Changes: Added union name to avoid warning C4408
+ struct _LDR_DATA_TABLE_ENTRY_STRUCT2 { //DevDiv LKG RC Changes: Added struct name to avoid warning C4201
+ ULONG TimeDateStamp;
+ };
+ struct _LDR_DATA_TABLE_ENTRY_STRUCT3 { //DevDiv LKG RC Changes: Added struct name to avoid warning C4201
+ PVOID LoadedImports;
+ };
+ };
+ struct _ACTIVATION_CONTEXT * EntryPointActivationContext;
+
+ PVOID PatchInformation;
+
+} PRIVATE_LDR_DATA_TABLE_ENTRY, *PRIVATE_PLDR_DATA_TABLE_ENTRY;
+
+
+#ifndef FEATURE_PAL
+static void DllsNameFromPeb(
+ ULONG_PTR addrContaining,
+ __out_ecount (MAX_LONGPATH) WCHAR *dllName
+ )
+{
+ ULONG64 ProcessPeb;
+ g_ExtSystem->GetCurrentProcessPeb (&ProcessPeb);
+
+ ULONG64 pLdrEntry;
+ ULONG64 PebLdrAddress;
+ ULONG64 Next;
+ ULONG64 OrderModuleListStart;
+
+ //
+ // Capture PebLdrData
+ //
+
+ static ULONG Offset_Ldr = -1;
+ if (Offset_Ldr == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase))
+ && SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "Ldr", &Offset_Ldr)))
+ Offset_Ldr = -1;
+ }
+ }
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_Ldr == -1)
+ Offset_Ldr = offsetof (DT_PEB, Ldr);
+
+ DT_PEB peb = {0};
+ if (FAILED(g_ExtData->ReadVirtual(ProcessPeb+Offset_Ldr, &peb.Ldr,
+ sizeof(peb.Ldr), NULL)))
+ {
+ ExtOut ( " Unable to read PEB_LDR_DATA address at %p\n", SOS_PTR(ProcessPeb+Offset_Ldr));
+ return;
+ }
+
+ PebLdrAddress = (ULONG64)peb.Ldr;
+
+ //
+ // Walk through the loaded module table and display all ldr data
+ //
+
+ static ULONG Offset_ModuleList = -1;
+ if (Offset_ModuleList == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase))
+ && SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB_LDR_DATA",
+ &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "InMemoryOrderModuleList",
+ &Offset_ModuleList)))
+ Offset_ModuleList = -1;
+ }
+ }
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_ModuleList == -1)
+ Offset_ModuleList = offsetof (DT_PEB_LDR_DATA, InMemoryOrderModuleList);
+
+ OrderModuleListStart = PebLdrAddress + Offset_ModuleList;
+ DT_PEB_LDR_DATA Ldr = {0};
+ if (FAILED(g_ExtData->ReadVirtual(OrderModuleListStart,
+ &Ldr.InMemoryOrderModuleList,
+ sizeof(Ldr.InMemoryOrderModuleList),
+ NULL)))
+ {
+ ExtOut ( " Unable to read InMemoryOrderModuleList address at %p\n", SOS_PTR(OrderModuleListStart));
+ return;
+ }
+ Next = (ULONG64)Ldr.InMemoryOrderModuleList.Flink;
+
+ static ULONG Offset_OrderLinks = -1;
+ static ULONG Offset_FullDllName = -1;
+ static ULONG Offset_DllBase = -1;
+ static ULONG Offset_SizeOfImage = -1;
+ if (Offset_OrderLinks == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase))
+ && SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "LDR_DATA_TABLE_ENTRY",
+ &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "InMemoryOrderLinks",
+ &Offset_OrderLinks)))
+ Offset_OrderLinks = -1;
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "FullDllName",
+ &Offset_FullDllName)))
+ Offset_FullDllName = -1;
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "DllBase",
+ &Offset_DllBase)))
+ Offset_DllBase = -1;
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "SizeOfImage",
+ &Offset_SizeOfImage)))
+ Offset_SizeOfImage = -1;
+ }
+ }
+
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_OrderLinks == -1 || Offset_OrderLinks == 0)
+ {
+ Offset_OrderLinks = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ InMemoryOrderLinks);
+ Offset_FullDllName = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ FullDllName);
+ Offset_DllBase = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ DllBase);
+ Offset_SizeOfImage = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ SizeOfImage);
+ }
+
+ _UNICODE_STRING FullDllName;
+ __try {
+ while (Next != OrderModuleListStart) {
+ if (IsInterrupt())
+ return;
+
+ pLdrEntry = Next - Offset_OrderLinks;
+
+ //
+ // Capture LdrEntry
+ //
+ if (FAILED(g_ExtData->ReadVirtual(pLdrEntry + Offset_FullDllName,
+ &FullDllName,
+ sizeof(FullDllName),
+ NULL)))
+ {
+ ExtOut ( " Unable to read FullDllName address at %p\n",
+ pLdrEntry + Offset_FullDllName);
+ return;
+ }
+ ZeroMemory( dllName, MAX_LONGPATH * sizeof (WCHAR) );
+ if (FAILED(g_ExtData->ReadVirtual((ULONG64)FullDllName.Buffer,
+ dllName,
+ MAX_LONGPATH < FullDllName.Length ? MAX_LONGPATH : FullDllName.Length,
+ NULL)))
+ {
+#if 0
+ ExtOut ( " Unable to read FullDllName.Buffer address at %p\n",
+ SOS_PTR(FullDllName.Buffer));
+#endif
+ ZeroMemory( dllName, MAX_LONGPATH * sizeof (WCHAR) );
+ }
+
+ //
+ // Dump the ldr entry data
+ // (dump all the entries if no containing address specified)
+ //
+ PRIVATE_LDR_DATA_TABLE_ENTRY LdrEntry = {0};
+ if (SUCCEEDED(g_ExtData->ReadVirtual(pLdrEntry + Offset_DllBase,
+ &LdrEntry.DllBase,
+ sizeof(LdrEntry.DllBase),
+ NULL))
+ &&
+ SUCCEEDED(g_ExtData->ReadVirtual(pLdrEntry + Offset_SizeOfImage,
+ &LdrEntry.SizeOfImage,
+ sizeof(LdrEntry.SizeOfImage),
+ NULL))
+ )
+ {
+ if (((ULONG_PTR)LdrEntry.DllBase <= addrContaining) &&
+ (addrContaining <= (ULONG_PTR)LdrEntry.DllBase + (ULONG_PTR)LdrEntry.SizeOfImage))
+ break;
+ }
+
+ ZeroMemory( dllName, MAX_LONGPATH * sizeof (WCHAR) );
+ if (FAILED(g_ExtData->ReadVirtual(pLdrEntry + Offset_OrderLinks,
+ &LdrEntry.InMemoryOrderLinks,
+ sizeof(LdrEntry.InMemoryOrderLinks),
+ NULL)))
+ break;
+
+ Next = (ULONG64)LdrEntry.InMemoryOrderLinks.Flink;
+ }
+ } __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ ExtOut ("exception during reading PEB\n");
+ return;
+ }
+}
+#endif
+
+HRESULT
+DllsName(
+ ULONG_PTR addrContaining,
+ __out_ecount (MAX_LONGPATH) WCHAR *dllName
+ )
+{
+ dllName[0] = L'\0';
+
+ ULONG Index;
+ ULONG64 base;
+ HRESULT hr = g_ExtSymbols->GetModuleByOffset(addrContaining, 0, &Index, &base);
+ if (FAILED(hr))
+ return hr;
+
+ CHAR name[MAX_LONGPATH+1];
+ ULONG length;
+
+ hr = g_ExtSymbols->GetModuleNames(Index,base,name,MAX_LONGPATH,&length,NULL,0,NULL,NULL,0,NULL);
+
+ if (SUCCEEDED(hr))
+ {
+ MultiByteToWideChar (CP_ACP,0,name,-1,dllName,MAX_LONGPATH);
+ }
+
+#ifndef FEATURE_PAL
+ if (_wcsrchr (dllName, '\\') == NULL) {
+ DllsNameFromPeb (addrContaining,dllName);
+ }
+#endif
+
+ return hr;
+}
diff --git a/src/ToolBox/SOS/Strike/eeheap.cpp b/src/ToolBox/SOS/Strike/eeheap.cpp
new file mode 100644
index 0000000000..ac41e2deb6
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/eeheap.cpp
@@ -0,0 +1,1913 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#include <assert.h>
+#include "sos.h"
+#include "safemath.h"
+
+
+// This is the increment for the segment lookup data
+const int nSegLookupStgIncrement = 100;
+
+#define CCH_STRING_PREFIX_SUMMARY 64
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to update GC heap statistics. *
+* *
+\**********************************************************************/
+void HeapStat::Add(DWORD_PTR aData, DWORD aSize)
+{
+ if (head == 0)
+ {
+ head = new Node();
+ if (head == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+
+ if (bHasStrings)
+ {
+ size_t capacity_pNew = _wcslen((WCHAR*)aData) + 1;
+ WCHAR *pNew = new WCHAR[capacity_pNew];
+ if (pNew == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+ wcscpy_s(pNew, capacity_pNew, (WCHAR*)aData);
+ aData = (DWORD_PTR)pNew;
+ }
+
+ head->data = aData;
+ }
+ Node *walk = head;
+ int cmp = 0;
+
+ for (;;)
+ {
+ if (IsInterrupt())
+ return;
+
+ cmp = CompareData(aData, walk->data);
+
+ if (cmp == 0)
+ break;
+
+ if (cmp < 0)
+ {
+ if (walk->left == NULL)
+ break;
+ walk = walk->left;
+ }
+ else
+ {
+ if (walk->right == NULL)
+ break;
+ walk = walk->right;
+ }
+ }
+
+ if (cmp == 0)
+ {
+ walk->count ++;
+ walk->totalSize += aSize;
+ }
+ else
+ {
+ Node *node = new Node();
+ if (node == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+
+ if (bHasStrings)
+ {
+ size_t capacity_pNew = _wcslen((WCHAR*)aData) + 1;
+ WCHAR *pNew = new WCHAR[capacity_pNew];
+ if (pNew == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+ wcscpy_s(pNew, capacity_pNew, (WCHAR*)aData);
+ aData = (DWORD_PTR)pNew;
+ }
+
+ node->data = aData;
+ node->totalSize = aSize;
+ node->count ++;
+
+ if (cmp < 0)
+ {
+ walk->left = node;
+ }
+ else
+ {
+ walk->right = node;
+ }
+ }
+}
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function compares two nodes in the tree. *
+* *
+\**********************************************************************/
+int HeapStat::CompareData(DWORD_PTR d1, DWORD_PTR d2)
+{
+ if (bHasStrings)
+ return _wcscmp((WCHAR*)d1, (WCHAR*)d2);
+
+ if (d1 > d2)
+ return 1;
+
+ if (d1 < d2)
+ return -1;
+
+ return 0;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to sort all entries in the heap stat. *
+* *
+\**********************************************************************/
+void HeapStat::Sort ()
+{
+ Node *root = head;
+ head = NULL;
+ ReverseLeftMost (root);
+
+ Node *sortRoot = NULL;
+ while (head)
+ {
+ Node *tmp = head;
+ head = head->left;
+ if (tmp->right)
+ ReverseLeftMost (tmp->right);
+ // add tmp
+ tmp->right = NULL;
+ tmp->left = NULL;
+ SortAdd (sortRoot, tmp);
+ }
+ head = sortRoot;
+
+ Linearize();
+
+ //reverse the order
+ root = head;
+ head = NULL;
+ sortRoot = NULL;
+ while (root)
+ {
+ Node *tmp = root->right;
+ root->left = NULL;
+ root->right = NULL;
+ LinearAdd (sortRoot, root);
+ root = tmp;
+ }
+ head = sortRoot;
+}
+
+void HeapStat::Linearize()
+{
+ // Change binary tree to a linear tree
+ Node *root = head;
+ head = NULL;
+ ReverseLeftMost (root);
+ Node *sortRoot = NULL;
+ while (head)
+ {
+ Node *tmp = head;
+ head = head->left;
+ if (tmp->right)
+ ReverseLeftMost (tmp->right);
+ // add tmp
+ tmp->right = NULL;
+ tmp->left = NULL;
+ LinearAdd (sortRoot, tmp);
+ }
+ head = sortRoot;
+ fLinear = TRUE;
+}
+
+void HeapStat::ReverseLeftMost (Node *root)
+{
+ while (root)
+ {
+ Node *tmp = root->left;
+ root->left = head;
+ head = root;
+ root = tmp;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to help to sort heap stat. *
+* *
+\**********************************************************************/
+void HeapStat::SortAdd (Node *&root, Node *entry)
+{
+ if (root == NULL)
+ {
+ root = entry;
+ }
+ else
+ {
+ Node *parent = root;
+ Node *ptr = root;
+ while (ptr)
+ {
+ parent = ptr;
+ if (ptr->totalSize < entry->totalSize)
+ ptr = ptr->right;
+ else
+ ptr = ptr->left;
+ }
+ if (parent->totalSize < entry->totalSize)
+ parent->right = entry;
+ else
+ parent->left = entry;
+ }
+}
+
+void HeapStat::LinearAdd(Node *&root, Node *entry)
+{
+ if (root == NULL)
+ {
+ root = entry;
+ }
+ else
+ {
+ entry->right = root;
+ root = entry;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to print GC heap statistics. *
+* *
+\**********************************************************************/
+void HeapStat::Print(const char* label /* = NULL */)
+{
+ if (label == NULL)
+ {
+ label = "Statistics:\n";
+ }
+ ExtOut(label);
+ if (bHasStrings)
+ ExtOut("%8s %12s %s\n", "Count", "TotalSize", "String Value");
+ else
+ ExtOut("%" POINTERSIZE "s %8s %12s %s\n","MT", "Count", "TotalSize", "Class Name");
+
+ Node *root = head;
+ int ncount = 0;
+ while (root)
+ {
+ if (IsInterrupt())
+ return;
+
+ ncount += root->count;
+
+ if (bHasStrings)
+ {
+ ExtOut("%8d %12I64u \"%S\"\n", root->count, (unsigned __int64)root->totalSize, root->data);
+ }
+ else
+ {
+ DMLOut("%s %8d %12I64u ", DMLDumpHeapMT(root->data), root->count, (unsigned __int64)root->totalSize);
+ if (IsMTForFreeObj(root->data))
+ {
+ ExtOut("%9s\n", "Free");
+ }
+ else
+ {
+ wcscpy_s(g_mdName, mdNameLen, W("UNKNOWN"));
+ NameForMT_s((DWORD_PTR) root->data, g_mdName, mdNameLen);
+ ExtOut("%S\n", g_mdName);
+ }
+ }
+ root = root->right;
+
+ }
+ ExtOut ("Total %d objects\n", ncount);
+}
+
+void HeapStat::Delete()
+{
+ if (head == NULL)
+ return;
+
+ // Ensure the data structure is already linearized.
+ if (!fLinear)
+ Linearize();
+
+ while (head)
+ {
+ // The list is linearized on such that the left node is always null.
+ Node *tmp = head;
+ head = head->right;
+
+ if (bHasStrings)
+ delete[] ((WCHAR*)tmp->data);
+ delete tmp;
+ }
+
+ // return to default state
+ bHasStrings = FALSE;
+ fLinear = FALSE;
+}
+
+// -----------------------------------------------------------------------
+//
+// MethodTableCache implementation
+//
+// Used during heap traversals for quick object size computation
+//
+MethodTableInfo* MethodTableCache::Lookup (DWORD_PTR aData)
+{
+ Node** addHere = &head;
+ if (head != 0) {
+ Node *walk = head;
+ int cmp = 0;
+
+ for (;;)
+ {
+ cmp = CompareData(aData, walk->data);
+
+ if (cmp == 0)
+ return &walk->info;
+
+ if (cmp < 0)
+ {
+ if (walk->left == NULL)
+ {
+ addHere = &walk->left;
+ break;
+ }
+ walk = walk->left;
+ }
+ else
+ {
+ if (walk->right == NULL)
+ {
+ addHere = &walk->right;
+ break;
+ }
+ walk = walk->right;
+ }
+ }
+ }
+ Node* newNode = new Node(aData);
+ if (newNode == NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+ *addHere = newNode;
+ return &newNode->info;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function compares two nodes in the tree. *
+* *
+\**********************************************************************/
+int MethodTableCache::CompareData(DWORD_PTR d1, DWORD_PTR d2)
+{
+ if (d1 > d2)
+ return 1;
+
+ if (d1 < d2)
+ return -1;
+
+ return 0;
+}
+
+void MethodTableCache::ReverseLeftMost (Node *root)
+{
+ if (root)
+ {
+ if (root->left) ReverseLeftMost(root->left);
+ if (root->right) ReverseLeftMost(root->right);
+ delete root;
+ }
+}
+
+void MethodTableCache::Clear()
+{
+ Node *root = head;
+ head = NULL;
+ ReverseLeftMost (root);
+}
+
+MethodTableCache g_special_mtCache;
+
+size_t Align (size_t nbytes)
+{
+ return (nbytes + ALIGNCONST) & ~ALIGNCONST;
+}
+
+size_t AlignLarge(size_t nbytes)
+{
+ return (nbytes + ALIGNCONSTLARGE) & ~ALIGNCONSTLARGE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Print the gc heap info. *
+* *
+\**********************************************************************/
+void GCPrintGenerationInfo(const DacpGcHeapDetails &heap)
+{
+ UINT n;
+ for (n = 0; n <= GetMaxGeneration(); n ++)
+ {
+ if (IsInterrupt())
+ return;
+ ExtOut("generation %d starts at 0x%p\n",
+ n, SOS_PTR(heap.generation_table[n].allocation_start));
+ }
+
+ // We also need to look at the gen0 alloc context.
+ ExtOut("ephemeral segment allocation context: ");
+ if (heap.generation_table[0].allocContextPtr)
+ {
+ ExtOut("(0x%p, 0x%p)\n",
+ SOS_PTR(heap.generation_table[0].allocContextPtr),
+ SOS_PTR(heap.generation_table[0].allocContextLimit + Align(min_obj_size)));
+ }
+ else
+ {
+ ExtOut("none\n");
+ }
+}
+
+
+void GCPrintSegmentInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size)
+{
+ DWORD_PTR dwAddrSeg;
+ DacpHeapSegmentData segment;
+
+ dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].start_segment;
+ total_size = 0;
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (dwAddrSeg != (DWORD_PTR)heap.generation_table[0].start_segment)
+ {
+ if (IsInterrupt())
+ return;
+ if (segment.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
+ return;
+ }
+ ExtOut("%p %p %p 0x%" POINTERSIZE_TYPE "x(%" POINTERSIZE_TYPE "d)\n", SOS_PTR(dwAddrSeg),
+ SOS_PTR(segment.mem), SOS_PTR(segment.allocated),
+ (ULONG_PTR)(segment.allocated - segment.mem),
+ (ULONG_PTR)(segment.allocated - segment.mem));
+ total_size += (DWORD_PTR) (segment.allocated - segment.mem);
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ }
+
+ if (segment.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
+ return;
+ }
+
+ DWORD_PTR end = (DWORD_PTR)heap.alloc_allocated;
+ ExtOut("%p %p %p 0x%" POINTERSIZE_TYPE "x(%" POINTERSIZE_TYPE "d)\n", SOS_PTR(dwAddrSeg),
+ SOS_PTR(segment.mem), SOS_PTR(end),
+ (ULONG_PTR)(end - (DWORD_PTR)segment.mem),
+ (ULONG_PTR)(end - (DWORD_PTR)segment.mem));
+
+ total_size += end - (DWORD_PTR)segment.mem;
+
+}
+
+
+void GCPrintLargeHeapSegmentInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size)
+{
+ DWORD_PTR dwAddrSeg;
+ DacpHeapSegmentData segment;
+ dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].start_segment;
+
+ // total_size = 0;
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (dwAddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ return;
+ if (segment.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
+ return;
+ }
+ ExtOut("%p %p %p 0x%" POINTERSIZE_TYPE "x(%" POINTERSIZE_TYPE "d)\n", SOS_PTR(dwAddrSeg),
+ SOS_PTR(segment.mem), SOS_PTR(segment.allocated),
+ (ULONG_PTR)(segment.allocated - segment.mem),
+ segment.allocated - segment.mem);
+ total_size += (DWORD_PTR) (segment.allocated - segment.mem);
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ }
+}
+
+void GCHeapInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size)
+{
+ GCPrintGenerationInfo(heap);
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", "segment", "begin", "allocated", "size");
+ GCPrintSegmentInfo(heap, total_size);
+ ExtOut("Large object heap starts at 0x%p\n",
+ SOS_PTR(heap.generation_table[GetMaxGeneration()+1].allocation_start));
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", "segment", "begin", "allocated", "size");
+ GCPrintLargeHeapSegmentInfo(heap,total_size);
+}
+
+BOOL GCObjInGeneration(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ const TADDR_SEGINFO& /*seg*/, int& gen, TADDR_RANGE& allocCtx)
+{
+ gen = -1;
+ for (UINT n = 0; n <= GetMaxGeneration(); n ++)
+ {
+ if (taddrObj >= TO_TADDR(heap.generation_table[n].allocation_start))
+ {
+ gen = n;
+ break;
+ }
+ }
+
+ // We also need to look at the gen0 alloc context.
+ if (heap.generation_table[0].allocContextPtr
+ && taddrObj >= TO_TADDR(heap.generation_table[0].allocContextPtr)
+ && taddrObj < TO_TADDR(heap.generation_table[0].allocContextLimit) + Align(min_obj_size))
+ {
+ gen = 0;
+ allocCtx.start = (TADDR)heap.generation_table[0].allocContextPtr;
+ allocCtx.end = (TADDR)heap.generation_table[0].allocContextLimit;
+ }
+ else
+ {
+ allocCtx.start = allocCtx.end = 0;
+ }
+ return (gen != -1);
+}
+
+
+BOOL GCObjInSegment(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ TADDR_SEGINFO& rngSeg, int& gen, TADDR_RANGE& allocCtx)
+{
+ TADDR taddrSeg;
+ DacpHeapSegmentData dacpSeg;
+
+ taddrSeg = (TADDR)heap.generation_table[GetMaxGeneration()].start_segment;
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (taddrSeg != (TADDR)heap.generation_table[0].start_segment)
+ {
+ if (IsInterrupt())
+ return FALSE;
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+ if (taddrObj >= TO_TADDR(dacpSeg.mem) && taddrObj < TO_TADDR(dacpSeg.allocated))
+ {
+ rngSeg.segAddr = (TADDR)dacpSeg.segmentAddr;
+ rngSeg.start = (TADDR)dacpSeg.mem;
+ rngSeg.end = (TADDR)dacpSeg.allocated;
+ gen = 2;
+ allocCtx.start = allocCtx.end = 0;
+ return TRUE;
+ }
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+
+ // the ephemeral segment
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+
+ if (taddrObj >= TO_TADDR(dacpSeg.mem) && taddrObj < TO_TADDR(heap.alloc_allocated))
+ {
+ if (GCObjInGeneration(taddrObj, heap, rngSeg, gen, allocCtx))
+ {
+ rngSeg.segAddr = (TADDR)dacpSeg.segmentAddr;
+ rngSeg.start = (TADDR)dacpSeg.mem;
+ rngSeg.end = (TADDR)heap.alloc_allocated;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL GCObjInLargeSegment(TADDR taddrObj, const DacpGcHeapDetails &heap, TADDR_SEGINFO& rngSeg)
+{
+ TADDR taddrSeg;
+ DacpHeapSegmentData dacpSeg;
+ taddrSeg = (TADDR)heap.generation_table[GetMaxGeneration()+1].start_segment;
+
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (taddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ return FALSE;
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+ if (taddrObj >= TO_TADDR(dacpSeg.mem) && taddrObj && taddrObj < TO_TADDR(dacpSeg.allocated))
+ {
+ rngSeg.segAddr = (TADDR)dacpSeg.segmentAddr;
+ rngSeg.start = (TADDR)dacpSeg.mem;
+ rngSeg.end = (TADDR)dacpSeg.allocated;
+ return TRUE;
+ }
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+ return FALSE;
+}
+
+BOOL GCObjInHeap(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ TADDR_SEGINFO& rngSeg, int& gen, TADDR_RANGE& allocCtx, BOOL &bLarge)
+{
+ if (GCObjInSegment(taddrObj, heap, rngSeg, gen, allocCtx))
+ {
+ bLarge = FALSE;
+ return TRUE;
+ }
+ if (GCObjInLargeSegment(taddrObj, heap, rngSeg))
+ {
+ bLarge = TRUE;
+ gen = GetMaxGeneration()+1;
+ allocCtx.start = allocCtx.end = 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#ifndef FEATURE_PAL
+// this function updates genUsage to reflect statistics from the range defined by [start, end)
+void GCGenUsageStats(TADDR start, TADDR end, const std::unordered_set<TADDR> &liveObjs,
+ const DacpGcHeapDetails &heap, BOOL bLarge, const AllocInfo *pAllocInfo, GenUsageStat *genUsage)
+{
+ // if this is an empty segment or generation return
+ if (start >= end)
+ {
+ return;
+ }
+
+ // otherwise it should start with a valid object
+ _ASSERTE(sos::IsObject(start));
+
+ // update the "allocd" field
+ genUsage->allocd += end - start;
+
+ size_t objSize = 0;
+ for (TADDR taddrObj = start; taddrObj < end; taddrObj += objSize)
+ {
+ TADDR taddrMT;
+
+ move_xp(taddrMT, taddrObj);
+ taddrMT &= ~3;
+
+ // skip allocation contexts
+ if (!bLarge)
+ {
+ // Is this the beginning of an allocation context?
+ int i;
+ for (i = 0; i < pAllocInfo->num; i ++)
+ {
+ if (taddrObj == (TADDR)pAllocInfo->array[i].alloc_ptr)
+ {
+ ExtDbgOut("Skipping allocation context: [%#p-%#p)\n",
+ SOS_PTR(pAllocInfo->array[i].alloc_ptr), SOS_PTR(pAllocInfo->array[i].alloc_limit));
+ taddrObj =
+ (TADDR)pAllocInfo->array[i].alloc_limit + Align(min_obj_size);
+ break;
+ }
+ }
+ if (i < pAllocInfo->num)
+ {
+ // we already adjusted taddrObj, so reset objSize
+ objSize = 0;
+ continue;
+ }
+
+ // We also need to look at the gen0 alloc context.
+ if (taddrObj == (DWORD_PTR) heap.generation_table[0].allocContextPtr)
+ {
+ taddrObj = (DWORD_PTR) heap.generation_table[0].allocContextLimit + Align(min_obj_size);
+ // we already adjusted taddrObj, so reset objSize
+ objSize = 0;
+ continue;
+ }
+
+ // Are we at the end of gen 0?
+ if (taddrObj == end - Align(min_obj_size))
+ {
+ objSize = 0;
+ break;
+ }
+ }
+
+ BOOL bContainsPointers;
+ BOOL bMTOk = GetSizeEfficient(taddrObj, taddrMT, bLarge, objSize, bContainsPointers);
+ if (!bMTOk)
+ {
+ ExtErr("bad object: %#p - bad MT %#p\n", SOS_PTR(taddrObj), SOS_PTR(taddrMT));
+ // set objSize to size_t to look for the next valid MT
+ objSize = sizeof(TADDR);
+ continue;
+ }
+
+ // at this point we should have a valid objSize, and there whould be no
+ // integer overflow when moving on to next object in heap
+ _ASSERTE(objSize > 0 && taddrObj < taddrObj + objSize);
+ if (objSize == 0 || taddrObj > taddrObj + objSize)
+ {
+ break;
+ }
+
+ if (IsMTForFreeObj(taddrMT))
+ {
+ genUsage->freed += objSize;
+ }
+ else if (!(liveObjs.empty()) && liveObjs.find(taddrObj) == liveObjs.end())
+ {
+ genUsage->unrooted += objSize;
+ }
+ }
+}
+#endif // !FEATURE_PAL
+
+BOOL GCHeapUsageStats(const DacpGcHeapDetails& heap, BOOL bIncUnreachable, HeapUsageStat *hpUsage)
+{
+ memset(hpUsage, 0, sizeof(*hpUsage));
+
+ AllocInfo allocInfo;
+ allocInfo.Init();
+
+ // 1. Start with small object segments
+ TADDR taddrSeg;
+ DacpHeapSegmentData dacpSeg;
+
+ taddrSeg = (TADDR)heap.generation_table[GetMaxGeneration()].start_segment;
+
+#ifndef FEATURE_PAL
+ // this will create the bitmap of rooted objects only if bIncUnreachable is true
+ GCRootImpl gcroot;
+ std::unordered_set<TADDR> emptyLiveObjs;
+ const std::unordered_set<TADDR> &liveObjs = (bIncUnreachable ? gcroot.GetLiveObjects() : emptyLiveObjs);
+
+ // 1a. enumerate all non-ephemeral segments
+ while (taddrSeg != (TADDR)heap.generation_table[0].start_segment)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtErr("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+ GCGenUsageStats((TADDR)dacpSeg.mem, (TADDR)dacpSeg.allocated, liveObjs, heap, FALSE, &allocInfo, &hpUsage->genUsage[2]);
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+#endif
+
+ // 1b. now handle the ephemeral segment
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtErr("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+
+ TADDR endGen = TO_TADDR(heap.alloc_allocated);
+ for (UINT n = 0; n <= GetMaxGeneration(); n ++)
+ {
+ TADDR startGen;
+ // gen 2 starts at the beginning of the segment
+ if (n == GetMaxGeneration())
+ {
+ startGen = TO_TADDR(dacpSeg.mem);
+ }
+ else
+ {
+ startGen = TO_TADDR(heap.generation_table[n].allocation_start);
+ }
+
+#ifndef FEATURE_PAL
+ GCGenUsageStats(startGen, endGen, liveObjs, heap, FALSE, &allocInfo, &hpUsage->genUsage[n]);
+#endif
+ endGen = startGen;
+ }
+
+ // 2. Now process LOH
+ taddrSeg = (TADDR) heap.generation_table[GetMaxGeneration()+1].start_segment;
+ while (taddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtErr("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+
+#ifndef FEATURE_PAL
+ GCGenUsageStats((TADDR) dacpSeg.mem, (TADDR) dacpSeg.allocated, liveObjs, heap, TRUE, NULL, &hpUsage->genUsage[3]);
+#endif
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+
+ return TRUE;
+}
+
+DWORD GetNumComponents(TADDR obj)
+{
+ // The number of components is always the second pointer in the object.
+ DWORD Value = NULL;
+ HRESULT hr = MOVE(Value, obj + sizeof(size_t));
+
+ // If we fail to read out the number of components, let's assume 0 so we don't try to
+ // read further data from the object.
+ if (FAILED(hr))
+ return 0;
+
+ // The component size on a String does not contain the trailing NULL character,
+ // so we must add that ourselves.
+ if(IsStringObject(obj))
+ return Value+1;
+
+ return Value;
+}
+
+BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj,
+ DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers)
+{
+ // Remove lower bits in case we are in mark phase
+ dwAddrMethTable = dwAddrMethTable & ~3;
+ MethodTableInfo* info = g_special_mtCache.Lookup(dwAddrMethTable);
+ if (!info->IsInitialized()) // An uninitialized entry
+ {
+ // this is the first time we see this method table, so we need to get the information
+ // from the target
+ DacpMethodTableData dmtd;
+ // see code:ClrDataAccess::RequestMethodTableData for details
+ if (dmtd.Request(g_sos,dwAddrMethTable) != S_OK)
+ return FALSE;
+
+ info->BaseSize = dmtd.BaseSize;
+ info->ComponentSize = dmtd.ComponentSize;
+ info->bContainsPointers = dmtd.bContainsPointers;
+ }
+
+ bContainsPointers = info->bContainsPointers;
+ s = info->BaseSize;
+
+ if (info->ComponentSize)
+ {
+ // this is an array, so the size has to include the size of the components. We read the number
+ // of components from the target and multiply by the component size to get the size.
+ s += info->ComponentSize*GetNumComponents(dwAddrCurrObj);
+ }
+
+ // On x64 we do an optimization to save 4 bytes in almost every string we create
+ // IMPORTANT: This cannot be done in ObjectSize, which is a wrapper to this function,
+ // because we must Align only after these changes are made
+#ifdef _TARGET_WIN64_
+ // Pad to min object size if necessary
+ if (s < min_obj_size)
+ s = min_obj_size;
+#endif // _TARGET_WIN64_
+
+ s = (bLarge ? AlignLarge(s) : Align (s));
+ return TRUE;
+}
+
+// This function expects stat to be valid, and ready to get statistics.
+void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort)
+{
+ DWORD_PTR dwAddr=0;
+ UINT m;
+
+ if (!bShort)
+ {
+ for (m = 0; m <= GetMaxGeneration(); m ++)
+ {
+ if (IsInterrupt())
+ return;
+
+ ExtOut("generation %d has %d finalizable objects ", m,
+ (SegQueueLimit(heapDetails,gen_segment(m)) - SegQueue(heapDetails,gen_segment(m))) / sizeof(size_t));
+
+ ExtOut ("(%p->%p)\n",
+ SOS_PTR(SegQueue(heapDetails,gen_segment(m))),
+ SOS_PTR(SegQueueLimit(heapDetails,gen_segment(m))));
+ }
+ }
+#ifndef FEATURE_PAL
+ if (bAllReady)
+ {
+ if (!bShort)
+ {
+ ExtOut ("Finalizable but not rooted: ");
+ }
+
+ TADDR rngStart = (TADDR)SegQueue(heapDetails, gen_segment(GetMaxGeneration()));
+ TADDR rngEnd = (TADDR)SegQueueLimit(heapDetails, gen_segment(0));
+
+ PrintNotReachableInRange(rngStart, rngEnd, TRUE, bAllReady ? stat : NULL, bShort);
+ }
+#endif
+
+ if (!bShort)
+ {
+ ExtOut ("Ready for finalization %d objects ",
+ (SegQueueLimit(heapDetails,FinalizerListSeg)-SegQueue(heapDetails,CriticalFinalizerListSeg)) / sizeof(size_t));
+ ExtOut ("(%p->%p)\n",
+ SOS_PTR(SegQueue(heapDetails,CriticalFinalizerListSeg)),
+ SOS_PTR(SegQueueLimit(heapDetails,FinalizerListSeg)));
+ }
+
+ // if bAllReady we only count objects that are ready for finalization,
+ // otherwise we count all finalizable objects.
+ TADDR taddrLowerLimit = (bAllReady ? (TADDR)SegQueue(heapDetails, CriticalFinalizerListSeg) :
+ (DWORD_PTR)SegQueue(heapDetails, gen_segment(GetMaxGeneration())));
+ for (dwAddr = taddrLowerLimit;
+ dwAddr < (DWORD_PTR)SegQueueLimit(heapDetails, FinalizerListSeg);
+ dwAddr += sizeof (dwAddr))
+ {
+ if (IsInterrupt())
+ {
+ return;
+ }
+
+ DWORD_PTR objAddr = NULL,
+ MTAddr = NULL;
+
+ if (SUCCEEDED(MOVE(objAddr, dwAddr)) && SUCCEEDED(GetMTOfObject(objAddr, &MTAddr)) && MTAddr)
+ {
+ if (bShort)
+ {
+ DMLOut("%s\n", DMLObject(objAddr));
+ }
+ else
+ {
+ size_t s = ObjectSize(objAddr);
+ stat->Add(MTAddr, (DWORD)s);
+ }
+ }
+ }
+}
+
+BOOL GCHeapTraverse(const DacpGcHeapDetails &heap, AllocInfo* pallocInfo, VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify)
+{
+ DWORD_PTR begin_youngest;
+ DWORD_PTR end_youngest;
+ begin_youngest = (DWORD_PTR)heap.generation_table[0].allocation_start;
+ DWORD_PTR dwAddr = (DWORD_PTR)heap.ephemeral_heap_segment;
+ DacpHeapSegmentData segment;
+
+ end_youngest = (DWORD_PTR)heap.alloc_allocated;
+
+ DWORD_PTR dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].start_segment;
+ dwAddr = dwAddrSeg;
+
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+
+ // DWORD_PTR dwAddrCurrObj = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].allocation_start;
+ DWORD_PTR dwAddrCurrObj = (DWORD_PTR)segment.mem;
+
+ size_t s, sPrev=0;
+ BOOL bPrevFree=FALSE;
+ DWORD_PTR dwAddrMethTable;
+ DWORD_PTR dwAddrPrevObj=0;
+
+ while(1)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<heap walk interrupted>\n");
+ return FALSE;
+ }
+ DWORD_PTR end_of_segment = (DWORD_PTR)segment.allocated;
+ if (dwAddrSeg == (DWORD_PTR)heap.ephemeral_heap_segment)
+ {
+ end_of_segment = end_youngest;
+ if (dwAddrCurrObj - SIZEOF_OBJHEADER == end_youngest - Align(min_obj_size))
+ break;
+ }
+ if (dwAddrCurrObj >= (DWORD_PTR)end_of_segment)
+ {
+ if (dwAddrCurrObj > (DWORD_PTR)end_of_segment)
+ {
+ ExtOut ("curr_object: %p > heap_segment_allocated (seg: %p)\n",
+ SOS_PTR(dwAddrCurrObj), SOS_PTR(dwAddrSeg));
+ if (dwAddrPrevObj) {
+ ExtOut ("Last good object: %p\n", SOS_PTR(dwAddrPrevObj));
+ }
+ return FALSE;
+ }
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ if (dwAddrSeg)
+ {
+ dwAddr = dwAddrSeg;
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+ dwAddrCurrObj = (DWORD_PTR)segment.mem;
+ continue;
+ }
+ else
+ break; // Done Verifying Heap
+ }
+
+ if (dwAddrSeg == (DWORD_PTR)heap.ephemeral_heap_segment
+ && dwAddrCurrObj >= end_youngest)
+ {
+ if (dwAddrCurrObj > end_youngest)
+ {
+ // prev_object length is too long
+ ExtOut("curr_object: %p > end_youngest: %p\n",
+ SOS_PTR(dwAddrCurrObj), SOS_PTR(end_youngest));
+ if (dwAddrPrevObj) {
+ DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
+ }
+ return FALSE;
+ }
+ return FALSE;
+ }
+
+ if (FAILED(GetMTOfObject(dwAddrCurrObj, &dwAddrMethTable)))
+ {
+ return FALSE;
+ }
+
+ dwAddrMethTable = dwAddrMethTable & ~3;
+ if (dwAddrMethTable == 0)
+ {
+ // Is this the beginning of an allocation context?
+ int i;
+ for (i = 0; i < pallocInfo->num; i ++)
+ {
+ if (dwAddrCurrObj == (DWORD_PTR)pallocInfo->array[i].alloc_ptr)
+ {
+ dwAddrCurrObj =
+ (DWORD_PTR)pallocInfo->array[i].alloc_limit + Align(min_obj_size);
+ break;
+ }
+ }
+ if (i < pallocInfo->num)
+ continue;
+
+ // We also need to look at the gen0 alloc context.
+ if (dwAddrCurrObj == (DWORD_PTR) heap.generation_table[0].allocContextPtr)
+ {
+ dwAddrCurrObj = (DWORD_PTR) heap.generation_table[0].allocContextLimit + Align(min_obj_size);
+ continue;
+ }
+ }
+
+ BOOL bContainsPointers;
+ BOOL bMTOk = GetSizeEfficient(dwAddrCurrObj, dwAddrMethTable, FALSE, s, bContainsPointers);
+ if (verify && bMTOk)
+ bMTOk = VerifyObject (heap, dwAddrCurrObj, dwAddrMethTable, s, TRUE);
+ if (!bMTOk)
+ {
+ DMLOut("curr_object: %s\n", DMLListNearObj(dwAddrCurrObj));
+ if (dwAddrPrevObj)
+ DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
+
+ ExtOut ("----------------\n");
+ return FALSE;
+ }
+
+ pFunc (dwAddrCurrObj, s, dwAddrMethTable, token);
+
+ // We believe we did this alignment in ObjectSize above.
+ assert((s & ALIGNCONST) == 0);
+ dwAddrPrevObj = dwAddrCurrObj;
+ sPrev = s;
+ bPrevFree = IsMTForFreeObj(dwAddrMethTable);
+
+ dwAddrCurrObj += s;
+ }
+
+ // Now for the large object generation:
+ dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].start_segment;
+ dwAddr = dwAddrSeg;
+
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+
+ // dwAddrCurrObj = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].allocation_start;
+ dwAddrCurrObj = (DWORD_PTR)segment.mem;
+
+ dwAddrPrevObj=0;
+
+ while(1)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<heap traverse interrupted>\n");
+ return FALSE;
+ }
+
+ DWORD_PTR end_of_segment = (DWORD_PTR)segment.allocated;
+
+ if (dwAddrCurrObj >= (DWORD_PTR)end_of_segment)
+ {
+ if (dwAddrCurrObj > (DWORD_PTR)end_of_segment)
+ {
+ ExtOut("curr_object: %p > heap_segment_allocated (seg: %p)\n",
+ SOS_PTR(dwAddrCurrObj), SOS_PTR(dwAddrSeg));
+ if (dwAddrPrevObj) {
+ ExtOut("Last good object: %p\n", SOS_PTR(dwAddrPrevObj));
+ }
+ return FALSE;
+ }
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ if (dwAddrSeg)
+ {
+ dwAddr = dwAddrSeg;
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+ dwAddrCurrObj = (DWORD_PTR)segment.mem;
+ continue;
+ }
+ else
+ break; // Done Verifying Heap
+ }
+
+ if (FAILED(GetMTOfObject(dwAddrCurrObj, &dwAddrMethTable)))
+ {
+ return FALSE;
+ }
+
+ dwAddrMethTable = dwAddrMethTable & ~3;
+ BOOL bContainsPointers;
+ BOOL bMTOk = GetSizeEfficient(dwAddrCurrObj, dwAddrMethTable, TRUE, s, bContainsPointers);
+ if (verify && bMTOk)
+ bMTOk = VerifyObject (heap, dwAddrCurrObj, dwAddrMethTable, s, TRUE);
+ if (!bMTOk)
+ {
+ DMLOut("curr_object: %s\n", DMLListNearObj(dwAddrCurrObj));
+
+ if (dwAddrPrevObj)
+ DMLOut("Last good object: %s\n", dwAddrPrevObj);
+
+ ExtOut ("----------------\n");
+ return FALSE;
+ }
+
+ pFunc (dwAddrCurrObj, s, dwAddrMethTable, token);
+
+ // We believe we did this alignment in ObjectSize above.
+ assert((s & ALIGNCONSTLARGE) == 0);
+ dwAddrPrevObj = dwAddrCurrObj;
+ dwAddrCurrObj += s;
+ }
+
+ return TRUE;
+}
+
+BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify)
+{
+ // Obtain allocation context for each managed thread.
+ AllocInfo allocInfo;
+ allocInfo.Init();
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting gc heap details\n");
+ return FALSE;
+ }
+
+ return GCHeapTraverse (heapDetails, &allocInfo, pFunc, token, verify);
+ }
+ else
+ {
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return FALSE;
+ }
+
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = gcheap.HeapCount;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow error\n");
+ return FALSE;
+ }
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return FALSE;
+ }
+
+ DWORD n;
+ for (n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return FALSE;
+ }
+
+ if (!GCHeapTraverse (heapDetails, &allocInfo, pFunc, token, verify))
+ {
+ ExtOut("Traversing a gc heap failed\n");
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+GCHeapSnapshot::GCHeapSnapshot()
+{
+ m_isBuilt = FALSE;
+ m_heapDetails = NULL;
+}
+
+///////////////////////////////////////////////////////////
+SegmentLookup::SegmentLookup()
+{
+ m_iSegmentsSize = m_iSegmentCount = 0;
+
+ m_segments = new DacpHeapSegmentData[nSegLookupStgIncrement];
+ if (m_segments == NULL)
+ {
+ ReportOOM();
+ }
+ else
+ {
+ m_iSegmentsSize = nSegLookupStgIncrement;
+ }
+}
+
+BOOL SegmentLookup::AddSegment(DacpHeapSegmentData *pData)
+{
+ // appends the address of a new (initialized) instance of DacpHeapSegmentData to the list of segments
+ // (m_segments) adding space for a segment when necessary.
+ // @todo Microsoft: The field name m_iSegmentSize is a little misleading. It's not the size in bytes,
+ // but the number of elements allocated for the array. It probably should have been named something like
+ // m_iMaxSegments instead.
+ if (m_iSegmentCount >= m_iSegmentsSize)
+ {
+ // expand buffer--allocate enough space to hold the elements we already have plus nSegLookupStgIncrement
+ // more elements
+ DacpHeapSegmentData *pNewBuffer = new DacpHeapSegmentData[m_iSegmentsSize+nSegLookupStgIncrement];
+ if (pNewBuffer==NULL)
+ return FALSE;
+
+ // copy the old elements into the new array
+ memcpy(pNewBuffer, m_segments, sizeof(DacpHeapSegmentData)*m_iSegmentsSize);
+
+ // record the new number of elements available
+ m_iSegmentsSize+=nSegLookupStgIncrement;
+
+ // delete the old array
+ delete [] m_segments;
+
+ // set m_segments to point to the new array
+ m_segments = pNewBuffer;
+ }
+
+ // add pData to the array
+ m_segments[m_iSegmentCount++] = *pData;
+
+ return TRUE;
+}
+
+SegmentLookup::~SegmentLookup()
+{
+ if (m_segments)
+ {
+ delete [] m_segments;
+ m_segments = NULL;
+ }
+}
+
+void SegmentLookup::Clear()
+{
+ m_iSegmentCount = 0;
+}
+
+CLRDATA_ADDRESS SegmentLookup::GetHeap(CLRDATA_ADDRESS object, BOOL& bFound)
+{
+ CLRDATA_ADDRESS ret = NULL;
+ bFound = FALSE;
+
+ // Visit our segments
+ for (int i=0; i<m_iSegmentCount; i++)
+ {
+ if (TO_TADDR(m_segments[i].mem) <= TO_TADDR(object) &&
+ TO_TADDR(m_segments[i].highAllocMark) > TO_TADDR(object))
+ {
+ ret = m_segments[i].gc_heap;
+ bFound = TRUE;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+BOOL GCHeapSnapshot::Build()
+{
+ Clear();
+
+ m_isBuilt = FALSE;
+
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /// 1. Get some basic information such as the heap type (SVR or WKS), how many heaps there are, mode and max generation
+ /// (See code:ClrDataAccess::RequestGCHeapData)
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ if (m_gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return FALSE;
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> heapAddrs = NULL;
+
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /// 2. Get a list of the addresses of the heaps when we have multiple heaps in server mode
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ if (m_gcheap.bServerMode)
+ {
+ UINT AllocSize;
+ // allocate an array to hold the starting addresses of each heap when we're in server mode
+ if (!ClrSafeInt<UINT>::multiply(sizeof(CLRDATA_ADDRESS), m_gcheap.HeapCount, AllocSize) ||
+ (heapAddrs = new CLRDATA_ADDRESS [m_gcheap.HeapCount]) == NULL)
+ {
+ ReportOOM();
+ return FALSE;
+ }
+
+ // and initialize it with their addresses (see code:ClrDataAccess::RequestGCHeapList
+ // for details)
+ if (g_sos->GetGCHeapList(m_gcheap.HeapCount, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return FALSE;
+ }
+ }
+
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /// 3. Get some necessary information about each heap, such as the card table location, the generation
+ /// table, the heap bounds, etc., and retrieve the heap segments
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // allocate an array to hold the information
+ m_heapDetails = new DacpGcHeapDetails[m_gcheap.HeapCount];
+
+ if (m_heapDetails == NULL)
+ {
+ ReportOOM();
+ return FALSE;
+ }
+
+ // get the heap information for each heap
+ // See code:ClrDataAccess::RequestGCHeapDetails for details
+ for (UINT n = 0; n < m_gcheap.HeapCount; n ++)
+ {
+ if (m_gcheap.bServerMode)
+ {
+ if (m_heapDetails[n].Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (m_heapDetails[n].Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return FALSE;
+ }
+ }
+
+ // now get information about the heap segments for this heap
+ if (!AddSegments(m_heapDetails[n]))
+ {
+ ExtOut("Failed to retrieve segments for gc heap\n");
+ return FALSE;
+ }
+ }
+
+ m_isBuilt = TRUE;
+ return TRUE;
+}
+
+BOOL GCHeapSnapshot::AddSegments(DacpGcHeapDetails& details)
+{
+ int n = 0;
+ DacpHeapSegmentData segment;
+
+ // This array of two addresses gives us access to all the segments. The generation segments are linked
+ // to each other, starting with the maxGeneration segment. The second address gives us the large object heap.
+ CLRDATA_ADDRESS AddrSegs[] =
+ {
+ details.generation_table[GetMaxGeneration()].start_segment,
+ details.generation_table[GetMaxGeneration()+1].start_segment // large object heap
+ };
+
+ // this loop will get information for all the heap segments in this heap. The outer loop iterates once
+ // for the "normal" generation segments and once for the large object heap. The inner loop follows the chain
+ // of segments rooted at AddrSegs[i]
+ for (unsigned int i = 0; i < sizeof(AddrSegs)/sizeof(AddrSegs[0]); ++i)
+ {
+ CLRDATA_ADDRESS AddrSeg = AddrSegs[i];
+
+ while (AddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ {
+ return FALSE;
+ }
+ // Initialize segment by copying fields from the target's heap segment at AddrSeg.
+ // See code:ClrDataAccess::RequestGCHeapSegment for details.
+ if (segment.Request(g_sos, AddrSeg, details) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(AddrSeg));
+ return FALSE;
+ }
+ if (n++ > nMaxHeapSegmentCount) // that would be insane
+ {
+ ExtOut("More than %d heap segments, there must be an error\n", nMaxHeapSegmentCount);
+ return FALSE;
+ }
+
+ // add the new segment to the array of segments. This will expand the array if necessary
+ if (!m_segments.AddSegment(&segment))
+ {
+ ExtOut("strike: Failed to store segment\n");
+ return FALSE;
+ }
+ // get the next segment in the chain
+ AddrSeg = segment.next;
+ }
+ }
+
+ return TRUE;
+}
+
+void GCHeapSnapshot::Clear()
+{
+ if (m_heapDetails != NULL)
+ {
+ delete [] m_heapDetails;
+ m_heapDetails = NULL;
+ }
+
+ m_segments.Clear();
+
+ m_isBuilt = FALSE;
+}
+
+GCHeapSnapshot g_snapshot;
+
+DacpGcHeapDetails *GCHeapSnapshot::GetHeap(CLRDATA_ADDRESS objectPointer)
+{
+ // We need bFound because heap will be NULL if we are Workstation Mode.
+ // We still need a way to know if the address was found in our segment
+ // list.
+ BOOL bFound = FALSE;
+ CLRDATA_ADDRESS heap = m_segments.GetHeap(objectPointer, bFound);
+ if (heap)
+ {
+ for (UINT i=0; i<m_gcheap.HeapCount; i++)
+ {
+ if (m_heapDetails[i].heapAddr == heap)
+ return m_heapDetails + i;
+ }
+ }
+ else if (!m_gcheap.bServerMode)
+ {
+ if (bFound)
+ {
+ return m_heapDetails;
+ }
+ }
+
+ // Not found
+ return NULL;
+}
+
+// TODO: Do we need to handle the LOH here?
+int GCHeapSnapshot::GetGeneration(CLRDATA_ADDRESS objectPointer)
+{
+ DacpGcHeapDetails *pDetails = GetHeap(objectPointer);
+ if (pDetails == NULL)
+ {
+ ExtOut("Object %p has no generation\n", SOS_PTR(objectPointer));
+ return 0;
+ }
+
+ TADDR taObj = TO_TADDR(objectPointer);
+ // The DAC doesn't fill the generation table with true CLRDATA_ADDRESS values
+ // but rather with ULONG64 values (i.e. non-sign-extended 64-bit values)
+ // We use the TO_TADDR below to ensure we won't break if this will ever
+ // be fixed in the DAC.
+ if (taObj >= TO_TADDR(pDetails->generation_table[0].allocation_start) &&
+ taObj <= TO_TADDR(pDetails->alloc_allocated))
+ return 0;
+
+ if (taObj >= TO_TADDR(pDetails->generation_table[1].allocation_start) &&
+ taObj <= TO_TADDR(pDetails->generation_table[0].allocation_start))
+ return 1;
+
+ return 2;
+}
+
+
+DWORD_PTR g_trav_totalSize = 0;
+DWORD_PTR g_trav_wastedSize = 0;
+
+void LoaderHeapTraverse(CLRDATA_ADDRESS blockData,size_t blockSize,BOOL blockIsCurrentBlock)
+{
+ DWORD_PTR dwAddr1;
+ DWORD_PTR curSize = 0;
+ char ch;
+ for (dwAddr1 = (DWORD_PTR)blockData;
+ dwAddr1 < (DWORD_PTR)blockData + blockSize;
+ dwAddr1 += OSPageSize())
+ {
+ if (IsInterrupt())
+ break;
+ if (SafeReadMemory(dwAddr1, &ch, sizeof(ch), NULL))
+ {
+ curSize += OSPageSize();
+ }
+ else
+ break;
+ }
+
+ if (!blockIsCurrentBlock)
+ {
+ g_trav_wastedSize += blockSize - curSize;
+ }
+
+ g_trav_totalSize += curSize;
+ ExtOut("%p(%x:%x) ", SOS_PTR(blockData), blockSize, curSize);
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the size for various heaps. *
+* total - the total size of the heap *
+* wasted - the amount of size wasted by the heap. *
+* *
+\**********************************************************************/
+void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted)
+{
+ ExtOut("Size: 0x%" POINTERSIZE_TYPE "x (%" POINTERSIZE_TYPE "lu) bytes", total, total);
+ if (wasted)
+ ExtOut(" total, 0x%" POINTERSIZE_TYPE "x (%" POINTERSIZE_TYPE "lu) bytes wasted", wasted, wasted);
+ ExtOut(".\n");
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the size information for the JIT heap. *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR JitHeapInfo()
+{
+ // walk ExecutionManager__m_pJitList
+ unsigned int count = 0;
+ if (FAILED(g_sos->GetJitManagerList(0, NULL, &count)))
+ {
+ ExtOut("Unable to get JIT info\n");
+ return 0;
+ }
+
+ ArrayHolder<DacpJitManagerInfo> pArray = new DacpJitManagerInfo[count];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return 0;
+ }
+
+ if (g_sos->GetJitManagerList(count, pArray, NULL) != S_OK)
+ {
+ ExtOut("Unable to get array of JIT Managers\n");
+ return 0;
+ }
+
+ DWORD_PTR totalSize = 0;
+ DWORD_PTR wasted = 0;
+
+ for (unsigned int n=0; n < count; n++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (IsMiIL(pArray[n].codeType)) // JIT
+ {
+ unsigned int heapCount = 0;
+ if (FAILED(g_sos->GetCodeHeapList(pArray[n].managerAddr, 0, NULL, &heapCount)))
+ {
+ ExtOut("Error getting EEJitManager code heaps\n");
+ break;
+ }
+
+ if (heapCount > 0)
+ {
+ ArrayHolder<DacpJitCodeHeapInfo> codeHeapInfo = new DacpJitCodeHeapInfo[heapCount];
+ if (codeHeapInfo == NULL)
+ {
+ ReportOOM();
+ break;
+ }
+
+ if (g_sos->GetCodeHeapList(pArray[n].managerAddr, heapCount, codeHeapInfo, NULL) != S_OK)
+ {
+ ExtOut("Unable to get code heap info\n");
+ break;
+ }
+
+ for (unsigned int iHeaps = 0; iHeaps < heapCount; iHeaps++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (codeHeapInfo[iHeaps].codeHeapType == CODEHEAP_LOADER)
+ {
+ ExtOut("LoaderCodeHeap: ");
+ totalSize += LoaderHeapInfo(codeHeapInfo[iHeaps].LoaderHeap, &wasted);
+ }
+ else if (codeHeapInfo[iHeaps].codeHeapType == CODEHEAP_HOST)
+ {
+ ExtOut("HostCodeHeap: ");
+ ExtOut("%p ", SOS_PTR(codeHeapInfo[iHeaps].HostData.baseAddr));
+ DWORD dwSize = (DWORD)(codeHeapInfo[iHeaps].HostData.currentAddr - codeHeapInfo[iHeaps].HostData.baseAddr);
+ PrintHeapSize(dwSize, 0);
+ totalSize += dwSize;
+ }
+ }
+ }
+ }
+ else if (!IsMiNative(pArray[n].codeType)) // ignore native heaps for now
+ {
+ ExtOut("Unknown Jit encountered, ignored\n");
+ }
+ }
+
+ ExtOut("Total size: ");
+ PrintHeapSize(totalSize, wasted);
+
+ return totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the loader heap info for a single AD. *
+* pLoaderHeapAddr - pointer to the loader heap *
+* wasted - a pointer to store the number of bytes wasted in this *
+* VSDHeap (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted)
+{
+ g_trav_totalSize = 0;
+ g_trav_wastedSize = 0;
+
+ if (pLoaderHeapAddr)
+ g_sos->TraverseLoaderHeap(pLoaderHeapAddr, LoaderHeapTraverse);
+
+ PrintHeapSize(g_trav_totalSize, g_trav_wastedSize);
+
+ if (wasted)
+ *wasted += g_trav_wastedSize;
+ return g_trav_totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for a single VSDHeap. *
+* name - the name to print *
+* type - the type of heap *
+* appDomain - the app domain in which this resides *
+* wasted - a pointer to store the number of bytes wasted in this *
+* VSDHeap (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+static DWORD_PTR PrintOneVSDHeap(const char *name, VCSHeapType type, CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted)
+{
+ g_trav_totalSize = 0; g_trav_wastedSize = 0;
+
+ ExtOut(name);
+ g_sos->TraverseVirtCallStubHeap(appDomain, type, LoaderHeapTraverse);
+
+ PrintHeapSize(g_trav_totalSize, g_trav_wastedSize);
+ if (wasted)
+ *wasted += g_trav_wastedSize;
+ return g_trav_totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for VSDHeaps. *
+* appDomain - The AppDomain to print info for. *
+* wasted - a pointer to store the number of bytes wasted in this *
+* AppDomain (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted)
+{
+ DWORD_PTR totalSize = 0;
+
+ if (appDomain)
+ {
+ totalSize += PrintOneVSDHeap(" IndcellHeap: ", IndcellHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" LookupHeap: ", LookupHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" ResolveHeap: ", ResolveHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" DispatchHeap: ", DispatchHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" CacheEntryHeap: ", CacheEntryHeap, appDomain, wasted);
+ }
+
+ return totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for a domain *
+* name - the name of the domain (to be printed) *
+* adPtr - a pointer to the AppDomain to print info about *
+* outSize - a pointer to an int to store the size at (this may be *
+* NULL) *
+* outWasted - a pointer to an int to store the number of bytes this *
+* domain is wasting (this may be NULL) *
+* *
+* returns: SUCCESS if we successfully printed out the domain heap *
+* info, FAILED otherwise; if FAILED, outSize and *
+* outWasted are untouched. *
+* *
+\**********************************************************************/
+HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *outSize, DWORD_PTR *outWasted)
+{
+ DacpAppDomainData appDomain;
+ HRESULT hr = appDomain.Request(g_sos, adPtr);
+ if (FAILED(hr))
+ {
+ ExtOut("Unable to get information for %s.\n", name);
+ return hr;
+ }
+
+ ExtOut("--------------------------------------\n");
+
+ const int column = 19;
+ ExtOut("%s:", name);
+ WhitespaceOut(column - (int)strlen(name) - 1);
+ DMLOut("%s\n", DMLDomain(adPtr));
+
+ DWORD_PTR domainHeapSize = 0;
+ DWORD_PTR wasted = 0;
+
+ ExtOut("LowFrequencyHeap: ");
+ domainHeapSize += LoaderHeapInfo(appDomain.pLowFrequencyHeap, &wasted);
+
+ ExtOut("HighFrequencyHeap: ");
+ domainHeapSize += LoaderHeapInfo(appDomain.pHighFrequencyHeap, &wasted);
+
+ ExtOut("StubHeap: ");
+ domainHeapSize += LoaderHeapInfo(appDomain.pStubHeap, &wasted);
+
+ ExtOut("Virtual Call Stub Heap:\n");
+ domainHeapSize += VSDHeapInfo(appDomain.AppDomainPtr, &wasted);
+
+ ExtOut("Total size: ");
+ PrintHeapSize(domainHeapSize, wasted);
+
+ if (outSize)
+ *outSize += domainHeapSize;
+ if (outWasted)
+ *outWasted += wasted;
+
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for a list of modules. *
+* moduleList - an array of modules *
+* count - the number of modules in moduleList *
+* type - the type of heap *
+* outWasted - a pointer to store the number of bytes wasted in this *
+* heap (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR PrintModuleHeapInfo(__out_ecount(count) DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *outWasted)
+{
+ DWORD_PTR toReturn = 0;
+ DWORD_PTR wasted = 0;
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut("<no information>\n");
+ }
+ else
+ {
+ DWORD_PTR thunkHeapSize = 0;
+
+ for (int i = 0; i < count; i++)
+ {
+ CLRDATA_ADDRESS addr = moduleList[i];
+ DacpModuleData dmd;
+ if (dmd.Request(g_sos, addr) != S_OK)
+ {
+ ExtOut("Unable to read module %p\n", SOS_PTR(addr));
+ }
+ else
+ {
+ DMLOut("Module %s: ", DMLModule(addr));
+ CLRDATA_ADDRESS heap = type == ModuleHeapType_ThunkHeap ? dmd.pThunkHeap : dmd.pLookupTableHeap;
+ thunkHeapSize += LoaderHeapInfo(heap, &wasted);
+ }
+ }
+
+ ExtOut("Total size: " WIN86_8SPACES);
+ PrintHeapSize(thunkHeapSize, wasted);
+
+ toReturn = thunkHeapSize;
+ }
+
+ if (outWasted)
+ *outWasted += wasted;
+
+ return toReturn;
+}
diff --git a/src/ToolBox/SOS/Strike/exts.cpp b/src/ToolBox/SOS/Strike/exts.cpp
new file mode 100644
index 0000000000..0b1f976cc2
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/exts.cpp
@@ -0,0 +1,435 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#include "exts.h"
+#include "disasm.h"
+#ifndef FEATURE_PAL
+#include "EventCallbacks.h"
+
+#define VER_PRODUCTVERSION_W (0x0100)
+
+//
+// globals
+//
+EXT_API_VERSION ApiVersion = { (VER_PRODUCTVERSION_W >> 8), (VER_PRODUCTVERSION_W & 0xff), EXT_API_VERSION_NUMBER64, 0 };
+WINDBG_EXTENSION_APIS ExtensionApis;
+
+ULONG PageSize;
+
+OnUnloadTask *OnUnloadTask::s_pUnloadTaskList = NULL;
+
+//
+// Valid for the lifetime of the debug session.
+//
+
+ULONG TargetMachine;
+BOOL Connected;
+ULONG g_TargetClass;
+DWORD_PTR g_filterHint = 0;
+
+PDEBUG_CLIENT g_ExtClient;
+PDEBUG_DATA_SPACES2 g_ExtData2;
+PDEBUG_SYMBOLS2 g_ExtSymbols2;
+PDEBUG_ADVANCED3 g_ExtAdvanced3;
+PDEBUG_CLIENT g_pCallbacksClient;
+
+#else
+
+DebugClient* g_DebugClient;
+ILLDBServices* g_ExtServices;
+
+#endif // FEATURE_PAL
+
+IMachine* g_targetMachine = NULL;
+BOOL g_bDacBroken = FALSE;
+
+PDEBUG_CONTROL2 g_ExtControl;
+PDEBUG_DATA_SPACES g_ExtData;
+PDEBUG_REGISTERS g_ExtRegisters;
+PDEBUG_SYMBOLS g_ExtSymbols;
+PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
+
+#define SOS_ExtQueryFailGo(var, riid) \
+ var = NULL; \
+ if ((Status = client->QueryInterface(__uuidof(riid), \
+ (void **)&var)) != S_OK) \
+ { \
+ goto Fail; \
+ }
+
+// Queries for all debugger interfaces.
+#ifndef FEATURE_PAL
+extern "C" HRESULT
+ExtQuery(PDEBUG_CLIENT client)
+{
+ g_ExtClient = client;
+#else
+extern "C" HRESULT
+ExtQuery(ILLDBServices* services)
+{
+ g_ExtServices = services;
+ DebugClient* client = new DebugClient(services);
+ g_DebugClient = client;
+#endif
+ HRESULT Status;
+ SOS_ExtQueryFailGo(g_ExtControl, IDebugControl2);
+ SOS_ExtQueryFailGo(g_ExtData, IDebugDataSpaces);
+ SOS_ExtQueryFailGo(g_ExtRegisters, IDebugRegisters);
+ SOS_ExtQueryFailGo(g_ExtSymbols, IDebugSymbols);
+ SOS_ExtQueryFailGo(g_ExtSystem, IDebugSystemObjects);
+#ifndef FEATURE_PAL
+ SOS_ExtQueryFailGo(g_ExtData2, IDebugDataSpaces2);
+ SOS_ExtQueryFailGo(g_ExtSymbols2, IDebugSymbols2);
+ SOS_ExtQueryFailGo(g_ExtAdvanced3, IDebugAdvanced3);
+#endif // FEATURE_PAL
+ return S_OK;
+
+ Fail:
+ if (Status == E_OUTOFMEMORY)
+ ReportOOM();
+
+ ExtRelease();
+ return Status;
+}
+
+extern "C" HRESULT
+ArchQuery(void)
+{
+ ULONG targetArchitecture;
+ IMachine* targetMachine = NULL;
+
+ g_ExtControl->GetExecutingProcessorType(&targetArchitecture);
+
+#ifdef SOS_TARGET_AMD64
+ if(targetArchitecture == IMAGE_FILE_MACHINE_AMD64)
+ {
+ targetMachine = AMD64Machine::GetInstance();
+ }
+#endif // SOS_TARGET_AMD64
+#ifdef SOS_TARGET_X86
+ if (targetArchitecture == IMAGE_FILE_MACHINE_I386)
+ {
+ targetMachine = X86Machine::GetInstance();
+ }
+#endif // SOS_TARGET_X86
+#ifdef SOS_TARGET_ARM
+ if (targetArchitecture == IMAGE_FILE_MACHINE_ARMNT)
+ {
+ targetMachine = ARMMachine::GetInstance();
+ }
+#endif // SOS_TARGET_ARM
+#ifdef SOS_TARGET_ARM64
+ if (targetArchitecture == IMAGE_FILE_MACHINE_ARM64)
+ {
+ targetMachine = ARM64Machine::GetInstance();
+ }
+#endif // SOS_TARGET_ARM64
+
+ if (targetMachine == NULL)
+ {
+ g_targetMachine = NULL;
+ ExtErr("SOS does not support the current target architecture.\n");
+ return E_FAIL;
+ }
+
+ g_targetMachine = targetMachine;
+ return S_OK;
+}
+
+// Cleans up all debugger interfaces.
+void
+ExtRelease(void)
+{
+ EXT_RELEASE(g_ExtControl);
+ EXT_RELEASE(g_ExtData);
+ EXT_RELEASE(g_ExtRegisters);
+ EXT_RELEASE(g_ExtSymbols);
+ EXT_RELEASE(g_ExtSystem);
+#ifndef FEATURE_PAL
+ EXT_RELEASE(g_ExtData2);
+ EXT_RELEASE(g_ExtSymbols2);
+ EXT_RELEASE(g_ExtAdvanced3);
+ g_ExtClient = NULL;
+#else
+ EXT_RELEASE(g_DebugClient);
+ g_ExtServices = NULL;
+#endif // FEATURE_PAL
+}
+
+#ifndef FEATURE_PAL
+
+BOOL IsMiniDumpFileNODAC();
+extern HMODULE g_hInstance;
+
+// This function throws an exception that can be caught by the debugger,
+// instead of allowing the default CRT behavior of invoking Watson to failfast.
+void __cdecl _SOS_invalid_parameter(
+ const WCHAR * expression,
+ const WCHAR * function,
+ const WCHAR * file,
+ unsigned int line,
+ uintptr_t pReserved
+)
+{
+ ExtErr("\nSOS failure!\n");
+ throw "SOS failure";
+}
+
+// Unregisters our windbg event callbacks and releases the client, event callback objects
+void CleanupEventCallbacks()
+{
+ if(g_pCallbacksClient != NULL)
+ {
+ g_pCallbacksClient->Release();
+ g_pCallbacksClient = NULL;
+ }
+}
+
+bool g_Initialized = false;
+
+extern "C"
+HRESULT
+CALLBACK
+DebugExtensionInitialize(PULONG Version, PULONG Flags)
+{
+ IDebugClient *DebugClient;
+ PDEBUG_CONTROL DebugControl;
+ HRESULT Hr;
+
+ *Version = DEBUG_EXTENSION_VERSION(1, 0);
+ *Flags = 0;
+
+ if (g_Initialized)
+ {
+ return S_OK;
+ }
+ g_Initialized = true;
+
+ if ((Hr = DebugCreate(__uuidof(IDebugClient),
+ (void **)&DebugClient)) != S_OK)
+ {
+ return Hr;
+ }
+ if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
+ (void **)&DebugControl)) != S_OK)
+ {
+ return Hr;
+ }
+
+ ExtensionApis.nSize = sizeof (ExtensionApis);
+ if ((Hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis)) != S_OK)
+ {
+ return Hr;
+ }
+
+ // Fixes the "Unable to read dynamic function table entries" error messages by disabling the WinDbg security
+ // feature that prevents the loading of unknown out of proc tack walkers.
+ DebugControl->Execute(DEBUG_OUTCTL_IGNORE, ".settings set EngineInitialization.VerifyFunctionTableCallbacks=false",
+ DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT);
+
+ ExtQuery(DebugClient);
+ if (IsMiniDumpFileNODAC())
+ {
+ ExtOut (
+ "----------------------------------------------------------------------------\n"
+ "The user dump currently examined is a minidump. Consequently, only a subset\n"
+ "of sos.dll functionality will be available. If needed, attaching to the live\n"
+ "process or debugging a full dump will allow access to sos.dll's full feature\n"
+ "set.\n"
+ "To create a full user dump use the command: .dump /ma <filename>\n"
+ "----------------------------------------------------------------------------\n");
+ }
+ ExtRelease();
+
+ OnUnloadTask::Register(CleanupEventCallbacks);
+ g_pCallbacksClient = DebugClient;
+ EventCallbacks* pCallbacksObj = new EventCallbacks(DebugClient);
+ IDebugEventCallbacks* pCallbacks = NULL;
+ pCallbacksObj->QueryInterface(__uuidof(IDebugEventCallbacks), (void**)&pCallbacks);
+ pCallbacksObj->Release();
+
+ if(FAILED(Hr = g_pCallbacksClient->SetEventCallbacks(pCallbacks)))
+ {
+ ExtOut ("SOS: Failed to register callback events\n");
+ pCallbacks->Release();
+ return Hr;
+ }
+ pCallbacks->Release();
+
+#ifndef _ARM_
+ // Make sure we do not tear down the debugger when a security function fails
+ // Since we link statically against CRT this will only affect the SOS module.
+ _set_invalid_parameter_handler(_SOS_invalid_parameter);
+#endif
+
+ DebugControl->Release();
+ return S_OK;
+}
+
+extern "C"
+void
+CALLBACK
+DebugExtensionNotify(ULONG Notify, ULONG64 /*Argument*/)
+{
+ //
+ // The first time we actually connect to a target, get the page size
+ //
+
+ if ((Notify == DEBUG_NOTIFY_SESSION_ACCESSIBLE) && (!Connected))
+ {
+ IDebugClient *DebugClient;
+ PDEBUG_DATA_SPACES DebugDataSpaces;
+ PDEBUG_CONTROL DebugControl;
+ HRESULT Hr;
+ ULONG64 Page;
+
+ if ((Hr = DebugCreate(__uuidof(IDebugClient),
+ (void **)&DebugClient)) == S_OK)
+ {
+ //
+ // Get the page size and PAE enable flag
+ //
+
+ if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugDataSpaces),
+ (void **)&DebugDataSpaces)) == S_OK)
+ {
+ if ((Hr = DebugDataSpaces->ReadDebuggerData(
+ DEBUG_DATA_MmPageSize, &Page,
+ sizeof(Page), NULL)) == S_OK)
+ {
+ PageSize = (ULONG)(ULONG_PTR)Page;
+ }
+
+ DebugDataSpaces->Release();
+ }
+ //
+ // Get the architecture type.
+ //
+
+ if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
+ (void **)&DebugControl)) == S_OK)
+ {
+ if ((Hr = DebugControl->GetActualProcessorType(
+ &TargetMachine)) == S_OK)
+ {
+ Connected = TRUE;
+ }
+ ULONG Qualifier;
+ if ((Hr = DebugControl->GetDebuggeeType(&g_TargetClass, &Qualifier)) == S_OK)
+ {
+ }
+
+ DebugControl->Release();
+ }
+
+ DebugClient->Release();
+ }
+ }
+
+
+ if (Notify == DEBUG_NOTIFY_SESSION_INACTIVE)
+ {
+ Connected = FALSE;
+ PageSize = 0;
+ TargetMachine = 0;
+ }
+
+ return;
+}
+
+extern "C"
+void
+CALLBACK
+DebugExtensionUninitialize(void)
+{
+ // execute all registered cleanup tasks
+ OnUnloadTask::Run();
+ return;
+}
+
+
+BOOL DllInit(
+ HANDLE /*hModule*/,
+ DWORD dwReason,
+ DWORD /*dwReserved*/
+ )
+{
+ switch (dwReason) {
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ break;
+
+ case DLL_PROCESS_ATTACH:
+ break;
+ }
+
+ return TRUE;
+}
+
+BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ g_hInstance = (HMODULE) hInstance;
+ }
+ return true;
+}
+
+#else // FEATURE_PAL
+
+HRESULT
+DebugClient::QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface
+ )
+{
+ if (InterfaceId == __uuidof(IUnknown) ||
+ InterfaceId == __uuidof(IDebugControl2) ||
+ InterfaceId == __uuidof(IDebugControl4) ||
+ InterfaceId == __uuidof(IDebugDataSpaces) ||
+ InterfaceId == __uuidof(IDebugSymbols) ||
+ InterfaceId == __uuidof(IDebugSystemObjects) ||
+ InterfaceId == __uuidof(IDebugRegisters))
+ {
+ *Interface = this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG
+DebugClient::AddRef()
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+ULONG
+DebugClient::Release()
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ m_lldbservices->Release();
+ delete this;
+ }
+ return ref;
+}
+
+#endif // FEATURE_PAL
diff --git a/src/ToolBox/SOS/Strike/exts.h b/src/ToolBox/SOS/Strike/exts.h
new file mode 100644
index 0000000000..36b5230c37
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/exts.h
@@ -0,0 +1,513 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __exts_h__
+#define __exts_h__
+
+#define KDEXT_64BIT
+
+#include <windows.h>
+#include <winternl.h>
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4245) // signed/unsigned mismatch
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4127) // conditional expression is constant
+#pragma warning(disable:4430) // missing type specifier: C++ doesn't support default-int
+#endif
+#include "strike.h"
+#include <wdbgexts.h>
+#include <dbgeng.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// wdbgexts.h defines StackTrace which interferes with other parts of the
+// system that use the StackTrace identifier
+#ifdef StackTrace
+#undef StackTrace
+#endif
+
+#include "platformspecific.h"
+
+// We need to define the target address type. This has to be used in the
+// functions that read directly from the debuggee address space, vs. using
+// the DAC to read the DAC-ized data structures.
+#include "daccess.h"
+
+#include "gcinfo.h"
+
+// Convert between CLRDATA_ADDRESS and TADDR.
+#define TO_TADDR(cdaddr) ((TADDR)(cdaddr))
+#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))
+
+// We also need a "correction" macro: there are a number of places in the DAC
+// where instead of using the CLRDATA_ADDRESS sign-extension convention
+// we 0-extend (most notably DacpGcHeapDetails)
+#define NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
+#if NEED_DAC_CLRDATA_ADDRESS_CORRECTION == 1
+ // the macro below "corrects" a CDADDR to always represent the
+ // sign-extended equivalent ULONG64 value of the original TADDR
+ #define UL64_TO_CDA(ul64) (TO_CDADDR(TO_TADDR(ul64)))
+#else
+ #define UL64_TO_CDA(ul64) (ul64)
+#endif // NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
+
+// The macro below removes the sign extension, returning the
+// equivalent ULONG64 value to the original TADDR. Useful when
+// printing CDA values.
+#define CDA_TO_UL64(cda) ((ULONG64)(TO_TADDR(cda)))
+
+typedef struct _TADDR_RANGE
+{
+ TADDR start;
+ TADDR end;
+} TADDR_RANGE;
+
+typedef struct _TADDR_SEGINFO
+{
+ TADDR segAddr;
+ TADDR start;
+ TADDR end;
+} TADDR_SEGINFO;
+
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Cleanup tasks to be executed when the extension is unloaded
+class OnUnloadTask
+{
+public:
+ FORCEINLINE static void Register(void (*fn)())
+ {
+ // append a new unload task to the head of the list
+ OnUnloadTask *pNew = new OnUnloadTask(fn);
+ pNew->pNext = s_pUnloadTaskList;
+ s_pUnloadTaskList = pNew;
+ }
+
+ static void Run()
+ {
+ // walk the list of UnloadTasks and execute each in turn
+ OnUnloadTask* pCur = s_pUnloadTaskList;
+ while (pCur != NULL)
+ {
+ OnUnloadTask* pNext = pCur->pNext;
+ pCur->OnUnloadFn();
+ delete pCur;
+ pCur = pNext;
+ }
+ s_pUnloadTaskList = NULL;
+ }
+
+private:
+ OnUnloadTask(void(*fn)())
+ : OnUnloadFn(fn)
+ , pNext(NULL)
+ { }
+
+private:
+ void (*OnUnloadFn)();
+ OnUnloadTask* pNext;
+
+ static OnUnloadTask *s_pUnloadTaskList;
+};
+
+#ifndef MINIDUMP
+
+#define EXIT_API ExtRelease
+
+// Safe release and NULL.
+#define EXT_RELEASE(Unk) \
+ ((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL)
+
+extern PDEBUG_CONTROL2 g_ExtControl;
+extern PDEBUG_DATA_SPACES g_ExtData;
+extern PDEBUG_SYMBOLS g_ExtSymbols;
+extern PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
+extern PDEBUG_REGISTERS g_ExtRegisters;
+
+#ifndef FEATURE_PAL
+
+// Global variables initialized by query.
+extern PDEBUG_CLIENT g_ExtClient;
+extern PDEBUG_DATA_SPACES2 g_ExtData2;
+extern PDEBUG_SYMBOLS2 g_ExtSymbols2;
+extern PDEBUG_ADVANCED3 g_ExtAdvanced3;
+
+#else // FEATURE_PAL
+
+extern ILLDBServices* g_ExtServices;
+
+#endif // FEATURE_PAL
+
+HRESULT
+ExtQuery(PDEBUG_CLIENT client);
+
+HRESULT
+ArchQuery(void);
+
+void
+ExtRelease(void);
+
+#ifdef _DEBUG
+extern DWORD_PTR g_filterHint;
+#endif
+
+extern BOOL ControlC;
+
+inline BOOL IsInterrupt()
+{
+ if (!ControlC && g_ExtControl->GetInterrupt() == S_OK)
+ {
+ ExtOut("Command cancelled at the user's request.\n");
+ ControlC = TRUE;
+ }
+
+ return ControlC;
+}
+
+//
+// undef the wdbgexts
+//
+#undef DECLARE_API
+
+#define DECLARE_API(extension) \
+CPPMOD HRESULT CALLBACK extension(PDEBUG_CLIENT client, PCSTR args)
+
+class __ExtensionCleanUp
+{
+public:
+ __ExtensionCleanUp(){}
+ ~__ExtensionCleanUp(){ExtRelease();}
+};
+
+inline void EENotLoadedMessage(HRESULT Status)
+{
+ ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status);
+ ExtOut("Extension commands need it in order to have something to do.\n");
+}
+
+inline void DACMessage(HRESULT Status)
+{
+ ExtOut("Failed to load data access DLL, 0x%08x\n", Status);
+#ifndef FEATURE_PAL
+ ExtOut("Verify that 1) you have a recent build of the debugger (6.2.14 or newer)\n");
+ ExtOut(" 2) the file mscordacwks.dll that matches your version of coreclr.dll is \n");
+ ExtOut(" in the version directory or on the symbol path\n");
+ ExtOut(" 3) or, if you are debugging a dump file, verify that the file \n");
+ ExtOut(" mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.\n");
+ ExtOut(" 4) you are debugging on supported cross platform architecture as \n");
+ ExtOut(" the dump file. For example, an ARM dump file must be debugged\n");
+ ExtOut(" on an X86 or an ARM machine; an AMD64 dump file must be\n");
+ ExtOut(" debugged on an AMD64 machine.\n");
+ ExtOut("\n");
+ ExtOut("You can also run the debugger command .cordll to control the debugger's\n");
+ ExtOut("load of mscordacwks.dll. .cordll -ve -u -l will do a verbose reload.\n");
+ ExtOut("If that succeeds, the SOS command should work on retry.\n");
+ ExtOut("\n");
+ ExtOut("If you are debugging a minidump, you need to make sure that your executable\n");
+ ExtOut("path is pointing to coreclr.dll as well.\n");
+#else // FEATURE_PAL
+ if (Status == CORDBG_E_MISSING_DEBUGGER_EXPORTS)
+ {
+ ExtOut("You can run the debugger command 'setclrpath' to control the load of %s.\n", MAKEDLLNAME_A("mscordaccore"));
+ ExtOut("If that succeeds, the SOS command should work on retry.\n");
+ }
+ else
+ {
+ ExtOut("Can not load or initialize %s. The target runtime may not be initialized.\n", MAKEDLLNAME_A("mscordaccore"));
+ }
+#endif // FEATURE_PAL
+}
+
+HRESULT CheckEEDll();
+
+#define INIT_API_NOEE() \
+ HRESULT Status; \
+ __ExtensionCleanUp __extensionCleanUp; \
+ if ((Status = ExtQuery(client)) != S_OK) return Status; \
+ if ((Status = ArchQuery()) != S_OK) return Status; \
+ ControlC = FALSE; \
+ g_bDacBroken = TRUE; \
+ g_clrData = NULL; \
+ g_sos = NULL;
+
+#define INIT_API_EE() \
+ if ((Status = CheckEEDll()) != S_OK) \
+ { \
+ EENotLoadedMessage(Status); \
+ return Status; \
+ }
+
+#define INIT_API_NODAC() \
+ INIT_API_NOEE() \
+ INIT_API_EE()
+
+#define INIT_API_DAC() \
+ if ((Status = LoadClrDebugDll()) != S_OK) \
+ { \
+ DACMessage(Status); \
+ return Status; \
+ } \
+ g_bDacBroken = FALSE; \
+ /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
+ /* We may reconsider caching g_clrData in the future */ \
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData); \
+ ToRelease<ISOSDacInterface> spISD(g_sos); \
+ ResetGlobals();
+
+#define INIT_API() \
+ INIT_API_NODAC() \
+ INIT_API_DAC()
+
+// Attempt to initialize DAC and SOS globals, but do not "return" on failure.
+// Instead, mark the failure to initialize the DAC by setting g_bDacBroken to TRUE.
+// This should be used from extension commands that should work OK even when no
+// runtime is loaded in the debuggee, e.g. DumpLog, DumpStack. These extensions
+// and functions they call should test g_bDacBroken before calling any DAC enabled
+// feature.
+#define INIT_API_NO_RET_ON_FAILURE() \
+ INIT_API_NOEE() \
+ if ((Status = CheckEEDll()) != S_OK) \
+ { \
+ ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status); \
+ ExtOut("Some functionality may be impaired\n"); \
+ } \
+ else if ((Status = LoadClrDebugDll()) != S_OK) \
+ { \
+ ExtOut("Failed to load data access DLL (%s), 0x%08x\n", MAKEDLLNAME_A("mscordaccore"), Status); \
+ ExtOut("Some functionality may be impaired\n"); \
+ } \
+ else \
+ { \
+ g_bDacBroken = FALSE; \
+ ResetGlobals(); \
+ } \
+ /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
+ /* We may reconsider caching g_clrData in the future */ \
+ ToRelease<ISOSDacInterface> spISD(g_sos); \
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData);
+
+extern BOOL g_bDacBroken;
+
+#define PAGE_ALIGN64(Va) ((ULONG64)((Va) & ~((ULONG64) ((LONG64) (LONG) PageSize - 1))))
+
+extern ULONG PageSize;
+
+
+//-----------------------------------------------------------------------------------------
+//
+// Target platform abstraction
+//
+//-----------------------------------------------------------------------------------------
+
+// some needed forward declarations
+struct StackTrace_SimpleContext;
+struct GCEncodingInfo;
+struct SOSEHInfo;
+class GCDump;
+
+///
+/// IMachine interface
+///
+/// Note:
+/// The methods accepting target address args take them as size_t==DWORD_PTR==TADDR,
+/// which means this can only provide cross platform support for same-word size
+/// architectures (only ARM on x86 currently). Since this is not exposed outside SOS
+/// and since the some-word-size limitation exists across EE/DAC/SOS this is not an
+/// actual limitation.
+///
+
+class IMachine
+{
+public:
+ // Returns the IMAGE_FILE_MACHINE_*** constant corresponding to the target machine
+ virtual ULONG GetPlatform() const = 0;
+ // Returns the size of the CONTEXT for the target machine
+ virtual ULONG GetContextSize() const = 0;
+
+ // Disassembles a managed method specified by the IPBegin-IPEnd range
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const = 0;
+
+ // Validates whether retAddr represents a return address by unassembling backwards.
+ // If the instruction before retAddr represents a target-specific call instruction
+ // it attempts to identify the target of the call. If successful it sets *whereCalled
+ // to the call target, otherwise it sets it to 0xffffffff.
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const = 0;
+
+ // If, while unwinding the stack, "PC" represents a known return address in
+ // KiUserExceptionDispatcher, "stack" is used to retrieve an exception context record
+ // in "cxr", and an exception record in "exr"
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const = 0;
+
+ // Retrieves stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+
+ // Fills dest's data fields from a target specific context
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const = 0;
+ // Fills a target specific context, destCtx, from the idx-th location in a target specific
+ // array of contexts that start at srcCtx
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const = 0;
+
+ // Retrieve some target specific output strings
+ virtual LPCSTR GetDumpStackHeading() const = 0;
+ virtual LPCSTR GetDumpStackObjectsHeading() const = 0;
+ virtual LPCSTR GetSPName() const = 0;
+ // Retrieves the non-volatile registers reported to the GC
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const = 0;
+
+ typedef void (*printfFtn)(const char* fmt, ...);
+ // Dumps the GCInfo
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
+
+protected:
+ IMachine() {}
+ virtual ~IMachine() {}
+
+private:
+ IMachine(const IMachine& machine); // undefined
+ IMachine & operator=(const IMachine&); // undefined
+}; // class IMachine
+
+
+extern IMachine* g_targetMachine;
+
+
+inline BOOL IsDbgTargetX86() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_I386; }
+inline BOOL IsDbgTargetAmd64() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_AMD64; }
+inline BOOL IsDbgTargetArm() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_ARMNT; }
+inline BOOL IsDbgTargetWin64() { return IsDbgTargetAmd64(); }
+
+/* Returns the instruction pointer for the given CONTEXT. We need this and its family of
+ * functions because certain headers are inconsistantly included on the various platforms,
+ * meaning that we cannot use GetIP and GetSP as defined by CLR.
+ */
+inline CLRDATA_ADDRESS GetIP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetIP(context));
+}
+
+/* Returns the stack pointer for the given CONTEXT.
+ */
+inline CLRDATA_ADDRESS GetSP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetSP(context));
+}
+
+/* Returns the base/frame pointer for the given CONTEXT.
+ */
+inline CLRDATA_ADDRESS GetBP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetBP(context));
+}
+
+
+//-----------------------------------------------------------------------------------------
+//
+// api declaration macros & api access macros
+//
+//-----------------------------------------------------------------------------------------
+
+#ifndef FEATURE_PAL
+
+extern WINDBG_EXTENSION_APIS ExtensionApis;
+#define GetExpression (ExtensionApis.lpGetExpressionRoutine)
+
+extern ULONG TargetMachine;
+extern ULONG g_TargetClass;
+extern ULONG g_VDbgEng;
+
+#else // FEATURE_PAL
+
+#define GetExpression(exp) g_ExtServices->GetExpression(exp)
+
+#endif // FEATURE_PAL
+
+#define CACHE_SIZE DT_OS_PAGE_SIZE
+
+struct ReadVirtualCache
+{
+ BYTE m_cache[CACHE_SIZE];
+ TADDR m_startCache;
+ BOOL m_cacheValid;
+ ULONG m_cacheSize;
+
+ ReadVirtualCache() { Clear(); }
+ HRESULT Read(TADDR Offset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead);
+ void Clear() { m_cacheValid = FALSE; m_cacheSize = CACHE_SIZE; }
+};
+
+extern ReadVirtualCache *rvCache;
+
+#define MOVE(dst, src) rvCache->Read(TO_TADDR(src), &(dst), sizeof(dst), NULL)
+#define MOVEBLOCK(dst, src, size) rvCache->Read(TO_TADDR(src), &(dst), size, NULL)
+
+#define moveN(dst, src) \
+{ \
+ HRESULT ret = MOVE(dst, src); \
+ if (FAILED(ret)) return ret; \
+}
+
+#define moveBlockN(dst, src, size) \
+{ \
+ HRESULT ret = MOVEBLOCK(dst, src, size); \
+ if (FAILED(ret)) return ret; \
+}
+
+// move cross-process: reads memory from the debuggee into
+// debugger address space and returns in case of error
+#define move_xp(dst, src) \
+{ \
+ HRESULT ret = MOVE(dst, src); \
+ if (FAILED(ret)) return; \
+}
+
+#define moveBlock(dst, src, size) \
+{ \
+ HRESULT ret = MOVEBLOCK(dst, src, size); \
+ if (FAILED(ret)) return; \
+}
+
+#ifdef __cplusplus
+#define CPPMOD extern "C"
+#else
+#define CPPMOD
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __exts_h__
+
diff --git a/src/ToolBox/SOS/Strike/gchist.cpp b/src/ToolBox/SOS/Strike/gchist.cpp
new file mode 100644
index 0000000000..ee9d5b4033
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/gchist.cpp
@@ -0,0 +1,636 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+/****************************************************************************
+* STRIKE.C *
+* Routines for the NTSD extension - STRIKE *
+* *
+* History: *
+* 09/07/99 Microsoft Created *
+* *
+* *
+\***************************************************************************/
+#include <windows.h>
+#include <winternl.h>
+#include <winver.h>
+#include <wchar.h>
+
+#define NOEXTAPI
+#define KDEXT_64BIT
+#include <wdbgexts.h>
+#undef DECLARE_API
+#undef StackTrace
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "strike.h"
+// We need to define the target address type. This will be used in the
+// functions that read directly from the debuggee address space, vs. using
+// the DAC tgo read the DAC-ized data structures.
+#include "daccess.h"
+//#include "dbgeng.h"
+
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+#include "dacprivate.h"
+
+#define CORHANDLE_MASK 0x1
+#define DEFINE_EXT_GLOBALS
+
+#include "util.h"
+
+#ifndef _ASSERTE
+#ifdef _DEBUG
+#define _ASSERTE(expr) \
+ do { if (!(expr) ) { ExtOut(#expr); DebugBreak(); } } while (0)
+#else // _DEBUG
+#define _ASSERTE(expr)
+#endif // _DEBUG else
+#endif // !_ASSERTE
+
+#ifdef _MSC_VER
+#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
+#pragma warning(disable:4189) // local variable is initialized but not referenced
+#endif // _MSC_VER
+
+struct PlugRecord
+{
+ PlugRecord *next;
+
+ size_t PlugStart;
+ size_t PlugEnd;
+ size_t Delta;
+
+ PlugRecord() { ZeroMemory(this,sizeof(PlugRecord)); }
+};
+
+struct PromoteRecord
+{
+ PromoteRecord *next;
+
+ size_t Root;
+ size_t Value;
+ size_t methodTable;
+
+ PromoteRecord() { ZeroMemory(this,sizeof(PromoteRecord)); }
+};
+
+struct RelocRecord
+{
+ RelocRecord *next;
+
+ size_t Root;
+ size_t PrevValue;
+ size_t NewValue;
+ size_t methodTable;
+
+ RelocRecord() { ZeroMemory(this,sizeof(RelocRecord)); }
+};
+
+struct GCRecord
+{
+ ULONG64 GCCount;
+
+ // BOOL IsComplete() { return bFinished && bHaveStart; }
+
+ PlugRecord *PlugList;
+ RelocRecord *RelocList;
+ PromoteRecord *PromoteList;
+
+ void AddPlug(PlugRecord& p) {
+ PlugRecord *pTmp = PlugList;
+ PlugList = new PlugRecord(p);
+ PlugList->next = pTmp;
+ }
+
+ void AddReloc(RelocRecord& r) {
+ RelocRecord *pTmp = RelocList;
+ RelocList = new RelocRecord(r);
+ RelocList->next = pTmp;
+ }
+
+ void AddPromote(PromoteRecord& r) {
+ PromoteRecord *pTmp = PromoteList;
+ PromoteList = new PromoteRecord(r);
+ PromoteList->next = pTmp;
+ }
+
+ UINT PlugCount() {
+ UINT ret = 0;
+ PlugRecord *Iter = PlugList;
+ while (Iter) {
+ Iter = Iter->next;
+ ret++;
+ }
+ return ret;
+ }
+
+ UINT RelocCount() {
+ UINT ret = 0;
+ RelocRecord *Iter = RelocList;
+ while (Iter) {
+ Iter = Iter->next;
+ ret++;
+ }
+ return ret;
+ }
+
+ UINT PromoteCount() {
+ UINT ret = 0;
+ PromoteRecord *Iter = PromoteList;
+ while (Iter) {
+ Iter = Iter->next;
+ ret++;
+ }
+ return ret;
+ }
+
+ void Clear() {
+
+ PlugRecord *pTrav = PlugList;
+ while (pTrav) {
+ PlugRecord *pTmp = pTrav->next;
+ delete pTrav;
+ pTrav = pTmp;
+ }
+
+ RelocRecord *pTravR = RelocList;
+ while (pTravR) {
+ RelocRecord *pTmp = pTravR->next;
+ delete pTravR;
+ pTravR = pTmp;
+ }
+
+ PromoteRecord *pTravP = PromoteList;
+ while (pTravP) {
+ PromoteRecord *pTmp = pTravP->next;
+ delete pTravP;
+ pTravP = pTmp;
+ }
+
+ ZeroMemory(this,sizeof(GCRecord));
+ }
+
+};
+
+#define MAX_GCRECORDS 500
+UINT g_recordCount = 0;
+GCRecord g_records[MAX_GCRECORDS];
+
+void GcHistClear()
+{
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ g_records[i].Clear();
+ }
+ g_recordCount = 0;
+}
+
+void GcHistAddLog(LPCSTR msg, StressMsg* stressMsg)
+{
+ if (g_recordCount >= MAX_GCRECORDS)
+ {
+ return;
+ }
+
+ if (strcmp(msg, ThreadStressLog::gcPlugMoveMsg()) == 0)
+ {
+ PlugRecord pr;
+ // this is a plug message
+ _ASSERTE(stressMsg->numberOfArgs == 3);
+ pr.PlugStart = (size_t) stressMsg->args[0];
+ pr.PlugEnd = (size_t) stressMsg->args[1];
+ pr.Delta = (size_t) stressMsg->args[2];
+
+ g_records[g_recordCount].AddPlug(pr);
+ }
+ else if (strcmp(msg, ThreadStressLog::gcRootMsg()) == 0)
+ {
+ // this is a root message
+ _ASSERTE(stressMsg->numberOfArgs == 4);
+ RelocRecord rr;
+ rr.Root = (size_t) stressMsg->args[0];
+ rr.PrevValue = (size_t) stressMsg->args[1];
+ rr.NewValue = (size_t) stressMsg->args[2];
+ rr.methodTable = (size_t) stressMsg->args[3];
+ g_records[g_recordCount].AddReloc(rr);
+ }
+ else if (strcmp(msg, ThreadStressLog::gcRootPromoteMsg()) == 0)
+ {
+ // this is a promote message
+ _ASSERTE(stressMsg->numberOfArgs == 3);
+ PromoteRecord pr;
+ pr.Root = (size_t) stressMsg->args[0];
+ pr.Value = (size_t) stressMsg->args[1];
+ pr.methodTable = (size_t) stressMsg->args[2];
+ g_records[g_recordCount].AddPromote(pr);
+ }
+ else if (strcmp(msg, ThreadStressLog::gcStartMsg()) == 0)
+ {
+ // Gc start!
+ _ASSERTE(stressMsg->numberOfArgs == 3);
+ ULONG64 gc_count = (ULONG64) stressMsg->args[0];
+ g_records[g_recordCount].GCCount = gc_count;
+ g_recordCount++;
+ }
+ else if (strcmp(msg, ThreadStressLog::gcEndMsg()) == 0)
+ {
+ // Gc end!
+ // ULONG64 gc_count = (ULONG64) stressMsg->data;
+ // ExtOut ("ENDGC %d\n", gc_count);
+ }
+}
+
+DECLARE_API(HistStats)
+{
+ INIT_API();
+
+ ExtOut ("%8s %8s %8s\n",
+ "GCCount", "Promotes", "Relocs");
+ ExtOut ("-----------------------------------\n");
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ UINT PromoteCount = g_records[i].PromoteCount();
+ UINT RelocCount = g_records[i].RelocCount();
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ ExtOut ("%8d %8d %8d\n",
+ GCCount,
+ PromoteCount,
+ RelocCount);
+ }
+
+ BOOL bErrorFound = FALSE;
+
+ // Check for duplicate Reloc or Promote messages within one gc.
+ // Method is very inefficient, improve it later.
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ { // Promotes
+ PromoteRecord *Iter = g_records[i].PromoteList;
+ UINT GCCount = (UINT) g_records[i].GCCount;
+ while (Iter)
+ {
+ PromoteRecord *innerIter = Iter->next;
+ while (innerIter)
+ {
+ if (Iter->Root == innerIter->Root)
+ {
+ ExtOut ("Root %p promoted multiple times in gc %d\n",
+ SOS_PTR(Iter->Root),
+ GCCount);
+ bErrorFound = TRUE;
+ }
+ innerIter = innerIter->next;
+ }
+
+ Iter = Iter->next;
+ }
+ }
+
+ { // Relocates
+ RelocRecord *Iter = g_records[i].RelocList;
+ UINT GCCount = (UINT) g_records[i].GCCount;
+ while (Iter)
+ {
+ RelocRecord *innerIter = Iter->next;
+ while (innerIter)
+ {
+ if (Iter->Root == innerIter->Root)
+ {
+ ExtOut ("Root %p relocated multiple times in gc %d\n",
+ SOS_PTR(Iter->Root),
+ GCCount);
+ bErrorFound = TRUE;
+ }
+ innerIter = innerIter->next;
+ }
+
+ Iter = Iter->next;
+ }
+ }
+ }
+
+ if (!bErrorFound)
+ {
+ ExtOut ("No duplicate promote or relocate messages found in the log.\n");
+ }
+
+ return Status;
+}
+
+DECLARE_API(HistRoot)
+{
+ INIT_API();
+ size_t nArg;
+
+ StringHolder rootstr;
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&rootstr.data, COSTRING},
+ };
+
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ return Status;
+
+ if (nArg != 1)
+ {
+ ExtOut ("!Root <valid object pointer>\n");
+ return Status;
+ }
+
+ size_t Root = (size_t) GetExpression(rootstr.data);
+
+ ExtOut ("%8s %" POINTERSIZE "s %" POINTERSIZE "s %9s %20s\n",
+ "GCCount", "Value", "MT", "Promoted?", "Notes");
+ ExtOut ("---------------------------------------------------------\n");
+
+ bool bBoringPeople = false;
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ // Find promotion records...there should only be one.
+ PromoteRecord *pPtr = g_records[i].PromoteList;
+ PromoteRecord *pPromoteRec = NULL;
+ bool bPromotedMoreThanOnce = false;
+ while(pPtr)
+ {
+ if (pPtr->Root == Root)
+ {
+ if (pPromoteRec)
+ {
+ bPromotedMoreThanOnce = true;
+ }
+ else
+ {
+ pPromoteRec = pPtr;
+ }
+ }
+ pPtr = pPtr->next;
+ }
+
+ RelocRecord *pReloc = g_records[i].RelocList;
+ RelocRecord *pRelocRec = NULL;
+ bool bRelocatedMoreThanOnce = false;
+ while(pReloc)
+ {
+ if (pReloc->Root == Root)
+ {
+ if (pRelocRec)
+ {
+ bRelocatedMoreThanOnce = true;
+ }
+ else
+ {
+ pRelocRec = pReloc;
+ }
+ }
+ pReloc = pReloc->next;
+ }
+
+ // Validate the records found for this root.
+ if (pRelocRec != NULL)
+ {
+ bBoringPeople = false;
+
+ ExtOut ("%8d %p %p %9s ", GCCount,
+ SOS_PTR(pRelocRec->NewValue),
+ SOS_PTR(pRelocRec->methodTable),
+ pPromoteRec ? "yes" : "no");
+ if (pPromoteRec != NULL)
+ {
+ // There should be similarities between the promote and reloc record
+ if (pPromoteRec->Value != pRelocRec->PrevValue ||
+ pPromoteRec->methodTable != pRelocRec->methodTable)
+ {
+ ExtOut ("promote/reloc records in error ");
+ }
+
+ if (bPromotedMoreThanOnce || bRelocatedMoreThanOnce)
+ {
+ ExtOut ("Duplicate promote/relocs");
+ }
+ }
+ ExtOut ("\n");
+ }
+ else if (pPromoteRec)
+ {
+ ExtOut ("Error: There is a promote record for root %p, but no relocation record\n",
+ (ULONG64) pPromoteRec->Root);
+ }
+ else
+ {
+ if (!bBoringPeople)
+ {
+ ExtOut ("...\n");
+ bBoringPeople = true;
+ }
+ }
+ }
+ return Status;
+}
+
+DECLARE_API(HistObjFind)
+{
+ INIT_API();
+ size_t nArg;
+
+ StringHolder objstr;
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&objstr.data, COSTRING},
+ };
+
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ return Status;
+
+ if (nArg != 1)
+ {
+ ExtOut ("!ObjSearch <valid object pointer>\n");
+ return Status;
+ }
+
+ size_t object = (size_t) GetExpression(objstr.data);
+
+ ExtOut ("%8s %" POINTERSIZE "s %40s\n",
+ "GCCount", "Object", "Message");
+ ExtOut ("---------------------------------------------------------\n");
+
+ size_t curAddress = object;
+ bool bBoringPeople = false;
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ if (curAddress == 0)
+ {
+ break;
+ }
+
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ PromoteRecord *pPtr = g_records[i].PromoteList;
+ while(pPtr)
+ {
+ if (pPtr->Value == curAddress)
+ {
+ bBoringPeople = false;
+ ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
+ ExtOut ("Promotion for root %p (MT = %p)\n",
+ SOS_PTR(pPtr->Root),
+ SOS_PTR(pPtr->methodTable));
+ }
+ pPtr = pPtr->next;
+ }
+
+ RelocRecord *pReloc = g_records[i].RelocList;
+ while(pReloc)
+ {
+ if (pReloc->NewValue == curAddress ||
+ pReloc->PrevValue == curAddress)
+ {
+ bBoringPeople = false;
+ ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
+ ExtOut ("Relocation %s for root %p\n",
+ (pReloc->NewValue == curAddress) ? "NEWVALUE" : "PREVVALUE",
+ SOS_PTR(pReloc->Root));
+ }
+ pReloc = pReloc->next;
+ }
+
+ if (!bBoringPeople)
+ {
+ ExtOut ("...\n");
+ bBoringPeople = true;
+ }
+
+ }
+ return Status;
+}
+
+DECLARE_API(HistObj)
+{
+ INIT_API();
+ size_t nArg;
+
+ StringHolder objstr;
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&objstr.data, COSTRING},
+ };
+
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ return Status;
+
+ if (nArg != 1)
+ {
+ ExtOut ("!object <valid object pointer>\n");
+ return Status;
+ }
+
+ size_t object = (size_t) GetExpression(objstr.data);
+
+ ExtOut ("%8s %" POINTERSIZE "s %40s\n",
+ "GCCount", "Object", "Roots");
+ ExtOut ("---------------------------------------------------------\n");
+
+ size_t curAddress = object;
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ if (curAddress == 0)
+ {
+ break;
+ }
+
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
+
+ RelocRecord *pReloc = g_records[i].RelocList;
+ size_t candidateCurAddress = curAddress;
+ bool bFirstReloc = true;
+ while(pReloc)
+ {
+ if (pReloc->NewValue == curAddress)
+ {
+ ExtOut ("%p, ", SOS_PTR(pReloc->Root));
+ if (bFirstReloc)
+ {
+ candidateCurAddress = pReloc->PrevValue;
+ bFirstReloc = false;
+ }
+ else if (candidateCurAddress != pReloc->PrevValue)
+ {
+ ExtOut ("differing reloc values for this object!\n");
+ }
+ }
+ pReloc = pReloc->next;
+ }
+
+ ExtOut ("\n");
+ curAddress = candidateCurAddress;
+ }
+ return Status;
+}
+
+DECLARE_API(HistInit)
+{
+ INIT_API();
+
+ GcHistClear();
+
+ CLRDATA_ADDRESS stressLogAddr = 0;
+ if (g_sos->GetStressLogAddress(&stressLogAddr) != S_OK)
+ {
+ ExtOut("Unable to find stress log via DAC\n");
+ return E_FAIL;
+ }
+
+ ExtOut ("Attempting to read Stress log\n");
+
+ Status = StressLog::Dump(stressLogAddr, NULL, g_ExtData);
+ if (Status == S_OK)
+ ExtOut("SUCCESS: GCHist structures initialized\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, GCHist commands unavailable\n");
+ else
+ ExtOut("FAILURE: Stress log unreadable\n");
+
+ return Status;
+}
+
+DECLARE_API(HistClear)
+{
+ INIT_API();
+ GcHistClear();
+ ExtOut("Completed successfully.\n");
+ return Status;
+}
+
diff --git a/src/ToolBox/SOS/Strike/gcroot.cpp b/src/ToolBox/SOS/Strike/gcroot.cpp
new file mode 100644
index 0000000000..f68b935e21
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/gcroot.cpp
@@ -0,0 +1,2503 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+/*
+ * This file defines the support classes that allow us to operate on the object graph of the process that SOS
+ * is analyzing.
+ *
+ * The GCRoot algorithm is based on three simple principles:
+ * 1. Only consider an object once. When we inspect an object, read its references and don't ever touch
+ * it again. This ensures that our upper bound on the amount of time we spend walking the object
+ * graph very quickly reaches resolution. The objects we've already inspected (and thus won't inspect
+ * again) is tracked by the mConsidered variable.
+ * 2. Be extremely careful about reads from the target process. We use a linear cache for reading from
+ * object data. We also cache everything about the method tables we read out of, as well as caching
+ * the GCDesc which is required to walk the object's references.
+ * 3. Use O(1) data structures for anything perf-critical. Almost all of the data structures we use to
+ * keep track of data have very fast lookups. For example, to keep track of the objects we've considered
+ * we use a unordered_set. Similarly to keep track of MethodTable data we use a unordered_map to track the
+ * mt -> mtinfo mapping.
+ */
+
+#include "sos.h"
+#include "disasm.h"
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+#define _ASSERTE(a) {;}
+
+#include "gcdesc.h"
+
+#include "safemath.h"
+
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+#ifndef _ASSERTE
+#ifdef _DEBUG
+#define _ASSERTE(expr) \
+ do { if (!(expr) ) { ExtErr("_ASSERTE fired:\n\t%s\n", #expr); if (IsDebuggerPresent()) DebugBreak(); } } while (0)
+#else
+#define _ASSERTE(x)
+#endif
+#endif // ASSERTE
+
+inline size_t ALIGN_DOWN( size_t val, size_t alignment )
+{
+ // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
+ _ASSERTE( 0 == (alignment & (alignment - 1)) );
+ size_t result = val & ~(alignment - 1);
+ return result;
+}
+
+inline void* ALIGN_DOWN( void* val, size_t alignment )
+{
+ return (void*) ALIGN_DOWN( (size_t)val, alignment );
+}
+
+LinearReadCache::LinearReadCache(ULONG pageSize)
+ : mCurrPageStart(0), mPageSize(pageSize), mCurrPageSize(0), mPage(0)
+{
+ mPage = new BYTE[pageSize];
+ ClearStats();
+}
+
+LinearReadCache::~LinearReadCache()
+{
+ if (mPage)
+ delete [] mPage;
+}
+
+bool LinearReadCache::MoveToPage(TADDR addr, unsigned int size)
+{
+ if (size > mPageSize)
+ size = mPageSize;
+
+ mCurrPageStart = addr;
+ HRESULT hr = g_ExtData->ReadVirtual(mCurrPageStart, mPage, size, &mCurrPageSize);
+
+ if (hr != S_OK)
+ {
+ mCurrPageStart = 0;
+ mCurrPageSize = 0;
+ return false;
+ }
+
+#ifdef _DEBUG
+ mMisses++;
+#endif
+ return true;
+}
+
+
+static const char *NameForHandle(unsigned int type)
+{
+ switch (type)
+ {
+ case 0:
+ return "weak short";
+ case 1:
+ return "weak long";
+ case 2:
+ return "strong";
+ case 3:
+ return "pinned";
+ case 5:
+ return "ref counted";
+ case 6:
+ return "dependent";
+ case 7:
+ return "async pinned";
+ case 8:
+ return "sized ref";
+ }
+
+ return "unknown";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GCRoot functions to cleanup data.
+///////////////////////////////////////////////////////////////////////////////
+void GCRootImpl::ClearSizeData()
+{
+ mConsidered.clear();
+ mSizes.clear();
+}
+
+void GCRootImpl::ClearAll()
+{
+ ClearNodes();
+
+ {
+ std::unordered_map<TADDR, MTInfo*>::iterator itr;
+ for (itr = mMTs.begin(); itr != mMTs.end(); ++itr)
+ delete itr->second;
+ }
+
+ {
+ std::unordered_map<TADDR, RootNode*>::iterator itr;
+ for (itr = mTargets.begin(); itr != mTargets.end(); ++itr)
+ delete itr->second;
+ }
+
+ mMTs.clear();
+ mTargets.clear();
+ mConsidered.clear();
+ mSizes.clear();
+ mDependentHandleMap.clear();
+ mCache.ClearStats();
+
+ mAll = false;
+ mSize = false;
+}
+
+void GCRootImpl::ClearNodes()
+{
+ std::list<RootNode*>::iterator itr;
+
+ for (itr = mCleanupList.begin(); itr != mCleanupList.end(); ++itr)
+ delete *itr;
+
+ mCleanupList.clear();
+ mRootNewList.clear();
+}
+
+GCRootImpl::RootNode *GCRootImpl::NewNode(TADDR obj, MTInfo *mtInfo, bool fromDependent)
+{
+ // We need to create/destroy a TON of nodes during execution of GCRoot functions.
+ // To avoid heap fragmentation (and since it's faster), we don't actually new/delete
+ // nodes unless we have to. Instead we keep a stl list with free nodes to use.
+ RootNode *toReturn = NULL;
+
+ if (mRootNewList.size())
+ {
+ toReturn = mRootNewList.back();
+ mRootNewList.pop_back();
+ }
+ else
+ {
+ toReturn = new RootNode();
+ mCleanupList.push_back(toReturn);
+ }
+
+ toReturn->Object = obj;
+ toReturn->MTInfo = mtInfo;
+ toReturn->FromDependentHandle = fromDependent;
+ return toReturn;
+}
+
+void GCRootImpl::DeleteNode(RootNode *node)
+{
+ // Add node to the "new list".
+ node->Clear();
+ mRootNewList.push_back(node);
+}
+
+void GCRootImpl::GetDependentHandleMap(std::unordered_map<TADDR, std::list<TADDR>> &map)
+{
+ unsigned int type = HNDTYPE_DEPENDENT;
+ ToRelease<ISOSHandleEnum> handles;
+
+ HRESULT hr = g_sos->GetHandleEnumForTypes(&type, 1, &handles);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to walk dependent handles. GCRoot may miss paths.\n");
+ return;
+ }
+
+ SOSHandleData data[4];
+ unsigned int fetched = 0;
+
+ do
+ {
+ hr = handles->Next(_countof(data), data, &fetched);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Error walking dependent handles. GCRoot may miss paths.\n");
+ return;
+ }
+
+ for (unsigned int i = 0; i < fetched; ++i)
+ {
+ if (data[i].Secondary != 0)
+ {
+ TADDR obj = 0;
+ TADDR target = TO_TADDR(data[i].Secondary);
+
+ MOVE(obj, TO_TADDR(data[i].Handle));
+
+ map[obj].push_back(target);
+ }
+ }
+ } while (fetched == _countof(data));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Public functions.
+///////////////////////////////////////////////////////////////////////////////
+int GCRootImpl::PrintRootsForObject(TADDR target, bool all, bool noStacks)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ mAll = all;
+
+ // Add "target" to the mTargets list.
+ TADDR mt = ReadPointerCached(target);
+ RootNode *node = NewNode(target, GetMTInfo(mt));
+ mTargets[target] = node;
+
+ // Look for roots on the HandleTable, FQ, and all threads.
+ int count = 0;
+
+ if (!noStacks)
+ count += PrintRootsOnAllThreads();
+
+ count += PrintRootsOnHandleTable();
+ count += PrintRootsOnFQ();
+
+ if(count == 0)
+ {
+ count += PrintRootsOnFQ(true);
+ if(count)
+ {
+ ExtOut("Warning: These roots are from finalizable objects that are not yet ready for finalization.\n");
+ ExtOut("This is to handle the case where objects re-register themselves for finalization.\n");
+ ExtOut("These roots may be false positives.\n");
+ }
+ }
+
+ mCache.PrintStats(__FUNCTION__);
+ return count;
+}
+
+
+bool GCRootImpl::PrintPathToObject(TADDR root, TADDR target)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ // Add "target" to the mTargets list.
+ TADDR mt = ReadPointerCached(target);
+ RootNode *node = NewNode(target, GetMTInfo(mt));
+ mTargets[target] = node;
+
+ // Check to see if the root reaches the target.
+ RootNode *path = FindPathToTarget(root);
+ if (path)
+ {
+ ExtOut("%p %S\n", SOS_PTR(path->Object), path->GetTypeName());
+ path = path->Next;
+
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n",SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ mCache.PrintStats(__FUNCTION__);
+ return true;
+ }
+
+ mCache.PrintStats(__FUNCTION__);
+ return false;
+}
+
+size_t GCRootImpl::ObjSize(TADDR root)
+{
+ // Calculates the size of the closure of objects kept alive by root.
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ // mSize tells GCRootImpl to build the "mSizes" table with the total size
+ // each object roots.
+ mSize = true;
+
+ // Note that we are calling the same method as we would to locate the rooting
+ // chain for an object, but we haven't added any items to mTargets. This means
+ // the algorithm will scan all objects and never terminate until it has walked
+ // all objects in the closure.
+ FindPathToTarget(root);
+
+ mCache.PrintStats(__FUNCTION__);
+ return mSizes[root];
+}
+
+void GCRootImpl::ObjSize()
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+ mSize = true;
+
+ // Walks all roots in the process, and prints out the object size for each one.
+ PrintRootsOnAllThreads();
+ PrintRootsOnHandleTable();
+ PrintRootsOnFQ();
+
+ mCache.PrintStats(__FUNCTION__);
+}
+
+
+const std::unordered_set<TADDR> &GCRootImpl::GetLiveObjects(bool excludeFQ)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ // Walk each root in the process without setting a target. This has the effect of
+ // causing us to walk every object in the process, adding them to mConsidered as we
+ // go.
+ PrintRootsOnAllThreads();
+ PrintRootsOnHandleTable();
+
+ if (!excludeFQ)
+ PrintRootsOnFQ();
+
+ mCache.PrintStats(__FUNCTION__);
+ return mConsidered;
+}
+
+int GCRootImpl::FindRoots(int gen, TADDR target)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ if (gen == -1 || ((UINT)gen) == GetMaxGeneration())
+ {
+ // If this is a gen 2 !FindRoots, just do a regular !GCRoot
+ return PrintRootsForObject(target, false, false);
+ }
+ else
+ {
+ // Otherwise walk all roots for only the given generation.
+ int count = PrintRootsInOlderGen();
+ count += PrintRootsOnHandleTable(gen);
+ count += PrintRootsOnFQ();
+ return count;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// GCRoot Methods for printing out results.
+///////////////////////////////////////////////////////////////////////////////
+void GCRootImpl::ReportSizeInfo(const SOSHandleData &handle, TADDR obj)
+{
+ // Print size for a handle (!objsize)
+ TADDR mt = ReadPointer(obj);
+ MTInfo *mtInfo = GetMTInfo(mt);
+
+ const WCHAR *type = mtInfo ? mtInfo->GetTypeName() : W("unknown type");
+
+ size_t size = mSizes[obj];
+ ExtOut("Handle (%s): %p -> %p: %d (0x%x) bytes (%S)\n", NameForHandle(handle.Type), SOS_PTR(handle.Handle),
+ SOS_PTR(obj), size, size, type);
+}
+
+
+void GCRootImpl::ReportSizeInfo(DWORD thread, const SOSStackRefData &stackRef, TADDR obj)
+{
+ // Print size for a stack root (!objsize)
+ WString frame;
+ if (stackRef.SourceType == SOS_StackSourceIP)
+ frame = MethodNameFromIP(stackRef.Source);
+ else
+ frame = GetFrameFromAddress(TO_TADDR(stackRef.Source));
+
+ WString regOutput = BuildRegisterOutput(stackRef);
+
+ TADDR mt = ReadPointer(obj);
+ MTInfo *mtInfo = GetMTInfo(mt);
+ const WCHAR *type = mtInfo ? mtInfo->GetTypeName() : W("unknown type");
+
+ size_t size = mSizes[obj];
+ ExtOut("Thread %x (%S): %S: %d (0x%x) bytes (%S)\n", thread, frame.c_str(), regOutput.c_str(), size, size, type);
+}
+
+void GCRootImpl::ReportOneHandlePath(const SOSHandleData &handle, RootNode *path, bool printHeader)
+{
+ if (printHeader)
+ ExtOut("HandleTable:\n");
+
+ ExtOut(" %p (%s handle)\n", SOS_PTR(handle.Handle), NameForHandle(handle.Type));
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+void GCRootImpl::ReportOnePath(DWORD thread, const SOSStackRefData &stackRef, RootNode *path, bool printThread, bool printFrame)
+{
+ if (printThread)
+ ExtOut("Thread %x:\n", thread);
+
+ if (printFrame)
+ {
+ if (stackRef.SourceType == SOS_StackSourceIP)
+ {
+ WString methodName = MethodNameFromIP(stackRef.Source);
+ ExtOut(" %p %p %S\n", SOS_PTR(stackRef.StackPointer), SOS_PTR(stackRef.Source), methodName.c_str());
+ }
+ else
+ {
+ WString frameName = GetFrameFromAddress(TO_TADDR(stackRef.Source));
+ ExtOut(" %p %S\n", SOS_PTR(stackRef.Source), frameName.c_str());
+ }
+ }
+
+ WString regOutput = BuildRegisterOutput(stackRef, false);
+ ExtOut(" %S\n", regOutput.c_str());
+
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+void GCRootImpl::ReportOneFQEntry(TADDR root, RootNode *path, bool printHeader)
+{
+ if (printHeader)
+ ExtOut("Finalizer Queue:\n");
+
+ ExtOut(" %p\n", SOS_PTR(root));
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+void GCRootImpl::ReportOlderGenEntry(TADDR root, RootNode *path, bool printHeader)
+{
+ if (printHeader)
+ ExtOut("Older Generation:\n");
+
+ ExtOut(" %p\n", SOS_PTR(root));
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+//////////////////////////////////////////////////////
+int GCRootImpl::PrintRootsInOlderGen()
+{
+ // Use a different linear read cache here instead of polluting the object read cache.
+ LinearReadCache cache(512);
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapAnalyzeData analyzeData;
+ if (analyzeData.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting gc heap analyze data\n");
+ return 0;
+ }
+
+ if (!analyzeData.heap_analyze_success)
+ {
+ ExtOut("Failed to gather needed data, possibly due to memory contraints in the debuggee.\n");
+ ExtOut("To try again re-issue the !FindRoots -gen <N> command.\n");
+ return 0;
+ }
+
+ ExtDbgOut("internal_root_array = %#p\n", SOS_PTR(analyzeData.internal_root_array));
+ ExtDbgOut("internal_root_array_index = %#p\n", SOS_PTR(analyzeData.internal_root_array_index));
+
+ TADDR start = TO_TADDR(analyzeData.internal_root_array);
+ TADDR stop = TO_TADDR(analyzeData.internal_root_array + sizeof(TADDR) * (size_t)analyzeData.internal_root_array_index);
+
+ return PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOlderGenEntry, true);
+ }
+ else
+ {
+ int total = 0;
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = GetGcHeapCount();
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtErr("Failed to get GCHeaps: integer overflow\n");
+ return 0;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtErr("Failed to get GCHeaps\n");
+ return 0;
+ }
+
+ for (UINT n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapAnalyzeData analyzeData;
+ if (analyzeData.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtErr("Error requesting gc heap analyze data for heap %p\n", SOS_PTR(heapAddrs[n]));
+ continue;
+ }
+
+ if (!analyzeData.heap_analyze_success)
+ {
+ ExtOut("Failed to gather needed data, possibly due to memory contraints in the debuggee.\n");
+ ExtOut("To try again re-issue the !FindRoots -gen <N> command.\n");
+ continue;
+ }
+
+ ExtDbgOut("internal_root_array = %#p\n", SOS_PTR(analyzeData.internal_root_array));
+ ExtDbgOut("internal_root_array_index = %#p\n", SOS_PTR(analyzeData.internal_root_array_index));
+
+ TADDR start = TO_TADDR(analyzeData.internal_root_array);
+ TADDR stop = TO_TADDR(analyzeData.internal_root_array + sizeof(TADDR) * (size_t)analyzeData.internal_root_array_index);
+
+ total += PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOlderGenEntry, total == 0);
+ }
+
+ return total;
+ }
+}
+
+
+int GCRootImpl::PrintRootsOnFQ(bool notReadyForFinalization)
+{
+ // Here are objects kept alive by objects in the finalizer queue.
+ DacpGcHeapDetails heapDetails;
+
+ // Use a different linear read cache here instead of polluting the object read cache.
+ LinearReadCache cache(512);
+
+ if (!IsServerBuild())
+ {
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting heap data.\n");
+ return 0;
+ }
+
+ // If we include objects that are not ready for finalization, we may report
+ // false positives. False positives occur if the object is not ready for finalization
+ // and does not re-register itself for finalization inside the finalizer.
+ TADDR start = 0;
+ TADDR stop = 0;
+ if(notReadyForFinalization)
+ {
+ start = TO_TADDR(SegQueue(heapDetails, gen_segment(GetMaxGeneration())));
+ stop = TO_TADDR(SegQueueLimit(heapDetails, CriticalFinalizerListSeg));
+ }
+ else
+ {
+ start = TO_TADDR(SegQueue(heapDetails, CriticalFinalizerListSeg));
+ stop = TO_TADDR(SegQueue(heapDetails, FinalizerListSeg));
+ }
+
+ return PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOneFQEntry, true);
+ }
+ else
+ {
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = GetGcHeapCount();
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtErr("Failed to get GCHeaps: integer overflow\n");
+ return 0;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtErr("Error requesting heap data.\n");
+ return 0;
+ }
+
+ int total = 0;
+ for (UINT n = 0; n < dwNHeaps; n ++)
+ {
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtErr("Error requesting heap data for heap %d.\n", n);
+ continue;
+ }
+
+ // If we include objects that are not ready for finalization, we may report
+ // false positives. False positives occur if the object is not ready for finalization
+ // and does not re-register itself for finalization inside the finalizer.
+ TADDR start = 0;
+ TADDR stop = 0;
+ if(notReadyForFinalization)
+ {
+ start = TO_TADDR(SegQueue(heapDetails, gen_segment(GetMaxGeneration())));
+ stop = TO_TADDR(SegQueueLimit(heapDetails, CriticalFinalizerListSeg));
+ }
+ else
+ {
+ start = TO_TADDR(SegQueue(heapDetails, CriticalFinalizerListSeg));
+ stop = TO_TADDR(SegQueueLimit(heapDetails, FinalizerListSeg));
+ }
+
+ total += PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOneFQEntry, total == 0);
+ }
+
+ return total;
+ }
+}
+
+int GCRootImpl::PrintRootsInRange(LinearReadCache &cache, TADDR start, TADDR stop, ReportCallback func, bool printHeader)
+{
+ int total = 0;
+
+ // Walk the range [start, stop) and consider each pointer in the range as a root.
+ while (start < stop)
+ {
+ if (IsInterrupt())
+ return total;
+
+ // Use the cache parameter here instead of mCache. If you use mCache it will be reset
+ // when calling into FindPathToTarget.
+ TADDR root = 0;
+ bool res = cache.Read(start, &root, true);
+
+ if (res && root)
+ {
+ RootNode *path = FindPathToTarget(root);
+ if (path)
+ {
+ func(root, path, printHeader);
+ total++;
+ printHeader = false;
+ }
+ }
+
+ start += sizeof(TADDR);
+ }
+
+ return total;
+}
+
+int GCRootImpl::PrintRootsOnAllThreads()
+{
+ ArrayHolder<DWORD_PTR> threadList = NULL;
+ int numThreads = 0;
+
+ // GetThreadList calls ReportOOM so we don't need to do that here.
+ HRESULT hr = GetThreadList(&threadList, &numThreads);
+ if (FAILED(hr) || !threadList)
+ return 0;
+
+ // Walk each thread and process the roots on it.
+ int total = 0;
+ DacpThreadData vThread;
+ for (int i = 0; i < numThreads; i++)
+ {
+ if (IsInterrupt())
+ return total;
+
+ if (FAILED(vThread.Request(g_sos, threadList[i])))
+ continue;
+
+ if (vThread.osThreadId)
+ total += PrintRootsOnThread(vThread.osThreadId);
+ }
+
+ return total;
+}
+
+int GCRootImpl::PrintRootsOnThread(DWORD osThreadId)
+{
+ // Grab all object rootson the thread.
+ unsigned int refCount = 0;
+ ArrayHolder<SOSStackRefData> refs = NULL;
+
+ int total = 0;
+ bool first = true;
+ if (FAILED(::GetGCRefs(osThreadId, &refs, &refCount, NULL, NULL)))
+ {
+ ExtOut("Failed to walk thread %x\n", osThreadId);
+ return total;
+ }
+
+ // Walk each non-null root, find if it contains a path to the target,
+ // and if so print it out.
+ CLRDATA_ADDRESS prevSource = 0, prevSP = 0;
+ for (unsigned int i = 0; i < refCount; ++i)
+ {
+ if (IsInterrupt())
+ return total;
+
+ if (refs[i].Object)
+ {
+ if (mSize)
+ ClearSizeData();
+
+ RootNode *path = FindPathToTarget(TO_TADDR(refs[i].Object));
+ if (path)
+ {
+ bool reportFrame = refs[i].Source != prevSource || refs[i].StackPointer != prevSP;
+ ReportOnePath(osThreadId, refs[i], path, first, reportFrame);
+ first = false;
+ total++;
+ }
+
+ if (mSize)
+ ReportSizeInfo(osThreadId, refs[i], TO_TADDR(refs[i].Object));
+ }
+ }
+
+ return total;
+}
+
+int GCRootImpl::PrintRootsOnHandleTable(int gen)
+{
+ // Get handle data.
+ ToRelease<ISOSHandleEnum> pEnum = NULL;
+ HRESULT hr = S_OK;
+
+ if (gen == -1 || (ULONG)gen == GetMaxGeneration())
+ hr = g_sos->GetHandleEnum(&pEnum);
+ else
+ hr = g_sos->GetHandleEnumForGC(gen, &pEnum);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to walk the HandleTable!\n");
+ return 0;
+ }
+
+ int total = 0;
+ unsigned int fetched = 0;
+ SOSHandleData handles[8];
+
+ bool printHeader = true;
+ do
+ {
+ // Fetch more handles
+ hr = pEnum->Next(_countof(handles), handles, &fetched);
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to request more handles.");
+ return total;
+ }
+
+ // Find rooting info for each handle.
+ for (unsigned int i = 0; i < fetched; ++i)
+ {
+ if (IsInterrupt())
+ return total;
+
+ // Ignore handles which aren't actually roots.
+ if (!handles[i].StrongReference)
+ continue;
+
+ // clear the size table
+ if (mAll)
+ ClearSizeData();
+
+ // Get the object the handle points to.
+ TADDR root = ReadPointer(TO_TADDR(handles[i].Handle));
+
+ // Only inspect handle if the object is non-null, and not one we've already walked.
+ if (root)
+ {
+ // Find all paths to the object and don't clean up the return value.
+ // It's tracked by mCleanupList.
+ RootNode *path = FindPathToTarget(root);
+ if (path)
+ {
+ ReportOneHandlePath(handles[i], path, printHeader);
+ printHeader = false;
+ total++;
+ }
+
+ if (mSize)
+ ReportSizeInfo(handles[i], root);
+ }
+ }
+ }
+ while (_countof(handles) == fetched);
+
+ return total;
+}
+
+GCRootImpl::RootNode *GCRootImpl::FilterRoots(RootNode *&list)
+{
+ // Filter the list of GC refs:
+ // - Remove objects that we have already considered
+ // - Check to see if we've located the target object (or an object which points to the target).
+ RootNode *curr = list;
+ RootNode *keep = NULL;
+
+ while (curr)
+ {
+ // We don't check for Control-C in this loop to avoid inconsistent data.
+ RootNode *curr_next = curr->Next;
+
+ std::unordered_map<TADDR, RootNode *>::iterator targetItr = mTargets.find(curr->Object);
+ if (targetItr != mTargets.end())
+ {
+ // We found the object we are looking for (or an object which points to it)!
+ // Return the target, propogate whether we got the target from a dependent handle.
+ targetItr->second->FromDependentHandle = curr->FromDependentHandle;
+ return targetItr->second;
+ }
+ else if (mConsidered.find(curr->Object) != mConsidered.end())
+ {
+ curr->Remove(list);
+
+ DeleteNode(curr);
+ }
+
+ curr = curr_next;
+ }
+
+ return NULL;
+}
+
+/* This is the core of the GCRoot algorithm. It is:
+ * 1. Start with a list of "targets" (objects we are trying to find the roots for) and a root
+ * in the process.
+ * 2. Let the root be "curr".
+ * 3. Find all objects curr points to and place them in curr->GCRefs (a linked list).
+ * 4. Walk curr->GCRefs. If we find any of the "targets", return the current path. If not,
+ * filter out any objects we've already considered (the mConsidered set).
+ * 5. Look at curr->GCRefs:
+ * 5a. If curr->GCRefs is NULL then we have walked all references of this object. Pop "curr"
+ * from the current path (curr = curr->Prev). If curr is NULL, we walked all objects and
+ * didn't find a target, return NULL. If curr is non-null, goto 5.
+ * 5b. If curr->GCRefs is non-NULL, pop one entry and push it onto the path (that is:
+ * curr->Next = curr->GCRefs; curr = curr->Next; curr->GCRefs = curr->GCRefs->Next)
+ * 6. Goto 3.
+ */
+GCRootImpl::RootNode *GCRootImpl::FindPathToTarget(TADDR root)
+{
+ // Early out. We may have already looked at this object.
+ std::unordered_map<TADDR, RootNode *>::iterator targetItr = mTargets.find(root);
+ if (targetItr != mTargets.end())
+ return targetItr->second;
+ else if (mConsidered.find(root) != mConsidered.end())
+ return NULL;
+
+ // Add obj as a considered node (since we are considering it now).
+ mConsidered.insert(root);
+
+ // Create path.
+ RootNode *path = NewNode(root);
+
+ RootNode *curr = path;
+ while (curr)
+ {
+ if (IsInterrupt())
+ return NULL;
+
+ // If this is a new reference we are walking, we haven't filled the list of objects
+ // this one points to. Update that first.
+ if (!curr->FilledRefs)
+ {
+ // Get the list of GC refs.
+ curr->GCRefs = GetGCRefs(path, curr);
+
+ // Filter the refs to remove objects we've already inspected.
+ RootNode *foundTarget = FilterRoots(curr->GCRefs);
+
+ // If we've found the target, great! Return the path to the target.
+ if (foundTarget)
+ {
+ // Link the current to the target.
+ curr->Next = foundTarget;
+ foundTarget->Prev = curr;
+
+ // If the user requested all paths, set each node in the path to be a target.
+ // Normally, we don't consider a node we've already seen, which means if we don't
+ // get a *completely* unique path, it's not printed out. By adding each of the
+ // nodes in the paths we find as potential targets, it prints out *every* path
+ // to the target, including ones with duplicate nodes. This is much slower.
+ if (mAll)
+ {
+ RootNode *tmp = path;
+
+ while (tmp)
+ {
+ if (mTargets.find(tmp->Object) != mTargets.end())
+ break;
+
+ mTargets[tmp->Object] = tmp;
+ tmp = tmp->Next;
+ }
+ }
+
+ return path;
+ }
+ }
+
+ // We have filled the references, now walk them depth-first.
+ if (curr->GCRefs)
+ {
+ RootNode *next = curr->GCRefs;
+ curr->GCRefs = next->Next;
+
+ if (mConsidered.find(next->Object) != mConsidered.end())
+ {
+ // Whoops. This object was considered deeper down the tree, so we
+ // don't need to do it again. Delete this node and continue looping.
+ DeleteNode(next);
+ }
+ else
+ {
+ // Clear associations.
+ if (curr->GCRefs)
+ curr->GCRefs->Prev = NULL;
+
+ next->Next = NULL;
+
+ // Link curr and next, make next the current node.
+ curr->Next = next;
+ next->Prev = curr;
+ curr = next;
+
+ // Finally, insert the current object into the considered set.
+ mConsidered.insert(curr->Object);
+ // Now the next iteration will operate on "next".
+ }
+ }
+ else
+ {
+ // This object has no more GCRefs. We now need to "pop" it from the current path.
+ RootNode *tmp = curr;
+ curr = curr->Prev;
+ DeleteNode(tmp);
+ }
+ }
+
+ return NULL;
+}
+
+
+GCRootImpl::RootNode *GCRootImpl::GetGCRefs(RootNode *path, RootNode *node)
+{
+ // If this node doesn't have the method table ready, fill it out
+ TADDR obj = node->Object;
+ if (!node->MTInfo)
+ {
+ TADDR mt = ReadPointerCached(obj);
+ MTInfo *mtInfo = GetMTInfo(mt);
+ node->MTInfo = mtInfo;
+ }
+
+ node->FilledRefs = true;
+
+ // MTInfo can be null if we encountered an error reading out of the target
+ // process, just early out here as if it has no references.
+ if (!node->MTInfo)
+ return NULL;
+
+ // Only calculate the size if we need it.
+ size_t objSize = 0;
+ if (mSize || node->MTInfo->ContainsPointers)
+ {
+ objSize = GetSizeOfObject(obj, node->MTInfo);
+
+ // Update object size list, if requested.
+ if (mSize)
+ {
+ mSizes[obj] = 0;
+
+ while (path)
+ {
+ mSizes[path->Object] += objSize;
+ path = path->Next;
+ }
+ }
+ }
+
+ // Early out: If the object doesn't contain any pointers, return.
+ if (!node->MTInfo->ContainsPointers)
+ return NULL;
+
+ // Make sure we have the object's data in the cache.
+ mCache.EnsureRangeInCache(obj, (unsigned int)objSize);
+
+ // Storage for the gc refs.
+ RootNode *refs = NewNode();
+ RootNode *curr = refs;
+
+ // Walk the GCDesc, fill "refs" with non-null references.
+ CGCDesc *gcdesc = node->MTInfo->GCDesc;
+ bool aovc = node->MTInfo->ArrayOfVC;
+ for (sos::RefIterator itr(obj, gcdesc, aovc, &mCache); itr; ++itr)
+ {
+ if (*itr)
+ {
+ curr->Next = NewNode(*itr);
+ curr->Next->Prev = curr;
+ curr = curr->Next;
+ }
+ }
+
+ // Add edges from dependent handles.
+ std::unordered_map<TADDR, std::list<TADDR>>::iterator itr = mDependentHandleMap.find(obj);
+ if (itr != mDependentHandleMap.end())
+ {
+ for (std::list<TADDR>::iterator litr = itr->second.begin(); litr != itr->second.end(); ++litr)
+ {
+ curr->Next = NewNode(*litr, NULL, true);
+ curr->Next->Prev = curr;
+ curr = curr->Next;
+ }
+ }
+
+ // The gcrefs actually start on refs->Next.
+ curr = refs;
+ refs = refs->Next;
+ DeleteNode(curr);
+
+ return refs;
+}
+
+DWORD GCRootImpl::GetComponents(TADDR obj, TADDR mt)
+{
+ // Get the number of components in the object (for arrays and such).
+ DWORD Value = 0;
+
+ // If we fail to read out the number of components, let's assume 0 so we don't try to
+ // read further data from the object.
+ if (!mCache.Read(obj + sizeof(TADDR), &Value, false))
+ return 0;
+
+ // The component size on a String does not contain the trailing NULL character,
+ // so we must add that ourselves.
+ if(TO_TADDR(g_special_usefulGlobals.StringMethodTable) == mt)
+ return Value+1;
+
+ return Value;
+}
+
+// Get the size of the object.
+size_t GCRootImpl::GetSizeOfObject(TADDR obj, MTInfo *info)
+{
+ size_t res = info->BaseSize;
+
+ if (info->ComponentSize)
+ {
+ // this is an array, so the size has to include the size of the components. We read the number
+ // of components from the target and multiply by the component size to get the size.
+ DWORD components = GetComponents(obj, info->MethodTable);
+ res += info->ComponentSize * components;
+ }
+
+#ifdef _TARGET_WIN64_
+ // On x64 we do an optimization to save 4 bytes in almost every string we create, so
+ // pad to min object size if necessary.
+ if (res < min_obj_size)
+ res = min_obj_size;
+#endif // _TARGET_WIN64_
+
+ return (res > 0x10000) ? AlignLarge(res) : Align(res);
+}
+
+GCRootImpl::MTInfo *GCRootImpl::GetMTInfo(TADDR mt)
+{
+ // Remove lower bits in case we are in mark phase
+ mt &= ~3;
+
+ // Do we already have this MethodTable?
+ std::unordered_map<TADDR, MTInfo *>::iterator itr = mMTs.find(mt);
+
+ if (itr != mMTs.end())
+ return itr->second;
+
+ MTInfo *curr = new MTInfo;
+ curr->MethodTable = mt;
+
+ // Get Base/Component size.
+ DacpMethodTableData dmtd;
+
+ if (dmtd.Request(g_sos, mt) != S_OK)
+ {
+ delete curr;
+ return NULL;
+ }
+
+ // Fill out size info.
+ curr->BaseSize = (size_t)dmtd.BaseSize;
+ curr->ComponentSize = (size_t)dmtd.ComponentSize;
+ curr->ContainsPointers = dmtd.bContainsPointers ? true : false;
+
+ // If this method table contains pointers, fill out and cache the GCDesc.
+ if (curr->ContainsPointers)
+ {
+ int nEntries;
+
+ if (FAILED(MOVE(nEntries, mt-sizeof(TADDR))))
+ {
+ ExtOut("Failed to request number of entries.");
+ delete curr;
+ return NULL;
+ }
+
+ if (nEntries < 0)
+ {
+ curr->ArrayOfVC = true;
+ nEntries = -nEntries;
+ }
+ else
+ {
+ curr->ArrayOfVC = false;
+ }
+
+ size_t nSlots = 1 + nEntries * sizeof(CGCDescSeries)/sizeof(TADDR);
+ curr->Buffer = new TADDR[nSlots];
+
+ if (curr->Buffer == NULL)
+ {
+ ReportOOM();
+ delete curr;
+ return NULL;
+ }
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(mt - nSlots*sizeof(TADDR)), curr->Buffer, (ULONG)(nSlots*sizeof(TADDR)), NULL)))
+ {
+ ExtOut("Failed to read GCDesc for MethodTable %p.\n", SOS_PTR(mt));
+ delete curr;
+ return NULL;
+ }
+
+ // Construct the GCDesc map and series.
+ curr->GCDesc = (CGCDesc *)(curr->Buffer+nSlots);
+ }
+
+ mMTs[mt] = curr;
+ return curr;
+}
+
+
+TADDR GCRootImpl::ReadPointer(TADDR location)
+{
+ // Reads a pointer from the cache, but doesn't update the cache if it wasn't in it.
+ TADDR obj = NULL;
+ bool res = mCache.Read(location, &obj, false);
+
+ if (!res)
+ return NULL;
+
+ return obj;
+}
+
+TADDR GCRootImpl::ReadPointerCached(TADDR location)
+{
+ // Reads a pointer from the cache, but updates the cache if it wasn't in it.
+ TADDR obj = NULL;
+ bool res = mCache.Read(location, &obj, true);
+
+ if (!res)
+ return NULL;
+
+ return obj;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+UINT FindAllPinnedAndStrong(DWORD_PTR handlearray[], UINT arraySize)
+{
+ unsigned int fetched = 0;
+ SOSHandleData data[64];
+ UINT pos = 0;
+
+ // We do not call GetHandleEnumByType here with a list of strong handles since we would be
+ // statically setting the list of strong handles, which could change in a future release.
+ // Instead we rely on the dac to provide whether a handle is strong or not.
+ ToRelease<ISOSHandleEnum> handles;
+ HRESULT hr = g_sos->GetHandleEnum(&handles);
+ if (FAILED(hr))
+ {
+ // This should basically never happen unless there's an OOM.
+ ExtOut("Failed to enumerate GC handles. HRESULT=%x.\n", hr);
+ return 0;
+ }
+
+ do
+ {
+ hr = handles->Next(_countof(data), data, &fetched);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to enumerate GC handles. HRESULT=%x.\n", hr);
+ break;
+ }
+
+ for (unsigned int i = 0; i < fetched; ++i)
+ {
+ if (pos >= arraySize)
+ {
+ ExtOut("Buffer overflow while enumerating handles.\n");
+ return pos;
+ }
+
+ if (data[i].StrongReference)
+ {
+ handlearray[pos++] = (DWORD_PTR)data[i].Handle;
+ }
+ }
+ } while (fetched == _countof(data));
+
+ return pos;
+}
+
+
+
+void PrintNotReachableInRange(TADDR rngStart, TADDR rngEnd, BOOL bExcludeReadyForFinalization, HeapStat* hpstat, BOOL bShort)
+{
+ GCRootImpl gcroot;
+ const std::unordered_set<TADDR> &liveObjs = gcroot.GetLiveObjects(bExcludeReadyForFinalization == TRUE);
+
+ LinearReadCache cache(512);
+ cache.EnsureRangeInCache(rngStart, (unsigned int)(rngEnd-rngStart));
+
+ for (TADDR p = rngStart; p < rngEnd; p += sizeof(TADDR))
+ {
+ if (IsInterrupt())
+ break;
+
+ TADDR header = 0;
+ TADDR obj = 0;
+ TADDR taddrMT = 0;
+
+ bool read = cache.Read(p-sizeof(SIZEOF_OBJHEADER), &header);
+ read = read && cache.Read(p, &obj);
+ if (read && ((header & BIT_SBLK_FINALIZER_RUN) == 0) && liveObjs.find(obj) == liveObjs.end())
+ {
+ if (bShort)
+ {
+ DMLOut("%s\n", DMLObject(obj));
+ }
+ else
+ {
+ DMLOut("%s ", DMLObject(obj));
+ if (SUCCEEDED(GetMTOfObject(obj, &taddrMT)) && taddrMT)
+ {
+ size_t s = ObjectSize(obj);
+ if (hpstat)
+ {
+ hpstat->Add(taddrMT, (DWORD)s);
+ }
+ }
+ }
+ }
+ }
+
+ if (!bShort)
+ ExtOut("\n");
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Some defines for cards taken from gc code
+//
+#define card_word_width ((size_t)32)
+
+//
+// The value of card_size is determined empirically according to the average size of an object
+// In the code we also rely on the assumption that one card_table entry (DWORD) covers an entire os page
+//
+#if defined (_TARGET_WIN64_)
+#define card_size ((size_t)(2*DT_OS_PAGE_SIZE/card_word_width))
+#else
+#define card_size ((size_t)(DT_OS_PAGE_SIZE/card_word_width))
+#endif //_TARGET_WIN64_
+
+// so card_size = 128 on x86, 256 on x64
+
+inline
+size_t card_word (size_t card)
+{
+ return card / card_word_width;
+}
+
+inline
+unsigned card_bit (size_t card)
+{
+ return (unsigned)(card % card_word_width);
+}
+
+inline
+size_t card_of ( BYTE* object)
+{
+ return (size_t)(object) / card_size;
+}
+
+BOOL CardIsSet(const DacpGcHeapDetails &heap, TADDR objAddr)
+{
+ // The card table has to be translated to look at the refcount, etc.
+ // g_card_table[card_word(card_of(g_lowest_address))].
+
+ TADDR card_table = TO_TADDR(heap.card_table);
+ card_table = card_table + card_word(card_of((BYTE *)heap.lowest_address))*sizeof(DWORD);
+
+ do
+ {
+ TADDR card_table_lowest_addr;
+ TADDR card_table_next;
+
+ if (MOVE(card_table_lowest_addr, ALIGN_DOWN(card_table, 0x1000) + sizeof(PVOID)) != S_OK)
+ {
+ ExtErr("Error getting card table lowest address\n");
+ return FALSE;
+ }
+
+ if (MOVE(card_table_next, card_table - sizeof(PVOID)) != S_OK)
+ {
+ ExtErr("Error getting next card table\n");
+ return FALSE;
+ }
+
+ size_t card = (objAddr - card_table_lowest_addr) / card_size;
+ DWORD value;
+ if (MOVE(value, card_table + card_word(card)*sizeof(DWORD)) != S_OK)
+ {
+ ExtErr("Error reading card bits\n");
+ return FALSE;
+ }
+
+ if (value & 1<<card_bit(card))
+ return TRUE;
+
+ card_table = card_table_next;
+ }
+ while(card_table);
+
+ return FALSE;
+}
+
+BOOL NeedCard(TADDR parent, TADDR child)
+{
+ int iChildGen = g_snapshot.GetGeneration(child);
+
+ if (iChildGen == 2)
+ return FALSE;
+
+ int iParentGen = g_snapshot.GetGeneration(parent);
+
+ return (iChildGen < iParentGen);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Some defines for mark_array taken from gc code
+//
+
+#define mark_bit_pitch 8
+#define mark_word_width 32
+#define mark_word_size (mark_word_width * mark_bit_pitch)
+#define heap_segment_flags_swept 16
+
+inline
+size_t mark_bit_bit_of(CLRDATA_ADDRESS add)
+{
+ return (size_t)((add / mark_bit_pitch) % mark_word_width);
+}
+
+inline
+size_t mark_word_of(CLRDATA_ADDRESS add)
+{
+ return (size_t)(add / mark_word_size);
+}
+
+inline BOOL mark_array_marked(const DacpGcHeapDetails &heap, CLRDATA_ADDRESS add)
+{
+
+ DWORD entry = 0;
+ HRESULT hr = MOVE(entry, heap.mark_array + sizeof(DWORD) * mark_word_of(add));
+
+ if (FAILED(hr))
+ ExtOut("Failed to read card table entry.\n");
+
+ return entry & (1 << mark_bit_bit_of(add));
+}
+
+BOOL background_object_marked(const DacpGcHeapDetails &heap, CLRDATA_ADDRESS o)
+{
+ BOOL m = TRUE;
+
+ if ((o >= heap.background_saved_lowest_address) && (o < heap.background_saved_highest_address))
+ m = mark_array_marked(heap, o);
+
+ return m;
+}
+
+BOOL fgc_should_consider_object(const DacpGcHeapDetails &heap,
+ CLRDATA_ADDRESS o,
+ const DacpHeapSegmentData &seg,
+ BOOL consider_bgc_mark_p,
+ BOOL check_current_sweep_p,
+ BOOL check_saved_sweep_p)
+{
+ // the logic for this function must be kept in sync with the analogous function in gc.cpp
+ BOOL no_bgc_mark_p = FALSE;
+
+ if (consider_bgc_mark_p)
+ {
+ if (check_current_sweep_p && (o < heap.next_sweep_obj))
+ {
+ no_bgc_mark_p = TRUE;
+ }
+
+ if (!no_bgc_mark_p)
+ {
+ if(check_saved_sweep_p && (o >= heap.saved_sweep_ephemeral_start))
+ {
+ no_bgc_mark_p = TRUE;
+ }
+
+ if (!check_saved_sweep_p)
+ {
+ CLRDATA_ADDRESS background_allocated = seg.background_allocated;
+ if (o >= background_allocated)
+ {
+ no_bgc_mark_p = TRUE;
+ }
+ }
+ }
+ }
+ else
+ {
+ no_bgc_mark_p = TRUE;
+ }
+
+ return no_bgc_mark_p ? TRUE : background_object_marked(heap, o);
+}
+
+enum c_gc_state
+{
+ c_gc_state_marking,
+ c_gc_state_planning,
+ c_gc_state_free
+};
+
+inline BOOL in_range_for_segment(const DacpHeapSegmentData &seg, CLRDATA_ADDRESS addr)
+{
+ return (addr >= seg.mem) && (addr < seg.reserved);
+}
+
+void should_check_bgc_mark(const DacpGcHeapDetails &heap,
+ const DacpHeapSegmentData &seg,
+ BOOL* consider_bgc_mark_p,
+ BOOL* check_current_sweep_p,
+ BOOL* check_saved_sweep_p)
+{
+ // the logic for this function must be kept in sync with the analogous function in gc.cpp
+ *consider_bgc_mark_p = FALSE;
+ *check_current_sweep_p = FALSE;
+ *check_saved_sweep_p = FALSE;
+
+ if (heap.current_c_gc_state == c_gc_state_planning)
+ {
+ // We are doing the next_sweep_obj comparison here because we have yet to
+ // turn on the swept flag for the segment but in_range_for_segment will return
+ // FALSE if the address is the same as reserved.
+ if ((seg.flags & heap_segment_flags_swept) || (heap.next_sweep_obj == seg.reserved))
+ {
+ // this seg was already swept.
+ }
+ else
+ {
+ *consider_bgc_mark_p = TRUE;
+
+ if (seg.segmentAddr == heap.saved_sweep_ephemeral_seg)
+ {
+ *check_saved_sweep_p = TRUE;
+ }
+
+ if (in_range_for_segment(seg, heap.next_sweep_obj))
+ {
+ *check_current_sweep_p = TRUE;
+ }
+ }
+ }
+}
+
+// TODO: FACTOR TOGETHER THE OBJECT MEMBER WALKING CODE FROM
+// TODO: VerifyObjectMember(), GetListOfRefs(), HeapTraverser::PrintRefs()
+BOOL VerifyObjectMember(const DacpGcHeapDetails &heap, DWORD_PTR objAddr)
+{
+ BOOL ret = TRUE;
+ BOOL bCheckCard = TRUE;
+ size_t size = 0;
+ {
+ DWORD_PTR dwAddrCard = objAddr;
+ while (dwAddrCard < objAddr + size)
+ {
+ if (CardIsSet(heap, dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ break;
+ }
+ dwAddrCard += card_size;
+ }
+
+ if (bCheckCard)
+ {
+ dwAddrCard = objAddr + size - 2*sizeof(PVOID);
+ if (CardIsSet(heap, dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ }
+ }
+ }
+
+ for (sos::RefIterator itr(TO_TADDR(objAddr)); itr; ++itr)
+ {
+ TADDR dwAddr1 = (DWORD_PTR)*itr;
+ if (dwAddr1)
+ {
+ TADDR dwChild = dwAddr1;
+ // Try something more efficient than IsObject here. Is the methodtable valid?
+ size_t s;
+ BOOL bPointers;
+ TADDR dwAddrMethTable;
+ if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
+ (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
+ {
+ DMLOut("object %s: bad member %p at %p\n", DMLObject(objAddr), SOS_PTR(dwAddr1), SOS_PTR(itr.GetOffset()));
+ ret = FALSE;
+ }
+
+ if (IsMTForFreeObj(dwAddrMethTable))
+ {
+ DMLOut("object %s contains free object %p at %p\n", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+itr.GetOffset()));
+ ret = FALSE;
+ }
+
+ // verify card table
+ if (bCheckCard && NeedCard(objAddr+itr.GetOffset(), dwAddr1))
+ {
+ DMLOut("object %s:%s missing card_table entry for %p\n",
+ DMLObject(objAddr), (dwChild == dwAddr1) ? "" : " maybe",
+ SOS_PTR(objAddr+itr.GetOffset()));
+ ret = FALSE;
+ }
+ }
+ }
+
+ return ret;
+}
+
+// search for can_verify_deep in gc.cpp for examples of how these functions are used.
+BOOL VerifyObject(const DacpGcHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
+ BOOL bVerifyMember)
+{
+ if (IsMTForFreeObj(MTAddr))
+ {
+ return TRUE;
+ }
+
+ if (objSize < min_obj_size)
+ {
+ DMLOut("object %s: size %d too small\n", DMLObject(objAddr), objSize);
+ return FALSE;
+ }
+
+ // If we requested to verify the object's members, the GC may be in a state where that's not possible.
+ // Here we check to see if the object in question needs to have its members updated. If so, we turn off
+ // verification for the object.
+ if (bVerifyMember)
+ {
+ BOOL consider_bgc_mark = FALSE, check_current_sweep = FALSE, check_saved_sweep = FALSE;
+ should_check_bgc_mark(heap, seg, &consider_bgc_mark, &check_current_sweep, &check_saved_sweep);
+ bVerifyMember = fgc_should_consider_object(heap, objAddr, seg, consider_bgc_mark, check_current_sweep, check_saved_sweep);
+ }
+
+ return bVerifyMember ? VerifyObjectMember(heap, objAddr) : TRUE;
+}
+
+
+BOOL FindSegment(const DacpGcHeapDetails &heap, DacpHeapSegmentData &seg, CLRDATA_ADDRESS addr)
+{
+ CLRDATA_ADDRESS dwAddrSeg = heap.generation_table[GetMaxGeneration()].start_segment;
+
+ // Request the inital segment.
+ if (seg.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p.\n", SOS_PTR(dwAddrSeg));
+ return FALSE;
+ }
+
+ // Loop while the object is not in range of the segment.
+ while (addr < TO_TADDR(seg.mem) ||
+ addr >= (dwAddrSeg == heap.ephemeral_heap_segment ? heap.alloc_allocated : TO_TADDR(seg.allocated)))
+ {
+ // get the next segment
+ dwAddrSeg = seg.next;
+
+ // We reached the last segment without finding the object.
+ if (dwAddrSeg == NULL)
+ return FALSE;
+
+ if (seg.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p.\n", SOS_PTR(dwAddrSeg));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL VerifyObject(const DacpGcHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize, BOOL bVerifyMember)
+{
+ // This is only used by the other VerifyObject function if bVerifyMember is true,
+ // so we only intialize it if we need it for verifying object members.
+ DacpHeapSegmentData seg;
+
+ if (bVerifyMember)
+ {
+ // if we fail to find the segment, we cannot verify the object's members
+ bVerifyMember = FindSegment(heap, seg, objAddr);
+ }
+
+ return VerifyObject(heap, seg, objAddr, MTAddr, objSize, bVerifyMember);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+typedef void (*TYPETREEVISIT)(size_t methodTable, size_t ID, LPVOID token);
+
+// TODO remove this. MethodTableCache already maps method tables to
+// various information. We don't need TypeTree to do this too.
+// Straightfoward to do, but low priority.
+class TypeTree
+{
+private:
+ size_t methodTable;
+ size_t ID;
+ TypeTree *pLeft;
+ TypeTree *pRight;
+
+public:
+ TypeTree(size_t MT) : methodTable(MT),ID(0),pLeft(NULL),pRight(NULL) { }
+
+ BOOL isIn(size_t MT, size_t *pID)
+ {
+ TypeTree *pCur = this;
+
+ while (pCur)
+ {
+ if (MT == pCur->methodTable)
+ {
+ if (pID)
+ *pID = pCur->ID;
+ return TRUE;
+ }
+ else if (MT < pCur->methodTable)
+ pCur = pCur->pLeft;
+ else
+ pCur = pCur->pRight;
+ }
+
+ return FALSE;
+ }
+
+ BOOL insert(size_t MT)
+ {
+ TypeTree *pCur = this;
+
+ while (pCur)
+ {
+ if (MT == pCur->methodTable)
+ return TRUE;
+ else if ((MT < pCur->methodTable))
+ {
+ if (pCur->pLeft)
+ pCur = pCur->pLeft;
+ else
+ break;
+ }
+ else if (pCur->pRight)
+ pCur = pCur->pRight;
+ else
+ break;
+ }
+
+ // If we got here, we need to append at the current node.
+ TypeTree *pNewNode = new TypeTree(MT);
+ if (pNewNode == NULL)
+ return FALSE;
+
+ if (MT < pCur->methodTable)
+ pCur->pLeft = pNewNode;
+ else
+ pCur->pRight = pNewNode;
+
+ return TRUE;
+ }
+
+ static void destroy(TypeTree *pStart)
+ {
+ TypeTree *pCur = pStart;
+
+ if (pCur)
+ {
+ destroy(pCur->pLeft);
+ destroy(pCur->pRight);
+ delete [] pCur;
+ }
+ }
+
+ static void visit_inorder(TypeTree *pStart, TYPETREEVISIT pFunc, LPVOID token)
+ {
+ TypeTree *pCur = pStart;
+
+ if (pCur)
+ {
+ visit_inorder(pCur->pLeft, pFunc, token);
+ pFunc (pCur->methodTable, pCur->ID, token);
+ visit_inorder(pCur->pRight, pFunc, token);
+ }
+ }
+
+ static void setTypeIDs(TypeTree *pStart, size_t *pCurID)
+ {
+ TypeTree *pCur = pStart;
+
+ if (pCur)
+ {
+ setTypeIDs(pCur->pLeft, pCurID);
+ pCur->ID = *pCurID;
+ (*pCurID)++;
+ setTypeIDs(pCur->pRight, pCurID);
+ }
+ }
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+HeapTraverser::HeapTraverser(bool verify)
+{
+ m_format = 0;
+ m_file = NULL;
+ m_objVisited = 0;
+ m_pTypeTree = NULL;
+ m_curNID = 1;
+ m_verify = verify;
+}
+
+HeapTraverser::~HeapTraverser()
+{
+ if (m_pTypeTree) {
+ TypeTree::destroy(m_pTypeTree);
+ m_pTypeTree = NULL;
+ }
+}
+
+BOOL HeapTraverser::Initialize()
+{
+ if (!GCHeapsTraverse (HeapTraverser::GatherTypes, this, m_verify))
+ {
+ ExtOut("Error during heap traverse\n");
+ return FALSE;
+ }
+
+ GCRootImpl::GetDependentHandleMap(mDependentHandleMap);
+
+ size_t startID = 1;
+ TypeTree::setTypeIDs(m_pTypeTree, &startID);
+
+ return TRUE;
+}
+
+BOOL HeapTraverser::CreateReport (FILE *fp, int format)
+{
+ if (fp == NULL || (format!=FORMAT_XML && format != FORMAT_CLRPROFILER))
+ {
+ return FALSE;
+ }
+
+ m_file = fp;
+ m_format = format;
+
+ PrintSection(TYPE_START,TRUE);
+
+ PrintSection(TYPE_TYPES,TRUE);
+ TypeTree::visit_inorder(m_pTypeTree, HeapTraverser::PrintOutTree, this);
+ PrintSection(TYPE_TYPES,FALSE);
+
+ ExtOut("tracing roots...\n");
+ PrintSection(TYPE_ROOTS,TRUE);
+ PrintRootHead();
+
+ TraceHandles();
+ FindGCRootOnStacks();
+
+ PrintRootTail();
+ PrintSection(TYPE_ROOTS,FALSE);
+
+ // now print type tree
+ PrintSection(TYPE_OBJECTS,TRUE);
+ ExtOut("\nWalking heap...\n");
+ m_objVisited = 0; // for UI updates
+ GCHeapsTraverse (HeapTraverser::PrintHeap, this, FALSE); // Never verify on the second pass
+ PrintSection(TYPE_OBJECTS,FALSE);
+
+ PrintSection(TYPE_START,FALSE);
+
+ m_file = NULL;
+ return TRUE;
+}
+
+void HeapTraverser::insert(size_t mTable)
+{
+ if (m_pTypeTree == NULL)
+ {
+ m_pTypeTree = new TypeTree(mTable);
+ if (m_pTypeTree == NULL)
+ {
+ ReportOOM();
+ return;
+ }
+ }
+ else
+ {
+ m_pTypeTree->insert(mTable);
+ }
+}
+
+size_t HeapTraverser::getID(size_t mTable)
+{
+ if (m_pTypeTree == NULL)
+ {
+ return 0;
+ }
+ // IDs start at 1, so we can return 0 if not found.
+ size_t ret;
+ if (m_pTypeTree->isIn(mTable,&ret))
+ {
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifndef FEATURE_PAL
+void replace(std::wstring &str, const WCHAR *toReplace, const WCHAR *replaceWith)
+{
+ const size_t replaceLen = _wcslen(toReplace);
+ const size_t replaceWithLen = _wcslen(replaceWith);
+
+ size_t i = str.find(toReplace);
+ while (i != std::wstring::npos)
+ {
+ str.replace(i, replaceLen, replaceWith);
+ i = str.find(toReplace, i + replaceWithLen);
+ }
+}
+#endif
+
+void HeapTraverser::PrintType(size_t ID,LPCWSTR name)
+{
+ if (m_format==FORMAT_XML)
+ {
+#ifndef FEATURE_PAL
+ // Sanitize name based on XML spec.
+ std::wstring wname = name;
+ replace(wname, W("&"), W("&amp;"));
+ replace(wname, W("\""), W("&quot;"));
+ replace(wname, W("'"), W("&apos;"));
+ replace(wname, W("<"), W("&lt;"));
+ replace(wname, W(">"), W("&gt;"));
+ name = wname.c_str();
+#endif
+ fprintf(m_file,
+ "<type id=\"%d\" name=\"%S\"/>\n",
+ ID, name);
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "t %d 0 %S\n",
+ ID,name);
+ }
+}
+
+void HeapTraverser::PrintObjectHead(size_t objAddr,size_t typeID,size_t Size)
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ "<object address=\"0x%p\" typeid=\"%d\" size=\"%d\">\n",
+ (PBYTE)objAddr,typeID, Size);
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "n %d 1 %d %d\n",
+ m_curNID,typeID,Size);
+
+ fprintf(m_file,
+ "! 1 0x%p %d\n",
+ (PBYTE)objAddr,m_curNID);
+
+ m_curNID++;
+
+ fprintf(m_file,
+ "o 0x%p %d %d ",
+ (PBYTE)objAddr,typeID,Size);
+ }
+}
+
+void HeapTraverser::PrintObjectMember(size_t memberValue, bool dependentHandle)
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ " <member address=\"0x%p\"%s/>\n",
+ (PBYTE)memberValue, dependentHandle ? " dependentHandle=\"1\"" : "");
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ " 0x%p",
+ (PBYTE)memberValue);
+ }
+}
+
+void HeapTraverser::PrintObjectTail()
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ "</object>\n");
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "\n");
+ }
+}
+
+void HeapTraverser::PrintRootHead()
+{
+ if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "r ");
+ }
+}
+
+void HeapTraverser::PrintRoot(LPCWSTR kind,size_t Value)
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ "<root kind=\"%S\" address=\"0x%p\"/>\n",
+ kind,
+ (PBYTE)Value);
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "0x%p ",
+ (PBYTE)Value);
+ }
+}
+
+void HeapTraverser::PrintRootTail()
+{
+ if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "\n");
+ }
+}
+
+void HeapTraverser::PrintSection(int Type,BOOL bOpening)
+{
+ const char *const pTypes[] = {"<gcheap>","<types>","<roots>","<objects>"};
+ const char *const pTypeEnds[] = {"</gcheap>","</types>","</roots>","</objects>"};
+
+ if (m_format==FORMAT_XML)
+ {
+ if ((Type >= 0) && (Type < TYPE_HIGHEST))
+ {
+ fprintf(m_file,"%s\n",bOpening ? pTypes[Type] : pTypeEnds[Type]);
+ }
+ else
+ {
+ ExtOut ("INVALID TYPE %d\n", Type);
+ }
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ if ((Type == TYPE_START) && !bOpening) // a final newline is needed
+ {
+ fprintf(m_file,"\n");
+ }
+ }
+}
+
+void HeapTraverser::FindGCRootOnStacks()
+{
+ ArrayHolder<DWORD_PTR> threadList = NULL;
+ int numThreads = 0;
+
+ // GetThreadList calls ReportOOM so we don't need to do that here.
+ HRESULT hr = GetThreadList(&threadList, &numThreads);
+ if (FAILED(hr) || !threadList)
+ {
+ ExtOut("Failed to enumerate threads in the process.\n");
+ return;
+ }
+
+ int total = 0;
+ DacpThreadData vThread;
+ for (int i = 0; i < numThreads; i++)
+ {
+ if (FAILED(vThread.Request(g_sos, threadList[i])))
+ continue;
+
+ if (vThread.osThreadId)
+ {
+ unsigned int refCount = 0;
+ ArrayHolder<SOSStackRefData> refs = NULL;
+
+ if (FAILED(::GetGCRefs(vThread.osThreadId, &refs, &refCount, NULL, NULL)))
+ {
+ ExtOut("Failed to walk thread %x\n", vThread.osThreadId);
+ continue;
+ }
+
+ for (unsigned int i = 0; i < refCount; ++i)
+ if (refs[i].Object)
+ PrintRoot(W("stack"), TO_TADDR(refs[i].Object));
+ }
+ }
+
+}
+
+
+/* static */ void HeapTraverser::PrintOutTree(size_t methodTable, size_t ID,
+ LPVOID token)
+{
+ HeapTraverser *pHolder = (HeapTraverser *) token;
+ NameForMT_s(methodTable, g_mdName, mdNameLen);
+ pHolder->PrintType(ID,g_mdName);
+}
+
+
+/* static */ void HeapTraverser::PrintHeap(DWORD_PTR objAddr,size_t Size,
+ DWORD_PTR methodTable, LPVOID token)
+{
+ if (!IsMTForFreeObj (methodTable))
+ {
+ HeapTraverser *pHolder = (HeapTraverser *) token;
+ pHolder->m_objVisited++;
+ size_t ID = pHolder->getID(methodTable);
+
+ pHolder->PrintObjectHead(objAddr, ID, Size);
+ pHolder->PrintRefs(objAddr, methodTable, Size);
+ pHolder->PrintObjectTail();
+
+ if (pHolder->m_objVisited % 1024 == 0) {
+ ExtOut(".");
+ if (pHolder->m_objVisited % (1024*64) == 0)
+ ExtOut("\r\n");
+ }
+ }
+}
+
+void HeapTraverser::TraceHandles()
+{
+ unsigned int fetched = 0;
+ SOSHandleData data[64];
+
+ ToRelease<ISOSHandleEnum> handles;
+ HRESULT hr = g_sos->GetHandleEnum(&handles);
+ if (FAILED(hr))
+ return;
+
+ do
+ {
+ hr = handles->Next(_countof(data), data, &fetched);
+
+ if (FAILED(hr))
+ break;
+
+ for (unsigned int i = 0; i < fetched; ++i)
+ PrintRoot(W("handle"), (size_t)data[i].Handle);
+ } while (fetched == _countof(data));
+}
+
+/* static */ void HeapTraverser::GatherTypes(DWORD_PTR objAddr,size_t Size,
+ DWORD_PTR methodTable, LPVOID token)
+{
+ if (!IsMTForFreeObj (methodTable))
+ {
+ HeapTraverser *pHolder = (HeapTraverser *) token;
+ pHolder->insert(methodTable);
+ }
+}
+
+void HeapTraverser::PrintRefs(size_t obj, size_t methodTable, size_t size)
+{
+ DWORD_PTR dwAddr = methodTable;
+
+ // TODO: pass info to callback having to lookup the MethodTableInfo again
+ MethodTableInfo* info = g_special_mtCache.Lookup((DWORD_PTR)methodTable);
+ _ASSERTE(info->IsInitialized()); // This is the second pass, so we should be intialized
+
+ if (!info->bContainsPointers)
+ return;
+
+ // Fetch the GCInfo from the other process
+ CGCDesc *map = info->GCInfo;
+ if (map == NULL)
+ {
+ INT_PTR nEntries;
+ move_xp (nEntries, dwAddr-sizeof(PVOID));
+ bool arrayOfVC = false;
+ if (nEntries<0)
+ {
+ arrayOfVC = true;
+ nEntries = -nEntries;
+ }
+
+ size_t nSlots = 1+nEntries*sizeof(CGCDescSeries)/sizeof(DWORD_PTR);
+ info->GCInfoBuffer = new DWORD_PTR[nSlots];
+ if (info->GCInfoBuffer == NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ if (FAILED(rvCache->Read(TO_CDADDR(dwAddr - nSlots*sizeof(DWORD_PTR)),
+ info->GCInfoBuffer, (ULONG) (nSlots*sizeof(DWORD_PTR)), NULL)))
+ return;
+
+ map = info->GCInfo = (CGCDesc*)(info->GCInfoBuffer+nSlots);
+ info->ArrayOfVC = arrayOfVC;
+ }
+
+ mCache.EnsureRangeInCache((TADDR)obj, (unsigned int)size);
+ for (sos::RefIterator itr(obj, info->GCInfo, info->ArrayOfVC, &mCache); itr; ++itr)
+ {
+ if (*itr && (!m_verify || sos::IsObject(*itr)))
+ PrintObjectMember(*itr, false);
+ }
+
+ std::unordered_map<TADDR, std::list<TADDR>>::iterator itr = mDependentHandleMap.find((TADDR)obj);
+ if (itr != mDependentHandleMap.end())
+ {
+ for (std::list<TADDR>::iterator litr = itr->second.begin(); litr != itr->second.end(); ++litr)
+ {
+ PrintObjectMember(*litr, true);
+ }
+ }
+}
+
+
+void sos::ObjectIterator::BuildError(char *out, size_t count, const char *format, ...) const
+{
+ if (out == NULL || count == 0)
+ return;
+
+ va_list args;
+ va_start(args, format);
+
+ int written = vsprintf_s(out, count, format, args);
+ if (written > 0 && mLastObj)
+ sprintf_s(out+written, count-written, "\nLast good object: %p.\n", (int*)mLastObj);
+
+ va_end(args);
+}
+
+bool sos::ObjectIterator::VerifyObjectMembers(char *reason, size_t count) const
+{
+ if (!mCurrObj.HasPointers())
+ return true;
+
+ size_t size = mCurrObj.GetSize();
+ size_t objAddr = (size_t)mCurrObj.GetAddress();
+ TADDR mt = mCurrObj.GetMT();
+
+ INT_PTR nEntries;
+ MOVE(nEntries, mt-sizeof(PVOID));
+ if (nEntries < 0)
+ nEntries = -nEntries;
+
+ size_t nSlots = 1 + nEntries * sizeof(CGCDescSeries)/sizeof(DWORD_PTR);
+ ArrayHolder<DWORD_PTR> buffer = new DWORD_PTR[nSlots];
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(mt - nSlots*sizeof(DWORD_PTR)),
+ buffer, (ULONG) (nSlots*sizeof(DWORD_PTR)), NULL)))
+ {
+ BuildError(reason, count, "Object %s has a bad GCDesc.", DMLObject(objAddr));
+ return false;
+ }
+
+ CGCDesc *map = (CGCDesc *)(buffer+nSlots);
+ CGCDescSeries* cur = map->GetHighestSeries();
+ CGCDescSeries* last = map->GetLowestSeries();
+
+ const size_t bufferSize = sizeof(size_t)*128;
+ size_t objBuffer[bufferSize/sizeof(size_t)];
+ size_t dwBeginAddr = (size_t)objAddr;
+ size_t bytesInBuffer = bufferSize;
+ if (size < bytesInBuffer)
+ bytesInBuffer = size;
+
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer,NULL)))
+ {
+ BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
+ return false;
+ }
+
+ BOOL bCheckCard = TRUE;
+ {
+ DWORD_PTR dwAddrCard = (DWORD_PTR)objAddr;
+ while (dwAddrCard < objAddr + size)
+ {
+ if (CardIsSet (mHeaps[mCurrHeap], dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ break;
+ }
+ dwAddrCard += card_size;
+ }
+ if (bCheckCard)
+ {
+ dwAddrCard = objAddr + size - 2*sizeof(PVOID);
+ if (CardIsSet (mHeaps[mCurrHeap], dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ }
+ }
+ }
+
+ if (cur >= last)
+ {
+ do
+ {
+ BYTE** parm = (BYTE**)((objAddr) + cur->GetSeriesOffset());
+ BYTE** ppstop =
+ (BYTE**)((BYTE*)parm + cur->GetSeriesSize() + (size));
+ while (parm < ppstop)
+ {
+ CheckInterrupt();
+ size_t dwAddr1;
+
+ // Do we run out of cache?
+ if ((size_t)parm >= dwBeginAddr+bytesInBuffer)
+ {
+ // dwBeginAddr += bytesInBuffer;
+ dwBeginAddr = (size_t)parm;
+ if (dwBeginAddr >= objAddr + size)
+ {
+ return true;
+ }
+ bytesInBuffer = bufferSize;
+ if (objAddr+size-dwBeginAddr < bytesInBuffer)
+ {
+ bytesInBuffer = objAddr+size-dwBeginAddr;
+ }
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer, NULL)))
+ {
+ BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
+ return false;
+ }
+ }
+ dwAddr1 = objBuffer[((size_t)parm-dwBeginAddr)/sizeof(size_t)];
+ if (dwAddr1) {
+ DWORD_PTR dwChild = dwAddr1;
+ // Try something more efficient than IsObject here. Is the methodtable valid?
+ size_t s;
+ BOOL bPointers;
+ DWORD_PTR dwAddrMethTable;
+ if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
+ (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
+ {
+ BuildError(reason, count, "object %s: bad member %p at %p", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+
+ if (IsMTForFreeObj(dwAddrMethTable))
+ {
+ sos::Throw<HeapCorruption>("object %s contains free object %p at %p", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+ }
+
+ // verify card table
+ if (bCheckCard &&
+ NeedCard(objAddr+(size_t)parm-objAddr,dwChild))
+ {
+ BuildError(reason, count, "Object %s: %s missing card_table entry for %p",
+ DMLObject(objAddr), (dwChild == dwAddr1)? "" : " maybe",
+ SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+ }
+ parm++;
+ }
+ cur--;
+ CheckInterrupt();
+
+ } while (cur >= last);
+ }
+ else
+ {
+ int cnt = (int) map->GetNumSeries();
+ BYTE** parm = (BYTE**)((objAddr) + cur->startoffset);
+ while ((BYTE*)parm < (BYTE*)((objAddr)+(size)-plug_skew))
+ {
+ for (int __i = 0; __i > cnt; __i--)
+ {
+ CheckInterrupt();
+
+ unsigned skip = cur->val_serie[__i].skip;
+ unsigned nptrs = cur->val_serie[__i].nptrs;
+ BYTE** ppstop = parm + nptrs;
+ do
+ {
+ size_t dwAddr1;
+ // Do we run out of cache?
+ if ((size_t)parm >= dwBeginAddr+bytesInBuffer)
+ {
+ // dwBeginAddr += bytesInBuffer;
+ dwBeginAddr = (size_t)parm;
+ if (dwBeginAddr >= objAddr + size)
+ return true;
+
+ bytesInBuffer = bufferSize;
+ if (objAddr+size-dwBeginAddr < bytesInBuffer)
+ bytesInBuffer = objAddr+size-dwBeginAddr;
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer, NULL)))
+ {
+ BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
+ return false;
+ }
+ }
+ dwAddr1 = objBuffer[((size_t)parm-dwBeginAddr)/sizeof(size_t)];
+ {
+ if (dwAddr1)
+ {
+ DWORD_PTR dwChild = dwAddr1;
+ // Try something more efficient than IsObject here. Is the methodtable valid?
+ size_t s;
+ BOOL bPointers;
+ DWORD_PTR dwAddrMethTable;
+ if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
+ (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
+ {
+ BuildError(reason, count, "Object %s: Bad member %p at %p.\n", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+
+ if (IsMTForFreeObj(dwAddrMethTable))
+ {
+ BuildError(reason, count, "Object %s contains free object %p at %p.", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+ return false;
+ }
+
+ // verify card table
+ if (bCheckCard &&
+ NeedCard (objAddr+(size_t)parm-objAddr,dwAddr1))
+ {
+ BuildError(reason, count, "Object %s:%s missing card_table entry for %p",
+ DMLObject(objAddr), (dwChild == dwAddr1) ? "" : " maybe",
+ SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+ }
+ }
+ parm++;
+ CheckInterrupt();
+ } while (parm < ppstop);
+ parm = (BYTE**)((BYTE*)parm + skip);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool sos::ObjectIterator::Verify(char *reason, size_t count) const
+{
+ try
+ {
+ TADDR mt = mCurrObj.GetMT();
+
+ if (MethodTable::GetFreeMT() == mt)
+ {
+ return true;
+ }
+
+ size_t size = mCurrObj.GetSize();
+ if (size < min_obj_size)
+ {
+ BuildError(reason, count, "Object %s: Size %d is too small.", DMLObject(mCurrObj.GetAddress()), size);
+ return false;
+ }
+
+ if (mCurrObj.GetAddress() + mCurrObj.GetSize() > mSegmentEnd)
+ {
+ BuildError(reason, count, "Object %s is too large. End of segment at %p.", DMLObject(mCurrObj), mSegmentEnd);
+ return false;
+ }
+
+ BOOL bVerifyMember = TRUE;
+
+ // If we requested to verify the object's members, the GC may be in a state where that's not possible.
+ // Here we check to see if the object in question needs to have its members updated. If so, we turn off
+ // verification for the object.
+ BOOL consider_bgc_mark = FALSE, check_current_sweep = FALSE, check_saved_sweep = FALSE;
+ should_check_bgc_mark(mHeaps[mCurrHeap], mSegment, &consider_bgc_mark, &check_current_sweep, &check_saved_sweep);
+ bVerifyMember = fgc_should_consider_object(mHeaps[mCurrHeap], mCurrObj.GetAddress(), mSegment,
+ consider_bgc_mark, check_current_sweep, check_saved_sweep);
+
+ if (bVerifyMember)
+ return VerifyObjectMembers(reason, count);
+ }
+ catch(const sos::Exception &e)
+ {
+ BuildError(reason, count, e.GetMesssage());
+ return false;
+ }
+
+ return true;
+}
+
+bool sos::ObjectIterator::Verify() const
+{
+ char *c = NULL;
+ return Verify(c, 0);
+}
diff --git a/src/ToolBox/SOS/Strike/inc/.gitmirror b/src/ToolBox/SOS/Strike/inc/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/inc/dbgeng.h b/src/ToolBox/SOS/Strike/inc/dbgeng.h
new file mode 100644
index 0000000000..73e4d19f99
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/dbgeng.h
@@ -0,0 +1,16122 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//----------------------------------------------------------------------------
+//
+// Debugger engine interfaces.
+//
+
+//
+//----------------------------------------------------------------------------
+
+#ifndef __DBGENG_H__
+#define __DBGENG_H__
+
+#include <stdarg.h>
+#include <objbase.h>
+
+#ifndef _WDBGEXTS_
+typedef struct _WINDBG_EXTENSION_APIS32* PWINDBG_EXTENSION_APIS32;
+typedef struct _WINDBG_EXTENSION_APIS64* PWINDBG_EXTENSION_APIS64;
+#endif
+
+#ifndef _CRASHLIB_
+typedef struct _MEMORY_BASIC_INFORMATION64* PMEMORY_BASIC_INFORMATION64;
+#endif
+
+#ifndef __specstrings
+// Should include SpecStrings.h to get proper definitions.
+#define __in
+#define __in_opt
+#define __in_bcount(x)
+#define __in_bcount_opt(x)
+#define __in_ecount(x)
+#define __in_ecount_opt(x)
+#define __out
+#define __out_opt
+#define __out_bcount(x)
+#define __out_bcount_opt(x)
+#define __out_ecount(x)
+#define __out_ecount_opt(x)
+#define __out_xcount(x)
+#define __inout
+#define __inout_opt
+#define __reserved
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//----------------------------------------------------------------------------
+//
+// GUIDs and interface forward declarations.
+//
+//----------------------------------------------------------------------------
+
+/* f2df5f53-071f-47bd-9de6-5734c3fed689 */
+DEFINE_GUID(IID_IDebugAdvanced, 0xf2df5f53, 0x071f, 0x47bd,
+ 0x9d, 0xe6, 0x57, 0x34, 0xc3, 0xfe, 0xd6, 0x89);
+/* 716d14c9-119b-4ba5-af1f-0890e672416a */
+DEFINE_GUID(IID_IDebugAdvanced2, 0x716d14c9, 0x119b, 0x4ba5,
+ 0xaf, 0x1f, 0x08, 0x90, 0xe6, 0x72, 0x41, 0x6a);
+/* cba4abb4-84c4-444d-87ca-a04e13286739 */
+DEFINE_GUID(IID_IDebugAdvanced3, 0xcba4abb4, 0x84c4, 0x444d,
+ 0x87, 0xca, 0xa0, 0x4e, 0x13, 0x28, 0x67, 0x39);
+/* 5bd9d474-5975-423a-b88b-65a8e7110e65 */
+DEFINE_GUID(IID_IDebugBreakpoint, 0x5bd9d474, 0x5975, 0x423a,
+ 0xb8, 0x8b, 0x65, 0xa8, 0xe7, 0x11, 0x0e, 0x65);
+/* 1b278d20-79f2-426e-a3f9-c1ddf375d48e */
+DEFINE_GUID(IID_IDebugBreakpoint2, 0x1b278d20, 0x79f2, 0x426e,
+ 0xa3, 0xf9, 0xc1, 0xdd, 0xf3, 0x75, 0xd4, 0x8e);
+/* 27fe5639-8407-4f47-8364-ee118fb08ac8 */
+DEFINE_GUID(IID_IDebugClient, 0x27fe5639, 0x8407, 0x4f47,
+ 0x83, 0x64, 0xee, 0x11, 0x8f, 0xb0, 0x8a, 0xc8);
+/* edbed635-372e-4dab-bbfe-ed0d2f63be81 */
+DEFINE_GUID(IID_IDebugClient2, 0xedbed635, 0x372e, 0x4dab,
+ 0xbb, 0xfe, 0xed, 0x0d, 0x2f, 0x63, 0xbe, 0x81);
+/* dd492d7f-71b8-4ad6-a8dc-1c887479ff91 */
+DEFINE_GUID(IID_IDebugClient3, 0xdd492d7f, 0x71b8, 0x4ad6,
+ 0xa8, 0xdc, 0x1c, 0x88, 0x74, 0x79, 0xff, 0x91);
+/* ca83c3de-5089-4cf8-93c8-d892387f2a5e */
+DEFINE_GUID(IID_IDebugClient4, 0xca83c3de, 0x5089, 0x4cf8,
+ 0x93, 0xc8, 0xd8, 0x92, 0x38, 0x7f, 0x2a, 0x5e);
+/* e3acb9d7-7ec2-4f0c-a0da-e81e0cbbe628 */
+DEFINE_GUID(IID_IDebugClient5, 0xe3acb9d7, 0x7ec2, 0x4f0c,
+ 0xa0, 0xda, 0xe8, 0x1e, 0x0c, 0xbb, 0xe6, 0x28);
+/* 5182e668-105e-416e-ad92-24ef800424ba */
+DEFINE_GUID(IID_IDebugControl, 0x5182e668, 0x105e, 0x416e,
+ 0xad, 0x92, 0x24, 0xef, 0x80, 0x04, 0x24, 0xba);
+/* d4366723-44df-4bed-8c7e-4c05424f4588 */
+DEFINE_GUID(IID_IDebugControl2, 0xd4366723, 0x44df, 0x4bed,
+ 0x8c, 0x7e, 0x4c, 0x05, 0x42, 0x4f, 0x45, 0x88);
+/* 7df74a86-b03f-407f-90ab-a20dadcead08 */
+DEFINE_GUID(IID_IDebugControl3, 0x7df74a86, 0xb03f, 0x407f,
+ 0x90, 0xab, 0xa2, 0x0d, 0xad, 0xce, 0xad, 0x08);
+/* 94e60ce9-9b41-4b19-9fc0-6d9eb35272b3 */
+DEFINE_GUID(IID_IDebugControl4, 0x94e60ce9, 0x9b41, 0x4b19,
+ 0x9f, 0xc0, 0x6d, 0x9e, 0xb3, 0x52, 0x72, 0xb3);
+/* 88f7dfab-3ea7-4c3a-aefb-c4e8106173aa */
+DEFINE_GUID(IID_IDebugDataSpaces, 0x88f7dfab, 0x3ea7, 0x4c3a,
+ 0xae, 0xfb, 0xc4, 0xe8, 0x10, 0x61, 0x73, 0xaa);
+/* 7a5e852f-96e9-468f-ac1b-0b3addc4a049 */
+DEFINE_GUID(IID_IDebugDataSpaces2, 0x7a5e852f, 0x96e9, 0x468f,
+ 0xac, 0x1b, 0x0b, 0x3a, 0xdd, 0xc4, 0xa0, 0x49);
+/* 23f79d6c-8aaf-4f7c-a607-9995f5407e63 */
+DEFINE_GUID(IID_IDebugDataSpaces3, 0x23f79d6c, 0x8aaf, 0x4f7c,
+ 0xa6, 0x07, 0x99, 0x95, 0xf5, 0x40, 0x7e, 0x63);
+/* d98ada1f-29e9-4ef5-a6c0-e53349883212 */
+DEFINE_GUID(IID_IDebugDataSpaces4, 0xd98ada1f, 0x29e9, 0x4ef5,
+ 0xa6, 0xc0, 0xe5, 0x33, 0x49, 0x88, 0x32, 0x12);
+/* 337be28b-5036-4d72-b6bf-c45fbb9f2eaa */
+DEFINE_GUID(IID_IDebugEventCallbacks, 0x337be28b, 0x5036, 0x4d72,
+ 0xb6, 0xbf, 0xc4, 0x5f, 0xbb, 0x9f, 0x2e, 0xaa);
+/* 0690e046-9c23-45ac-a04f-987ac29ad0d3 */
+DEFINE_GUID(IID_IDebugEventCallbacksWide, 0x0690e046, 0x9c23, 0x45ac,
+ 0xa0, 0x4f, 0x98, 0x7a, 0xc2, 0x9a, 0xd0, 0xd3);
+/* 9f50e42c-f136-499e-9a97-73036c94ed2d */
+DEFINE_GUID(IID_IDebugInputCallbacks, 0x9f50e42c, 0xf136, 0x499e,
+ 0x9a, 0x97, 0x73, 0x03, 0x6c, 0x94, 0xed, 0x2d);
+/* 4bf58045-d654-4c40-b0af-683090f356dc */
+DEFINE_GUID(IID_IDebugOutputCallbacks, 0x4bf58045, 0xd654, 0x4c40,
+ 0xb0, 0xaf, 0x68, 0x30, 0x90, 0xf3, 0x56, 0xdc);
+/* 4c7fd663-c394-4e26-8ef1-34ad5ed3764c */
+DEFINE_GUID(IID_IDebugOutputCallbacksWide, 0x4c7fd663, 0xc394, 0x4e26,
+ 0x8e, 0xf1, 0x34, 0xad, 0x5e, 0xd3, 0x76, 0x4c);
+/* 67721fe9-56d2-4a44-a325-2b65513ce6eb */
+DEFINE_GUID(IID_IDebugOutputCallbacks2, 0x67721fe9, 0x56d2, 0x4a44,
+ 0xa3, 0x25, 0x2b, 0x65, 0x51, 0x3c, 0xe6, 0xeb);
+/* ce289126-9e84-45a7-937e-67bb18691493 */
+DEFINE_GUID(IID_IDebugRegisters, 0xce289126, 0x9e84, 0x45a7,
+ 0x93, 0x7e, 0x67, 0xbb, 0x18, 0x69, 0x14, 0x93);
+/* 1656afa9-19c6-4e3a-97e7-5dc9160cf9c4 */
+DEFINE_GUID(IID_IDebugRegisters2, 0x1656afa9, 0x19c6, 0x4e3a,
+ 0x97, 0xe7, 0x5d, 0xc9, 0x16, 0x0c, 0xf9, 0xc4);
+/* f2528316-0f1a-4431-aeed-11d096e1e2ab */
+DEFINE_GUID(IID_IDebugSymbolGroup, 0xf2528316, 0x0f1a, 0x4431,
+ 0xae, 0xed, 0x11, 0xd0, 0x96, 0xe1, 0xe2, 0xab);
+/* 6a7ccc5f-fb5e-4dcc-b41c-6c20307bccc7 */
+DEFINE_GUID(IID_IDebugSymbolGroup2, 0x6a7ccc5f, 0xfb5e, 0x4dcc,
+ 0xb4, 0x1c, 0x6c, 0x20, 0x30, 0x7b, 0xcc, 0xc7);
+/* 8c31e98c-983a-48a5-9016-6fe5d667a950 */
+DEFINE_GUID(IID_IDebugSymbols, 0x8c31e98c, 0x983a, 0x48a5,
+ 0x90, 0x16, 0x6f, 0xe5, 0xd6, 0x67, 0xa9, 0x50);
+/* 3a707211-afdd-4495-ad4f-56fecdf8163f */
+DEFINE_GUID(IID_IDebugSymbols2, 0x3a707211, 0xafdd, 0x4495,
+ 0xad, 0x4f, 0x56, 0xfe, 0xcd, 0xf8, 0x16, 0x3f);
+/* f02fbecc-50ac-4f36-9ad9-c975e8f32ff8 */
+DEFINE_GUID(IID_IDebugSymbols3, 0xf02fbecc, 0x50ac, 0x4f36,
+ 0x9a, 0xd9, 0xc9, 0x75, 0xe8, 0xf3, 0x2f, 0xf8);
+/* 6b86fe2c-2c4f-4f0c-9da2-174311acc327 */
+DEFINE_GUID(IID_IDebugSystemObjects, 0x6b86fe2c, 0x2c4f, 0x4f0c,
+ 0x9d, 0xa2, 0x17, 0x43, 0x11, 0xac, 0xc3, 0x27);
+/* 0ae9f5ff-1852-4679-b055-494bee6407ee */
+DEFINE_GUID(IID_IDebugSystemObjects2, 0x0ae9f5ff, 0x1852, 0x4679,
+ 0xb0, 0x55, 0x49, 0x4b, 0xee, 0x64, 0x07, 0xee);
+/* e9676e2f-e286-4ea3-b0f9-dfe5d9fc330e */
+DEFINE_GUID(IID_IDebugSystemObjects3, 0xe9676e2f, 0xe286, 0x4ea3,
+ 0xb0, 0xf9, 0xdf, 0xe5, 0xd9, 0xfc, 0x33, 0x0e);
+/* 489468e6-7d0f-4af5-87ab-25207454d553 */
+DEFINE_GUID(IID_IDebugSystemObjects4, 0x489468e6, 0x7d0f, 0x4af5,
+ 0x87, 0xab, 0x25, 0x20, 0x74, 0x54, 0xd5, 0x53);
+
+typedef interface DECLSPEC_UUID("f2df5f53-071f-47bd-9de6-5734c3fed689")
+ IDebugAdvanced* PDEBUG_ADVANCED;
+typedef interface DECLSPEC_UUID("716d14c9-119b-4ba5-af1f-0890e672416a")
+ IDebugAdvanced2* PDEBUG_ADVANCED2;
+typedef interface DECLSPEC_UUID("cba4abb4-84c4-444d-87ca-a04e13286739")
+ IDebugAdvanced3* PDEBUG_ADVANCED3;
+typedef interface DECLSPEC_UUID("5bd9d474-5975-423a-b88b-65a8e7110e65")
+ IDebugBreakpoint* PDEBUG_BREAKPOINT;
+typedef interface DECLSPEC_UUID("1b278d20-79f2-426e-a3f9-c1ddf375d48e")
+ IDebugBreakpoint2* PDEBUG_BREAKPOINT2;
+typedef interface DECLSPEC_UUID("27fe5639-8407-4f47-8364-ee118fb08ac8")
+ IDebugClient* PDEBUG_CLIENT;
+typedef interface DECLSPEC_UUID("edbed635-372e-4dab-bbfe-ed0d2f63be81")
+ IDebugClient2* PDEBUG_CLIENT2;
+typedef interface DECLSPEC_UUID("dd492d7f-71b8-4ad6-a8dc-1c887479ff91")
+ IDebugClient3* PDEBUG_CLIENT3;
+typedef interface DECLSPEC_UUID("ca83c3de-5089-4cf8-93c8-d892387f2a5e")
+ IDebugClient4* PDEBUG_CLIENT4;
+typedef interface DECLSPEC_UUID("e3acb9d7-7ec2-4f0c-a0da-e81e0cbbe628")
+ IDebugClient5* PDEBUG_CLIENT5;
+typedef interface DECLSPEC_UUID("5182e668-105e-416e-ad92-24ef800424ba")
+ IDebugControl* PDEBUG_CONTROL;
+typedef interface DECLSPEC_UUID("d4366723-44df-4bed-8c7e-4c05424f4588")
+ IDebugControl2* PDEBUG_CONTROL2;
+typedef interface DECLSPEC_UUID("7df74a86-b03f-407f-90ab-a20dadcead08")
+ IDebugControl3* PDEBUG_CONTROL3;
+typedef interface DECLSPEC_UUID("94e60ce9-9b41-4b19-9fc0-6d9eb35272b3")
+ IDebugControl4* PDEBUG_CONTROL4;
+typedef interface DECLSPEC_UUID("88f7dfab-3ea7-4c3a-aefb-c4e8106173aa")
+ IDebugDataSpaces* PDEBUG_DATA_SPACES;
+typedef interface DECLSPEC_UUID("7a5e852f-96e9-468f-ac1b-0b3addc4a049")
+ IDebugDataSpaces2* PDEBUG_DATA_SPACES2;
+typedef interface DECLSPEC_UUID("23f79d6c-8aaf-4f7c-a607-9995f5407e63")
+ IDebugDataSpaces3* PDEBUG_DATA_SPACES3;
+typedef interface DECLSPEC_UUID("d98ada1f-29e9-4ef5-a6c0-e53349883212")
+ IDebugDataSpaces4* PDEBUG_DATA_SPACES4;
+typedef interface DECLSPEC_UUID("337be28b-5036-4d72-b6bf-c45fbb9f2eaa")
+ IDebugEventCallbacks* PDEBUG_EVENT_CALLBACKS;
+typedef interface DECLSPEC_UUID("0690e046-9c23-45ac-a04f-987ac29ad0d3")
+ IDebugEventCallbacksWide* PDEBUG_EVENT_CALLBACKS_WIDE;
+typedef interface DECLSPEC_UUID("9f50e42c-f136-499e-9a97-73036c94ed2d")
+ IDebugInputCallbacks* PDEBUG_INPUT_CALLBACKS;
+typedef interface DECLSPEC_UUID("4bf58045-d654-4c40-b0af-683090f356dc")
+ IDebugOutputCallbacks* PDEBUG_OUTPUT_CALLBACKS;
+typedef interface DECLSPEC_UUID("4c7fd663-c394-4e26-8ef1-34ad5ed3764c")
+ IDebugOutputCallbacksWide* PDEBUG_OUTPUT_CALLBACKS_WIDE;
+typedef interface DECLSPEC_UUID("67721fe9-56d2-4a44-a325-2b65513ce6eb")
+ IDebugOutputCallbacks2* PDEBUG_OUTPUT_CALLBACKS2;
+typedef interface DECLSPEC_UUID("ce289126-9e84-45a7-937e-67bb18691493")
+ IDebugRegisters* PDEBUG_REGISTERS;
+typedef interface DECLSPEC_UUID("1656afa9-19c6-4e3a-97e7-5dc9160cf9c4")
+ IDebugRegisters2* PDEBUG_REGISTERS2;
+typedef interface DECLSPEC_UUID("f2528316-0f1a-4431-aeed-11d096e1e2ab")
+ IDebugSymbolGroup* PDEBUG_SYMBOL_GROUP;
+typedef interface DECLSPEC_UUID("6a7ccc5f-fb5e-4dcc-b41c-6c20307bccc7")
+ IDebugSymbolGroup2* PDEBUG_SYMBOL_GROUP2;
+typedef interface DECLSPEC_UUID("8c31e98c-983a-48a5-9016-6fe5d667a950")
+ IDebugSymbols* PDEBUG_SYMBOLS;
+typedef interface DECLSPEC_UUID("3a707211-afdd-4495-ad4f-56fecdf8163f")
+ IDebugSymbols2* PDEBUG_SYMBOLS2;
+typedef interface DECLSPEC_UUID("f02fbecc-50ac-4f36-9ad9-c975e8f32ff8")
+ IDebugSymbols3* PDEBUG_SYMBOLS3;
+typedef interface DECLSPEC_UUID("6b86fe2c-2c4f-4f0c-9da2-174311acc327")
+ IDebugSystemObjects* PDEBUG_SYSTEM_OBJECTS;
+typedef interface DECLSPEC_UUID("0ae9f5ff-1852-4679-b055-494bee6407ee")
+ IDebugSystemObjects2* PDEBUG_SYSTEM_OBJECTS2;
+typedef interface DECLSPEC_UUID("e9676e2f-e286-4ea3-b0f9-dfe5d9fc330e")
+ IDebugSystemObjects3* PDEBUG_SYSTEM_OBJECTS3;
+typedef interface DECLSPEC_UUID("489468e6-7d0f-4af5-87ab-25207454d553")
+ IDebugSystemObjects4* PDEBUG_SYSTEM_OBJECTS4;
+
+//----------------------------------------------------------------------------
+//
+// Macros.
+//
+//----------------------------------------------------------------------------
+
+// Extends a 32-bit address into a 64-bit address.
+#define DEBUG_EXTEND64(Addr) ((ULONG64)(LONG64)(LONG)(Addr))
+
+//----------------------------------------------------------------------------
+//
+// Client creation functions.
+//
+//----------------------------------------------------------------------------
+
+// RemoteOptions specifies connection types and
+// their parameters. Supported strings are:
+// npipe:Server=<Machine>,Pipe=<Pipe name>
+// tcp:Server=<Machine>,Port=<IP port>
+STDAPI
+DebugConnect(
+ __in PCSTR RemoteOptions,
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ );
+
+STDAPI
+DebugConnectWide(
+ __in PCWSTR RemoteOptions,
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ );
+
+STDAPI
+DebugCreate(
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ );
+
+//----------------------------------------------------------------------------
+//
+// IDebugAdvanced.
+//
+//----------------------------------------------------------------------------
+
+typedef struct _DEBUG_OFFSET_REGION
+{
+ ULONG64 Base;
+ ULONG64 Size;
+} DEBUG_OFFSET_REGION, *PDEBUG_OFFSET_REGION;
+
+#undef INTERFACE
+#define INTERFACE IDebugAdvanced
+DECLARE_INTERFACE_(IDebugAdvanced, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugAdvanced.
+
+ // Get/SetThreadContext offer control over
+ // the full processor context for a thread.
+ // Higher-level functions, such as the
+ // IDebugRegisters interface, allow similar
+ // access in simpler and more generic ways.
+ // Get/SetThreadContext are useful when
+ // large amounts of thread context must
+ // be changed and processor-specific code
+ // is not a problem.
+ STDMETHOD(GetThreadContext)(
+ THIS_
+ __out_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+ STDMETHOD(SetThreadContext)(
+ THIS_
+ __in_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+};
+
+typedef struct _DEBUG_READ_USER_MINIDUMP_STREAM
+{
+ IN ULONG StreamType;
+ IN ULONG Flags;
+ IN ULONG64 Offset;
+ OUT PVOID Buffer;
+ IN ULONG BufferSize;
+ OUT ULONG BufferUsed;
+} DEBUG_READ_USER_MINIDUMP_STREAM, *PDEBUG_READ_USER_MINIDUMP_STREAM;
+
+#define DEBUG_GET_TEXT_COMPLETIONS_NO_DOT_COMMANDS 0x00000001
+#define DEBUG_GET_TEXT_COMPLETIONS_NO_EXTENSION_COMMANDS 0x00000002
+#define DEBUG_GET_TEXT_COMPLETIONS_NO_SYMBOLS 0x00000004
+
+typedef struct _DEBUG_GET_TEXT_COMPLETIONS_IN
+{
+ ULONG Flags;
+ ULONG MatchCountLimit;
+ ULONG64 Reserved[3];
+ // Input text string follows.
+} DEBUG_GET_TEXT_COMPLETIONS_IN, *PDEBUG_GET_TEXT_COMPLETIONS_IN;
+
+#define DEBUG_GET_TEXT_COMPLETIONS_IS_DOT_COMMAND 0x00000001
+#define DEBUG_GET_TEXT_COMPLETIONS_IS_EXTENSION_COMMAND 0x00000002
+#define DEBUG_GET_TEXT_COMPLETIONS_IS_SYMBOL 0x00000004
+
+typedef struct _DEBUG_GET_TEXT_COMPLETIONS_OUT
+{
+ ULONG Flags;
+ // Char index in input string where completions start.
+ ULONG ReplaceIndex;
+ ULONG MatchCount;
+ ULONG Reserved1;
+ ULONG64 Reserved2[2];
+ // Completions follow.
+ // Completion data is zero-terminated strings ended
+ // by a final zero double-terminator.
+} DEBUG_GET_TEXT_COMPLETIONS_OUT, *PDEBUG_GET_TEXT_COMPLETIONS_OUT;
+
+typedef struct _DEBUG_CACHED_SYMBOL_INFO
+{
+ ULONG64 ModBase;
+ ULONG64 Arg1;
+ ULONG64 Arg2;
+ ULONG Id;
+ ULONG Arg3;
+} DEBUG_CACHED_SYMBOL_INFO, *PDEBUG_CACHED_SYMBOL_INFO;
+
+//
+// Request requests.
+//
+
+// InBuffer - Unused.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_SOURCE_PATH_HAS_SOURCE_SERVER 0
+
+// InBuffer - Unused.
+// OutBuffer - Machine-specific CONTEXT.
+#define DEBUG_REQUEST_TARGET_EXCEPTION_CONTEXT 1
+
+// InBuffer - Unused.
+// OutBuffer - ULONG system ID of thread.
+#define DEBUG_REQUEST_TARGET_EXCEPTION_THREAD 2
+
+// InBuffer - Unused.
+// OutBuffer - EXCEPTION_RECORD64.
+#define DEBUG_REQUEST_TARGET_EXCEPTION_RECORD 3
+
+// InBuffer - Unused.
+// OutBuffer - DEBUG_CREATE_PROCESS_OPTIONS.
+#define DEBUG_REQUEST_GET_ADDITIONAL_CREATE_OPTIONS 4
+
+// InBuffer - DEBUG_CREATE_PROCESS_OPTIONS.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_SET_ADDITIONAL_CREATE_OPTIONS 5
+
+// InBuffer - Unused.
+// OutBuffer - ULONG[2] major/minor.
+#define DEBUG_REQUEST_GET_WIN32_MAJOR_MINOR_VERSIONS 6
+
+// InBuffer - DEBUG_READ_USER_MINIDUMP_STREAM.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_READ_USER_MINIDUMP_STREAM 7
+
+// InBuffer - Unused.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_TARGET_CAN_DETACH 8
+
+// InBuffer - PTSTR.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_SET_LOCAL_IMPLICIT_COMMAND_LINE 9
+
+// InBuffer - Unused.
+// OutBuffer - Event code stream offset.
+#define DEBUG_REQUEST_GET_CAPTURED_EVENT_CODE_OFFSET 10
+
+// InBuffer - Unused.
+// OutBuffer - Event code stream information.
+#define DEBUG_REQUEST_READ_CAPTURED_EVENT_CODE_STREAM 11
+
+// InBuffer - Input data block.
+// OutBuffer - Processed data block.
+#define DEBUG_REQUEST_EXT_TYPED_DATA_ANSI 12
+
+// InBuffer - Unused.
+// OutBuffer - Returned path.
+#define DEBUG_REQUEST_GET_EXTENSION_SEARCH_PATH_WIDE 13
+
+// InBuffer - DEBUG_GET_TEXT_COMPLETIONS_IN.
+// OutBuffer - DEBUG_GET_TEXT_COMPLETIONS_OUT.
+#define DEBUG_REQUEST_GET_TEXT_COMPLETIONS_WIDE 14
+
+// InBuffer - ULONG64 cookie.
+// OutBuffer - DEBUG_CACHED_SYMBOL_INFO.
+#define DEBUG_REQUEST_GET_CACHED_SYMBOL_INFO 15
+
+// InBuffer - DEBUG_CACHED_SYMBOL_INFO.
+// OutBuffer - ULONG64 cookie.
+#define DEBUG_REQUEST_ADD_CACHED_SYMBOL_INFO 16
+
+// InBuffer - ULONG64 cookie.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_REMOVE_CACHED_SYMBOL_INFO 17
+
+// InBuffer - DEBUG_GET_TEXT_COMPLETIONS_IN.
+// OutBuffer - DEBUG_GET_TEXT_COMPLETIONS_OUT.
+#define DEBUG_REQUEST_GET_TEXT_COMPLETIONS_ANSI 18
+
+// InBuffer - Unused.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_CURRENT_OUTPUT_CALLBACKS_ARE_DML_AWARE 19
+
+// InBuffer - ULONG64 offset.
+// OutBuffer - Unwind information.
+#define DEBUG_REQUEST_GET_OFFSET_UNWIND_INFORMATION 20
+
+// InBuffer - Unused
+// OutBuffer - returned DUMP_HEADER32/DUMP_HEADER64 structure.
+#define DEBUG_REQUEST_GET_DUMP_HEADER 21
+
+// InBuffer - DUMP_HEADER32/DUMP_HEADER64 structure.
+// OutBuffer - Unused
+#define DEBUG_REQUEST_SET_DUMP_HEADER 22
+
+//
+// GetSourceFileInformation requests.
+//
+
+// Arg64 - Module base.
+// Arg32 - Unused.
+#define DEBUG_SRCFILE_SYMBOL_TOKEN 0
+
+// Arg64 - Module base.
+// Arg32 - Unused.
+#define DEBUG_SRCFILE_SYMBOL_TOKEN_SOURCE_COMMAND_WIDE 1
+
+//
+// GetSymbolInformation requests.
+//
+
+// Arg64 - Unused.
+// Arg32 - Breakpoint ID.
+// Buffer - ULONG line number.
+// String - File name.
+#define DEBUG_SYMINFO_BREAKPOINT_SOURCE_LINE 0
+
+// Arg64 - Module base.
+// Arg32 - Unused.
+// Buffer - IMAGEHLP_MODULEW64.
+// String - Unused.
+#define DEBUG_SYMINFO_IMAGEHLP_MODULEW64 1
+
+// Arg64 - Offset.
+// Arg32 - Symbol tag.
+// Buffer - Unicode symbol name strings. Could have multiple strings.
+// String - Unused, strings are returned in Buffer as there
+// may be more than one.
+#define DEBUG_SYMINFO_GET_SYMBOL_NAME_BY_OFFSET_AND_TAG_WIDE 2
+
+// Arg64 - Module base.
+// Arg32 - Symbol tag.
+// Buffer - Array of symbol addresses.
+// String - Concatenated symbol strings. Individual symbol
+// strings are zero-terminated and the final string in
+// a symbol is double-zero-terminated.
+#define DEBUG_SYMINFO_GET_MODULE_SYMBOL_NAMES_AND_OFFSETS 3
+
+//
+// GetSystemObjectInformation requests.
+//
+
+// Arg64 - Unused.
+// Arg32 - Debugger thread ID.
+// Buffer - DEBUG_THREAD_BASIC_INFORMATION.
+#define DEBUG_SYSOBJINFO_THREAD_BASIC_INFORMATION 0
+
+// Arg64 - Unused.
+// Arg32 - Debugger thread ID.
+// Buffer - Unicode name string.
+#define DEBUG_SYSOBJINFO_THREAD_NAME_WIDE 1
+
+// Arg64 - Unused.
+// Arg32 - Unused.
+// Buffer - ULONG cookie value.
+#define DEBUG_SYSOBJINFO_CURRENT_PROCESS_COOKIE 2
+
+#define DEBUG_TBINFO_EXIT_STATUS 0x00000001
+#define DEBUG_TBINFO_PRIORITY_CLASS 0x00000002
+#define DEBUG_TBINFO_PRIORITY 0x00000004
+#define DEBUG_TBINFO_TIMES 0x00000008
+#define DEBUG_TBINFO_START_OFFSET 0x00000010
+#define DEBUG_TBINFO_AFFINITY 0x00000020
+#define DEBUG_TBINFO_ALL 0x0000003f
+
+typedef struct _DEBUG_THREAD_BASIC_INFORMATION
+{
+ // Valid members have a DEBUG_TBINFO bit set in Valid.
+ ULONG Valid;
+ ULONG ExitStatus;
+ ULONG PriorityClass;
+ ULONG Priority;
+ ULONG64 CreateTime;
+ ULONG64 ExitTime;
+ ULONG64 KernelTime;
+ ULONG64 UserTime;
+ ULONG64 StartOffset;
+ ULONG64 Affinity;
+} DEBUG_THREAD_BASIC_INFORMATION, *PDEBUG_THREAD_BASIC_INFORMATION;
+
+#undef INTERFACE
+#define INTERFACE IDebugAdvanced2
+DECLARE_INTERFACE_(IDebugAdvanced2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugAdvanced.
+
+ // Get/SetThreadContext offer control over
+ // the full processor context for a thread.
+ // Higher-level functions, such as the
+ // IDebugRegisters interface, allow similar
+ // access in simpler and more generic ways.
+ // Get/SetThreadContext are useful when
+ // large amounts of thread context must
+ // be changed and processor-specific code
+ // is not a problem.
+ STDMETHOD(GetThreadContext)(
+ THIS_
+ __out_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+ STDMETHOD(SetThreadContext)(
+ THIS_
+ __in_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+
+ // IDebugAdvanced2.
+
+ //
+ // Generalized open-ended methods for querying
+ // and manipulation. The open-ended nature of
+ // these methods makes it easy to add new requests,
+ // although at a cost in convenience of calling.
+ // Sufficiently common requests may have more specific,
+ // simpler methods elsewhere.
+ //
+
+ STDMETHOD(Request)(
+ THIS_
+ __in ULONG Request,
+ __in_bcount_opt(InBufferSize) PVOID InBuffer,
+ __in ULONG InBufferSize,
+ __out_bcount_opt(OutBufferSize) PVOID OutBuffer,
+ __in ULONG OutBufferSize,
+ __out_opt PULONG OutSize
+ ) PURE;
+
+ STDMETHOD(GetSourceFileInformation)(
+ THIS_
+ __in ULONG Which,
+ __in PSTR SourceFile,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+ STDMETHOD(FindSourceFileAndToken)(
+ THIS_
+ __in ULONG StartElement,
+ __in ULONG64 ModAddr,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __in_bcount_opt(FileTokenSize) PVOID FileToken,
+ __in ULONG FileTokenSize,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+
+ STDMETHOD(GetSymbolInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize,
+ __out_ecount_opt(StringBufferSize) PSTR StringBuffer,
+ __in ULONG StringBufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+
+ STDMETHOD(GetSystemObjectInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugAdvanced3
+DECLARE_INTERFACE_(IDebugAdvanced3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugAdvanced.
+
+ // Get/SetThreadContext offer control over
+ // the full processor context for a thread.
+ // Higher-level functions, such as the
+ // IDebugRegisters interface, allow similar
+ // access in simpler and more generic ways.
+ // Get/SetThreadContext are useful when
+ // large amounts of thread context must
+ // be changed and processor-specific code
+ // is not a problem.
+ STDMETHOD(GetThreadContext)(
+ THIS_
+ __out_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+ STDMETHOD(SetThreadContext)(
+ THIS_
+ __in_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+
+ // IDebugAdvanced2.
+
+ //
+ // Generalized open-ended methods for querying
+ // and manipulation. The open-ended nature of
+ // these methods makes it easy to add new requests,
+ // although at a cost in convenience of calling.
+ // Sufficiently common requests may have more specific,
+ // simpler methods elsewhere.
+ //
+
+ STDMETHOD(Request)(
+ THIS_
+ __in ULONG Request,
+ __in_bcount_opt(InBufferSize) PVOID InBuffer,
+ __in ULONG InBufferSize,
+ __out_bcount_opt(OutBufferSize) PVOID OutBuffer,
+ __in ULONG OutBufferSize,
+ __out_opt PULONG OutSize
+ ) PURE;
+
+ STDMETHOD(GetSourceFileInformation)(
+ THIS_
+ __in ULONG Which,
+ __in PSTR SourceFile,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+ STDMETHOD(FindSourceFileAndToken)(
+ THIS_
+ __in ULONG StartElement,
+ __in ULONG64 ModAddr,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __in_bcount_opt(FileTokenSize) PVOID FileToken,
+ __in ULONG FileTokenSize,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+
+ STDMETHOD(GetSymbolInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize,
+ __out_ecount_opt(StringBufferSize) PSTR StringBuffer,
+ __in ULONG StringBufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+
+ STDMETHOD(GetSystemObjectInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+
+ // IDebugAdvanced3.
+
+ STDMETHOD(GetSourceFileInformationWide)(
+ THIS_
+ __in ULONG Which,
+ __in PWSTR SourceFile,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+ STDMETHOD(FindSourceFileAndTokenWide)(
+ THIS_
+ __in ULONG StartElement,
+ __in ULONG64 ModAddr,
+ __in PCWSTR File,
+ __in ULONG Flags,
+ __in_bcount_opt(FileTokenSize) PVOID FileToken,
+ __in ULONG FileTokenSize,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+
+ STDMETHOD(GetSymbolInformationWide)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize,
+ __out_ecount_opt(StringBufferSize) PWSTR StringBuffer,
+ __in ULONG StringBufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugBreakpoint.
+//
+//----------------------------------------------------------------------------
+
+// Types of breakpoints.
+#define DEBUG_BREAKPOINT_CODE 0
+#define DEBUG_BREAKPOINT_DATA 1
+#define DEBUG_BREAKPOINT_TIME 2
+
+// Breakpoint flags.
+// Go-only breakpoints are only active when
+// the engine is in unrestricted execution
+// mode. They do not fire when the engine
+// is stepping.
+#define DEBUG_BREAKPOINT_GO_ONLY 0x00000001
+// A breakpoint is flagged as deferred as long as
+// its offset expression cannot be evaluated.
+// A deferred breakpoint is not active.
+#define DEBUG_BREAKPOINT_DEFERRED 0x00000002
+#define DEBUG_BREAKPOINT_ENABLED 0x00000004
+// The adder-only flag does not affect breakpoint
+// operation. It is just a marker to restrict
+// output and notifications for the breakpoint to
+// the client that added the breakpoint. Breakpoint
+// callbacks for adder-only breaks will only be delivered
+// to the adding client. The breakpoint can not
+// be enumerated and accessed by other clients.
+#define DEBUG_BREAKPOINT_ADDER_ONLY 0x00000008
+// One-shot breakpoints automatically clear themselves
+// the first time they are hit.
+#define DEBUG_BREAKPOINT_ONE_SHOT 0x00000010
+
+// Data breakpoint access types.
+// Different architectures support different
+// sets of these bits.
+#define DEBUG_BREAK_READ 0x00000001
+#define DEBUG_BREAK_WRITE 0x00000002
+#define DEBUG_BREAK_EXECUTE 0x00000004
+#define DEBUG_BREAK_IO 0x00000008
+
+// Structure for querying breakpoint information
+// all at once.
+typedef struct _DEBUG_BREAKPOINT_PARAMETERS
+{
+ ULONG64 Offset;
+ ULONG Id;
+ ULONG BreakType;
+ ULONG ProcType;
+ ULONG Flags;
+ ULONG DataSize;
+ ULONG DataAccessType;
+ ULONG PassCount;
+ ULONG CurrentPassCount;
+ ULONG MatchThread;
+ ULONG CommandSize;
+ ULONG OffsetExpressionSize;
+} DEBUG_BREAKPOINT_PARAMETERS, *PDEBUG_BREAKPOINT_PARAMETERS;
+
+#undef INTERFACE
+#define INTERFACE IDebugBreakpoint
+DECLARE_INTERFACE_(IDebugBreakpoint, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugBreakpoint.
+
+ // Retrieves debugger engine unique ID
+ // for the breakpoint. This ID is
+ // fixed as long as the breakpoint exists
+ // but after that may be reused.
+ STDMETHOD(GetId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Retrieves the type of break and
+ // processor type for the breakpoint.
+ STDMETHOD(GetType)(
+ THIS_
+ __out PULONG BreakType,
+ __out PULONG ProcType
+ ) PURE;
+ // Returns the client that called AddBreakpoint.
+ STDMETHOD(GetAdder)(
+ THIS_
+ __out PDEBUG_CLIENT* Adder
+ ) PURE;
+
+ STDMETHOD(GetFlags)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ // Only certain flags can be changed. Flags
+ // are: GO_ONLY, ENABLE.
+ // Sets the given flags.
+ STDMETHOD(AddFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Clears the given flags.
+ STDMETHOD(RemoveFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Sets the flags.
+ STDMETHOD(SetFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+
+ // Controls the offset of the breakpoint. The
+ // interpretation of the offset value depends on
+ // the type of breakpoint and its settings. It
+ // may be a code address, a data address, an
+ // I/O port, etc.
+ STDMETHOD(GetOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // Data breakpoint methods will fail if the
+ // target platform does not support the
+ // parameters used.
+ // These methods only function for breakpoints
+ // created as data breakpoints.
+ STDMETHOD(GetDataParameters)(
+ THIS_
+ __out PULONG Size,
+ __out PULONG AccessType
+ ) PURE;
+ STDMETHOD(SetDataParameters)(
+ THIS_
+ __in ULONG Size,
+ __in ULONG AccessType
+ ) PURE;
+
+ // Pass count defaults to one.
+ STDMETHOD(GetPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(SetPassCount)(
+ THIS_
+ __in ULONG Count
+ ) PURE;
+ // Gets the current number of times
+ // the breakpoint has been hit since
+ // it was last triggered.
+ STDMETHOD(GetCurrentPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+
+ // If a match thread is set this breakpoint will
+ // only trigger if it occurs on the match thread.
+ // Otherwise it triggers for all threads.
+ // Thread restrictions are not currently supported
+ // in kernel mode.
+ STDMETHOD(GetMatchThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetMatchThreadId)(
+ THIS_
+ __in ULONG Thread
+ ) PURE;
+
+ // The command for a breakpoint is automatically
+ // executed by the engine before the event
+ // is propagated. If the breakpoint continues
+ // execution the event will begin with a continue
+ // status. If the breakpoint does not continue
+ // the event will begin with a break status.
+ // This allows breakpoint commands to participate
+ // in the normal event status voting.
+ // Breakpoint commands are only executed until
+ // the first command that alters the execution
+ // status, such as g, p and t.
+ STDMETHOD(GetCommand)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetCommand)(
+ THIS_
+ __in PCSTR Command
+ ) PURE;
+
+ // Offset expressions are evaluated immediately
+ // and at module load and unload events. If the
+ // evaluation is successful the breakpoints
+ // offset is updated and the breakpoint is
+ // handled normally. If the expression cannot
+ // be evaluated the breakpoint is deferred.
+ // Currently the only offset expression
+ // supported is a module-relative symbol
+ // of the form <Module>!<Symbol>.
+ STDMETHOD(GetOffsetExpression)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExpressionSize
+ ) PURE;
+ STDMETHOD(SetOffsetExpression)(
+ THIS_
+ __in PCSTR Expression
+ ) PURE;
+
+ STDMETHOD(GetParameters)(
+ THIS_
+ __out PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugBreakpoint2
+DECLARE_INTERFACE_(IDebugBreakpoint2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugBreakpoint.
+
+ // Retrieves debugger engine unique ID
+ // for the breakpoint. This ID is
+ // fixed as long as the breakpoint exists
+ // but after that may be reused.
+ STDMETHOD(GetId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Retrieves the type of break and
+ // processor type for the breakpoint.
+ STDMETHOD(GetType)(
+ THIS_
+ __out PULONG BreakType,
+ __out PULONG ProcType
+ ) PURE;
+ // Returns the client that called AddBreakpoint.
+ STDMETHOD(GetAdder)(
+ THIS_
+ __out PDEBUG_CLIENT* Adder
+ ) PURE;
+
+ STDMETHOD(GetFlags)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ // Only certain flags can be changed. Flags
+ // are: GO_ONLY, ENABLE.
+ // Sets the given flags.
+ STDMETHOD(AddFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Clears the given flags.
+ STDMETHOD(RemoveFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Sets the flags.
+ STDMETHOD(SetFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+
+ // Controls the offset of the breakpoint. The
+ // interpretation of the offset value depends on
+ // the type of breakpoint and its settings. It
+ // may be a code address, a data address, an
+ // I/O port, etc.
+ STDMETHOD(GetOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // Data breakpoint methods will fail if the
+ // target platform does not support the
+ // parameters used.
+ // These methods only function for breakpoints
+ // created as data breakpoints.
+ STDMETHOD(GetDataParameters)(
+ THIS_
+ __out PULONG Size,
+ __out PULONG AccessType
+ ) PURE;
+ STDMETHOD(SetDataParameters)(
+ THIS_
+ __in ULONG Size,
+ __in ULONG AccessType
+ ) PURE;
+
+ // Pass count defaults to one.
+ STDMETHOD(GetPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(SetPassCount)(
+ THIS_
+ __in ULONG Count
+ ) PURE;
+ // Gets the current number of times
+ // the breakpoint has been hit since
+ // it was last triggered.
+ STDMETHOD(GetCurrentPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+
+ // If a match thread is set this breakpoint will
+ // only trigger if it occurs on the match thread.
+ // Otherwise it triggers for all threads.
+ // Thread restrictions are not currently supported
+ // in kernel mode.
+ STDMETHOD(GetMatchThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetMatchThreadId)(
+ THIS_
+ __in ULONG Thread
+ ) PURE;
+
+ // The command for a breakpoint is automatically
+ // executed by the engine before the event
+ // is propagated. If the breakpoint continues
+ // execution the event will begin with a continue
+ // status. If the breakpoint does not continue
+ // the event will begin with a break status.
+ // This allows breakpoint commands to participate
+ // in the normal event status voting.
+ // Breakpoint commands are only executed until
+ // the first command that alters the execution
+ // status, such as g, p and t.
+ STDMETHOD(GetCommand)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetCommand)(
+ THIS_
+ __in PCSTR Command
+ ) PURE;
+
+ // Offset expressions are evaluated immediately
+ // and at module load and unload events. If the
+ // evaluation is successful the breakpoints
+ // offset is updated and the breakpoint is
+ // handled normally. If the expression cannot
+ // be evaluated the breakpoint is deferred.
+ // Currently the only offset expression
+ // supported is a module-relative symbol
+ // of the form <Module>!<Symbol>.
+ STDMETHOD(GetOffsetExpression)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExpressionSize
+ ) PURE;
+ STDMETHOD(SetOffsetExpression)(
+ THIS_
+ __in PCSTR Expression
+ ) PURE;
+
+ STDMETHOD(GetParameters)(
+ THIS_
+ __out PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+
+ // IDebugBreakpoint2.
+
+ STDMETHOD(GetCommandWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetCommandWide)(
+ THIS_
+ __in PCWSTR Command
+ ) PURE;
+
+ STDMETHOD(GetOffsetExpressionWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExpressionSize
+ ) PURE;
+ STDMETHOD(SetOffsetExpressionWide)(
+ THIS_
+ __in PCWSTR Expression
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugClient.
+//
+//----------------------------------------------------------------------------
+
+// Kernel attach flags.
+#define DEBUG_ATTACH_KERNEL_CONNECTION 0x00000000
+// Attach to the local machine. If this flag is not set
+// a connection is made to a separate target machine using
+// the given connection options.
+#define DEBUG_ATTACH_LOCAL_KERNEL 0x00000001
+// Attach to an eXDI driver.
+#define DEBUG_ATTACH_EXDI_DRIVER 0x00000002
+
+// GetRunningProcessSystemIdByExecutableName flags.
+// By default the match allows a tail match on
+// just the filename. The match returns the first hit
+// even if multiple matches exist.
+#define DEBUG_GET_PROC_DEFAULT 0x00000000
+// The name must match fully.
+#define DEBUG_GET_PROC_FULL_MATCH 0x00000001
+// The match must be the only match.
+#define DEBUG_GET_PROC_ONLY_MATCH 0x00000002
+// The name is a service name instead of an executable name.
+#define DEBUG_GET_PROC_SERVICE_NAME 0x00000004
+
+// GetRunningProcessDescription flags.
+#define DEBUG_PROC_DESC_DEFAULT 0x00000000
+// Return only filenames, not full paths.
+#define DEBUG_PROC_DESC_NO_PATHS 0x00000001
+// Dont look up service names.
+#define DEBUG_PROC_DESC_NO_SERVICES 0x00000002
+// Dont look up MTS package names.
+#define DEBUG_PROC_DESC_NO_MTS_PACKAGES 0x00000004
+// Dont retrieve the command line.
+#define DEBUG_PROC_DESC_NO_COMMAND_LINE 0x00000008
+// Dont retrieve the session ID.
+#define DEBUG_PROC_DESC_NO_SESSION_ID 0x00000010
+// Dont retrieve the process's user name.
+#define DEBUG_PROC_DESC_NO_USER_NAME 0x00000020
+
+//
+// Attach flags.
+//
+
+// Call DebugActiveProcess when attaching.
+#define DEBUG_ATTACH_DEFAULT 0x00000000
+// When attaching to a process just examine
+// the process state and suspend the threads.
+// DebugActiveProcess is not called so the process
+// is not actually being debugged. This is useful
+// for debugging processes holding locks which
+// interfere with the operation of DebugActiveProcess
+// or in situations where it is not desirable to
+// actually set up as a debugger.
+#define DEBUG_ATTACH_NONINVASIVE 0x00000001
+// Attempt to attach to a process that was abandoned
+// when being debugged. This is only supported in
+// some system versions.
+// This flag also allows multiple debuggers to
+// attach to the same process, which can result
+// in numerous problems unless very carefully
+// managed.
+#define DEBUG_ATTACH_EXISTING 0x00000002
+// When attaching non-invasively, do not suspend
+// threads. It is the callers responsibility
+// to either suspend the threads itself or be
+// aware that the attach state may not reflect
+// the current state of the process if threads
+// are still running.
+#define DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND 0x00000004
+// When doing an invasive attach do not inject
+// a break-in thread to generate the initial break-in
+// event. This can be useful to save resources when
+// an initial break is not necessary or when injecting
+// a thread might affect the debuggee's state. This
+// option is only supported on Windows XP and above.
+#define DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK 0x00000008
+// When doing an invasive attach resume all threads at the
+// time of attach. This makes it possible to attach
+// to a process created suspended and cause it to start running.
+#define DEBUG_ATTACH_INVASIVE_RESUME_PROCESS 0x00000010
+// When doing a non-invasive attach the engine must
+// recover information for all debuggee elements. The
+// engine may not have permissions for all elements,
+// for example it may not be able to open all threads,
+// and that would ordinarily block the attach. This
+// flag allows unusable elements to be ignored.
+#define DEBUG_ATTACH_NONINVASIVE_ALLOW_PARTIAL 0x00000020
+
+
+//
+// Process creation flags to merge with Win32 flags.
+//
+
+// On Windows XP this flag prevents the debug
+// heap from being used in the new process.
+#define DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP CREATE_UNICODE_ENVIRONMENT
+// Indicates that the native NT RTL process creation
+// routines should be used instead of Win32. This
+// is only meaningful for special processes that run
+// as NT native processes.
+#define DEBUG_CREATE_PROCESS_THROUGH_RTL STACK_SIZE_PARAM_IS_A_RESERVATION
+
+//
+// Process creation flags specific to the debugger engine.
+//
+
+#define DEBUG_ECREATE_PROCESS_DEFAULT 0x00000000
+#define DEBUG_ECREATE_PROCESS_INHERIT_HANDLES 0x00000001
+#define DEBUG_ECREATE_PROCESS_USE_VERIFIER_FLAGS 0x00000002
+#define DEBUG_ECREATE_PROCESS_USE_IMPLICIT_COMMAND_LINE 0x00000004
+
+typedef struct _DEBUG_CREATE_PROCESS_OPTIONS
+{
+ // Win32 create flags.
+ ULONG CreateFlags;
+ // DEBUG_ECREATE_PROCESS_* flags.
+ ULONG EngCreateFlags;
+ // Application Verifier flags,
+ // if DEBUG_ECREATE_PROCESS_USE_VERIFIER_FLAGS is set.
+ ULONG VerifierFlags;
+ // Must be zero.
+ ULONG Reserved;
+} DEBUG_CREATE_PROCESS_OPTIONS, *PDEBUG_CREATE_PROCESS_OPTIONS;
+
+//
+// Process options.
+//
+
+// Indicates that the debuggee process should be
+// automatically detached when the debugger exits.
+// A debugger can explicitly detach on exit or this
+// flag can be set so that detach occurs regardless
+// of how the debugger exits.
+// This is only supported on some system versions.
+#define DEBUG_PROCESS_DETACH_ON_EXIT 0x00000001
+// Indicates that processes created by the current
+// process should not be debugged.
+// Modifying this flag is only supported on some
+// system versions.
+#define DEBUG_PROCESS_ONLY_THIS_PROCESS 0x00000002
+
+// ConnectSession flags.
+// Default connect.
+#define DEBUG_CONNECT_SESSION_DEFAULT 0x00000000
+// Do not output the debugger version.
+#define DEBUG_CONNECT_SESSION_NO_VERSION 0x00000001
+// Do not announce the connection.
+#define DEBUG_CONNECT_SESSION_NO_ANNOUNCE 0x00000002
+
+// OutputServers flags.
+// Debugger servers from StartSever.
+#define DEBUG_SERVERS_DEBUGGER 0x00000001
+// Process servers from StartProcessServer.
+#define DEBUG_SERVERS_PROCESS 0x00000002
+#define DEBUG_SERVERS_ALL 0x00000003
+
+// EndSession flags.
+// Perform cleanup for the session.
+#define DEBUG_END_PASSIVE 0x00000000
+// Actively terminate the session and then perform cleanup.
+#define DEBUG_END_ACTIVE_TERMINATE 0x00000001
+// If possible, detach from all processes and then perform cleanup.
+#define DEBUG_END_ACTIVE_DETACH 0x00000002
+// Perform whatever cleanup is possible that doesn't require
+// acquiring any locks. This is useful for situations where
+// a thread is currently using the engine but the application
+// needs to exit and still wants to give the engine
+// the opportunity to clean up as much as possible.
+// This may leave the engine in an indeterminate state so
+// further engine calls should not be made.
+// When making a reentrant EndSession call from a remote
+// client it is the callers responsibility to ensure
+// that the server can process the request. It is best
+// to avoid making such calls.
+#define DEBUG_END_REENTRANT 0x00000003
+// Notify a server that a remote client is disconnecting.
+// This isnt required but if it isnt called then
+// no disconnect messages will be generated by the server.
+#define DEBUG_END_DISCONNECT 0x00000004
+
+// Output mask bits.
+// Normal output.
+#define DEBUG_OUTPUT_NORMAL 0x00000001
+// Error output.
+#define DEBUG_OUTPUT_ERROR 0x00000002
+// Warnings.
+#define DEBUG_OUTPUT_WARNING 0x00000004
+// Additional output.
+#define DEBUG_OUTPUT_VERBOSE 0x00000008
+// Prompt output.
+#define DEBUG_OUTPUT_PROMPT 0x00000010
+// Register dump before prompt.
+#define DEBUG_OUTPUT_PROMPT_REGISTERS 0x00000020
+// Warnings specific to extension operation.
+#define DEBUG_OUTPUT_EXTENSION_WARNING 0x00000040
+// Debuggee debug output, such as from OutputDebugString.
+#define DEBUG_OUTPUT_DEBUGGEE 0x00000080
+// Debuggee-generated prompt, such as from DbgPrompt.
+#define DEBUG_OUTPUT_DEBUGGEE_PROMPT 0x00000100
+// Symbol messages, such as for !sym noisy.
+#define DEBUG_OUTPUT_SYMBOLS 0x00000200
+
+// Internal debugger output, used mainly
+// for debugging the debugger. Output
+// may only occur in debug builds.
+// KD protocol output.
+#define DEBUG_IOUTPUT_KD_PROTOCOL 0x80000000
+// Remoting output.
+#define DEBUG_IOUTPUT_REMOTING 0x40000000
+// Breakpoint output.
+#define DEBUG_IOUTPUT_BREAKPOINT 0x20000000
+// Event output.
+#define DEBUG_IOUTPUT_EVENT 0x10000000
+
+// OutputIdentity flags.
+#define DEBUG_OUTPUT_IDENTITY_DEFAULT 0x00000000
+
+#undef INTERFACE
+#define INTERFACE IDebugClient
+DECLARE_INTERFACE_(IDebugClient, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+};
+
+// Per-dump-format control flags.
+#define DEBUG_FORMAT_DEFAULT 0x00000000
+// When creating a CAB with secondary images do searches
+// for all image files, regardless of whether they're
+// needed for the current session or not.
+#define DEBUG_FORMAT_CAB_SECONDARY_ALL_IMAGES 0x10000000
+// Write dump to a temporary file, then package it
+// into a CAB file and delete the temporary file.
+#define DEBUG_FORMAT_WRITE_CAB 0x20000000
+// When creating a CAB add secondary files such as
+// current symbols and mapped images.
+#define DEBUG_FORMAT_CAB_SECONDARY_FILES 0x40000000
+// Don't overwrite existing files.
+#define DEBUG_FORMAT_NO_OVERWRITE 0x80000000
+
+#define DEBUG_FORMAT_USER_SMALL_FULL_MEMORY 0x00000001
+#define DEBUG_FORMAT_USER_SMALL_HANDLE_DATA 0x00000002
+#define DEBUG_FORMAT_USER_SMALL_UNLOADED_MODULES 0x00000004
+#define DEBUG_FORMAT_USER_SMALL_INDIRECT_MEMORY 0x00000008
+#define DEBUG_FORMAT_USER_SMALL_DATA_SEGMENTS 0x00000010
+#define DEBUG_FORMAT_USER_SMALL_FILTER_MEMORY 0x00000020
+#define DEBUG_FORMAT_USER_SMALL_FILTER_PATHS 0x00000040
+#define DEBUG_FORMAT_USER_SMALL_PROCESS_THREAD_DATA 0x00000080
+#define DEBUG_FORMAT_USER_SMALL_PRIVATE_READ_WRITE_MEMORY 0x00000100
+#define DEBUG_FORMAT_USER_SMALL_NO_OPTIONAL_DATA 0x00000200
+#define DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO 0x00000400
+#define DEBUG_FORMAT_USER_SMALL_THREAD_INFO 0x00000800
+#define DEBUG_FORMAT_USER_SMALL_CODE_SEGMENTS 0x00001000
+#define DEBUG_FORMAT_USER_SMALL_NO_AUXILIARY_STATE 0x00002000
+#define DEBUG_FORMAT_USER_SMALL_FULL_AUXILIARY_STATE 0x00004000
+
+//
+// Dump information file types.
+//
+
+// Base dump file, returned when querying for dump files.
+#define DEBUG_DUMP_FILE_BASE 0xffffffff
+// Single file containing packed page file information.
+#define DEBUG_DUMP_FILE_PAGE_FILE_DUMP 0x00000000
+
+#undef INTERFACE
+#define INTERFACE IDebugClient2
+DECLARE_INTERFACE_(IDebugClient2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugClient3
+DECLARE_INTERFACE_(IDebugClient3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+
+ // IDebugClient3.
+
+ STDMETHOD(GetRunningProcessSystemIdByExecutableNameWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCWSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescriptionWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PWSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ STDMETHOD(CreateProcessWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttachWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+};
+
+//
+// Special indices for GetDumpFile to return
+// alternate filenames.
+//
+
+// Special index that returns the name of the last .dmp file
+// that failed to load (whether directly or from inside a
+// .cab file).
+#define DEBUG_DUMP_FILE_LOAD_FAILED_INDEX 0xffffffff
+// Index that returns last cab file opened, this is needed to
+// get the name of original CAB file since debugger returns the
+// extracted dump file in the GetDumpFile method.
+#define DEBUG_DUMP_FILE_ORIGINAL_CAB_INDEX 0xfffffffe
+
+#undef INTERFACE
+#define INTERFACE IDebugClient4
+DECLARE_INTERFACE_(IDebugClient4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+
+ // IDebugClient3.
+
+ STDMETHOD(GetRunningProcessSystemIdByExecutableNameWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCWSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescriptionWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PWSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ STDMETHOD(CreateProcessWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttachWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+
+ // IDebugClient4.
+
+ // In the following methods both a filename and a file
+ // handle can be passed in. If a file handle is given
+ // the filename may be omitted, although providing it
+ // allows the debugger to properly report the name when
+ // queried.
+ // File handles cannot be used in remote calls.
+ STDMETHOD(OpenDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle
+ ) PURE;
+ STDMETHOD(WriteDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCWSTR Comment
+ ) PURE;
+ STDMETHOD(AddDumpInformationFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Type
+ ) PURE;
+ // These methods can be used to retrieve
+ // file information for all targets that
+ // involve files.
+ STDMETHOD(GetNumberDumpFiles)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDumpFile)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(GetDumpFileWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugClient5
+DECLARE_INTERFACE_(IDebugClient5, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+
+ // IDebugClient3.
+
+ STDMETHOD(GetRunningProcessSystemIdByExecutableNameWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCWSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescriptionWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PWSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ STDMETHOD(CreateProcessWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttachWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+
+ // IDebugClient4.
+
+ // In the following methods both a filename and a file
+ // handle can be passed in. If a file handle is given
+ // the filename may be omitted, although providing it
+ // allows the debugger to properly report the name when
+ // queried.
+ // File handles cannot be used in remote calls.
+ STDMETHOD(OpenDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle
+ ) PURE;
+ STDMETHOD(WriteDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCWSTR Comment
+ ) PURE;
+ STDMETHOD(AddDumpInformationFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Type
+ ) PURE;
+ // These methods can be used to retrieve
+ // file information for all targets that
+ // involve files.
+ STDMETHOD(GetNumberDumpFiles)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDumpFile)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(GetDumpFileWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+
+ // IDebugClient5.
+
+ STDMETHOD(AttachKernelWide)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCWSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptionsWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ STDMETHOD(SetKernelConnectionOptionsWide)(
+ THIS_
+ __in PCWSTR Options
+ ) PURE;
+
+ STDMETHOD(StartProcessServerWide)(
+ THIS_
+ __in ULONG Flags,
+ __in PCWSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServerWide)(
+ THIS_
+ __in PCWSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+
+ STDMETHOD(StartServerWide)(
+ THIS_
+ __in PCWSTR Options
+ ) PURE;
+ STDMETHOD(OutputServersWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCWSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ STDMETHOD(GetOutputCallbacksWide)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS_WIDE* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacksWide)(
+ THIS_
+ __in PDEBUG_OUTPUT_CALLBACKS_WIDE Callbacks
+ ) PURE;
+ STDMETHOD(GetOutputLinePrefixWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefixWide)(
+ THIS_
+ __in_opt PCWSTR Prefix
+ ) PURE;
+
+ STDMETHOD(GetIdentityWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ STDMETHOD(OutputIdentityWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCWSTR Format
+ ) PURE;
+
+ STDMETHOD(GetEventCallbacksWide)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS_WIDE* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacksWide)(
+ THIS_
+ __in PDEBUG_EVENT_CALLBACKS_WIDE Callbacks
+ ) PURE;
+
+ STDMETHOD(CreateProcess2)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCSTR InitialDirectory,
+ __in_opt PCSTR Environment
+ ) PURE;
+ STDMETHOD(CreateProcess2Wide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCWSTR InitialDirectory,
+ __in_opt PCWSTR Environment
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttach2)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCSTR InitialDirectory,
+ __in_opt PCSTR Environment,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttach2Wide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCWSTR InitialDirectory,
+ __in_opt PCWSTR Environment,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+
+ // Helpers for saving and restoring the
+ // current output line prefix.
+ STDMETHOD(PushOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR NewPrefix,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(PushOutputLinePrefixWide)(
+ THIS_
+ __in_opt PCWSTR NewPrefix,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(PopOutputLinePrefix)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ // Queries to determine if any clients
+ // could potentially respond to the given callback.
+ STDMETHOD(GetNumberInputCallbacks)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(GetNumberOutputCallbacks)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(GetNumberEventCallbacks)(
+ THIS_
+ __in ULONG EventFlags,
+ __out PULONG Count
+ ) PURE;
+
+ // Control over locking the session against
+ // undesired quits. The quit lock string
+ // cannot be retrieved from a secure session.
+ STDMETHOD(GetQuitLockString)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(SetQuitLockString)(
+ THIS_
+ __in PCSTR String
+ ) PURE;
+ STDMETHOD(GetQuitLockStringWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(SetQuitLockStringWide)(
+ THIS_
+ __in PCWSTR String
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugControl.
+//
+//----------------------------------------------------------------------------
+
+// Execution status codes used for waiting,
+// for returning current status and for
+// event method return values.
+#define DEBUG_STATUS_NO_CHANGE 0
+#define DEBUG_STATUS_GO 1
+#define DEBUG_STATUS_GO_HANDLED 2
+#define DEBUG_STATUS_GO_NOT_HANDLED 3
+#define DEBUG_STATUS_STEP_OVER 4
+#define DEBUG_STATUS_STEP_INTO 5
+#define DEBUG_STATUS_BREAK 6
+#define DEBUG_STATUS_NO_DEBUGGEE 7
+#define DEBUG_STATUS_STEP_BRANCH 8
+#define DEBUG_STATUS_IGNORE_EVENT 9
+#define DEBUG_STATUS_RESTART_REQUESTED 10
+#define DEBUG_STATUS_REVERSE_GO 11
+#define DEBUG_STATUS_REVERSE_STEP_BRANCH 12
+#define DEBUG_STATUS_REVERSE_STEP_OVER 13
+#define DEBUG_STATUS_REVERSE_STEP_INTO 14
+
+#define DEBUG_STATUS_MASK 0xf
+
+// This bit is added in DEBUG_CES_EXECUTION_STATUS
+// notifications when the engines execution status
+// is changing due to operations performed during
+// a wait, such as making synchronous callbacks. If
+// the bit is not set the execution status is changing
+// due to a wait being satisfied.
+#define DEBUG_STATUS_INSIDE_WAIT 0x100000000
+// This bit is added in DEBUG_CES_EXECUTION_STATUS
+// notifications when the engines execution status
+// update is coming after a wait has timed-out.
+// It indicates that the execution status change
+// was not due to an actual event.
+#define DEBUG_STATUS_WAIT_TIMEOUT 0x200000000
+
+// Output control flags.
+// Output generated by methods called by this
+// client will be sent only to this clients
+// output callbacks.
+#define DEBUG_OUTCTL_THIS_CLIENT 0x00000000
+// Output will be sent to all clients.
+#define DEBUG_OUTCTL_ALL_CLIENTS 0x00000001
+// Output will be sent to all clients except
+// the client generating the output.
+#define DEBUG_OUTCTL_ALL_OTHER_CLIENTS 0x00000002
+// Output will be discarded immediately and will not
+// be logged or sent to callbacks.
+#define DEBUG_OUTCTL_IGNORE 0x00000003
+// Output will be logged but not sent to callbacks.
+#define DEBUG_OUTCTL_LOG_ONLY 0x00000004
+// All send control bits.
+#define DEBUG_OUTCTL_SEND_MASK 0x00000007
+// Do not place output from this client in
+// the global log file.
+#define DEBUG_OUTCTL_NOT_LOGGED 0x00000008
+// Send output to clients regardless of whether the
+// mask allows it or not.
+#define DEBUG_OUTCTL_OVERRIDE_MASK 0x00000010
+// Text is markup instead of plain text.
+#define DEBUG_OUTCTL_DML 0x00000020
+
+// Special values which mean leave the output settings
+// unchanged.
+#define DEBUG_OUTCTL_AMBIENT_DML 0xfffffffe
+#define DEBUG_OUTCTL_AMBIENT_TEXT 0xffffffff
+
+// Old ambient flag which maps to text.
+#define DEBUG_OUTCTL_AMBIENT DEBUG_OUTCTL_AMBIENT_TEXT
+
+// Interrupt types.
+// Force a break in if the debuggee is running.
+#define DEBUG_INTERRUPT_ACTIVE 0
+// Notify but do not force a break in.
+#define DEBUG_INTERRUPT_PASSIVE 1
+// Try and get the current engine operation to
+// complete so that the engine will be available
+// again. If no wait is active this is the same
+// as a passive interrupt. If a wait is active
+// this will try to cause the wait to fail without
+// breaking in to the debuggee. There is
+// no guarantee that issuing an exit interrupt
+// will cause the engine to become available
+// as not all operations are arbitrarily
+// interruptible.
+#define DEBUG_INTERRUPT_EXIT 2
+
+// OutputCurrentState flags. These flags
+// allow a particular type of information
+// to be displayed but do not guarantee
+// that it will be displayed. Other global
+// settings may override these flags or
+// the particular state may not be available.
+// For example, source line information may
+// not be present so source line information
+// may not be displayed.
+#define DEBUG_CURRENT_DEFAULT 0x0000000f
+#define DEBUG_CURRENT_SYMBOL 0x00000001
+#define DEBUG_CURRENT_DISASM 0x00000002
+#define DEBUG_CURRENT_REGISTERS 0x00000004
+#define DEBUG_CURRENT_SOURCE_LINE 0x00000008
+
+//
+// Disassemble flags.
+//
+
+// Compute the effective address from current register
+// information and display it.
+#define DEBUG_DISASM_EFFECTIVE_ADDRESS 0x00000001
+// If the current disassembly offset has an exact
+// symbol match output the symbol.
+#define DEBUG_DISASM_MATCHING_SYMBOLS 0x00000002
+// Output the source line number for each disassembly offset.
+#define DEBUG_DISASM_SOURCE_LINE_NUMBER 0x00000004
+// Output the source file name (no path) for each disassembly offset.
+#define DEBUG_DISASM_SOURCE_FILE_NAME 0x00000008
+
+// Code interpretation levels for stepping
+// and other operations.
+#define DEBUG_LEVEL_SOURCE 0
+#define DEBUG_LEVEL_ASSEMBLY 1
+
+// Engine control flags.
+#define DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION 0x00000001
+#define DEBUG_ENGOPT_IGNORE_EXTENSION_VERSIONS 0x00000002
+// If neither allow nor disallow is specified
+// the engine will pick one based on what kind
+// of debugging is going on.
+#define DEBUG_ENGOPT_ALLOW_NETWORK_PATHS 0x00000004
+#define DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS 0x00000008
+#define DEBUG_ENGOPT_NETWORK_PATHS (0x00000004 | 0x00000008)
+// Ignore loader-generated first-chance exceptions.
+#define DEBUG_ENGOPT_IGNORE_LOADER_EXCEPTIONS 0x00000010
+// Break in on a debuggees initial event. In user-mode
+// this will break at the initial system breakpoint
+// for every created process. In kernel-mode it
+// will attempt break in on the target at the first
+// WaitForEvent.
+#define DEBUG_ENGOPT_INITIAL_BREAK 0x00000020
+// Break in on the first module load for a debuggee.
+#define DEBUG_ENGOPT_INITIAL_MODULE_BREAK 0x00000040
+// Break in on a debuggees final event. In user-mode
+// this will break on process exit for every process.
+// In kernel-mode it currently does nothing.
+#define DEBUG_ENGOPT_FINAL_BREAK 0x00000080
+// By default Execute will repeat the last command
+// if it is given an empty string. The flags to
+// Execute can override this behavior for a single
+// command or this engine option can be used to
+// change the default globally.
+#define DEBUG_ENGOPT_NO_EXECUTE_REPEAT 0x00000100
+// Disable places in the engine that have fallback
+// code when presented with incomplete information.
+// 1. Fails minidump module loads unless matching
+// executables can be mapped.
+#define DEBUG_ENGOPT_FAIL_INCOMPLETE_INFORMATION 0x00000200
+// Allow the debugger to manipulate page protections
+// in order to insert code breakpoints on pages that
+// do not have write access. This option is not on
+// by default as it allows breakpoints to be set
+// in potentially hazardous memory areas.
+#define DEBUG_ENGOPT_ALLOW_READ_ONLY_BREAKPOINTS 0x00000400
+// When using a software (bp/bu) breakpoint in code
+// that will be executed by multiple threads it is
+// possible for breakpoint management to cause the
+// breakpoint to be missed or for spurious single-step
+// exceptions to be generated. This flag suspends
+// all but the active thread when doing breakpoint
+// management and thereby avoids multithreading
+// problems. Care must be taken when using it, though,
+// as the suspension of threads can cause deadlocks
+// if the suspended threads are holding resources that
+// the active thread needs. Additionally, there
+// are still rare situations where problems may
+// occur, but setting this flag corrects nearly
+// all multithreading issues with software breakpoints.
+// Thread-restricted stepping and execution supersedes
+// this flags effect.
+// This flag is ignored in kernel sessions as there
+// is no way to restrict processor execution.
+#define DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS 0x00000800
+// Disallows executing shell commands through the
+// engine with .shell (!!).
+#define DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS 0x00001000
+// Turns on "quiet mode", a somewhat less verbose mode
+// of operation supported in the debuggers that were
+// superseded by dbgeng.dll. This equates to the KDQUIET
+// environment variable.
+#define DEBUG_ENGOPT_KD_QUIET_MODE 0x00002000
+// Disables managed code debugging support in the engine.
+// If managed support is already in use this flag has no effect.
+#define DEBUG_ENGOPT_DISABLE_MANAGED_SUPPORT 0x00004000
+// Disables symbol loading for all modules created
+// after this flag is set.
+#define DEBUG_ENGOPT_DISABLE_MODULE_SYMBOL_LOAD 0x00008000
+// Disables execution commands.
+#define DEBUG_ENGOPT_DISABLE_EXECUTION_COMMANDS 0x00010000
+// Disallows mapping of image files from disk for any use.
+// For example, this disallows image mapping for memory
+// content when debugging minidumps.
+// Does not affect existing mappings, only future attempts.
+#define DEBUG_ENGOPT_DISALLOW_IMAGE_FILE_MAPPING 0x00020000
+// Requests that dbgeng run DML-enhanced versions of commands
+// and operations by default.
+#define DEBUG_ENGOPT_PREFER_DML 0x00040000
+#define DEBUG_ENGOPT_ALL 0x0007FFFF
+
+// General unspecified ID constant.
+#define DEBUG_ANY_ID 0xffffffff
+
+typedef struct _DEBUG_STACK_FRAME
+{
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[6];
+ BOOL Virtual;
+ ULONG FrameNumber;
+} DEBUG_STACK_FRAME, *PDEBUG_STACK_FRAME;
+
+// OutputStackTrace flags.
+// Display a small number of arguments for each call.
+// These may or may not be the actual arguments depending
+// on the architecture, particular function and
+// point during the execution of the function.
+// If the current code level is assembly arguments
+// are dumped as hex values. If the code level is
+// source the engine attempts to provide symbolic
+// argument information.
+#define DEBUG_STACK_ARGUMENTS 0x00000001
+// Displays information about the functions
+// frame such as __stdcall arguments, FPO
+// information and whatever else is available.
+#define DEBUG_STACK_FUNCTION_INFO 0x00000002
+// Displays source line information for each
+// frame of the stack trace.
+#define DEBUG_STACK_SOURCE_LINE 0x00000004
+// Show return, previous frame and other relevant address
+// values for each frame.
+#define DEBUG_STACK_FRAME_ADDRESSES 0x00000008
+// Show column names.
+#define DEBUG_STACK_COLUMN_NAMES 0x00000010
+// Show non-volatile register context for each
+// frame. This is only meaningful for some platforms.
+#define DEBUG_STACK_NONVOLATILE_REGISTERS 0x00000020
+// Show frame numbers
+#define DEBUG_STACK_FRAME_NUMBERS 0x00000040
+// Show typed source parameters.
+#define DEBUG_STACK_PARAMETERS 0x00000080
+// Show just return address in stack frame addresses.
+#define DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY 0x00000100
+// Show frame-to-frame memory usage.
+#define DEBUG_STACK_FRAME_MEMORY_USAGE 0x00000200
+// Show typed source parameters one to a line.
+#define DEBUG_STACK_PARAMETERS_NEWLINE 0x00000400
+// Produce stack output enhanced with DML content.
+#define DEBUG_STACK_DML 0x00000800
+
+// Classes of debuggee. Each class
+// has different qualifiers for specific
+// kinds of debuggees.
+#define DEBUG_CLASS_UNINITIALIZED 0
+#define DEBUG_CLASS_KERNEL 1
+#define DEBUG_CLASS_USER_WINDOWS 2
+#define DEBUG_CLASS_IMAGE_FILE 3
+
+// Generic dump types. These can be used
+// with either user or kernel sessions.
+// Session-type-specific aliases are also
+// provided.
+#define DEBUG_DUMP_SMALL 1024
+#define DEBUG_DUMP_DEFAULT 1025
+#define DEBUG_DUMP_FULL 1026
+#define DEBUG_DUMP_IMAGE_FILE 1027
+#define DEBUG_DUMP_TRACE_LOG 1028
+#define DEBUG_DUMP_WINDOWS_CE 1029
+
+// Specific types of kernel debuggees.
+#define DEBUG_KERNEL_CONNECTION 0
+#define DEBUG_KERNEL_LOCAL 1
+#define DEBUG_KERNEL_EXDI_DRIVER 2
+#define DEBUG_KERNEL_IDNA 3
+
+#define DEBUG_KERNEL_SMALL_DUMP DEBUG_DUMP_SMALL
+#define DEBUG_KERNEL_DUMP DEBUG_DUMP_DEFAULT
+#define DEBUG_KERNEL_FULL_DUMP DEBUG_DUMP_FULL
+
+#define DEBUG_KERNEL_TRACE_LOG DEBUG_DUMP_TRACE_LOG
+
+// Specific types of Windows user debuggees.
+#define DEBUG_USER_WINDOWS_PROCESS 0
+#define DEBUG_USER_WINDOWS_PROCESS_SERVER 1
+#define DEBUG_USER_WINDOWS_IDNA 2
+#define DEBUG_USER_WINDOWS_SMALL_DUMP DEBUG_DUMP_SMALL
+#define DEBUG_USER_WINDOWS_DUMP DEBUG_DUMP_DEFAULT
+#define DEBUG_USER_WINDOWS_DUMP_WINDOWS_CE DEBUG_DUMP_WINDOWS_CE
+
+// Extension flags.
+#define DEBUG_EXTENSION_AT_ENGINE 0x00000000
+
+// Execute and ExecuteCommandFile flags.
+// These flags only apply to the command
+// text itself; output from the executed
+// command is controlled by the output
+// control parameter.
+// Default execution. Command is logged
+// but not output.
+#define DEBUG_EXECUTE_DEFAULT 0x00000000
+// Echo commands during execution. In
+// ExecuteCommandFile also echoes the prompt
+// for each line of the file.
+#define DEBUG_EXECUTE_ECHO 0x00000001
+// Do not log or output commands during execution.
+// Overridden by DEBUG_EXECUTE_ECHO.
+#define DEBUG_EXECUTE_NOT_LOGGED 0x00000002
+// If this flag is not set an empty string
+// to Execute will repeat the last Execute
+// string.
+#define DEBUG_EXECUTE_NO_REPEAT 0x00000004
+
+// Specific event filter types. Some event
+// filters have optional arguments to further
+// qualify their operation.
+#define DEBUG_FILTER_CREATE_THREAD 0x00000000
+#define DEBUG_FILTER_EXIT_THREAD 0x00000001
+#define DEBUG_FILTER_CREATE_PROCESS 0x00000002
+#define DEBUG_FILTER_EXIT_PROCESS 0x00000003
+// Argument is the name of a module to break on.
+#define DEBUG_FILTER_LOAD_MODULE 0x00000004
+// Argument is the base address of a specific module to break on.
+#define DEBUG_FILTER_UNLOAD_MODULE 0x00000005
+#define DEBUG_FILTER_SYSTEM_ERROR 0x00000006
+// Initial breakpoint and initial module load are one-shot
+// events that are triggered at the appropriate points in
+// the beginning of a session. Their commands are executed
+// and then further processing is controlled by the normal
+// exception and load module filters.
+#define DEBUG_FILTER_INITIAL_BREAKPOINT 0x00000007
+#define DEBUG_FILTER_INITIAL_MODULE_LOAD 0x00000008
+// The debug output filter allows the debugger to stop
+// when output is produced so that the code causing
+// output can be tracked down or synchronized with.
+// This filter is not supported for live dual-machine
+// kernel debugging.
+#define DEBUG_FILTER_DEBUGGEE_OUTPUT 0x00000009
+
+// Event filter execution options.
+// Break in always.
+#define DEBUG_FILTER_BREAK 0x00000000
+// Break in on second-chance exceptions. For events
+// that are not exceptions this is the same as BREAK.
+#define DEBUG_FILTER_SECOND_CHANCE_BREAK 0x00000001
+// Output a message about the event but continue.
+#define DEBUG_FILTER_OUTPUT 0x00000002
+// Continue the event.
+#define DEBUG_FILTER_IGNORE 0x00000003
+// Used to remove general exception filters.
+#define DEBUG_FILTER_REMOVE 0x00000004
+
+// Event filter continuation options. These options are
+// only used when DEBUG_STATUS_GO is used to continue
+// execution. If a specific go status such as
+// DEBUG_STATUS_GO_NOT_HANDLED is used it controls
+// the continuation.
+#define DEBUG_FILTER_GO_HANDLED 0x00000000
+#define DEBUG_FILTER_GO_NOT_HANDLED 0x00000001
+
+// Specific event filter settings.
+typedef struct _DEBUG_SPECIFIC_FILTER_PARAMETERS
+{
+ ULONG ExecutionOption;
+ ULONG ContinueOption;
+ ULONG TextSize;
+ ULONG CommandSize;
+ // If ArgumentSize is zero this filter does
+ // not have an argument. An empty argument for
+ // a filter which does have an argument will take
+ // one byte for the terminator.
+ ULONG ArgumentSize;
+} DEBUG_SPECIFIC_FILTER_PARAMETERS, *PDEBUG_SPECIFIC_FILTER_PARAMETERS;
+
+// Exception event filter settings.
+typedef struct _DEBUG_EXCEPTION_FILTER_PARAMETERS
+{
+ ULONG ExecutionOption;
+ ULONG ContinueOption;
+ ULONG TextSize;
+ ULONG CommandSize;
+ ULONG SecondCommandSize;
+ ULONG ExceptionCode;
+} DEBUG_EXCEPTION_FILTER_PARAMETERS, *PDEBUG_EXCEPTION_FILTER_PARAMETERS;
+
+// Wait flags.
+#define DEBUG_WAIT_DEFAULT 0x00000000
+
+// Last event information structures.
+typedef struct _DEBUG_LAST_EVENT_INFO_BREAKPOINT
+{
+ ULONG Id;
+} DEBUG_LAST_EVENT_INFO_BREAKPOINT, *PDEBUG_LAST_EVENT_INFO_BREAKPOINT;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXCEPTION
+{
+ EXCEPTION_RECORD64 ExceptionRecord;
+ ULONG FirstChance;
+} DEBUG_LAST_EVENT_INFO_EXCEPTION, *PDEBUG_LAST_EVENT_INFO_EXCEPTION;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXIT_THREAD
+{
+ ULONG ExitCode;
+} DEBUG_LAST_EVENT_INFO_EXIT_THREAD, *PDEBUG_LAST_EVENT_INFO_EXIT_THREAD;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXIT_PROCESS
+{
+ ULONG ExitCode;
+} DEBUG_LAST_EVENT_INFO_EXIT_PROCESS, *PDEBUG_LAST_EVENT_INFO_EXIT_PROCESS;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_LOAD_MODULE
+{
+ ULONG64 Base;
+} DEBUG_LAST_EVENT_INFO_LOAD_MODULE, *PDEBUG_LAST_EVENT_INFO_LOAD_MODULE;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_UNLOAD_MODULE
+{
+ ULONG64 Base;
+} DEBUG_LAST_EVENT_INFO_UNLOAD_MODULE, *PDEBUG_LAST_EVENT_INFO_UNLOAD_MODULE;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_SYSTEM_ERROR
+{
+ ULONG Error;
+ ULONG Level;
+} DEBUG_LAST_EVENT_INFO_SYSTEM_ERROR, *PDEBUG_LAST_EVENT_INFO_SYSTEM_ERROR;
+
+// DEBUG_VALUE types.
+#define DEBUG_VALUE_INVALID 0
+#define DEBUG_VALUE_INT8 1
+#define DEBUG_VALUE_INT16 2
+#define DEBUG_VALUE_INT32 3
+#define DEBUG_VALUE_INT64 4
+#define DEBUG_VALUE_FLOAT32 5
+#define DEBUG_VALUE_FLOAT64 6
+#define DEBUG_VALUE_FLOAT80 7
+#define DEBUG_VALUE_FLOAT82 8
+#define DEBUG_VALUE_FLOAT128 9
+#define DEBUG_VALUE_VECTOR64 10
+#define DEBUG_VALUE_VECTOR128 11
+// Count of type indices.
+#define DEBUG_VALUE_TYPES 12
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+#pragma warning(disable:4201) /* Nameless struct/union */
+#endif
+#endif
+
+// We want the DEBUG_VALUE structure to have 8-byte alignment
+// and be 32 bytes total. This is tricky because the compiler
+// wants to pad the union of values out to a even 8-byte multiple,
+// pushing the type out too far. We can't use 4-packing because
+// then the 8-byte alignment requirement is lost, so instead
+// we shrink the union to 24 bytes and have a reserved field
+// before the type field. The same amount of space is available
+// and everybody's happy, but the structure is somewhat unusual.
+
+typedef struct _DEBUG_VALUE
+{
+ union
+ {
+ UCHAR I8;
+ USHORT I16;
+ ULONG I32;
+ struct
+ {
+ // Extra NAT indicator for IA64
+ // integer registers. NAT will
+ // always be false for other CPUs.
+ ULONG64 I64;
+ BOOL Nat;
+ };
+ float F32;
+ double F64;
+ UCHAR F80Bytes[10];
+ UCHAR F82Bytes[11];
+ UCHAR F128Bytes[16];
+ // Vector interpretations. The actual number
+ // of valid elements depends on the vector length.
+ UCHAR VI8[16];
+ USHORT VI16[8];
+ ULONG VI32[4];
+ ULONG64 VI64[2];
+ float VF32[4];
+ double VF64[2];
+ struct
+ {
+ ULONG LowPart;
+ ULONG HighPart;
+ } I64Parts32;
+ struct
+ {
+ ULONG64 LowPart;
+ LONG64 HighPart;
+ } F128Parts64;
+ // Allows raw byte access to content. Array
+ // can be indexed for as much data as Type
+ // describes. This array also serves to pad
+ // the structure out to 32 bytes and reserves
+ // space for future members.
+ UCHAR RawBytes[24];
+ };
+ ULONG TailOfRawBytes;
+ ULONG Type;
+} DEBUG_VALUE, *PDEBUG_VALUE;
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#else
+#pragma warning(default:4201) /* Nameless struct/union */
+#endif
+#endif
+#endif
+
+#undef INTERFACE
+#define INTERFACE IDebugControl
+DECLARE_INTERFACE_(IDebugControl, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // The following methods allow direct control
+ // over the distribution of the given output
+ // for situations where something other than
+ // the default is desired. These methods require
+ // extra work in the engine so they should
+ // only be used when necessary.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // Executes the given command string.
+ // If the string has multiple commands
+ // Execute will not return until all
+ // of them have been executed. If this
+ // requires waiting for the debuggee to
+ // execute an internal wait will be done
+ // so Execute can take an arbitrary amount
+ // of time.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // _EFN_ is automatically prepended to
+ // the name string given.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // Retrieves information about the last event that occurred.
+ // EventType is one of the event callback mask bits.
+ // ExtraInformation contains additional event-specific
+ // information. Not all events have additional information.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+};
+
+// OutputTextReplacements flags.
+#define DEBUG_OUT_TEXT_REPL_DEFAULT 0x00000000
+
+#undef INTERFACE
+#define INTERFACE IDebugControl2
+DECLARE_INTERFACE_(IDebugControl2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // The following methods allow direct control
+ // over the distribution of the given output
+ // for situations where something other than
+ // the default is desired. These methods require
+ // extra work in the engine so they should
+ // only be used when necessary.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // Executes the given command string.
+ // If the string has multiple commands
+ // Execute will not return until all
+ // of them have been executed. If this
+ // requires waiting for the debuggee to
+ // execute an internal wait will be done
+ // so Execute can take an arbitrary amount
+ // of time.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // Retrieves information about the last event that occurred.
+ // EventType is one of the event callback mask bits.
+ // ExtraInformation contains additional event-specific
+ // information. Not all events have additional information.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ // IDebugControl2.
+
+ STDMETHOD(GetCurrentTimeDate)(
+ THIS_
+ __out PULONG TimeDate
+ ) PURE;
+ // Retrieves the number of seconds since the
+ // machine started running.
+ STDMETHOD(GetCurrentSystemUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // If the current session is a dump session,
+ // retrieves any extended format information.
+ STDMETHOD(GetDumpFormatFlags)(
+ THIS_
+ __out PULONG FormatFlags
+ ) PURE;
+
+ // The debugger has been enhanced to allow
+ // arbitrary text replacements in addition
+ // to the simple $u0-$u9 text macros.
+ // Text replacement takes a given source
+ // text in commands and converts it to the
+ // given destination text. Replacements
+ // are named by their source text so that
+ // only one replacement for a source text
+ // string can exist.
+ STDMETHOD(GetNumberTextReplacements)(
+ THIS_
+ __out PULONG NumRepl
+ ) PURE;
+ // If SrcText is non-NULL the replacement
+ // is looked up by source text, otherwise
+ // Index is used to get the Nth replacement.
+ STDMETHOD(GetTextReplacement)(
+ THIS_
+ __in_opt PCSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ // Setting the destination text to
+ // NULL removes the alias.
+ STDMETHOD(SetTextReplacement)(
+ THIS_
+ __in PCSTR SrcText,
+ __in_opt PCSTR DstText
+ ) PURE;
+ STDMETHOD(RemoveTextReplacements)(
+ THIS
+ ) PURE;
+ // Outputs the complete list of current
+ // replacements.
+ STDMETHOD(OutputTextReplacements)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+};
+
+//
+// Assembly/disassembly options.
+//
+// The specific effects of these flags varies depending
+// on the particular instruction set.
+//
+
+#define DEBUG_ASMOPT_DEFAULT 0x00000000
+// Display additional information in disassembly.
+#define DEBUG_ASMOPT_VERBOSE 0x00000001
+// Do not display raw code bytes in disassembly.
+#define DEBUG_ASMOPT_NO_CODE_BYTES 0x00000002
+// Do not take the output width into account when
+// formatting disassembly.
+#define DEBUG_ASMOPT_IGNORE_OUTPUT_WIDTH 0x00000004
+// Display source file line number before each line if available.
+#define DEBUG_ASMOPT_SOURCE_LINE_NUMBER 0x00000008
+
+//
+// Expression syntax options.
+//
+
+// MASM-style expression evaluation.
+#define DEBUG_EXPR_MASM 0x00000000
+// C++-style expression evaluation.
+#define DEBUG_EXPR_CPLUSPLUS 0x00000001
+
+//
+// Event index description information.
+//
+
+#define DEBUG_EINDEX_NAME 0x00000000
+
+//
+// SetNextEventIndex relation options.
+//
+
+// Value increases forward from the first index.
+#define DEBUG_EINDEX_FROM_START 0x00000000
+// Value increases backwards from the last index.
+#define DEBUG_EINDEX_FROM_END 0x00000001
+// Value is a signed delta from the current index.
+#define DEBUG_EINDEX_FROM_CURRENT 0x00000002
+
+#undef INTERFACE
+#define INTERFACE IDebugControl3
+DECLARE_INTERFACE_(IDebugControl3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // The following methods allow direct control
+ // over the distribution of the given output
+ // for situations where something other than
+ // the default is desired. These methods require
+ // extra work in the engine so they should
+ // only be used when necessary.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // Executes the given command string.
+ // If the string has multiple commands
+ // Execute will not return until all
+ // of them have been executed. If this
+ // requires waiting for the debuggee to
+ // execute an internal wait will be done
+ // so Execute can take an arbitrary amount
+ // of time.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // Retrieves information about the last event that occurred.
+ // EventType is one of the event callback mask bits.
+ // ExtraInformation contains additional event-specific
+ // information. Not all events have additional information.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ // IDebugControl2.
+
+ STDMETHOD(GetCurrentTimeDate)(
+ THIS_
+ __out PULONG TimeDate
+ ) PURE;
+ // Retrieves the number of seconds since the
+ // machine started running.
+ STDMETHOD(GetCurrentSystemUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // If the current session is a dump session,
+ // retrieves any extended format information.
+ STDMETHOD(GetDumpFormatFlags)(
+ THIS_
+ __out PULONG FormatFlags
+ ) PURE;
+
+ // The debugger has been enhanced to allow
+ // arbitrary text replacements in addition
+ // to the simple $u0-$u9 text macros.
+ // Text replacement takes a given source
+ // text in commands and converts it to the
+ // given destination text. Replacements
+ // are named by their source text so that
+ // only one replacement for a source text
+ // string can exist.
+ STDMETHOD(GetNumberTextReplacements)(
+ THIS_
+ __out PULONG NumRepl
+ ) PURE;
+ // If SrcText is non-NULL the replacement
+ // is looked up by source text, otherwise
+ // Index is used to get the Nth replacement.
+ STDMETHOD(GetTextReplacement)(
+ THIS_
+ __in_opt PCSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ // Setting the destination text to
+ // NULL removes the alias.
+ STDMETHOD(SetTextReplacement)(
+ THIS_
+ __in PCSTR SrcText,
+ __in_opt PCSTR DstText
+ ) PURE;
+ STDMETHOD(RemoveTextReplacements)(
+ THIS
+ ) PURE;
+ // Outputs the complete list of current
+ // replacements.
+ STDMETHOD(OutputTextReplacements)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // IDebugControl3.
+
+ // Control options for assembly and disassembly.
+ STDMETHOD(GetAssemblyOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Control the expression syntax.
+ STDMETHOD(GetExpressionSyntax)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(SetExpressionSyntax)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Look up a syntax by its abbreviated
+ // name and set it.
+ STDMETHOD(SetExpressionSyntaxByName)(
+ THIS_
+ __in PCSTR AbbrevName
+ ) PURE;
+ STDMETHOD(GetNumberExpressionSyntaxes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetExpressionSyntaxNames)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ //
+ // Some debug sessions have only a single
+ // possible event, such as a snapshot dump
+ // file; some have dynamic events, such as
+ // a live debug session; and others may have
+ // multiple events, such as a dump file that
+ // contains snapshots from different points
+ // in time. The following methods allow
+ // discovery and selection of the available
+ // events for a session.
+ // Sessions with one or more static events
+ // will be able to report all of the events
+ // when queried. Sessions with dynamic events
+ // will only report a single event representing
+ // the current event.
+ // Switching events constitutes execution and
+ // changing the current event will alter the
+ // execution status to a running state, after
+ // which WaitForEvent must be used to process
+ // the selected event.
+ //
+
+ // GetNumberEvents returns S_OK if this is the
+ // complete set of events possible, such as for
+ // a static session; or S_FALSE if other events
+ // may be possible, such as for a dynamic session.
+ STDMETHOD(GetNumberEvents)(
+ THIS_
+ __out PULONG Events
+ ) PURE;
+ // Sessions may have descriptive information for
+ // the various events available. The amount of
+ // information varies according to the specific
+ // session and data.
+ STDMETHOD(GetEventIndexDescription)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG Which,
+ __in_opt PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DescSize
+ ) PURE;
+ STDMETHOD(GetCurrentEventIndex)(
+ THIS_
+ __out PULONG Index
+ ) PURE;
+ // SetNextEventIndex works like seek in that
+ // it can set an absolute or relative index.
+ // SetNextEventIndex works similarly to SetExecutionStatus
+ // by putting the session into a running state, after
+ // which the caller must call WaitForEvent. The
+ // current event index only changes when WaitForEvent
+ // is called.
+ STDMETHOD(SetNextEventIndex)(
+ THIS_
+ __in ULONG Relation,
+ __in ULONG Value,
+ __out PULONG NextIndex
+ ) PURE;
+};
+
+//
+// Log file flags.
+//
+
+#define DEBUG_LOG_DEFAULT 0x00000000
+#define DEBUG_LOG_APPEND 0x00000001
+#define DEBUG_LOG_UNICODE 0x00000002
+#define DEBUG_LOG_DML 0x00000004
+
+//
+// System version strings.
+//
+
+#define DEBUG_SYSVERSTR_SERVICE_PACK 0x00000000
+#define DEBUG_SYSVERSTR_BUILD 0x00000001
+
+//
+// GetManagedStatus flags and strings.
+//
+
+#define DEBUG_MANAGED_DISABLED 0x00000000
+#define DEBUG_MANAGED_ALLOWED 0x00000001
+#define DEBUG_MANAGED_DLL_LOADED 0x00000002
+
+#define DEBUG_MANSTR_NONE 0x00000000
+#define DEBUG_MANSTR_LOADED_SUPPORT_DLL 0x00000001
+#define DEBUG_MANSTR_LOAD_STATUS 0x00000002
+
+//
+// ResetManagedStatus flags.
+//
+
+// Reset state to default engine startup state with
+// no support loaded.
+#define DEBUG_MANRESET_DEFAULT 0x00000000
+// Force managed support DLL load attempt.
+#define DEBUG_MANRESET_LOAD_DLL 0x00000001
+
+#undef INTERFACE
+#define INTERFACE IDebugControl4
+DECLARE_INTERFACE_(IDebugControl4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // The following methods allow direct control
+ // over the distribution of the given output
+ // for situations where something other than
+ // the default is desired. These methods require
+ // extra work in the engine so they should
+ // only be used when necessary.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // Executes the given command string.
+ // If the string has multiple commands
+ // Execute will not return until all
+ // of them have been executed. If this
+ // requires waiting for the debuggee to
+ // execute an internal wait will be done
+ // so Execute can take an arbitrary amount
+ // of time.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // Retrieves information about the last event that occurred.
+ // EventType is one of the event callback mask bits.
+ // ExtraInformation contains additional event-specific
+ // information. Not all events have additional information.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ // IDebugControl2.
+
+ STDMETHOD(GetCurrentTimeDate)(
+ THIS_
+ __out PULONG TimeDate
+ ) PURE;
+ // Retrieves the number of seconds since the
+ // machine started running.
+ STDMETHOD(GetCurrentSystemUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // If the current session is a dump session,
+ // retrieves any extended format information.
+ STDMETHOD(GetDumpFormatFlags)(
+ THIS_
+ __out PULONG FormatFlags
+ ) PURE;
+
+ // The debugger has been enhanced to allow
+ // arbitrary text replacements in addition
+ // to the simple $u0-$u9 text macros.
+ // Text replacement takes a given source
+ // text in commands and converts it to the
+ // given destination text. Replacements
+ // are named by their source text so that
+ // only one replacement for a source text
+ // string can exist.
+ STDMETHOD(GetNumberTextReplacements)(
+ THIS_
+ __out PULONG NumRepl
+ ) PURE;
+ // If SrcText is non-NULL the replacement
+ // is looked up by source text, otherwise
+ // Index is used to get the Nth replacement.
+ STDMETHOD(GetTextReplacement)(
+ THIS_
+ __in_opt PCSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ // Setting the destination text to
+ // NULL removes the alias.
+ STDMETHOD(SetTextReplacement)(
+ THIS_
+ __in PCSTR SrcText,
+ __in_opt PCSTR DstText
+ ) PURE;
+ STDMETHOD(RemoveTextReplacements)(
+ THIS
+ ) PURE;
+ // Outputs the complete list of current
+ // replacements.
+ STDMETHOD(OutputTextReplacements)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // IDebugControl3.
+
+ // Control options for assembly and disassembly.
+ STDMETHOD(GetAssemblyOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Control the expression syntax.
+ STDMETHOD(GetExpressionSyntax)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(SetExpressionSyntax)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Look up a syntax by its abbreviated
+ // name and set it.
+ STDMETHOD(SetExpressionSyntaxByName)(
+ THIS_
+ __in PCSTR AbbrevName
+ ) PURE;
+ STDMETHOD(GetNumberExpressionSyntaxes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetExpressionSyntaxNames)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ //
+ // Some debug sessions have only a single
+ // possible event, such as a snapshot dump
+ // file; some have dynamic events, such as
+ // a live debug session; and others may have
+ // multiple events, such as a dump file that
+ // contains snapshots from different points
+ // in time. The following methods allow
+ // discovery and selection of the available
+ // events for a session.
+ // Sessions with one or more static events
+ // will be able to report all of the events
+ // when queried. Sessions with dynamic events
+ // will only report a single event representing
+ // the current event.
+ // Switching events constitutes execution and
+ // changing the current event will alter the
+ // execution status to a running state, after
+ // which WaitForEvent must be used to process
+ // the selected event.
+ //
+
+ // GetNumberEvents returns S_OK if this is the
+ // complete set of events possible, such as for
+ // a static session; or S_FALSE if other events
+ // may be possible, such as for a dynamic session.
+ STDMETHOD(GetNumberEvents)(
+ THIS_
+ __out PULONG Events
+ ) PURE;
+ // Sessions may have descriptive information for
+ // the various events available. The amount of
+ // information varies according to the specific
+ // session and data.
+ STDMETHOD(GetEventIndexDescription)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG Which,
+ __in_opt PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DescSize
+ ) PURE;
+ STDMETHOD(GetCurrentEventIndex)(
+ THIS_
+ __out PULONG Index
+ ) PURE;
+ // SetNextEventIndex works like seek in that
+ // it can set an absolute or relative index.
+ // SetNextEventIndex works similarly to SetExecutionStatus
+ // by putting the session into a running state, after
+ // which the caller must call WaitForEvent. The
+ // current event index only changes when WaitForEvent
+ // is called.
+ STDMETHOD(SetNextEventIndex)(
+ THIS_
+ __in ULONG Relation,
+ __in ULONG Value,
+ __out PULONG NextIndex
+ ) PURE;
+
+ // IDebugControl4.
+
+ STDMETHOD(GetLogFileWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ STDMETHOD(OpenLogFileWide)(
+ THIS_
+ __in PCWSTR File,
+ __in BOOL Append
+ ) PURE;
+
+ STDMETHOD(InputWide)(
+ THIS_
+ __out_ecount(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ STDMETHOD(ReturnInputWide)(
+ THIS_
+ __in PCWSTR Buffer
+ ) PURE;
+
+ STDMETHODV(OutputWide)(
+ THIS_
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaListWide)(
+ THIS_
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ __in va_list Args
+ ) PURE;
+ STDMETHODV(ControlledOutputWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaListWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ STDMETHODV(OutputPromptWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCWSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaListWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCWSTR Format,
+ __in va_list Args
+ ) PURE;
+ STDMETHOD(GetPromptTextWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+
+ STDMETHOD(AssembleWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCWSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(DisassembleWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+
+ STDMETHOD(GetProcessorTypeNamesWide)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PWSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PWSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ STDMETHOD(GetTextMacroWide)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacroWide)(
+ THIS_
+ __in ULONG Slot,
+ __in PCWSTR Macro
+ ) PURE;
+
+ STDMETHOD(EvaluateWide)(
+ THIS_
+ __in PCWSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+
+ STDMETHOD(ExecuteWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCWSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ExecuteCommandFileWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCWSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ STDMETHOD(GetBreakpointByIndex2)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT2* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById2)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT2* Bp
+ ) PURE;
+ STDMETHOD(AddBreakpoint2)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT2* Bp
+ ) PURE;
+ STDMETHOD(RemoveBreakpoint2)(
+ THIS_
+ __in PDEBUG_BREAKPOINT2 Bp
+ ) PURE;
+
+ STDMETHOD(AddExtensionWide)(
+ THIS_
+ __in PCWSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPathWide)(
+ THIS_
+ __in PCWSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(CallExtensionWide)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCWSTR Function,
+ __in_opt PCWSTR Arguments
+ ) PURE;
+ STDMETHOD(GetExtensionFunctionWide)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCWSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+
+ STDMETHOD(GetEventFilterTextWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ STDMETHOD(GetEventFilterCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterArgumentWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgumentWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Argument
+ ) PURE;
+ STDMETHOD(GetExceptionFilterSecondCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Command
+ ) PURE;
+
+ STDMETHOD(GetLastEventInformationWide)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ STDMETHOD(GetTextReplacementWide)(
+ THIS_
+ __in_opt PCWSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PWSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PWSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ STDMETHOD(SetTextReplacementWide)(
+ THIS_
+ __in PCWSTR SrcText,
+ __in_opt PCWSTR DstText
+ ) PURE;
+
+ STDMETHOD(SetExpressionSyntaxByNameWide)(
+ THIS_
+ __in PCWSTR AbbrevName
+ ) PURE;
+ STDMETHOD(GetExpressionSyntaxNamesWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(FullNameBufferSize) PWSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PWSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ STDMETHOD(GetEventIndexDescriptionWide)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG Which,
+ __in_opt PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DescSize
+ ) PURE;
+
+ STDMETHOD(GetLogFile2)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(OpenLogFile2)(
+ THIS_
+ __in PCSTR File,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(GetLogFile2Wide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(OpenLogFile2Wide)(
+ THIS_
+ __in PCWSTR File,
+ __in ULONG Flags
+ ) PURE;
+
+ // GetSystemVersion always returns the kd
+ // major/minor version numbers, which are
+ // different than the Win32 version numbers.
+ // GetSystemVersionValues can be used
+ // to determine the Win32 version values.
+ STDMETHOD(GetSystemVersionValues)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Win32Major,
+ __out PULONG Win32Minor,
+ __out_opt PULONG KdMajor,
+ __out_opt PULONG KdMinor
+ ) PURE;
+ // Strings are selected with DEBUG_SYSVERSTR_*.
+ STDMETHOD(GetSystemVersionString)(
+ THIS_
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(GetSystemVersionStringWide)(
+ THIS_
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+
+ // Stack tracing with a full initial context
+ // and full context return for each frame.
+ // The FrameContextsSize parameter is the total
+ // byte size of FrameContexts. FrameContextsEntrySize
+ // gives the byte size of each entry in
+ // FrameContexts.
+ STDMETHOD(GetContextStackTrace)(
+ THIS_
+ __in_bcount_opt(StartContextSize) PVOID StartContext,
+ __in ULONG StartContextSize,
+ __out_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_bcount_opt(FrameContextsSize) PVOID FrameContexts,
+ __in ULONG FrameContextsSize,
+ __in ULONG FrameContextsEntrySize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ STDMETHOD(OutputContextStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in_bcount(FrameContextsSize) PVOID FrameContexts,
+ __in ULONG FrameContextsSize,
+ __in ULONG FrameContextsEntrySize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Some targets, such as user-mode minidump files,
+ // have separate "event of interest" information
+ // stored within them. This method allows
+ // access to that information.
+ STDMETHOD(GetStoredEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ContextSize) PVOID Context,
+ __in ULONG ContextSize,
+ __out_opt PULONG ContextUsed,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed
+ ) PURE;
+
+ // Managed debugging support relies on debugging
+ // functionality provided by the Common Language Runtime.
+ // This method provides feedback on the engine's
+ // use of the runtime debugging APIs.
+ STDMETHOD(GetManagedStatus)(
+ THIS_
+ __out_opt PULONG Flags,
+ __in ULONG WhichString,
+ __out_ecount_opt(StringSize) PSTR String,
+ __in ULONG StringSize,
+ __out_opt PULONG StringNeeded
+ ) PURE;
+ STDMETHOD(GetManagedStatusWide)(
+ THIS_
+ __out_opt PULONG Flags,
+ __in ULONG WhichString,
+ __out_ecount_opt(StringSize) PWSTR String,
+ __in ULONG StringSize,
+ __out_opt PULONG StringNeeded
+ ) PURE;
+ // Clears and reinitializes the engine's
+ // managed code debugging support.
+ STDMETHOD(ResetManagedStatus)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugDataSpaces.
+//
+//----------------------------------------------------------------------------
+
+// Data space indices for callbacks and other methods.
+#define DEBUG_DATA_SPACE_VIRTUAL 0
+#define DEBUG_DATA_SPACE_PHYSICAL 1
+#define DEBUG_DATA_SPACE_CONTROL 2
+#define DEBUG_DATA_SPACE_IO 3
+#define DEBUG_DATA_SPACE_MSR 4
+#define DEBUG_DATA_SPACE_BUS_DATA 5
+#define DEBUG_DATA_SPACE_DEBUGGER_DATA 6
+// Count of data spaces.
+#define DEBUG_DATA_SPACE_COUNT 7
+
+// Indices for ReadDebuggerData interface
+#define DEBUG_DATA_KernBase 24
+#define DEBUG_DATA_BreakpointWithStatusAddr 32
+#define DEBUG_DATA_SavedContextAddr 40
+#define DEBUG_DATA_KiCallUserModeAddr 56
+#define DEBUG_DATA_KeUserCallbackDispatcherAddr 64
+#define DEBUG_DATA_PsLoadedModuleListAddr 72
+#define DEBUG_DATA_PsActiveProcessHeadAddr 80
+#define DEBUG_DATA_PspCidTableAddr 88
+#define DEBUG_DATA_ExpSystemResourcesListAddr 96
+#define DEBUG_DATA_ExpPagedPoolDescriptorAddr 104
+#define DEBUG_DATA_ExpNumberOfPagedPoolsAddr 112
+#define DEBUG_DATA_KeTimeIncrementAddr 120
+#define DEBUG_DATA_KeBugCheckCallbackListHeadAddr 128
+#define DEBUG_DATA_KiBugcheckDataAddr 136
+#define DEBUG_DATA_IopErrorLogListHeadAddr 144
+#define DEBUG_DATA_ObpRootDirectoryObjectAddr 152
+#define DEBUG_DATA_ObpTypeObjectTypeAddr 160
+#define DEBUG_DATA_MmSystemCacheStartAddr 168
+#define DEBUG_DATA_MmSystemCacheEndAddr 176
+#define DEBUG_DATA_MmSystemCacheWsAddr 184
+#define DEBUG_DATA_MmPfnDatabaseAddr 192
+#define DEBUG_DATA_MmSystemPtesStartAddr 200
+#define DEBUG_DATA_MmSystemPtesEndAddr 208
+#define DEBUG_DATA_MmSubsectionBaseAddr 216
+#define DEBUG_DATA_MmNumberOfPagingFilesAddr 224
+#define DEBUG_DATA_MmLowestPhysicalPageAddr 232
+#define DEBUG_DATA_MmHighestPhysicalPageAddr 240
+#define DEBUG_DATA_MmNumberOfPhysicalPagesAddr 248
+#define DEBUG_DATA_MmMaximumNonPagedPoolInBytesAddr 256
+#define DEBUG_DATA_MmNonPagedSystemStartAddr 264
+#define DEBUG_DATA_MmNonPagedPoolStartAddr 272
+#define DEBUG_DATA_MmNonPagedPoolEndAddr 280
+#define DEBUG_DATA_MmPagedPoolStartAddr 288
+#define DEBUG_DATA_MmPagedPoolEndAddr 296
+#define DEBUG_DATA_MmPagedPoolInformationAddr 304
+#define DEBUG_DATA_MmPageSize 312
+#define DEBUG_DATA_MmSizeOfPagedPoolInBytesAddr 320
+#define DEBUG_DATA_MmTotalCommitLimitAddr 328
+#define DEBUG_DATA_MmTotalCommittedPagesAddr 336
+#define DEBUG_DATA_MmSharedCommitAddr 344
+#define DEBUG_DATA_MmDriverCommitAddr 352
+#define DEBUG_DATA_MmProcessCommitAddr 360
+#define DEBUG_DATA_MmPagedPoolCommitAddr 368
+#define DEBUG_DATA_MmExtendedCommitAddr 376
+#define DEBUG_DATA_MmZeroedPageListHeadAddr 384
+#define DEBUG_DATA_MmFreePageListHeadAddr 392
+#define DEBUG_DATA_MmStandbyPageListHeadAddr 400
+#define DEBUG_DATA_MmModifiedPageListHeadAddr 408
+#define DEBUG_DATA_MmModifiedNoWritePageListHeadAddr 416
+#define DEBUG_DATA_MmAvailablePagesAddr 424
+#define DEBUG_DATA_MmResidentAvailablePagesAddr 432
+#define DEBUG_DATA_PoolTrackTableAddr 440
+#define DEBUG_DATA_NonPagedPoolDescriptorAddr 448
+#define DEBUG_DATA_MmHighestUserAddressAddr 456
+#define DEBUG_DATA_MmSystemRangeStartAddr 464
+#define DEBUG_DATA_MmUserProbeAddressAddr 472
+#define DEBUG_DATA_KdPrintCircularBufferAddr 480
+#define DEBUG_DATA_KdPrintCircularBufferEndAddr 488
+#define DEBUG_DATA_KdPrintWritePointerAddr 496
+#define DEBUG_DATA_KdPrintRolloverCountAddr 504
+#define DEBUG_DATA_MmLoadedUserImageListAddr 512
+#define DEBUG_DATA_NtBuildLabAddr 520
+#define DEBUG_DATA_KiNormalSystemCall 528
+#define DEBUG_DATA_KiProcessorBlockAddr 536
+#define DEBUG_DATA_MmUnloadedDriversAddr 544
+#define DEBUG_DATA_MmLastUnloadedDriverAddr 552
+#define DEBUG_DATA_MmTriageActionTakenAddr 560
+#define DEBUG_DATA_MmSpecialPoolTagAddr 568
+#define DEBUG_DATA_KernelVerifierAddr 576
+#define DEBUG_DATA_MmVerifierDataAddr 584
+#define DEBUG_DATA_MmAllocatedNonPagedPoolAddr 592
+#define DEBUG_DATA_MmPeakCommitmentAddr 600
+#define DEBUG_DATA_MmTotalCommitLimitMaximumAddr 608
+#define DEBUG_DATA_CmNtCSDVersionAddr 616
+#define DEBUG_DATA_MmPhysicalMemoryBlockAddr 624
+#define DEBUG_DATA_MmSessionBase 632
+#define DEBUG_DATA_MmSessionSize 640
+#define DEBUG_DATA_MmSystemParentTablePage 648
+#define DEBUG_DATA_MmVirtualTranslationBase 656
+#define DEBUG_DATA_OffsetKThreadNextProcessor 664
+#define DEBUG_DATA_OffsetKThreadTeb 666
+#define DEBUG_DATA_OffsetKThreadKernelStack 668
+#define DEBUG_DATA_OffsetKThreadInitialStack 670
+#define DEBUG_DATA_OffsetKThreadApcProcess 672
+#define DEBUG_DATA_OffsetKThreadState 674
+#define DEBUG_DATA_OffsetKThreadBStore 676
+#define DEBUG_DATA_OffsetKThreadBStoreLimit 678
+#define DEBUG_DATA_SizeEProcess 680
+#define DEBUG_DATA_OffsetEprocessPeb 682
+#define DEBUG_DATA_OffsetEprocessParentCID 684
+#define DEBUG_DATA_OffsetEprocessDirectoryTableBase 686
+#define DEBUG_DATA_SizePrcb 688
+#define DEBUG_DATA_OffsetPrcbDpcRoutine 690
+#define DEBUG_DATA_OffsetPrcbCurrentThread 692
+#define DEBUG_DATA_OffsetPrcbMhz 694
+#define DEBUG_DATA_OffsetPrcbCpuType 696
+#define DEBUG_DATA_OffsetPrcbVendorString 698
+#define DEBUG_DATA_OffsetPrcbProcessorState 700
+#define DEBUG_DATA_OffsetPrcbNumber 702
+#define DEBUG_DATA_SizeEThread 704
+#define DEBUG_DATA_KdPrintCircularBufferPtrAddr 712
+#define DEBUG_DATA_KdPrintBufferSizeAddr 720
+#define DEBUG_DATA_MmBadPagesDetected 800
+
+#define DEBUG_DATA_PaeEnabled 100000
+#define DEBUG_DATA_SharedUserData 100008
+#define DEBUG_DATA_ProductType 100016
+#define DEBUG_DATA_SuiteMask 100024
+#define DEBUG_DATA_DumpWriterStatus 100032
+#define DEBUG_DATA_DumpFormatVersion 100040
+#define DEBUG_DATA_DumpWriterVersion 100048
+#define DEBUG_DATA_DumpPowerState 100056
+#define DEBUG_DATA_DumpMmStorage 100064
+
+//
+// Processor information structures.
+//
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_ALPHA
+{
+ ULONG Type;
+ ULONG Revision;
+} DEBUG_PROCESSOR_IDENTIFICATION_ALPHA, *PDEBUG_PROCESSOR_IDENTIFICATION_ALPHA;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_AMD64
+{
+ ULONG Family;
+ ULONG Model;
+ ULONG Stepping;
+ CHAR VendorString[16];
+} DEBUG_PROCESSOR_IDENTIFICATION_AMD64, *PDEBUG_PROCESSOR_IDENTIFICATION_AMD64;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_IA64
+{
+ ULONG Model;
+ ULONG Revision;
+ ULONG Family;
+ ULONG ArchRev;
+ CHAR VendorString[16];
+} DEBUG_PROCESSOR_IDENTIFICATION_IA64, *PDEBUG_PROCESSOR_IDENTIFICATION_IA64;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_X86
+{
+ ULONG Family;
+ ULONG Model;
+ ULONG Stepping;
+ CHAR VendorString[16];
+} DEBUG_PROCESSOR_IDENTIFICATION_X86, *PDEBUG_PROCESSOR_IDENTIFICATION_X86;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_ARM
+{
+ ULONG Type;
+ ULONG Revision;
+} DEBUG_PROCESSOR_IDENTIFICATION_ARM, *PDEBUG_PROCESSOR_IDENTIFICATION_ARM;
+
+typedef union _DEBUG_PROCESSOR_IDENTIFICATION_ALL
+{
+ DEBUG_PROCESSOR_IDENTIFICATION_ALPHA Alpha;
+ DEBUG_PROCESSOR_IDENTIFICATION_AMD64 Amd64;
+ DEBUG_PROCESSOR_IDENTIFICATION_IA64 Ia64;
+ DEBUG_PROCESSOR_IDENTIFICATION_X86 X86;
+ DEBUG_PROCESSOR_IDENTIFICATION_ARM Arm;
+} DEBUG_PROCESSOR_IDENTIFICATION_ALL, *PDEBUG_PROCESSOR_IDENTIFICATION_ALL;
+
+// Indices for ReadProcessorSystemData.
+#define DEBUG_DATA_KPCR_OFFSET 0
+#define DEBUG_DATA_KPRCB_OFFSET 1
+#define DEBUG_DATA_KTHREAD_OFFSET 2
+#define DEBUG_DATA_BASE_TRANSLATION_VIRTUAL_OFFSET 3
+#define DEBUG_DATA_PROCESSOR_IDENTIFICATION 4
+#define DEBUG_DATA_PROCESSOR_SPEED 5
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces
+DECLARE_INTERFACE_(IDebugDataSpaces, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+};
+
+//
+// Handle data types and structures.
+//
+
+#define DEBUG_HANDLE_DATA_TYPE_BASIC 0
+#define DEBUG_HANDLE_DATA_TYPE_TYPE_NAME 1
+#define DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME 2
+#define DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT 3
+#define DEBUG_HANDLE_DATA_TYPE_TYPE_NAME_WIDE 4
+#define DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME_WIDE 5
+#define DEBUG_HANDLE_DATA_TYPE_MINI_THREAD_1 6
+#define DEBUG_HANDLE_DATA_TYPE_MINI_MUTANT_1 7
+#define DEBUG_HANDLE_DATA_TYPE_MINI_MUTANT_2 8
+#define DEBUG_HANDLE_DATA_TYPE_PER_HANDLE_OPERATIONS 9
+#define DEBUG_HANDLE_DATA_TYPE_ALL_HANDLE_OPERATIONS 10
+#define DEBUG_HANDLE_DATA_TYPE_MINI_PROCESS_1 11
+#define DEBUG_HANDLE_DATA_TYPE_MINI_PROCESS_2 12
+
+typedef struct _DEBUG_HANDLE_DATA_BASIC
+{
+ ULONG TypeNameSize;
+ ULONG ObjectNameSize;
+ ULONG Attributes;
+ ULONG GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+} DEBUG_HANDLE_DATA_BASIC, *PDEBUG_HANDLE_DATA_BASIC;
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces2
+DECLARE_INTERFACE_(IDebugDataSpaces2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // IDebugDataSpaces2.
+
+ STDMETHOD(VirtualToPhysical)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out PULONG64 Physical
+ ) PURE;
+ // Returns the physical addresses for the
+ // N levels of the systems paging structures.
+ // Level zero is the starting base physical
+ // address for virtual translations.
+ // Levels one-(N-1) will point to the appropriate
+ // paging descriptor for the virtual address at
+ // the given level of the paging hierarchy. The
+ // exact number of levels depends on many factors.
+ // The last level will be the fully translated
+ // physical address, matching what VirtualToPhysical
+ // returns. If the address can only be partially
+ // translated S_FALSE is returned.
+ STDMETHOD(GetVirtualTranslationPhysicalOffsets)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out_ecount_opt(OffsetsSize) PULONG64 Offsets,
+ __in ULONG OffsetsSize,
+ __out_opt PULONG Levels
+ ) PURE;
+
+ // System handle data is accessible in certain
+ // debug sessions. The particular data available
+ // varies from session to session and platform
+ // to platform.
+ STDMETHOD(ReadHandleData)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG DataType,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // Fills memory with the given pattern.
+ // The fill stops at the first non-writable byte.
+ STDMETHOD(FillVirtual)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+ STDMETHOD(FillPhysical)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+
+ // Queries virtual memory mapping information given
+ // an address similarly to the Win32 API VirtualQuery.
+ // MEMORY_BASIC_INFORMATION64 is defined in crash.h.
+ // This method currently only works for user-mode sessions.
+ STDMETHOD(QueryVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PMEMORY_BASIC_INFORMATION64 Info
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces3
+DECLARE_INTERFACE_(IDebugDataSpaces3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // IDebugDataSpaces2.
+
+ STDMETHOD(VirtualToPhysical)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out PULONG64 Physical
+ ) PURE;
+ // Returns the physical addresses for the
+ // N levels of the systems paging structures.
+ // Level zero is the starting base physical
+ // address for virtual translations.
+ // Levels one-(N-1) will point to the appropriate
+ // paging descriptor for the virtual address at
+ // the given level of the paging hierarchy. The
+ // exact number of levels depends on many factors.
+ // The last level will be the fully translated
+ // physical address, matching what VirtualToPhysical
+ // returns. If the address can only be partially
+ // translated S_FALSE is returned.
+ STDMETHOD(GetVirtualTranslationPhysicalOffsets)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out_ecount_opt(OffsetsSize) PULONG64 Offsets,
+ __in ULONG OffsetsSize,
+ __out_opt PULONG Levels
+ ) PURE;
+
+ // System handle data is accessible in certain
+ // debug sessions. The particular data available
+ // varies from session to session and platform
+ // to platform.
+ STDMETHOD(ReadHandleData)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG DataType,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // Fills memory with the given pattern.
+ // The fill stops at the first non-writable byte.
+ STDMETHOD(FillVirtual)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+ STDMETHOD(FillPhysical)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+
+ // Queries virtual memory mapping information given
+ // an address similarly to the Win32 API VirtualQuery.
+ // MEMORY_BASIC_INFORMATION64 is defined in crash.h.
+ // This method currently only works for user-mode sessions.
+ STDMETHOD(QueryVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PMEMORY_BASIC_INFORMATION64 Info
+ ) PURE;
+
+ // IDebugDataSpaces3.
+
+ // Convenience method for reading an image
+ // header from virtual memory. Given the
+ // image base, this method determines where
+ // the NT headers are, validates the necessary
+ // markers and converts the headers into
+ // 64-bit form for consistency.
+ // A caller can check whether the headers were
+ // originally 32-bit by checking the optional
+ // header magic value.
+ // This method will not read ROM headers.
+ STDMETHOD(ReadImageNtHeaders)(
+ THIS_
+ __in ULONG64 ImageBase,
+ __out PIMAGE_NT_HEADERS64 Headers
+ ) PURE;
+
+ // Some debug sessions have arbitrary additional
+ // data available. For example, additional dump
+ // information files may contain extra information
+ // gathered at the same time as the primary dump.
+ // Such information is tagged with a unique identifier
+ // and can only be retrieved via the tag.
+ // Tagged data cannot be partially available; the
+ // tagged block is either fully present or completely
+ // absent.
+ STDMETHOD(ReadTagged)(
+ THIS_
+ __in LPGUID Tag,
+ __in ULONG Offset,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TotalSize
+ ) PURE;
+ STDMETHOD(StartEnumTagged)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetNextTagged)(
+ THIS_
+ __in ULONG64 Handle,
+ __out LPGUID Tag,
+ __out PULONG Size
+ ) PURE;
+ STDMETHOD(EndEnumTagged)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+};
+
+#define DEBUG_OFFSINFO_VIRTUAL_SOURCE 0x00000001
+
+#define DEBUG_VSOURCE_INVALID 0x00000000
+#define DEBUG_VSOURCE_DEBUGGEE 0x00000001
+#define DEBUG_VSOURCE_MAPPED_IMAGE 0x00000002
+
+#define DEBUG_VSEARCH_DEFAULT 0x00000000
+#define DEBUG_VSEARCH_WRITABLE_ONLY 0x00000001
+
+#define DEBUG_PHYSICAL_DEFAULT 0x00000000
+#define DEBUG_PHYSICAL_CACHED 0x00000001
+#define DEBUG_PHYSICAL_UNCACHED 0x00000002
+#define DEBUG_PHYSICAL_WRITE_COMBINED 0x00000003
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces4
+DECLARE_INTERFACE_(IDebugDataSpaces4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // IDebugDataSpaces2.
+
+ STDMETHOD(VirtualToPhysical)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out PULONG64 Physical
+ ) PURE;
+ // Returns the physical addresses for the
+ // N levels of the systems paging structures.
+ // Level zero is the starting base physical
+ // address for virtual translations.
+ // Levels one-(N-1) will point to the appropriate
+ // paging descriptor for the virtual address at
+ // the given level of the paging hierarchy. The
+ // exact number of levels depends on many factors.
+ // The last level will be the fully translated
+ // physical address, matching what VirtualToPhysical
+ // returns. If the address can only be partially
+ // translated S_FALSE is returned.
+ STDMETHOD(GetVirtualTranslationPhysicalOffsets)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out_ecount_opt(OffsetsSize) PULONG64 Offsets,
+ __in ULONG OffsetsSize,
+ __out_opt PULONG Levels
+ ) PURE;
+
+ // System handle data is accessible in certain
+ // debug sessions. The particular data available
+ // varies from session to session and platform
+ // to platform.
+ STDMETHOD(ReadHandleData)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG DataType,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // Fills memory with the given pattern.
+ // The fill stops at the first non-writable byte.
+ STDMETHOD(FillVirtual)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+ STDMETHOD(FillPhysical)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+
+ // Queries virtual memory mapping information given
+ // an address similarly to the Win32 API VirtualQuery.
+ // MEMORY_BASIC_INFORMATION64 is defined in crash.h.
+ // This method currently only works for user-mode sessions.
+ STDMETHOD(QueryVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PMEMORY_BASIC_INFORMATION64 Info
+ ) PURE;
+
+ // IDebugDataSpaces3.
+
+ // Convenience method for reading an image
+ // header from virtual memory. Given the
+ // image base, this method determines where
+ // the NT headers are, validates the necessary
+ // markers and converts the headers into
+ // 64-bit form for consistency.
+ // A caller can check whether the headers were
+ // originally 32-bit by checking the optional
+ // header magic value.
+ // This method will not read ROM headers.
+ STDMETHOD(ReadImageNtHeaders)(
+ THIS_
+ __in ULONG64 ImageBase,
+ __out PIMAGE_NT_HEADERS64 Headers
+ ) PURE;
+
+ // Some debug sessions have arbitrary additional
+ // data available. For example, additional dump
+ // information files may contain extra information
+ // gathered at the same time as the primary dump.
+ // Such information is tagged with a unique identifier
+ // and can only be retrieved via the tag.
+ // Tagged data cannot be partially available; the
+ // tagged block is either fully present or completely
+ // absent.
+ STDMETHOD(ReadTagged)(
+ THIS_
+ __in LPGUID Tag,
+ __in ULONG Offset,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TotalSize
+ ) PURE;
+ STDMETHOD(StartEnumTagged)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetNextTagged)(
+ THIS_
+ __in ULONG64 Handle,
+ __out LPGUID Tag,
+ __out PULONG Size
+ ) PURE;
+ STDMETHOD(EndEnumTagged)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ // IDebugDataSpaces4.
+
+ // General information about an address in the given data space.
+ // Queries are from DEBUG_OFFSINFO_*.
+ STDMETHOD(GetOffsetInformation)(
+ THIS_
+ __in ULONG Space,
+ __in ULONG Which,
+ __in ULONG64 Offset,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+
+ // Given a particular address, return the
+ // next address which has a different validity.
+ // For example, in debug sessions such as a live
+ // user-mode session where virtual address validity
+ // changes from page to page this will return the
+ // page after the given page. In sessions such as
+ // a user-mode dump file where validity can change
+ // from byte to byte this will return the start of
+ // the next region that has different validity.
+ STDMETHOD(GetNextDifferentlyValidOffsetVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG64 NextOffset
+ ) PURE;
+
+ // Given a particular range of virtual addresses,
+ // find the first region which is valid memory.
+ STDMETHOD(GetValidRegionVirtual)(
+ THIS_
+ __in ULONG64 Base,
+ __in ULONG Size,
+ __out PULONG64 ValidBase,
+ __out PULONG ValidSize
+ ) PURE;
+
+ STDMETHOD(SearchVirtual2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in ULONG Flags,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+
+ // Attempts to read a multi-byte string
+ // starting at the given virtual address.
+ // The possible string length, including terminator,
+ // is capped at the given max size.
+ // If a return buffer is given it will always
+ // be terminated.
+ STDMETHOD(ReadMultiByteStringVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+ // Reads a multi-byte string and converts
+ // it to Unicode using the given code page.
+ STDMETHOD(ReadMultiByteStringVirtualWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __in ULONG CodePage,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+ STDMETHOD(ReadUnicodeStringVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __in ULONG CodePage,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+ STDMETHOD(ReadUnicodeStringVirtualWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+
+ STDMETHOD(ReadPhysical2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugEventCallbacks.
+//
+//----------------------------------------------------------------------------
+
+// Interest mask bits.
+#define DEBUG_EVENT_BREAKPOINT 0x00000001
+#define DEBUG_EVENT_EXCEPTION 0x00000002
+#define DEBUG_EVENT_CREATE_THREAD 0x00000004
+#define DEBUG_EVENT_EXIT_THREAD 0x00000008
+#define DEBUG_EVENT_CREATE_PROCESS 0x00000010
+#define DEBUG_EVENT_EXIT_PROCESS 0x00000020
+#define DEBUG_EVENT_LOAD_MODULE 0x00000040
+#define DEBUG_EVENT_UNLOAD_MODULE 0x00000080
+#define DEBUG_EVENT_SYSTEM_ERROR 0x00000100
+#define DEBUG_EVENT_SESSION_STATUS 0x00000200
+#define DEBUG_EVENT_CHANGE_DEBUGGEE_STATE 0x00000400
+#define DEBUG_EVENT_CHANGE_ENGINE_STATE 0x00000800
+#define DEBUG_EVENT_CHANGE_SYMBOL_STATE 0x00001000
+
+// SessionStatus flags.
+// A debuggee has been discovered for the session.
+#define DEBUG_SESSION_ACTIVE 0x00000000
+// The session has been ended by EndSession.
+#define DEBUG_SESSION_END_SESSION_ACTIVE_TERMINATE 0x00000001
+#define DEBUG_SESSION_END_SESSION_ACTIVE_DETACH 0x00000002
+#define DEBUG_SESSION_END_SESSION_PASSIVE 0x00000003
+// The debuggee has run to completion. User-mode only.
+#define DEBUG_SESSION_END 0x00000004
+// The target machine has rebooted. Kernel-mode only.
+#define DEBUG_SESSION_REBOOT 0x00000005
+// The target machine has hibernated. Kernel-mode only.
+#define DEBUG_SESSION_HIBERNATE 0x00000006
+// The engine was unable to continue the session.
+#define DEBUG_SESSION_FAILURE 0x00000007
+
+// ChangeDebuggeeState flags.
+// The debuggees state has changed generally, such
+// as when the debuggee has been executing.
+// Argument is zero.
+#define DEBUG_CDS_ALL 0xffffffff
+// Registers have changed. If only a single register
+// changed, argument is the index of the register.
+// Otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CDS_REGISTERS 0x00000001
+// Data spaces have changed. If only a single
+// space was affected, argument is the data
+// space. Otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CDS_DATA 0x00000002
+
+// ChangeEngineState flags.
+// The engine state has changed generally.
+// Argument is zero.
+#define DEBUG_CES_ALL 0xffffffff
+// Current thread changed. This may imply a change
+// of system and process also. Argument is the ID of the new
+// current thread or DEBUG_ANY_ID if no thread is current.
+#define DEBUG_CES_CURRENT_THREAD 0x00000001
+// Effective processor changed. Argument is the
+// new processor type.
+#define DEBUG_CES_EFFECTIVE_PROCESSOR 0x00000002
+// Breakpoints changed. If only a single breakpoint
+// changed, argument is the ID of the breakpoint.
+// Otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CES_BREAKPOINTS 0x00000004
+// Code interpretation level changed. Argument is
+// the new level.
+#define DEBUG_CES_CODE_LEVEL 0x00000008
+// Execution status changed. Argument is the new
+// execution status.
+#define DEBUG_CES_EXECUTION_STATUS 0x00000010
+// Engine options have changed. Argument is the new
+// options value.
+#define DEBUG_CES_ENGINE_OPTIONS 0x00000020
+// Log file information has changed. Argument
+// is TRUE if a log file was opened and FALSE if
+// a log file was closed.
+#define DEBUG_CES_LOG_FILE 0x00000040
+// Default number radix has changed. Argument
+// is the new radix.
+#define DEBUG_CES_RADIX 0x00000080
+// Event filters changed. If only a single filter
+// changed the argument is the filter's index,
+// otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CES_EVENT_FILTERS 0x00000100
+// Process options have changed. Argument is the new
+// options value.
+#define DEBUG_CES_PROCESS_OPTIONS 0x00000200
+// Extensions have been added or removed.
+#define DEBUG_CES_EXTENSIONS 0x00000400
+// Systems have been added or removed. The argument
+// is the system ID. Systems, unlike processes and
+// threads, may be created at any time and not
+// just during WaitForEvent.
+#define DEBUG_CES_SYSTEMS 0x00000800
+// Assembly/disassembly options have changed. Argument
+// is the new options value.
+#define DEBUG_CES_ASSEMBLY_OPTIONS 0x00001000
+// Expression syntax has changed. Argument
+// is the new syntax value.
+#define DEBUG_CES_EXPRESSION_SYNTAX 0x00002000
+// Text replacements have changed.
+#define DEBUG_CES_TEXT_REPLACEMENTS 0x00004000
+
+// ChangeSymbolState flags.
+// Symbol state has changed generally, such
+// as after reload operations. Argument is zero.
+#define DEBUG_CSS_ALL 0xffffffff
+// Modules have been loaded. If only a
+// single module changed, argument is the
+// base address of the module. Otherwise
+// it is zero.
+#define DEBUG_CSS_LOADS 0x00000001
+// Modules have been unloaded. If only a
+// single module changed, argument is the
+// base address of the module. Otherwise
+// it is zero.
+#define DEBUG_CSS_UNLOADS 0x00000002
+// Current symbol scope changed.
+#define DEBUG_CSS_SCOPE 0x00000004
+// Paths have changed.
+#define DEBUG_CSS_PATHS 0x00000008
+// Symbol options have changed. Argument is the new
+// options value.
+#define DEBUG_CSS_SYMBOL_OPTIONS 0x00000010
+// Type options have changed. Argument is the new
+// options value.
+#define DEBUG_CSS_TYPE_OPTIONS 0x00000020
+
+#undef INTERFACE
+#define INTERFACE IDebugEventCallbacks
+DECLARE_INTERFACE_(IDebugEventCallbacks, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugEventCallbacks.
+
+ // The engine calls GetInterestMask once when
+ // the event callbacks are set for a client.
+ STDMETHOD(GetInterestMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+
+ // A breakpoint event is generated when
+ // a breakpoint exception is received and
+ // it can be mapped to an existing breakpoint.
+ // The callback method is given a reference
+ // to the breakpoint and should release it when
+ // it is done with it.
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Exceptions include breaks which cannot
+ // be mapped to an existing breakpoint
+ // instance.
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ // Currently the kernel does not return thread
+ // or process change events.
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values may be zero.
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ ) PURE;
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in_opt PCSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ ) PURE;
+
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ ) PURE;
+
+ // Session status is synchronous like the other
+ // wait callbacks but it is called as the state
+ // of the session is changing rather than at
+ // specific events so its return value does not
+ // influence waiting. Implementations should just
+ // return DEBUG_STATUS_NO_CHANGE.
+ // Also, because some of the status
+ // notifications are very early or very
+ // late in the session lifetime there may not be
+ // current processes or threads when the notification
+ // is generated.
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // The following callbacks are informational
+ // callbacks notifying the provider about
+ // changes in debug state. The return value
+ // of these callbacks is ignored. Implementations
+ // can not call back into the engine.
+
+ // Debuggee state, such as registers or data spaces,
+ // has changed.
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Engine state has changed.
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Symbol state has changed.
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugEventCallbacksWide
+DECLARE_INTERFACE_(IDebugEventCallbacksWide, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugEventCallbacksWide.
+
+ // The engine calls GetInterestMask once when
+ // the event callbacks are set for a client.
+ STDMETHOD(GetInterestMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+
+ // A breakpoint event is generated when
+ // a breakpoint exception is received and
+ // it can be mapped to an existing breakpoint.
+ // The callback method is given a reference
+ // to the breakpoint and should release it when
+ // it is done with it.
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT2 Bp
+ ) PURE;
+
+ // Exceptions include breaks which cannot
+ // be mapped to an existing breakpoint
+ // instance.
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ // Currently the kernel does not return thread
+ // or process change events.
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCWSTR ModuleName,
+ __in_opt PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values may be zero.
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCWSTR ModuleName,
+ __in_opt PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ ) PURE;
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in_opt PCWSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ ) PURE;
+
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ ) PURE;
+
+ // Session status is synchronous like the other
+ // wait callbacks but it is called as the state
+ // of the session is changing rather than at
+ // specific events so its return value does not
+ // influence waiting. Implementations should just
+ // return DEBUG_STATUS_NO_CHANGE.
+ // Also, because some of the status
+ // notifications are very early or very
+ // late in the session lifetime there may not be
+ // current processes or threads when the notification
+ // is generated.
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // The following callbacks are informational
+ // callbacks notifying the provider about
+ // changes in debug state. The return value
+ // of these callbacks is ignored. Implementations
+ // can not call back into the engine.
+
+ // Debuggee state, such as registers or data spaces,
+ // has changed.
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Engine state has changed.
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Symbol state has changed.
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugInputCallbacks.
+//
+//----------------------------------------------------------------------------
+
+#undef INTERFACE
+#define INTERFACE IDebugInputCallbacks
+DECLARE_INTERFACE_(IDebugInputCallbacks, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugInputCallbacks.
+
+ // A call to the StartInput method is a request for
+ // a line of input from any client. The returned input
+ // should always be zero-terminated. The buffer size
+ // provided is only a guideline. A client can return
+ // more if necessary and the engine will truncate it
+ // before returning from IDebugControl::Input.
+ // The return value is ignored.
+ STDMETHOD(StartInput)(
+ THIS_
+ __in ULONG BufferSize
+ ) PURE;
+ // The return value is ignored.
+ STDMETHOD(EndInput)(
+ THIS
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugOutputCallbacks.
+//
+//----------------------------------------------------------------------------
+
+#undef INTERFACE
+#define INTERFACE IDebugOutputCallbacks
+DECLARE_INTERFACE_(IDebugOutputCallbacks, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugOutputCallbacks.
+
+ // This method is only called if the supplied mask
+ // is allowed by the clients output control.
+ // The return value is ignored.
+ STDMETHOD(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Text
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugOutputCallbacksWide
+DECLARE_INTERFACE_(IDebugOutputCallbacksWide, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugOutputCallbacksWide.
+
+ // This method is only called if the supplied mask
+ // is allowed by the clients output control.
+ // The return value is ignored.
+ STDMETHOD(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCWSTR Text
+ ) PURE;
+};
+
+//
+// IDebugOutputCallbacks2 interest mask flags.
+//
+
+// Indicates that the callback wants notifications
+// of all explicit flushes.
+#define DEBUG_OUTCBI_EXPLICIT_FLUSH 0x00000001
+// Indicates that the callback wants
+// content in text form.
+#define DEBUG_OUTCBI_TEXT 0x00000002
+// Indicates that the callback wants
+// content in markup form.
+#define DEBUG_OUTCBI_DML 0x00000004
+
+#define DEBUG_OUTCBI_ANY_FORMAT 0x00000006
+
+//
+// Different kinds of output callback notifications
+// that can be sent to Output2.
+//
+
+// Plain text content, flags are below, argument is mask.
+#define DEBUG_OUTCB_TEXT 0
+// Debugger markup content, flags are below, argument is mask.
+#define DEBUG_OUTCB_DML 1
+// Notification of an explicit output flush, flags and argument are zero.
+#define DEBUG_OUTCB_EXPLICIT_FLUSH 2
+
+//
+// Flags for various Output2 callbacks.
+//
+
+// The content string was followed by an
+// explicit flush. This flag will be used
+// instead of a separate DEBUG_OUTCB_EXPLICIT_FLUSH
+// callback when a flush has text to flush,
+// thus avoiding two callbacks.
+#define DEBUG_OUTCBF_COMBINED_EXPLICIT_FLUSH 0x00000001
+
+// The markup content string has embedded tags.
+#define DEBUG_OUTCBF_DML_HAS_TAGS 0x00000002
+// The markup content has encoded special characters like ", &, < and >.
+#define DEBUG_OUTCBF_DML_HAS_SPECIAL_CHARACTERS 0x00000004
+
+#undef INTERFACE
+#define INTERFACE IDebugOutputCallbacks2
+DECLARE_INTERFACE_(IDebugOutputCallbacks2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugOutputCallbacks.
+
+ // This method is not used.
+ STDMETHOD(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Text
+ ) PURE;
+
+ // IDebugOutputCallbacks2.
+
+ // The engine calls GetInterestMask once when
+ // the callbacks are set for a client.
+ STDMETHOD(GetInterestMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+
+ STDMETHOD(Output2)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Flags,
+ __in ULONG64 Arg,
+ __in_opt PCWSTR Text
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugRegisters.
+//
+//----------------------------------------------------------------------------
+
+#define DEBUG_REGISTERS_DEFAULT 0x00000000
+#define DEBUG_REGISTERS_INT32 0x00000001
+#define DEBUG_REGISTERS_INT64 0x00000002
+#define DEBUG_REGISTERS_FLOAT 0x00000004
+#define DEBUG_REGISTERS_ALL 0x00000007
+
+#define DEBUG_REGISTER_SUB_REGISTER 0x00000001
+
+typedef struct _DEBUG_REGISTER_DESCRIPTION
+{
+ // DEBUG_VALUE type.
+ ULONG Type;
+ ULONG Flags;
+
+ // If this is a subregister the full
+ // registers description index is
+ // given in SubregMaster. The length, mask
+ // and shift describe how the subregisters
+ // bits fit into the full register.
+ ULONG SubregMaster;
+ ULONG SubregLength;
+ ULONG64 SubregMask;
+ ULONG SubregShift;
+
+ ULONG Reserved0;
+} DEBUG_REGISTER_DESCRIPTION, *PDEBUG_REGISTER_DESCRIPTION;
+
+#undef INTERFACE
+#define INTERFACE IDebugRegisters
+DECLARE_INTERFACE_(IDebugRegisters, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugRegisters.
+ STDMETHOD(GetNumberRegisters)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDescription)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PDEBUG_REGISTER_DESCRIPTION Desc
+ ) PURE;
+ STDMETHOD(GetIndexByName)(
+ THIS_
+ __in PCSTR Name,
+ __out PULONG Index
+ ) PURE;
+
+ STDMETHOD(GetValue)(
+ THIS_
+ __in ULONG Register,
+ __out PDEBUG_VALUE Value
+ ) PURE;
+ // SetValue makes a best effort at coercing
+ // the given value into the given registers
+ // value type. If the given value is larger
+ // than the register can hold the least
+ // significant bits will be dropped. Float
+ // to int and int to float will be done
+ // if necessary. Subregister bits will be
+ // inserted into the master register.
+ STDMETHOD(SetValue)(
+ THIS_
+ __in ULONG Register,
+ __in PDEBUG_VALUE Value
+ ) PURE;
+ // Gets Count register values. If Indices is
+ // non-NULL it must contain Count register
+ // indices which control the registers affected.
+ // If Indices is NULL the registers from Start
+ // to Start + Count 1 are retrieved.
+ STDMETHOD(GetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(SetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+
+ // Outputs a group of registers in a well-formatted
+ // way thats specific to the platforms register set.
+ // Uses the line prefix.
+ STDMETHOD(OutputRegisters)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Abstracted pieces of processor information.
+ // The mapping of these values to architectural
+ // registers is architecture-specific and their
+ // interpretation and existence may vary. They
+ // are intended to be directly compatible with
+ // calls which take this information, such as
+ // stack walking.
+ STDMETHOD(GetInstructionOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetStackOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetFrameOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+};
+
+//
+// The engine maintains several separate
+// pieces of context information. There is
+// the current debuggee context, a possible
+// override context, such as from .cxr,
+// a context for the current scope frame and so on.
+//
+
+// Get register information from the debuggee.
+#define DEBUG_REGSRC_DEBUGGEE 0x00000000
+// Get register information from an explicit
+// override context, such as one set by .cxr.
+// If there is no override context the request will fail.
+#define DEBUG_REGSRC_EXPLICIT 0x00000001
+// Get register information from the current scope
+// frame. Note that stack unwinding does not guarantee
+// accurate updating of the register context,
+// so scope frame register context may not be accurate
+// in all cases.
+#define DEBUG_REGSRC_FRAME 0x00000002
+
+#undef INTERFACE
+#define INTERFACE IDebugRegisters2
+DECLARE_INTERFACE_(IDebugRegisters2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugRegisters.
+
+ STDMETHOD(GetNumberRegisters)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDescription)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PDEBUG_REGISTER_DESCRIPTION Desc
+ ) PURE;
+ STDMETHOD(GetIndexByName)(
+ THIS_
+ __in PCSTR Name,
+ __out PULONG Index
+ ) PURE;
+
+ STDMETHOD(GetValue)(
+ THIS_
+ __in ULONG Register,
+ __out PDEBUG_VALUE Value
+ ) PURE;
+ // SetValue makes a best effort at coercing
+ // the given value into the given registers
+ // value type. If the given value is larger
+ // than the register can hold the least
+ // significant bits will be dropped. Float
+ // to int and int to float will be done
+ // if necessary. Subregister bits will be
+ // inserted into the master register.
+ STDMETHOD(SetValue)(
+ THIS_
+ __in ULONG Register,
+ __in PDEBUG_VALUE Value
+ ) PURE;
+ // Gets Count register values. If Indices is
+ // non-NULL it must contain Count register
+ // indices which control the registers affected.
+ // If Indices is NULL the registers from Start
+ // to Start + Count 1 are retrieved.
+ STDMETHOD(GetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(SetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+
+ // Outputs a group of registers in a well-formatted
+ // way thats specific to the platforms register set.
+ // Uses the line prefix.
+ STDMETHOD(OutputRegisters)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Abstracted pieces of processor information.
+ // The mapping of these values to architectural
+ // registers is architecture-specific and their
+ // interpretation and existence may vary. They
+ // are intended to be directly compatible with
+ // calls which take this information, such as
+ // stack walking.
+ STDMETHOD(GetInstructionOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetStackOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetFrameOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+
+ // IDebugRegisters2.
+
+ STDMETHOD(GetDescriptionWide)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PDEBUG_REGISTER_DESCRIPTION Desc
+ ) PURE;
+ STDMETHOD(GetIndexByNameWide)(
+ THIS_
+ __in PCWSTR Name,
+ __out PULONG Index
+ ) PURE;
+
+ // Pseudo-registers are synthetic values derived
+ // by the engine that are presented in a manner
+ // similar to regular registers. They are simple
+ // value holders, similar to actual registers.
+ // Pseudo-registers are defined for concepts,
+ // such as current-instruction-pointer or
+ // current-thread-data. As such they have
+ // types appropriate for their data.
+ STDMETHOD(GetNumberPseudoRegisters)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPseudoDescription)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 TypeModule,
+ __out_opt PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetPseudoDescriptionWide)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 TypeModule,
+ __out_opt PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetPseudoIndexByName)(
+ THIS_
+ __in PCSTR Name,
+ __out PULONG Index
+ ) PURE;
+ STDMETHOD(GetPseudoIndexByNameWide)(
+ THIS_
+ __in PCWSTR Name,
+ __out PULONG Index
+ ) PURE;
+ // Some pseudo-register values are affected
+ // by the register source, others are not.
+ STDMETHOD(GetPseudoValues)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ // Many pseudo-registers are read-only and cannot be set.
+ STDMETHOD(SetPseudoValues)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+
+ // These expanded methods allow selection
+ // of the source of register information.
+ STDMETHOD(GetValues2)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(SetValues2)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(OutputRegisters2)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Source,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(GetInstructionOffset2)(
+ THIS_
+ __in ULONG Source,
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetStackOffset2)(
+ THIS_
+ __in ULONG Source,
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetFrameOffset2)(
+ THIS_
+ __in ULONG Source,
+ __out PULONG64 Offset
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugSymbolGroup
+//
+//----------------------------------------------------------------------------
+
+// OutputSymbols flags.
+// Default output contains
+// <Name>**NAME**<Offset>**OFF**<Value>**VALUE**<Type>**TYPE**
+// per symbol.
+#define DEBUG_OUTPUT_SYMBOLS_DEFAULT 0x00000000
+#define DEBUG_OUTPUT_SYMBOLS_NO_NAMES 0x00000001
+#define DEBUG_OUTPUT_SYMBOLS_NO_OFFSETS 0x00000002
+#define DEBUG_OUTPUT_SYMBOLS_NO_VALUES 0x00000004
+#define DEBUG_OUTPUT_SYMBOLS_NO_TYPES 0x00000010
+
+#define DEBUG_OUTPUT_NAME_END "**NAME**"
+#define DEBUG_OUTPUT_OFFSET_END "**OFF**"
+#define DEBUG_OUTPUT_VALUE_END "**VALUE**"
+#define DEBUG_OUTPUT_TYPE_END "**TYPE**"
+
+#define DEBUG_OUTPUT_NAME_END_WIDE L"**NAME**"
+#define DEBUG_OUTPUT_OFFSET_END_WIDE L"**OFF**"
+#define DEBUG_OUTPUT_VALUE_END_WIDE L"**VALUE**"
+#define DEBUG_OUTPUT_TYPE_END_WIDE L"**TYPE**"
+
+#ifdef UNICODE
+#define DEBUG_OUTPUT_NAME_END_T DEBUG_OUTPUT_NAME_END_WIDE
+#define DEBUG_OUTPUT_OFFSET_END_T DEBUG_OUTPUT_OFFSET_END_WIDE
+#define DEBUG_OUTPUT_VALUE_END_T DEBUG_OUTPUT_VALUE_END_WIDE
+#define DEBUG_OUTPUT_TYPE_END_T DEBUG_OUTPUT_TYPE_END_WIDE
+#else
+#define DEBUG_OUTPUT_NAME_END_T DEBUG_OUTPUT_NAME_END
+#define DEBUG_OUTPUT_OFFSET_END_T DEBUG_OUTPUT_OFFSET_END
+#define DEBUG_OUTPUT_VALUE_END_T DEBUG_OUTPUT_VALUE_END
+#define DEBUG_OUTPUT_TYPE_END_T DEBUG_OUTPUT_TYPE_END
+#endif
+
+// DEBUG_SYMBOL_PARAMETERS flags.
+// Cumulative expansion level, takes four bits.
+#define DEBUG_SYMBOL_EXPANSION_LEVEL_MASK 0x0000000f
+// Symbols subelements follow.
+#define DEBUG_SYMBOL_EXPANDED 0x00000010
+// Symbols value is read-only.
+#define DEBUG_SYMBOL_READ_ONLY 0x00000020
+// Symbol subelements are array elements.
+#define DEBUG_SYMBOL_IS_ARRAY 0x00000040
+// Symbol is a float value.
+#define DEBUG_SYMBOL_IS_FLOAT 0x00000080
+// Symbol is a scope argument.
+#define DEBUG_SYMBOL_IS_ARGUMENT 0x00000100
+// Symbol is a scope argument.
+#define DEBUG_SYMBOL_IS_LOCAL 0x00000200
+
+typedef struct _DEBUG_SYMBOL_PARAMETERS
+{
+ ULONG64 Module;
+ ULONG TypeId;
+ // ParentSymbol may be DEBUG_ANY_ID when unknown.
+ ULONG ParentSymbol;
+ // A subelement of a symbol can be a field, such
+ // as in structs, unions or classes; or an array
+ // element count for arrays.
+ ULONG SubElements;
+ ULONG Flags;
+ ULONG64 Reserved;
+} DEBUG_SYMBOL_PARAMETERS, *PDEBUG_SYMBOL_PARAMETERS;
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbolGroup
+DECLARE_INTERFACE_(IDebugSymbolGroup, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbolGroup.
+ STDMETHOD(GetNumberSymbols)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // On input Index indicates the desired insertion
+ // index. On output Index contains the actual index.
+ // Use DEBUG_ANY_ID to append a symbol to the end.
+ STDMETHOD(AddSymbol)(
+ THIS_
+ __in PCSTR Name,
+ __inout PULONG Index
+ ) PURE;
+ STDMETHOD(RemoveSymbolByName)(
+ THIS_
+ __in PCSTR Name
+ ) PURE;
+ STDMETHOD(RemoveSymbolByIndex)(
+ THIS_
+ __in ULONG Index
+ ) PURE;
+ STDMETHOD(GetSymbolName)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SYMBOL_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(ExpandSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in BOOL Expand
+ ) PURE;
+ // Uses the line prefix.
+ STDMETHOD(OutputSymbols)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in ULONG Start,
+ __in ULONG Count
+ ) PURE;
+ STDMETHOD(WriteSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Value
+ ) PURE;
+ STDMETHOD(OutputAsType)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Type
+ ) PURE;
+};
+
+#define DEBUG_SYMENT_IS_CODE 0x00000001
+#define DEBUG_SYMENT_IS_DATA 0x00000002
+#define DEBUG_SYMENT_IS_PARAMETER 0x00000004
+#define DEBUG_SYMENT_IS_LOCAL 0x00000008
+#define DEBUG_SYMENT_IS_MANAGED 0x00000010
+#define DEBUG_SYMENT_IS_SYNTHETIC 0x00000020
+
+typedef struct _DEBUG_SYMBOL_ENTRY
+{
+ ULONG64 ModuleBase;
+ ULONG64 Offset;
+ ULONG64 Id;
+ ULONG64 Arg64;
+ ULONG Size;
+ ULONG Flags;
+ ULONG TypeId;
+ ULONG NameSize;
+ ULONG Token;
+ ULONG Tag;
+ ULONG Arg32;
+ ULONG Reserved;
+} DEBUG_SYMBOL_ENTRY, *PDEBUG_SYMBOL_ENTRY;
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbolGroup2
+DECLARE_INTERFACE_(IDebugSymbolGroup2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbolGroup.
+
+ STDMETHOD(GetNumberSymbols)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // On input Index indicates the desired insertion
+ // index. On output Index contains the actual index.
+ // Use DEBUG_ANY_ID to append a symbol to the end.
+ STDMETHOD(AddSymbol)(
+ THIS_
+ __in PCSTR Name,
+ __inout PULONG Index
+ ) PURE;
+ STDMETHOD(RemoveSymbolByName)(
+ THIS_
+ __in PCSTR Name
+ ) PURE;
+ STDMETHOD(RemoveSymbolByIndex)(
+ THIS_
+ __in ULONG Index
+ ) PURE;
+ STDMETHOD(GetSymbolName)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SYMBOL_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(ExpandSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in BOOL Expand
+ ) PURE;
+ // Uses the line prefix.
+ STDMETHOD(OutputSymbols)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in ULONG Start,
+ __in ULONG Count
+ ) PURE;
+ STDMETHOD(WriteSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Value
+ ) PURE;
+ STDMETHOD(OutputAsType)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Type
+ ) PURE;
+
+ // IDebugSymbolGroup2.
+
+ STDMETHOD(AddSymbolWide)(
+ THIS_
+ __in PCWSTR Name,
+ __inout PULONG Index
+ ) PURE;
+ STDMETHOD(RemoveSymbolByNameWide)(
+ THIS_
+ __in PCWSTR Name
+ ) PURE;
+ STDMETHOD(GetSymbolNameWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(WriteSymbolWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Value
+ ) PURE;
+ STDMETHOD(OutputAsTypeWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Type
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeName)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolTypeNameWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolSize)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG Size
+ ) PURE;
+ // If the symbol has an absolute address
+ // this method will retrieve it.
+ STDMETHOD(GetSymbolOffset)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Offset
+ ) PURE;
+ // If the symbol is enregistered this
+ // method will return the register index.
+ STDMETHOD(GetSymbolRegister)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG Register
+ ) PURE;
+ STDMETHOD(GetSymbolValueText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolValueTextWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolEntryInformation)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_SYMBOL_ENTRY Entry
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugSymbols.
+//
+//----------------------------------------------------------------------------
+
+//
+// Information about a module.
+//
+
+// Flags.
+#define DEBUG_MODULE_LOADED 0x00000000
+#define DEBUG_MODULE_UNLOADED 0x00000001
+#define DEBUG_MODULE_USER_MODE 0x00000002
+#define DEBUG_MODULE_EXPLICIT 0x00000008
+#define DEBUG_MODULE_SECONDARY 0x00000010
+#define DEBUG_MODULE_SYNTHETIC 0x00000020
+#define DEBUG_MODULE_SYM_BAD_CHECKSUM 0x00010000
+
+// Symbol types.
+#define DEBUG_SYMTYPE_NONE 0
+#define DEBUG_SYMTYPE_COFF 1
+#define DEBUG_SYMTYPE_CODEVIEW 2
+#define DEBUG_SYMTYPE_PDB 3
+#define DEBUG_SYMTYPE_EXPORT 4
+#define DEBUG_SYMTYPE_DEFERRED 5
+#define DEBUG_SYMTYPE_SYM 6
+#define DEBUG_SYMTYPE_DIA 7
+
+typedef struct _DEBUG_MODULE_PARAMETERS
+{
+ ULONG64 Base;
+ ULONG Size;
+ ULONG TimeDateStamp;
+ ULONG Checksum;
+ ULONG Flags;
+ ULONG SymbolType;
+ ULONG ImageNameSize;
+ ULONG ModuleNameSize;
+ ULONG LoadedImageNameSize;
+ ULONG SymbolFileNameSize;
+ ULONG MappedImageNameSize;
+ ULONG64 Reserved[2];
+} DEBUG_MODULE_PARAMETERS, *PDEBUG_MODULE_PARAMETERS;
+
+// Scope arguments are function arguments
+// and thus only change when the scope
+// crosses functions.
+#define DEBUG_SCOPE_GROUP_ARGUMENTS 0x00000001
+// Scope locals are locals declared in a particular
+// scope and are only defined within that scope.
+#define DEBUG_SCOPE_GROUP_LOCALS 0x00000002
+// All symbols in the scope.
+#define DEBUG_SCOPE_GROUP_ALL 0x00000003
+
+// Typed data output control flags.
+#define DEBUG_OUTTYPE_DEFAULT 0x00000000
+#define DEBUG_OUTTYPE_NO_INDENT 0x00000001
+#define DEBUG_OUTTYPE_NO_OFFSET 0x00000002
+#define DEBUG_OUTTYPE_VERBOSE 0x00000004
+#define DEBUG_OUTTYPE_COMPACT_OUTPUT 0x00000008
+#define DEBUG_OUTTYPE_RECURSION_LEVEL(Max) (((Max) & 0xf) << 4)
+#define DEBUG_OUTTYPE_ADDRESS_OF_FIELD 0x00010000
+#define DEBUG_OUTTYPE_ADDRESS_AT_END 0x00020000
+#define DEBUG_OUTTYPE_BLOCK_RECURSE 0x00200000
+
+// FindSourceFile flags.
+#define DEBUG_FIND_SOURCE_DEFAULT 0x00000000
+// Returns fully-qualified paths only. If this
+// is not set the path returned may be relative.
+#define DEBUG_FIND_SOURCE_FULL_PATH 0x00000001
+// Scans all the path elements for a match and
+// returns the one that has the most similarity
+// between the given file and the matching element.
+#define DEBUG_FIND_SOURCE_BEST_MATCH 0x00000002
+// Do not search source server paths.
+#define DEBUG_FIND_SOURCE_NO_SRCSRV 0x00000004
+// Restrict FindSourceFileAndToken to token lookup only.
+#define DEBUG_FIND_SOURCE_TOKEN_LOOKUP 0x00000008
+
+// A special value marking an offset that should not
+// be treated as a valid offset. This is only used
+// in special situations where it is unlikely that
+// this value would be a valid offset.
+#define DEBUG_INVALID_OFFSET ((ULONG64)-1)
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbols
+DECLARE_INTERFACE_(IDebugSymbols, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbols.
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ STDMETHOD(GetSymbolOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ STDMETHOD(GetNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ // A symbol name may not be unique, particularly
+ // when overloaded functions exist which all
+ // have the same name. If GetOffsetByName
+ // finds multiple matches for the name it
+ // can return any one of them. In that
+ // case it will return S_FALSE to indicate
+ // that ambiguity was arbitrarily resolved.
+ // A caller can then use SearchSymbols to
+ // find all of the matches if it wishes to
+ // perform different disambiguation.
+ STDMETHOD(GetOffsetByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ // GetNearNameByOffset returns symbols
+ // located near the symbol closest to
+ // to the offset, such as the previous
+ // or next symbol. If Delta is zero it
+ // operates identically to GetNameByOffset.
+ // If Delta is nonzero and such a symbol
+ // does not exist an error is returned.
+ // The next symbol, if one exists, will
+ // always have a higher offset than the
+ // input offset so the displacement is
+ // always negative. The situation is
+ // reversed for the previous symbol.
+ STDMETHOD(GetNearNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ // Enumerates the engines list of modules
+ // loaded for the current process. This may
+ // or may not match the system module list
+ // for the process. Reload can be used to
+ // synchronize the engines list with the system
+ // if necessary.
+ // Some sessions also track recently unloaded
+ // code modules for help in analyzing failures
+ // where an attempt is made to call unloaded code.
+ // These modules are indexed after the loaded
+ // modules.
+ STDMETHOD(GetNumberModules)(
+ THIS_
+ __out PULONG Loaded,
+ __out PULONG Unloaded
+ ) PURE;
+ STDMETHOD(GetModuleByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Base
+ ) PURE;
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ STDMETHOD(GetModuleNames)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(ImageNameBufferSize) PSTR ImageNameBuffer,
+ __in ULONG ImageNameBufferSize,
+ __out_opt PULONG ImageNameSize,
+ __out_ecount_opt(ModuleNameBufferSize) PSTR ModuleNameBuffer,
+ __in ULONG ModuleNameBufferSize,
+ __out_opt PULONG ModuleNameSize,
+ __out_ecount_opt(LoadedImageNameBufferSize) PSTR LoadedImageNameBuffer,
+ __in ULONG LoadedImageNameBufferSize,
+ __out_opt PULONG LoadedImageNameSize
+ ) PURE;
+ STDMETHOD(GetModuleParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG64 Bases,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_MODULE_PARAMETERS Params
+ ) PURE;
+ // Looks up the module from a <Module>!<Symbol>
+ // string.
+ STDMETHOD(GetSymbolModule)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ // Returns the string name of a type.
+ STDMETHOD(GetTypeName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeId)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetTypeSize)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out PULONG Size
+ ) PURE;
+ // Given a type which can contain members
+ // this method returns the offset of a
+ // particular member within the type.
+ // TypeId should give the container type ID
+ // and Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeId)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+ // As with GetOffsetByName a symbol's
+ // name may be ambiguous. GetOffsetTypeId
+ // returns the type for the symbol closest
+ // to the given offset and can be used
+ // to avoid ambiguity.
+ STDMETHOD(GetOffsetTypeId)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ // Helpers for virtual and physical data
+ // which combine creation of a location with
+ // the actual operation.
+ STDMETHOD(ReadTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataVirtual)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ReadTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataPhysical)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+
+ // Function arguments and scope block symbols
+ // can be retrieved relative to currently
+ // executing code. A caller can provide just
+ // a code offset for scoping purposes and look
+ // up names or the caller can provide a full frame
+ // and look up actual values. The values for
+ // scoped symbols are best-guess and may or may not
+ // be accurate depending on program optimizations,
+ // the machine architecture, the current point
+ // in the programs execution and so on.
+ // A caller can also provide a complete register
+ // context for setting a scope to a previous
+ // machine state such as a context saved for
+ // an exception. Usually this isnt necessary
+ // and the current register context is used.
+ STDMETHOD(GetScope)(
+ THIS_
+ __out_opt PULONG64 InstructionOffset,
+ __out_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __out_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // If ScopeFrame or ScopeContext is non-NULL then
+ // InstructionOffset is ignored.
+ // If ScopeContext is NULL the current
+ // register context is used.
+ // If the scope identified by the given
+ // information is the same as before
+ // SetScope returns S_OK. If the scope
+ // information changes, such as when the
+ // scope moves between functions or scope
+ // blocks, SetScope returns S_FALSE.
+ STDMETHOD(SetScope)(
+ THIS_
+ __in ULONG64 InstructionOffset,
+ __in_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __in_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // ResetScope clears the scope information
+ // for situations where scoped symbols
+ // mask global symbols or when resetting
+ // from explicit information to the current
+ // information.
+ STDMETHOD(ResetScope)(
+ THIS
+ ) PURE;
+ // A scope symbol is tied to its particular
+ // scope and only is meaningful within the scope.
+ // The returned group can be updated by passing it back
+ // into the method for lower-cost
+ // incremental updates when stepping.
+ STDMETHOD(GetScopeSymbolGroup)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP Update,
+ __out PDEBUG_SYMBOL_GROUP* Symbols
+ ) PURE;
+
+ // Create a new symbol group.
+ STDMETHOD(CreateSymbolGroup)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP* Group
+ ) PURE;
+
+ // StartSymbolMatch matches symbol names
+ // against the given pattern using simple
+ // regular expressions. The search results
+ // are iterated through using GetNextSymbolMatch.
+ // When the caller is done examining results
+ // the match should be freed via EndSymbolMatch.
+ // If the match pattern contains a module name
+ // the search is restricted to a single module.
+ // Pattern matching is only done on symbol names,
+ // not module names.
+ // All active symbol match handles are invalidated
+ // when the set of loaded symbols changes.
+ STDMETHOD(StartSymbolMatch)(
+ THIS_
+ __in PCSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Buffer is NULL the match does not
+ // advance.
+ STDMETHOD(GetNextSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+ STDMETHOD(EndSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Reload)(
+ THIS_
+ __in PCSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Manipulate the path for executable images.
+ // Some dump files need to load executable images
+ // in order to resolve dump information. This
+ // path controls where the engine looks for
+ // images.
+ STDMETHOD(GetImagePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Path routines for source file location
+ // methods.
+ STDMETHOD(GetSourcePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ // Gets the nth part of the source path.
+ STDMETHOD(GetSourcePathElement)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+ // Uses the given file path and the source path
+ // information to try and locate an existing file.
+ // The given file path is merged with elements
+ // of the source path and checked for existence.
+ // If a match is found the element used is returned.
+ // A starting element can be specified to restrict
+ // the search to a subset of the path elements;
+ // this can be useful when checking for multiple
+ // matches along the source path.
+ // The returned element can be 1, indicating
+ // the file was found directly and not on the path.
+ STDMETHOD(FindSourceFile)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ // Retrieves all the line offset information
+ // for a particular source file. Buffer is
+ // first intialized to DEBUG_INVALID_OFFSET for
+ // every entry. Then for each piece of line
+ // symbol information Buffer[Line] set to
+ // Lines offset. This produces a per-line
+ // map of the offsets for the lines of the
+ // given file. Line numbers are decremented
+ // for the map so Buffer[0] contains the offset
+ // for line number 1.
+ // If there is no line information at all for
+ // the given file the method fails rather
+ // than returning a map of invalid offsets.
+ STDMETHOD(GetSourceFileLineOffsets)(
+ THIS_
+ __in PCSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+};
+
+//
+// GetModuleNameString strings.
+//
+
+#define DEBUG_MODNAME_IMAGE 0x00000000
+#define DEBUG_MODNAME_MODULE 0x00000001
+#define DEBUG_MODNAME_LOADED_IMAGE 0x00000002
+#define DEBUG_MODNAME_SYMBOL_FILE 0x00000003
+#define DEBUG_MODNAME_MAPPED_IMAGE 0x00000004
+
+//
+// Type options, used with Get/SetTypeOptions.
+//
+
+// Display PUSHORT and USHORT arrays in Unicode.
+#define DEBUG_TYPEOPTS_UNICODE_DISPLAY 0x00000001
+// Display LONG types in default base instead of decimal.
+#define DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY 0x00000002
+// Display integer types in default base instead of decimal.
+#define DEBUG_TYPEOPTS_FORCERADIX_OUTPUT 0x00000004
+// Search for the type/symbol with largest size when
+// multiple type/symbol match for a given name
+#define DEBUG_TYPEOPTS_MATCH_MAXSIZE 0x00000008
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbols2
+DECLARE_INTERFACE_(IDebugSymbols2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbols.
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ STDMETHOD(GetSymbolOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ STDMETHOD(GetNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ // A symbol name may not be unique, particularly
+ // when overloaded functions exist which all
+ // have the same name. If GetOffsetByName
+ // finds multiple matches for the name it
+ // can return any one of them. In that
+ // case it will return S_FALSE to indicate
+ // that ambiguity was arbitrarily resolved.
+ // A caller can then use SearchSymbols to
+ // find all of the matches if it wishes to
+ // perform different disambiguation.
+ STDMETHOD(GetOffsetByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ // GetNearNameByOffset returns symbols
+ // located near the symbol closest to
+ // to the offset, such as the previous
+ // or next symbol. If Delta is zero it
+ // operates identically to GetNameByOffset.
+ // If Delta is nonzero and such a symbol
+ // does not exist an error is returned.
+ // The next symbol, if one exists, will
+ // always have a higher offset than the
+ // input offset so the displacement is
+ // always negative. The situation is
+ // reversed for the previous symbol.
+ STDMETHOD(GetNearNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ // Enumerates the engines list of modules
+ // loaded for the current process. This may
+ // or may not match the system module list
+ // for the process. Reload can be used to
+ // synchronize the engines list with the system
+ // if necessary.
+ // Some sessions also track recently unloaded
+ // code modules for help in analyzing failures
+ // where an attempt is made to call unloaded code.
+ // These modules are indexed after the loaded
+ // modules.
+ STDMETHOD(GetNumberModules)(
+ THIS_
+ __out PULONG Loaded,
+ __out PULONG Unloaded
+ ) PURE;
+ STDMETHOD(GetModuleByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Base
+ ) PURE;
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ STDMETHOD(GetModuleNames)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(ImageNameBufferSize) PSTR ImageNameBuffer,
+ __in ULONG ImageNameBufferSize,
+ __out_opt PULONG ImageNameSize,
+ __out_ecount_opt(ModuleNameBufferSize) PSTR ModuleNameBuffer,
+ __in ULONG ModuleNameBufferSize,
+ __out_opt PULONG ModuleNameSize,
+ __out_ecount_opt(LoadedImageNameBufferSize) PSTR LoadedImageNameBuffer,
+ __in ULONG LoadedImageNameBufferSize,
+ __out_opt PULONG LoadedImageNameSize
+ ) PURE;
+ STDMETHOD(GetModuleParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG64 Bases,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_MODULE_PARAMETERS Params
+ ) PURE;
+ // Looks up the module from a <Module>!<Symbol>
+ // string.
+ STDMETHOD(GetSymbolModule)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ // Returns the string name of a type.
+ STDMETHOD(GetTypeName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeId)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetTypeSize)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out PULONG Size
+ ) PURE;
+ // Given a type which can contain members
+ // this method returns the offset of a
+ // particular member within the type.
+ // TypeId should give the container type ID
+ // and Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeId)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+ // As with GetOffsetByName a symbol's
+ // name may be ambiguous. GetOffsetTypeId
+ // returns the type for the symbol closest
+ // to the given offset and can be used
+ // to avoid ambiguity.
+ STDMETHOD(GetOffsetTypeId)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ // Helpers for virtual and physical data
+ // which combine creation of a location with
+ // the actual operation.
+ STDMETHOD(ReadTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataVirtual)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ReadTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataPhysical)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+
+ // Function arguments and scope block symbols
+ // can be retrieved relative to currently
+ // executing code. A caller can provide just
+ // a code offset for scoping purposes and look
+ // up names or the caller can provide a full frame
+ // and look up actual values. The values for
+ // scoped symbols are best-guess and may or may not
+ // be accurate depending on program optimizations,
+ // the machine architecture, the current point
+ // in the programs execution and so on.
+ // A caller can also provide a complete register
+ // context for setting a scope to a previous
+ // machine state such as a context saved for
+ // an exception. Usually this isnt necessary
+ // and the current register context is used.
+ STDMETHOD(GetScope)(
+ THIS_
+ __out_opt PULONG64 InstructionOffset,
+ __out_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __out_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // If ScopeFrame or ScopeContext is non-NULL then
+ // InstructionOffset is ignored.
+ // If ScopeContext is NULL the current
+ // register context is used.
+ // If the scope identified by the given
+ // information is the same as before
+ // SetScope returns S_OK. If the scope
+ // information changes, such as when the
+ // scope moves between functions or scope
+ // blocks, SetScope returns S_FALSE.
+ STDMETHOD(SetScope)(
+ THIS_
+ __in ULONG64 InstructionOffset,
+ __in_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __in_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // ResetScope clears the scope information
+ // for situations where scoped symbols
+ // mask global symbols or when resetting
+ // from explicit information to the current
+ // information.
+ STDMETHOD(ResetScope)(
+ THIS
+ ) PURE;
+ // A scope symbol is tied to its particular
+ // scope and only is meaningful within the scope.
+ // The returned group can be updated by passing it back
+ // into the method for lower-cost
+ // incremental updates when stepping.
+ STDMETHOD(GetScopeSymbolGroup)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP Update,
+ __out PDEBUG_SYMBOL_GROUP* Symbols
+ ) PURE;
+
+ // Create a new symbol group.
+ STDMETHOD(CreateSymbolGroup)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP* Group
+ ) PURE;
+
+ // StartSymbolMatch matches symbol names
+ // against the given pattern using simple
+ // regular expressions. The search results
+ // are iterated through using GetNextSymbolMatch.
+ // When the caller is done examining results
+ // the match should be freed via EndSymbolMatch.
+ // If the match pattern contains a module name
+ // the search is restricted to a single module.
+ // Pattern matching is only done on symbol names,
+ // not module names.
+ // All active symbol match handles are invalidated
+ // when the set of loaded symbols changes.
+ STDMETHOD(StartSymbolMatch)(
+ THIS_
+ __in PCSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Buffer is NULL the match does not
+ // advance.
+ STDMETHOD(GetNextSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+ STDMETHOD(EndSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Reload)(
+ THIS_
+ __in PCSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Manipulate the path for executable images.
+ // Some dump files need to load executable images
+ // in order to resolve dump information. This
+ // path controls where the engine looks for
+ // images.
+ STDMETHOD(GetImagePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Path routines for source file location
+ // methods.
+ STDMETHOD(GetSourcePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ // Gets the nth part of the source path.
+ STDMETHOD(GetSourcePathElement)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+ // Uses the given file path and the source path
+ // information to try and locate an existing file.
+ // The given file path is merged with elements
+ // of the source path and checked for existence.
+ // If a match is found the element used is returned.
+ // A starting element can be specified to restrict
+ // the search to a subset of the path elements;
+ // this can be useful when checking for multiple
+ // matches along the source path.
+ // The returned element can be 1, indicating
+ // the file was found directly and not on the path.
+ STDMETHOD(FindSourceFile)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ // Retrieves all the line offset information
+ // for a particular source file. Buffer is
+ // first intialized to DEBUG_INVALID_OFFSET for
+ // every entry. Then for each piece of line
+ // symbol information Buffer[Line] set to
+ // Lines offset. This produces a per-line
+ // map of the offsets for the lines of the
+ // given file. Line numbers are decremented
+ // for the map so Buffer[0] contains the offset
+ // for line number 1.
+ // If there is no line information at all for
+ // the given file the method fails rather
+ // than returning a map of invalid offsets.
+ STDMETHOD(GetSourceFileLineOffsets)(
+ THIS_
+ __in PCSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+
+ // IDebugSymbols2.
+
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // Item is specified as in VerQueryValue.
+ // Module version information is only
+ // available for loaded modules and may
+ // not be available in all debug sessions.
+ STDMETHOD(GetModuleVersionInformation)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __in PCSTR Item,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG VerInfoSize
+ ) PURE;
+ // Retrieves any available module name string
+ // such as module name or symbol file name.
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // If symbols are deferred an error will
+ // be returned.
+ // E_NOINTERFACE may be returned, indicating
+ // no information exists.
+ STDMETHOD(GetModuleNameString)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Returns the string name of a constant type.
+ STDMETHOD(GetConstantName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG64 Value,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Gets name of a field in a struct
+ // FieldNumber is 0 based index of field in a struct
+ STDMETHOD(GetFieldName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG FieldIndex,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Control options for typed values.
+ STDMETHOD(GetTypeOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+};
+
+//
+// GetModuleBy* flags.
+//
+
+// Scan all modules, loaded and unloaded.
+#define DEBUG_GETMOD_DEFAULT 0x00000000
+// Do not scan loaded modules.
+#define DEBUG_GETMOD_NO_LOADED_MODULES 0x00000001
+// Do not scan unloaded modules.
+#define DEBUG_GETMOD_NO_UNLOADED_MODULES 0x00000002
+
+//
+// AddSyntheticModule flags.
+//
+
+#define DEBUG_ADDSYNTHMOD_DEFAULT 0x00000000
+
+//
+// AddSyntheticSymbol flags.
+//
+
+#define DEBUG_ADDSYNTHSYM_DEFAULT 0x00000000
+
+//
+// OutputSymbolByOffset flags.
+//
+
+// Use the current debugger settings for symbol output.
+#define DEBUG_OUTSYM_DEFAULT 0x00000000
+// Always display the offset in addition to any symbol hit.
+#define DEBUG_OUTSYM_FORCE_OFFSET 0x00000001
+// Display source line information if found.
+#define DEBUG_OUTSYM_SOURCE_LINE 0x00000002
+// Output symbol hits that don't exactly match.
+#define DEBUG_OUTSYM_ALLOW_DISPLACEMENT 0x00000004
+
+//
+// GetFunctionEntryByOffset flags.
+//
+
+#define DEBUG_GETFNENT_DEFAULT 0x00000000
+// The engine provides artificial entries for well-known
+// cases. This flag limits the entry search to only
+// the raw entries and disables artificial entry lookup.
+#define DEBUG_GETFNENT_RAW_ENTRY_ONLY 0x00000001
+
+typedef struct _DEBUG_MODULE_AND_ID
+{
+ ULONG64 ModuleBase;
+ ULONG64 Id;
+} DEBUG_MODULE_AND_ID, *PDEBUG_MODULE_AND_ID;
+
+#define DEBUG_SOURCE_IS_STATEMENT 0x00000001
+
+//
+// GetSourceEntriesByLine flags.
+//
+
+#define DEBUG_GSEL_DEFAULT 0x00000000
+// Do not allow any extra symbols to load during the search.
+#define DEBUG_GSEL_NO_SYMBOL_LOADS 0x00000001
+// Allow source hits with lower line numbers.
+#define DEBUG_GSEL_ALLOW_LOWER 0x00000002
+// Allow source hits with higher line numbers.
+#define DEBUG_GSEL_ALLOW_HIGHER 0x00000004
+// Only return the nearest hits.
+#define DEBUG_GSEL_NEAREST_ONLY 0x00000008
+
+typedef struct _DEBUG_SYMBOL_SOURCE_ENTRY
+{
+ ULONG64 ModuleBase;
+ ULONG64 Offset;
+ ULONG64 FileNameId;
+ ULONG64 EngineInternal;
+ ULONG Size;
+ ULONG Flags;
+ ULONG FileNameSize;
+ // Line numbers are one-based.
+ // May be DEBUG_ANY_ID if unknown.
+ ULONG StartLine;
+ ULONG EndLine;
+ // Column numbers are one-based byte indices.
+ // May be DEBUG_ANY_ID if unknown.
+ ULONG StartColumn;
+ ULONG EndColumn;
+ ULONG Reserved;
+} DEBUG_SYMBOL_SOURCE_ENTRY, *PDEBUG_SYMBOL_SOURCE_ENTRY;
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbols3
+DECLARE_INTERFACE_(IDebugSymbols3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbols.
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ STDMETHOD(GetSymbolOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ STDMETHOD(GetNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ // A symbol name may not be unique, particularly
+ // when overloaded functions exist which all
+ // have the same name. If GetOffsetByName
+ // finds multiple matches for the name it
+ // can return any one of them. In that
+ // case it will return S_FALSE to indicate
+ // that ambiguity was arbitrarily resolved.
+ // A caller can then use SearchSymbols to
+ // find all of the matches if it wishes to
+ // perform different disambiguation.
+ STDMETHOD(GetOffsetByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ // GetNearNameByOffset returns symbols
+ // located near the symbol closest to
+ // to the offset, such as the previous
+ // or next symbol. If Delta is zero it
+ // operates identically to GetNameByOffset.
+ // If Delta is nonzero and such a symbol
+ // does not exist an error is returned.
+ // The next symbol, if one exists, will
+ // always have a higher offset than the
+ // input offset so the displacement is
+ // always negative. The situation is
+ // reversed for the previous symbol.
+ STDMETHOD(GetNearNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ // Enumerates the engines list of modules
+ // loaded for the current process. This may
+ // or may not match the system module list
+ // for the process. Reload can be used to
+ // synchronize the engines list with the system
+ // if necessary.
+ // Some sessions also track recently unloaded
+ // code modules for help in analyzing failures
+ // where an attempt is made to call unloaded code.
+ // These modules are indexed after the loaded
+ // modules.
+ STDMETHOD(GetNumberModules)(
+ THIS_
+ __out PULONG Loaded,
+ __out PULONG Unloaded
+ ) PURE;
+ STDMETHOD(GetModuleByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Base
+ ) PURE;
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ STDMETHOD(GetModuleNames)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(ImageNameBufferSize) PSTR ImageNameBuffer,
+ __in ULONG ImageNameBufferSize,
+ __out_opt PULONG ImageNameSize,
+ __out_ecount_opt(ModuleNameBufferSize) PSTR ModuleNameBuffer,
+ __in ULONG ModuleNameBufferSize,
+ __out_opt PULONG ModuleNameSize,
+ __out_ecount_opt(LoadedImageNameBufferSize) PSTR LoadedImageNameBuffer,
+ __in ULONG LoadedImageNameBufferSize,
+ __out_opt PULONG LoadedImageNameSize
+ ) PURE;
+ STDMETHOD(GetModuleParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG64 Bases,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_MODULE_PARAMETERS Params
+ ) PURE;
+ // Looks up the module from a <Module>!<Symbol>
+ // string.
+ STDMETHOD(GetSymbolModule)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ // Returns the string name of a type.
+ STDMETHOD(GetTypeName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeId)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetTypeSize)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out PULONG Size
+ ) PURE;
+ // Given a type which can contain members
+ // this method returns the offset of a
+ // particular member within the type.
+ // TypeId should give the container type ID
+ // and Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeId)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+ // As with GetOffsetByName a symbol's
+ // name may be ambiguous. GetOffsetTypeId
+ // returns the type for the symbol closest
+ // to the given offset and can be used
+ // to avoid ambiguity.
+ STDMETHOD(GetOffsetTypeId)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ // Helpers for virtual and physical data
+ // which combine creation of a location with
+ // the actual operation.
+ STDMETHOD(ReadTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataVirtual)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ReadTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataPhysical)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+
+ // Function arguments and scope block symbols
+ // can be retrieved relative to currently
+ // executing code. A caller can provide just
+ // a code offset for scoping purposes and look
+ // up names or the caller can provide a full frame
+ // and look up actual values. The values for
+ // scoped symbols are best-guess and may or may not
+ // be accurate depending on program optimizations,
+ // the machine architecture, the current point
+ // in the programs execution and so on.
+ // A caller can also provide a complete register
+ // context for setting a scope to a previous
+ // machine state such as a context saved for
+ // an exception. Usually this isnt necessary
+ // and the current register context is used.
+ STDMETHOD(GetScope)(
+ THIS_
+ __out_opt PULONG64 InstructionOffset,
+ __out_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __out_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // If ScopeFrame or ScopeContext is non-NULL then
+ // InstructionOffset is ignored.
+ // If ScopeContext is NULL the current
+ // register context is used.
+ // If the scope identified by the given
+ // information is the same as before
+ // SetScope returns S_OK. If the scope
+ // information changes, such as when the
+ // scope moves between functions or scope
+ // blocks, SetScope returns S_FALSE.
+ STDMETHOD(SetScope)(
+ THIS_
+ __in ULONG64 InstructionOffset,
+ __in_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __in_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // ResetScope clears the scope information
+ // for situations where scoped symbols
+ // mask global symbols or when resetting
+ // from explicit information to the current
+ // information.
+ STDMETHOD(ResetScope)(
+ THIS
+ ) PURE;
+ // A scope symbol is tied to its particular
+ // scope and only is meaningful within the scope.
+ // The returned group can be updated by passing it back
+ // into the method for lower-cost
+ // incremental updates when stepping.
+ STDMETHOD(GetScopeSymbolGroup)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP Update,
+ __out PDEBUG_SYMBOL_GROUP* Symbols
+ ) PURE;
+
+ // Create a new symbol group.
+ STDMETHOD(CreateSymbolGroup)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP* Group
+ ) PURE;
+
+ // StartSymbolMatch matches symbol names
+ // against the given pattern using simple
+ // regular expressions. The search results
+ // are iterated through using GetNextSymbolMatch.
+ // When the caller is done examining results
+ // the match should be freed via EndSymbolMatch.
+ // If the match pattern contains a module name
+ // the search is restricted to a single module.
+ // Pattern matching is only done on symbol names,
+ // not module names.
+ // All active symbol match handles are invalidated
+ // when the set of loaded symbols changes.
+ STDMETHOD(StartSymbolMatch)(
+ THIS_
+ __in PCSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Buffer is NULL the match does not
+ // advance.
+ STDMETHOD(GetNextSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+ STDMETHOD(EndSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Reload)(
+ THIS_
+ __in PCSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Manipulate the path for executable images.
+ // Some dump files need to load executable images
+ // in order to resolve dump information. This
+ // path controls where the engine looks for
+ // images.
+ STDMETHOD(GetImagePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Path routines for source file location
+ // methods.
+ STDMETHOD(GetSourcePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ // Gets the nth part of the source path.
+ STDMETHOD(GetSourcePathElement)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+ // Uses the given file path and the source path
+ // information to try and locate an existing file.
+ // The given file path is merged with elements
+ // of the source path and checked for existence.
+ // If a match is found the element used is returned.
+ // A starting element can be specified to restrict
+ // the search to a subset of the path elements;
+ // this can be useful when checking for multiple
+ // matches along the source path.
+ // The returned element can be 1, indicating
+ // the file was found directly and not on the path.
+ STDMETHOD(FindSourceFile)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ // Retrieves all the line offset information
+ // for a particular source file. Buffer is
+ // first intialized to DEBUG_INVALID_OFFSET for
+ // every entry. Then for each piece of line
+ // symbol information Buffer[Line] set to
+ // Lines offset. This produces a per-line
+ // map of the offsets for the lines of the
+ // given file. Line numbers are decremented
+ // for the map so Buffer[0] contains the offset
+ // for line number 1.
+ // If there is no line information at all for
+ // the given file the method fails rather
+ // than returning a map of invalid offsets.
+ STDMETHOD(GetSourceFileLineOffsets)(
+ THIS_
+ __in PCSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+
+ // IDebugSymbols2.
+
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // Item is specified as in VerQueryValue.
+ // Module version information is only
+ // available for loaded modules and may
+ // not be available in all debug sessions.
+ STDMETHOD(GetModuleVersionInformation)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __in PCSTR Item,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG VerInfoSize
+ ) PURE;
+ // Retrieves any available module name string
+ // such as module name or symbol file name.
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // If symbols are deferred an error will
+ // be returned.
+ // E_NOINTERFACE may be returned, indicating
+ // no information exists.
+ STDMETHOD(GetModuleNameString)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Returns the string name of a constant type.
+ STDMETHOD(GetConstantName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG64 Value,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Gets name of a field in a struct
+ // FieldNumber is 0 based index of field in a struct
+ STDMETHOD(GetFieldName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG FieldIndex,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Control options for typed values.
+ STDMETHOD(GetTypeOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // IDebugSymbols3.
+
+ STDMETHOD(GetNameByOffsetWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByNameWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetNearNameByOffsetWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffsetWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PWSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLineWide)(
+ THIS_
+ __in ULONG Line,
+ __in PCWSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ STDMETHOD(GetModuleByModuleNameWide)(
+ THIS_
+ __in PCWSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ STDMETHOD(GetSymbolModuleWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ STDMETHOD(GetTypeNameWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeIdWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCWSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetFieldOffsetWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCWSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeIdWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ STDMETHOD(GetScopeSymbolGroup2)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP2 Update,
+ __out PDEBUG_SYMBOL_GROUP2* Symbols
+ ) PURE;
+
+ STDMETHOD(CreateSymbolGroup2)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP2* Group
+ ) PURE;
+
+ STDMETHOD(StartSymbolMatchWide)(
+ THIS_
+ __in PCWSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetNextSymbolMatchWide)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+
+ STDMETHOD(ReloadWide)(
+ THIS_
+ __in PCWSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPathWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPathWide)(
+ THIS_
+ __in PCWSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPathWide)(
+ THIS_
+ __in PCWSTR Addition
+ ) PURE;
+
+ STDMETHOD(GetImagePathWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePathWide)(
+ THIS_
+ __in PCWSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePathWide)(
+ THIS_
+ __in PCWSTR Addition
+ ) PURE;
+
+ STDMETHOD(GetSourcePathWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(GetSourcePathElementWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePathWide)(
+ THIS_
+ __in PCWSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePathWide)(
+ THIS_
+ __in PCWSTR Addition
+ ) PURE;
+ STDMETHOD(FindSourceFileWide)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCWSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ STDMETHOD(GetSourceFileLineOffsetsWide)(
+ THIS_
+ __in PCWSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+
+ STDMETHOD(GetModuleVersionInformationWide)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __in PCWSTR Item,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG VerInfoSize
+ ) PURE;
+ STDMETHOD(GetModuleNameStringWide)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ STDMETHOD(GetConstantNameWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG64 Value,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ STDMETHOD(GetFieldNameWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG FieldIndex,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Returns S_OK if the engine is using managed
+ // debugging support when retriving information
+ // for the given module. This can be expensive
+ // to check.
+ STDMETHOD(IsManagedModule)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base
+ ) PURE;
+
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName2)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __in ULONG Flags,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ STDMETHOD(GetModuleByModuleName2Wide)(
+ THIS_
+ __in PCWSTR Name,
+ __in ULONG StartIndex,
+ __in ULONG Flags,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __in ULONG Flags,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+
+ // A caller can create artificial loaded modules in
+ // the engine's module list if desired.
+ // These modules only serve as names for
+ // a region of addresses. They cannot have
+ // real symbols loaded for them; if that
+ // is desired Reload can be used with explicit
+ // parameters to create a true module entry.
+ // The region must not be in use by any other
+ // module.
+ // A general reload will discard any synthetic modules.
+ STDMETHOD(AddSyntheticModule)(
+ THIS_
+ __in ULONG64 Base,
+ __in ULONG Size,
+ __in PCSTR ImagePath,
+ __in PCSTR ModuleName,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(AddSyntheticModuleWide)(
+ THIS_
+ __in ULONG64 Base,
+ __in ULONG Size,
+ __in PCWSTR ImagePath,
+ __in PCWSTR ModuleName,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(RemoveSyntheticModule)(
+ THIS_
+ __in ULONG64 Base
+ ) PURE;
+
+ // Modify the current frame used for scoping.
+ // This is equivalent to the '.frame' command.
+ STDMETHOD(GetCurrentScopeFrameIndex)(
+ THIS_
+ __out PULONG Index
+ ) PURE;
+ STDMETHOD(SetScopeFrameByIndex)(
+ THIS_
+ __in ULONG Index
+ ) PURE;
+
+ // Recovers JIT_DEBUG_INFO information at the given
+ // address from the debuggee and sets current
+ // debugger scope context from it.
+ // Equivalent to '.jdinfo' command.
+ STDMETHOD(SetScopeFromJitDebugInfo)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 InfoOffset
+ ) PURE;
+
+ // Switches the current debugger scope to
+ // the stored event information.
+ // Equivalent to the '.ecxr' command.
+ STDMETHOD(SetScopeFromStoredEvent)(
+ THIS
+ ) PURE;
+
+ // Takes the first symbol hit and outputs it.
+ // Controlled with DEBUG_OUTSYM_* flags.
+ STDMETHOD(OutputSymbolByOffset)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in ULONG64 Offset
+ ) PURE;
+
+ // Function entry information for a particular
+ // piece of code can be retrieved by this method.
+ // The actual data returned is system-dependent.
+ STDMETHOD(GetFunctionEntryByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BufferNeeded
+ ) PURE;
+
+ // Given a type which can contain members
+ // this method returns the type ID and offset of a
+ // particular member within the type.
+ // Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldTypeAndOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG ContainerTypeId,
+ __in PCSTR Field,
+ __out_opt PULONG FieldTypeId,
+ __out_opt PULONG Offset
+ ) PURE;
+ STDMETHOD(GetFieldTypeAndOffsetWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG ContainerTypeId,
+ __in PCWSTR Field,
+ __out_opt PULONG FieldTypeId,
+ __out_opt PULONG Offset
+ ) PURE;
+
+ // Artificial symbols can be created in any
+ // existing module as a way to name an address.
+ // The address must not already have symbol
+ // information.
+ // A reload will discard synthetic symbols
+ // for all address regions reloaded.
+ STDMETHOD(AddSyntheticSymbol)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Size,
+ __in PCSTR Name,
+ __in ULONG Flags,
+ __out_opt PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+ STDMETHOD(AddSyntheticSymbolWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Size,
+ __in PCWSTR Name,
+ __in ULONG Flags,
+ __out_opt PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+ STDMETHOD(RemoveSyntheticSymbol)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+
+ // The following methods can return multiple
+ // hits for symbol lookups to allow for all
+ // possible hits to be returned.
+ STDMETHOD(GetSymbolEntriesByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(IdsCount) PDEBUG_MODULE_AND_ID Ids,
+ __out_ecount_opt(IdsCount) PULONG64 Displacements,
+ __in ULONG IdsCount,
+ __out_opt PULONG Entries
+ ) PURE;
+ STDMETHOD(GetSymbolEntriesByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __in ULONG Flags,
+ __out_ecount_opt(IdsCount) PDEBUG_MODULE_AND_ID Ids,
+ __in ULONG IdsCount,
+ __out_opt PULONG Entries
+ ) PURE;
+ STDMETHOD(GetSymbolEntriesByNameWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __in ULONG Flags,
+ __out_ecount_opt(IdsCount) PDEBUG_MODULE_AND_ID Ids,
+ __in ULONG IdsCount,
+ __out_opt PULONG Entries
+ ) PURE;
+ // Symbol lookup by managed metadata token.
+ STDMETHOD(GetSymbolEntryByToken)(
+ THIS_
+ __in ULONG64 ModuleBase,
+ __in ULONG Token,
+ __out PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+
+ // Retrieves full symbol entry information from an ID.
+ STDMETHOD(GetSymbolEntryInformation)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __out PDEBUG_SYMBOL_ENTRY Info
+ ) PURE;
+ STDMETHOD(GetSymbolEntryString)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(GetSymbolEntryStringWide)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ // Returns all known memory regions associated
+ // with the given symbol. Simple symbols will
+ // have a single region starting from their base.
+ // More complicated regions, such as functions
+ // with multiple code areas, can have an arbitrarily
+ // large number of regions.
+ // The quality of information returned is highly
+ // dependent on the symbolic information availble.
+ STDMETHOD(GetSymbolEntryOffsetRegions)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __in ULONG Flags,
+ __out_ecount_opt(RegionsCount) PDEBUG_OFFSET_REGION Regions,
+ __in ULONG RegionsCount,
+ __out_opt PULONG RegionsAvail
+ ) PURE;
+
+ // This method allows navigating within the
+ // symbol entry hierarchy.
+ STDMETHOD(GetSymbolEntryBySymbolEntry)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID FromId,
+ __in ULONG Flags,
+ __out PDEBUG_MODULE_AND_ID ToId
+ ) PURE;
+
+ // The following methods can return multiple
+ // hits for source lookups to allow for all
+ // possible hits to be returned.
+ STDMETHOD(GetSourceEntriesByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(EntriesCount) PDEBUG_SYMBOL_SOURCE_ENTRY Entries,
+ __in ULONG EntriesCount,
+ __out_opt PULONG EntriesAvail
+ ) PURE;
+ STDMETHOD(GetSourceEntriesByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_ecount_opt(EntriesCount) PDEBUG_SYMBOL_SOURCE_ENTRY Entries,
+ __in ULONG EntriesCount,
+ __out_opt PULONG EntriesAvail
+ ) PURE;
+ STDMETHOD(GetSourceEntriesByLineWide)(
+ THIS_
+ __in ULONG Line,
+ __in PCWSTR File,
+ __in ULONG Flags,
+ __out_ecount_opt(EntriesCount) PDEBUG_SYMBOL_SOURCE_ENTRY Entries,
+ __in ULONG EntriesCount,
+ __out_opt PULONG EntriesAvail
+ ) PURE;
+
+ STDMETHOD(GetSourceEntryString)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY Entry,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(GetSourceEntryStringWide)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY Entry,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ // Returns all known memory regions associated
+ // with the given source entry. As with
+ // GetSymbolEntryOffsetRegions the regions available
+ // are variable.
+ STDMETHOD(GetSourceEntryOffsetRegions)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY Entry,
+ __in ULONG Flags,
+ __out_ecount_opt(RegionsCount) PDEBUG_OFFSET_REGION Regions,
+ __in ULONG RegionsCount,
+ __out_opt PULONG RegionsAvail
+ ) PURE;
+
+ // This method allows navigating within the
+ // source entries.
+ STDMETHOD(GetSourceEntryBySourceEntry)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY FromEntry,
+ __in ULONG Flags,
+ __out PDEBUG_SYMBOL_SOURCE_ENTRY ToEntry
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugSystemObjects
+//
+//----------------------------------------------------------------------------
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects
+DECLARE_INTERFACE_(IDebugSystemObjects, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // Controls implicit thread used by the
+ // debug engine. The debuggers current
+ // thread is just a piece of data held
+ // by the debugger for calls which use
+ // thread-specific information. In those
+ // calls the debuggers current thread is used.
+ // The debuggers current thread is not related
+ // to any system thread attribute.
+ // IDs for threads are small integer IDs
+ // maintained by the engine. They are not
+ // related to system thread IDs.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread ID.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects2
+DECLARE_INTERFACE_(IDebugSystemObjects2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // Controls implicit thread used by the
+ // debug engine. The debuggers current
+ // thread is just a piece of data held
+ // by the debugger for calls which use
+ // thread-specific information. In those
+ // calls the debuggers current thread is used.
+ // The debuggers current thread is not related
+ // to any system thread attribute.
+ // IDs for threads are small integer IDs
+ // maintained by the engine. They are not
+ // related to system thread IDs.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread ID.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ // IDebugSystemObjects2.
+
+ // Return the number of seconds that the current
+ // process has been running.
+ STDMETHOD(GetCurrentProcessUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // During kernel sessions the debugger retrieves
+ // some information from the system thread and process
+ // running on the current processor. For example,
+ // the debugger will retrieve virtual memory translation
+ // information for when the debugger needs to
+ // carry out its own virtual to physical translations.
+ // Occasionally it can be interesting to perform
+ // similar operations but on a process which isnt
+ // currently running. The follow methods allow a caller
+ // to override the data offsets used by the debugger
+ // so that other system threads and processes can
+ // be used instead. These values are defaulted to
+ // the thread and process running on the current
+ // processor each time the debuggee executes or
+ // the current processor changes.
+ // The thread and process settings are independent so
+ // it is possible to refer to a thread in a process
+ // other than the current process and vice versa.
+ // Setting an offset of zero will reload the
+ // default value.
+ STDMETHOD(GetImplicitThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitThreadDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetImplicitProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitProcessDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects3
+DECLARE_INTERFACE_(IDebugSystemObjects3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // Controls implicit thread used by the
+ // debug engine. The debuggers current
+ // thread is just a piece of data held
+ // by the debugger for calls which use
+ // thread-specific information. In those
+ // calls the debuggers current thread is used.
+ // The debuggers current thread is not related
+ // to any system thread attribute.
+ // IDs for threads are small integer IDs
+ // maintained by the engine. They are not
+ // related to system thread IDs.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread ID.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ // IDebugSystemObjects2.
+
+ // Return the number of seconds that the current
+ // process has been running.
+ STDMETHOD(GetCurrentProcessUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // During kernel sessions the debugger retrieves
+ // some information from the system thread and process
+ // running on the current processor. For example,
+ // the debugger will retrieve virtual memory translation
+ // information for when the debugger needs to
+ // carry out its own virtual to physical translations.
+ // Occasionally it can be interesting to perform
+ // similar operations but on a process which isnt
+ // currently running. The follow methods allow a caller
+ // to override the data offsets used by the debugger
+ // so that other system threads and processes can
+ // be used instead. These values are defaulted to
+ // the thread and process running on the current
+ // processor each time the debuggee executes or
+ // the current processor changes.
+ // The thread and process settings are independent so
+ // it is possible to refer to a thread in a process
+ // other than the current process and vice versa.
+ // Setting an offset of zero will reload the
+ // default value.
+ STDMETHOD(GetImplicitThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitThreadDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetImplicitProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitProcessDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // IDebugSystemObjects3.
+
+ STDMETHOD(GetEventSystem)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ STDMETHOD(GetCurrentSystemId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentSystemId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ STDMETHOD(GetNumberSystems)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSystemIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Ids
+ ) PURE;
+ STDMETHOD(GetTotalNumberThreadsAndProcesses)(
+ THIS_
+ __out PULONG TotalThreads,
+ __out PULONG TotalProcesses,
+ __out PULONG LargestProcessThreads,
+ __out PULONG LargestSystemThreads,
+ __out PULONG LargestSystemProcesses
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServer)(
+ THIS_
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(GetSystemByServer)(
+ THIS_
+ __in ULONG64 Server,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServerName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects4
+DECLARE_INTERFACE_(IDebugSystemObjects4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // Controls implicit thread used by the
+ // debug engine. The debuggers current
+ // thread is just a piece of data held
+ // by the debugger for calls which use
+ // thread-specific information. In those
+ // calls the debuggers current thread is used.
+ // The debuggers current thread is not related
+ // to any system thread attribute.
+ // IDs for threads are small integer IDs
+ // maintained by the engine. They are not
+ // related to system thread IDs.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread ID.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ // IDebugSystemObjects2.
+
+ // Return the number of seconds that the current
+ // process has been running.
+ STDMETHOD(GetCurrentProcessUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // During kernel sessions the debugger retrieves
+ // some information from the system thread and process
+ // running on the current processor. For example,
+ // the debugger will retrieve virtual memory translation
+ // information for when the debugger needs to
+ // carry out its own virtual to physical translations.
+ // Occasionally it can be interesting to perform
+ // similar operations but on a process which isnt
+ // currently running. The follow methods allow a caller
+ // to override the data offsets used by the debugger
+ // so that other system threads and processes can
+ // be used instead. These values are defaulted to
+ // the thread and process running on the current
+ // processor each time the debuggee executes or
+ // the current processor changes.
+ // The thread and process settings are independent so
+ // it is possible to refer to a thread in a process
+ // other than the current process and vice versa.
+ // Setting an offset of zero will reload the
+ // default value.
+ STDMETHOD(GetImplicitThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitThreadDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetImplicitProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitProcessDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // IDebugSystemObjects3.
+
+ STDMETHOD(GetEventSystem)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ STDMETHOD(GetCurrentSystemId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentSystemId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ STDMETHOD(GetNumberSystems)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSystemIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Ids
+ ) PURE;
+ STDMETHOD(GetTotalNumberThreadsAndProcesses)(
+ THIS_
+ __out PULONG TotalThreads,
+ __out PULONG TotalProcesses,
+ __out PULONG LargestProcessThreads,
+ __out PULONG LargestSystemThreads,
+ __out PULONG LargestSystemProcesses
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServer)(
+ THIS_
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(GetSystemByServer)(
+ THIS_
+ __in ULONG64 Server,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServerName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // IDebugSystemObjects4.
+
+ STDMETHOD(GetCurrentProcessExecutableNameWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ STDMETHOD(GetCurrentSystemServerNameWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// Debugger/debuggee communication.
+//
+// A distinguished exception, DBG_COMMAND_EXCEPTION (0x40010009),
+// can be used by a debuggee to communicate with the debugger.
+// The arguments of the exception must be:
+// 1. Exception ID.
+// 2. Command code.
+// 3. Size of argument.
+// 4. Pointer to argument.
+//
+// The arguments depend on the command code.
+//
+//----------------------------------------------------------------------------
+
+#define DEBUG_COMMAND_EXCEPTION_ID 0xdbe00dbe
+
+// Invalid command code.
+#define DEBUG_CMDEX_INVALID 0x00000000
+
+//
+// The debugger can collect strings for display at the
+// next event. A debuggee can use this to register information
+// about a program situation before places where an event
+// may occur, such as a risky operation or assertion.
+// The strings are automatically flushed on the next
+// event continuation. Strings are kept on a per-thread basis.
+//
+// When adding, the argument is the string to add.
+// Reset has no arguments and clears all strings.
+//
+#define DEBUG_CMDEX_ADD_EVENT_STRING 0x00000001
+#define DEBUG_CMDEX_RESET_EVENT_STRINGS 0x00000002
+
+#ifndef DEBUG_NO_IMPLEMENTATION
+
+FORCEINLINE void
+DebugCommandException(ULONG Command, ULONG ArgSize, PVOID Arg)
+{
+ ULONG_PTR ExArgs[4];
+
+ ExArgs[0] = DEBUG_COMMAND_EXCEPTION_ID;
+ ExArgs[1] = Command;
+ ExArgs[2] = ArgSize;
+ ExArgs[3] = (ULONG_PTR)Arg;
+ RaiseException(DBG_COMMAND_EXCEPTION, 0, 4, ExArgs);
+}
+
+#endif // #ifndef DEBUG_NO_IMPLEMENTATION
+
+//----------------------------------------------------------------------------
+//
+// Extension callbacks.
+//
+//----------------------------------------------------------------------------
+
+// Returns a version with the major version in
+// the high word and the minor version in the low word.
+#define DEBUG_EXTENSION_VERSION(Major, Minor) \
+ ((((Major) & 0xffff) << 16) | ((Minor) & 0xffff))
+
+//
+// Descriptive flags returned from extension initialization.
+//
+
+// Extension has a !help command which can give
+// per-command help.
+#define DEBUG_EXTINIT_HAS_COMMAND_HELP 0x00000001
+
+// Initialization routine. Called once when the extension DLL
+// is loaded. Returns a version and returns flags detailing
+// overall qualities of the extension DLL.
+// A session may or may not be active at the time the DLL
+// is loaded so initialization routines should not expect
+// to be able to query session information.
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_INITIALIZE)
+ (__out PULONG Version, __out PULONG Flags);
+// Exit routine. Called once just before the extension DLL is
+// unloaded. As with initialization, a session may or
+// may not be active at the time of the call.
+typedef void (CALLBACK* PDEBUG_EXTENSION_UNINITIALIZE)
+ (void);
+
+// A debuggee has been discovered for the session. It
+// is not necessarily halted.
+#define DEBUG_NOTIFY_SESSION_ACTIVE 0x00000000
+// The session no longer has a debuggee.
+#define DEBUG_NOTIFY_SESSION_INACTIVE 0x00000001
+// The debuggee is halted and accessible.
+#define DEBUG_NOTIFY_SESSION_ACCESSIBLE 0x00000002
+// The debuggee is running or inaccessible.
+#define DEBUG_NOTIFY_SESSION_INACCESSIBLE 0x00000003
+
+typedef void (CALLBACK* PDEBUG_EXTENSION_NOTIFY)
+ (__in ULONG Notify, __in ULONG64 Argument);
+
+// A PDEBUG_EXTENSION_CALL function can return this code
+// to indicate that it was unable to handle the request
+// and that the search for an extension function should
+// continue down the extension DLL chain.
+// Taken from STATUS_VALIDATE_CONTINUE.
+#define DEBUG_EXTENSION_CONTINUE_SEARCH \
+ HRESULT_FROM_NT(0xC0000271L)
+
+// A PDEBUG_EXTENSION_CALL function can return this code
+// to indicate that the engine should unload and reload
+// the extension binary. This allows extensions to implement
+// auto-update functionality.
+#define DEBUG_EXTENSION_RELOAD_EXTENSION \
+ HRESULT_FROM_NT(0xC00000EEL)
+
+// Every routine in an extension DLL has the following prototype.
+// The extension may be called from multiple clients so it
+// should not cache the client value between calls.
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_CALL)
+ (__in PDEBUG_CLIENT Client, __in_opt PCSTR Args);
+
+//
+// KnownStructOutput[Ex] flags
+//
+
+// Return names of supported structs.
+#define DEBUG_KNOWN_STRUCT_GET_NAMES 1
+// Return value output for type.
+#define DEBUG_KNOWN_STRUCT_GET_SINGLE_LINE_OUTPUT 2
+// Return S_OK if suppressing type name.
+#define DEBUG_KNOWN_STRUCT_SUPPRESS_TYPE_NAME 3
+
+// Extensions may export this callback in order to dump structs that
+// are well known to them. The engine calls this to inject extension
+// output into dt's struct dump.
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_KNOWN_STRUCT)
+ (__in ULONG Flags,
+ __in ULONG64 Offset,
+ __in_opt PSTR TypeName,
+ __out_ecount_opt(*BufferChars) PSTR Buffer,
+ __inout_opt PULONG BufferChars);
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_KNOWN_STRUCT_EX)
+ (__in PDEBUG_CLIENT Client,
+ __in ULONG Flags,
+ __in ULONG64 Offset,
+ __in_opt PCSTR TypeName,
+ __out_ecount_opt(*BufferChars) PSTR Buffer,
+ __inout_opt PULONG BufferChars);
+
+// Backwards compatibility with old, incorrect name.
+typedef PDEBUG_EXTENSION_KNOWN_STRUCT PDEBUG_ENTENSION_KNOWNSTRUCT;
+
+//
+// Extensions can provide pseudo-register values that
+// operate similiarly to the debugger's built-in $teb, etc.
+//
+
+#define DEBUG_EXT_QVALUE_DEFAULT 0x00000000
+
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_QUERY_VALUE_NAMES)
+ (__in PDEBUG_CLIENT Client,
+ __in ULONG Flags,
+ __out_ecount(BufferChars) PWSTR Buffer,
+ __in ULONG BufferChars,
+ __out PULONG BufferNeeded);
+
+#define DEBUG_EXT_PVALUE_DEFAULT 0x00000000
+
+#define DEBUG_EXT_PVTYPE_IS_VALUE 0x00000000
+#define DEBUG_EXT_PVTYPE_IS_POINTER 0x00000001
+
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_PROVIDE_VALUE)
+ (__in PDEBUG_CLIENT Client,
+ __in ULONG Flags,
+ __in PCWSTR Name,
+ __out PULONG64 Value,
+ __out PULONG64 TypeModBase,
+ __out PULONG TypeId,
+ __out PULONG TypeFlags);
+
+//----------------------------------------------------------------------------
+//
+// Extension functions.
+//
+// Extension functions differ from extension callbacks in that
+// they are arbitrary functions exported from an extension DLL
+// for other code callers instead of for human invocation from
+// debugger commands. Extension function pointers are retrieved
+// for an extension DLL with IDebugControl::GetExtensionFunction.
+//
+// Extension function names must begin with _EFN_. Other than that
+// they can have any name and prototype. Extension functions
+// must be public exports of their extension DLL. They should
+// have a typedef for their function pointer prototype in an
+// extension header so that callers have a header file to include
+// with a type that allows a correctly-formed invocation of the
+// extension function.
+//
+// The engine does not perform any validation of calls to
+// extension functions. Once the extension function pointer
+// is retrieved with GetExtensionFunction all calls go
+// directly between the caller and the extension function and
+// are not mediated by the engine.
+//
+//----------------------------------------------------------------------------
+
+#ifdef __cplusplus
+};
+
+//----------------------------------------------------------------------------
+//
+// C++ implementation helper classes.
+//
+//----------------------------------------------------------------------------
+
+#if !defined(DEBUG_NO_IMPLEMENTATION) && !defined(_M_CEE_PURE)
+
+//
+// DebugBaseEventCallbacks provides a do-nothing base implementation
+// of IDebugEventCallbacks. A program can derive their own
+// event callbacks class from DebugBaseEventCallbacks and implement
+// only the methods they are interested in. Programs must be
+// careful to implement GetInterestMask appropriately.
+//
+class DebugBaseEventCallbacks : public IDebugEventCallbacks
+{
+public:
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ )
+ {
+ *Interface = NULL;
+
+#if _MSC_VER >= 1100
+ if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
+ IsEqualIID(InterfaceId, __uuidof(IDebugEventCallbacks)))
+#else
+ if (IsEqualIID(InterfaceId, IID_IUnknown) ||
+ IsEqualIID(InterfaceId, IID_IDebugEventCallbacks))
+#endif
+ {
+ *Interface = (IDebugEventCallbacks *)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ }
+
+ // IDebugEventCallbacks.
+
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ )
+ {
+ UNREFERENCED_PARAMETER(Bp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ )
+ {
+ UNREFERENCED_PARAMETER(Exception);
+ UNREFERENCED_PARAMETER(FirstChance);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(DataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCSTR ModuleName,
+ __in PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ UNREFERENCED_PARAMETER(InitialThreadHandle);
+ UNREFERENCED_PARAMETER(ThreadDataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCSTR ModuleName,
+ __in PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in PCSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageBaseName);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ )
+ {
+ UNREFERENCED_PARAMETER(Error);
+ UNREFERENCED_PARAMETER(Level);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ )
+ {
+ UNREFERENCED_PARAMETER(Status);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+};
+
+class DebugBaseEventCallbacksWide : public IDebugEventCallbacksWide
+{
+public:
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ )
+ {
+ *Interface = NULL;
+
+#if _MSC_VER >= 1100
+ if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
+ IsEqualIID(InterfaceId, __uuidof(IDebugEventCallbacksWide)))
+#else
+ if (IsEqualIID(InterfaceId, IID_IUnknown) ||
+ IsEqualIID(InterfaceId, IID_IDebugEventCallbacksWide))
+#endif
+ {
+ *Interface = (IDebugEventCallbacksWide *)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ }
+
+ // IDebugEventCallbacksWide.
+
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT2 Bp
+ )
+ {
+ UNREFERENCED_PARAMETER(Bp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ )
+ {
+ UNREFERENCED_PARAMETER(Exception);
+ UNREFERENCED_PARAMETER(FirstChance);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(DataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCWSTR ModuleName,
+ __in PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ UNREFERENCED_PARAMETER(InitialThreadHandle);
+ UNREFERENCED_PARAMETER(ThreadDataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCWSTR ModuleName,
+ __in PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in PCWSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageBaseName);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ )
+ {
+ UNREFERENCED_PARAMETER(Error);
+ UNREFERENCED_PARAMETER(Level);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ )
+ {
+ UNREFERENCED_PARAMETER(Status);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+};
+
+#endif // #ifndef DEBUG_NO_IMPLEMENTATION
+
+#ifdef DEBUG_UNICODE_MACROS
+
+#ifdef UNICODE
+
+#define IDebugEventCallbacksT IDebugEventCallbacksWide
+#define IID_IDebugEventCallbacksT IID_IDebugEventCallbacksWide
+#define IDebugOutputCallbacksT IDebugOutputCallbacksWide
+#define IID_IDebugOutputCallbacksT IID_IDebugOutputCallbacksWide
+#define DebugBaseEventCallbacksT DebugBaseEventCallbacksWide
+
+#define DebugConnectT DebugConnectWide
+#define GetSourceFileInformationT GetSourceFileInformationWide
+#define FindSourceFileAndTokenT FindSourceFileAndTokenWide
+#define GetSymbolInformationT GetSymbolInformationWide
+#define GetCommandT GetCommandWide
+#define SetCommandT SetCommandWide
+#define GetOffsetExpressionT GetOffsetExpressionWide
+#define SetOffsetExpressionT SetOffsetExpressionWide
+#define GetRunningProcessSystemIdByExecutableNameT GetRunningProcessSystemIdByExecutableNameWide
+#define GetRunningProcessDescriptionT GetRunningProcessDescriptionWide
+#define CreateProcessT CreateProcessWide
+#define CreateProcessAndAttachT CreateProcessAndAttachWide
+#define AddDumpInformationFileT AddDumpInformationFileWide
+#define GetDumpFileT GetDumpFileWide
+#define AttachKernelT AttachKernelWide
+#define GetKernelConnectionOptionsT GetKernelConnectionOptionsWide
+#define SetKernelConnectionOptionsT SetKernelConnectionOptionsWide
+#define StartProcessServerT StartProcessServerWide
+#define ConnectProcessServerT ConnectProcessServerWide
+#define StartServerT StartServerWide
+#define OutputServersT OutputServersWide
+#define GetOutputCallbacksT GetOutputCallbacksWide
+#define SetOutputCallbacksT SetOutputCallbacksWide
+#define GetOutputLinePrefixT GetOutputLinePrefixWide
+#define SetOutputLinePrefixT SetOutputLinePrefixWide
+#define GetIdentityT GetIdentityWide
+#define OutputIdentityT OutputIdentityWide
+#define GetEventCallbacksT GetEventCallbacksWide
+#define SetEventCallbacksT SetEventCallbacksWide
+#define CreateProcess2T CreateProcess2Wide
+#define CreateProcessAndAttach2T CreateProcessAndAttach2Wide
+#define PushOutputLinePrefixT PushOutputLinePrefixWide
+#define GetQuitLockStringT GetQuitLockStringWide
+#define SetQuitLockStringT SetQuitLockStringWide
+#define GetLogFileT GetLogFileWide
+#define OpenLogFileT OpenLogFileWide
+#define InputT InputWide
+#define ReturnInputT ReturnInputWide
+#define OutputT OutputWide
+#define OutputVaListT OutputVaListWide
+#define ControlledOutputT ControlledOutputWide
+#define ControlledOutputVaListT ControlledOutputVaListWide
+#define OutputPromptT OutputPromptWide
+#define OutputPromptVaListT OutputPromptVaListWide
+#define GetPromptTextT GetPromptTextWide
+#define AssembleT AssembleWide
+#define DisassembleT DisassembleWide
+#define GetProcessorTypeNamesT GetProcessorTypeNamesWide
+#define GetTextMacroT GetTextMacroWide
+#define SetTextMacroT SetTextMacroWide
+#define EvaluateT EvaluateWide
+#define ExecuteT ExecuteWide
+#define ExecuteCommandFileT ExecuteCommandFileWide
+#define AddExtensionT AddExtensionWide
+#define GetExtensionByPathT GetExtensionByPathWide
+#define CallExtensionT CallExtensionWide
+#define GetExtensionFunctionT GetExtensionFunctionWide
+#define GetEventFilterTextT GetEventFilterTextWide
+#define GetEventFilterCommandT GetEventFilterCommandWide
+#define SetEventFilterCommandT SetEventFilterCommandWide
+#define GetSpecificFilterArgumentT GetSpecificFilterArgumentWide
+#define SetSpecificFilterArgumentT SetSpecificFilterArgumentWide
+#define GetExceptionFilterSecondCommandT GetExceptionFilterSecondCommandWide
+#define SetExceptionFilterSecondCommandT SetExceptionFilterSecondCommandWide
+#define GetLastEventInformationT GetLastEventInformationWide
+#define GetTextReplacementT GetTextReplacementWide
+#define SetTextReplacementT SetTextReplacementWide
+#define SetExpressionSyntaxByNameT SetExpressionSyntaxByNameWide
+#define GetExpressionSyntaxNamesT GetExpressionSyntaxNamesWide
+#define GetEventIndexDescriptionT GetEventIndexDescriptionWide
+#define GetLogFile2T GetLogFile2Wide
+#define OpenLogFile2T OpenLogFile2Wide
+#define GetSystemVersionStringT GetSystemVersionStringWide
+#define ReadMultiByteStringVirtualT ReadMultiByteStringVirtualWide
+#define ReadUnicodeStringVirtualT ReadUnicodeStringVirtualWide
+#define GetDescriptionT GetDescriptionWide
+#define GetIndexByNameT GetIndexByNameWide
+#define GetPseudoDescriptionT GetPseudoDescriptionWide
+#define GetPseudoIndexByNameT GetPseudoIndexByNameWide
+#define AddSymbolT AddSymbolWide
+#define RemoveSymbolByNameT RemoveSymbolByNameWide
+#define GetSymbolNameT GetSymbolNameWide
+#define WriteSymbolT WriteSymbolWide
+#define OutputAsTypeT OutputAsTypeWide
+#define GetSymbolTypeNameT GetSymbolTypeNameWide
+#define GetSymbolValueTextT GetSymbolValueTextWide
+#define GetNameByOffsetT GetNameByOffsetWide
+#define GetOffsetByNameT GetOffsetByNameWide
+#define GetNearNameByOffsetT GetNearNameByOffsetWide
+#define GetLineByOffsetT GetLineByOffsetWide
+#define GetOffsetByLineT GetOffsetByLineWide
+#define GetModuleByModuleNameT GetModuleByModuleNameWide
+#define GetModuleByModuleName2T GetModuleByModuleName2Wide
+#define GetSymbolModuleT GetSymbolModuleWide
+#define GetTypeNameT GetTypeNameWide
+#define GetTypeIdT GetTypeIdWide
+#define GetFieldOffsetT GetFieldOffsetWide
+#define GetSymbolTypeIdT GetSymbolTypeIdWide
+#define StartSymbolMatchT StartSymbolMatchWide
+#define GetNextSymbolMatchT GetNextSymbolMatchWide
+#define ReloadT ReloadWide
+#define GetSymbolPathT GetSymbolPathWide
+#define SetSymbolPathT SetSymbolPathWide
+#define AppendSymbolPathT AppendSymbolPathWide
+#define GetImagePathT GetImagePathWide
+#define SetImagePathT SetImagePathWide
+#define AppendImagePathT AppendImagePathWide
+#define GetSourcePathT GetSourcePathWide
+#define GetSourcePathElementT GetSourcePathElementWide
+#define SetSourcePathT SetSourcePathWide
+#define AppendSourcePathT AppendSourcePathWide
+#define FindSourceFileT FindSourceFileWide
+#define GetSourceFileLineOffsetsT GetSourceFileLineOffsetsWide
+#define GetModuleVersionInformationT GetModuleVersionInformationWide
+#define GetModuleNameStringT GetModuleNameStringWide
+#define GetConstantNameT GetConstantNameWide
+#define GetFieldNameT GetFieldNameWide
+#define GetFieldTypeAndOffsetT GetFieldTypeAndOffsetWide
+#define GetSymbolEntriesByNameT GetSymbolEntriesByNameWide
+#define GetSymbolEntryStringT GetSymbolEntryStringWide
+#define GetSourceEntriesByLineT GetSourceEntriesByLineWide
+#define GetSourceEntryStringT GetSourceEntryStringWide
+#define GetCurrentProcessExecutableNameT GetCurrentProcessExecutableNameWide
+#define GetCurrentSystemServerNameT GetCurrentSystemServerNameWide
+
+#else // #ifdef UNICODE
+
+#define IDebugEventCallbacksT IDebugEventCallbacks
+#define IID_IDebugEventCallbacksT IID_IDebugEventCallbacks
+#define IDebugOutputCallbacksT IDebugOutputCallbacks
+#define IID_IDebugOutputCallbacksT IID_IDebugOutputCallbacks
+#define DebugBaseEventCallbacksT DebugBaseEventCallbacks
+
+#define DebugConnectT DebugConnect
+#define GetSourceFileInformationT GetSourceFileInformation
+#define FindSourceFileAndTokenT FindSourceFileAndToken
+#define GetSymbolInformationT GetSymbolInformation
+#define GetCommandT GetCommand
+#define SetCommandT SetCommand
+#define GetOffsetExpressionT GetOffsetExpression
+#define SetOffsetExpressionT SetOffsetExpression
+#define GetRunningProcessSystemIdByExecutableNameT GetRunningProcessSystemIdByExecutableName
+#define GetRunningProcessDescriptionT GetRunningProcessDescription
+#define CreateProcessT CreateProcess
+#define CreateProcessAndAttachT CreateProcessAndAttach
+#define AddDumpInformationFileT AddDumpInformationFile
+#define GetDumpFileT GetDumpFile
+#define AttachKernelT AttachKernel
+#define GetKernelConnectionOptionsT GetKernelConnectionOptions
+#define SetKernelConnectionOptionsT SetKernelConnectionOptions
+#define StartProcessServerT StartProcessServer
+#define ConnectProcessServerT ConnectProcessServer
+#define StartServerT StartServer
+#define OutputServersT OutputServers
+#define GetOutputCallbacksT GetOutputCallbacks
+#define SetOutputCallbacksT SetOutputCallbacks
+#define GetOutputLinePrefixT GetOutputLinePrefix
+#define SetOutputLinePrefixT SetOutputLinePrefix
+#define GetIdentityT GetIdentity
+#define OutputIdentityT OutputIdentity
+#define GetEventCallbacksT GetEventCallbacks
+#define SetEventCallbacksT SetEventCallbacks
+#define CreateProcess2T CreateProcess2
+#define CreateProcessAndAttach2T CreateProcessAndAttach2
+#define PushOutputLinePrefixT PushOutputLinePrefix
+#define GetQuitLockStringT GetQuitLockString
+#define SetQuitLockStringT SetQuitLockString
+#define GetLogFileT GetLogFile
+#define OpenLogFileT OpenLogFile
+#define InputT Input
+#define ReturnInputT ReturnInput
+#define OutputT Output
+#define OutputVaListT OutputVaList
+#define ControlledOutputT ControlledOutput
+#define ControlledOutputVaListT ControlledOutputVaList
+#define OutputPromptT OutputPrompt
+#define OutputPromptVaListT OutputPromptVaList
+#define GetPromptTextT GetPromptText
+#define AssembleT Assemble
+#define DisassembleT Disassemble
+#define GetProcessorTypeNamesT GetProcessorTypeNames
+#define GetTextMacroT GetTextMacro
+#define SetTextMacroT SetTextMacro
+#define EvaluateT Evaluate
+#define ExecuteT Execute
+#define ExecuteCommandFileT ExecuteCommandFile
+#define AddExtensionT AddExtension
+#define GetExtensionByPathT GetExtensionByPath
+#define CallExtensionT CallExtension
+#define GetExtensionFunctionT GetExtensionFunction
+#define GetEventFilterTextT GetEventFilterText
+#define GetEventFilterCommandT GetEventFilterCommand
+#define SetEventFilterCommandT SetEventFilterCommand
+#define GetSpecificFilterArgumentT GetSpecificFilterArgument
+#define SetSpecificFilterArgumentT SetSpecificFilterArgument
+#define GetExceptionFilterSecondCommandT GetExceptionFilterSecondCommand
+#define SetExceptionFilterSecondCommandT SetExceptionFilterSecondCommand
+#define GetLastEventInformationT GetLastEventInformation
+#define GetTextReplacementT GetTextReplacement
+#define SetTextReplacementT SetTextReplacement
+#define SetExpressionSyntaxByNameT SetExpressionSyntaxByName
+#define GetExpressionSyntaxNamesT GetExpressionSyntaxNames
+#define GetEventIndexDescriptionT GetEventIndexDescription
+#define GetLogFile2T GetLogFile2
+#define OpenLogFile2T OpenLogFile2
+#define GetSystemVersionStringT GetSystemVersionString
+#define ReadMultiByteStringVirtualT ReadMultiByteStringVirtual
+#define ReadUnicodeStringVirtualT ReadUnicodeStringVirtual
+#define GetDescriptionT GetDescription
+#define GetIndexByNameT GetIndexByName
+#define GetPseudoDescriptionT GetPseudoDescription
+#define GetPseudoIndexByNameT GetPseudoIndexByName
+#define AddSymbolT AddSymbol
+#define RemoveSymbolByNameT RemoveSymbolByName
+#define GetSymbolNameT GetSymbolName
+#define WriteSymbolT WriteSymbol
+#define OutputAsTypeT OutputAsType
+#define GetSymbolTypeNameT GetSymbolTypeName
+#define GetSymbolValueTextT GetSymbolValueText
+#define GetNameByOffsetT GetNameByOffset
+#define GetOffsetByNameT GetOffsetByName
+#define GetNearNameByOffsetT GetNearNameByOffset
+#define GetLineByOffsetT GetLineByOffset
+#define GetOffsetByLineT GetOffsetByLine
+#define GetModuleByModuleNameT GetModuleByModuleName
+#define GetModuleByModuleName2T GetModuleByModuleName2
+#define GetSymbolModuleT GetSymbolModule
+#define GetTypeNameT GetTypeName
+#define GetTypeIdT GetTypeId
+#define GetFieldOffsetT GetFieldOffset
+#define GetSymbolTypeIdT GetSymbolTypeId
+#define StartSymbolMatchT StartSymbolMatch
+#define GetNextSymbolMatchT GetNextSymbolMatch
+#define ReloadT Reload
+#define GetSymbolPathT GetSymbolPath
+#define SetSymbolPathT SetSymbolPath
+#define AppendSymbolPathT AppendSymbolPath
+#define GetImagePathT GetImagePath
+#define SetImagePathT SetImagePath
+#define AppendImagePathT AppendImagePath
+#define GetSourcePathT GetSourcePath
+#define GetSourcePathElementT GetSourcePathElement
+#define SetSourcePathT SetSourcePath
+#define AppendSourcePathT AppendSourcePath
+#define FindSourceFileT FindSourceFile
+#define GetSourceFileLineOffsetsT GetSourceFileLineOffsets
+#define GetModuleVersionInformationT GetModuleVersionInformation
+#define GetModuleNameStringT GetModuleNameString
+#define GetConstantNameT GetConstantName
+#define GetFieldNameT GetFieldName
+#define GetFieldTypeAndOffsetT GetFieldTypeAndOffset
+#define GetSymbolEntriesByNameT GetSymbolEntriesByName
+#define GetSymbolEntryStringT GetSymbolEntryString
+#define GetSourceEntriesByLineT GetSourceEntriesByLine
+#define GetSourceEntryStringT GetSourceEntryString
+#define GetCurrentProcessExecutableNameT GetCurrentProcessExecutableName
+#define GetCurrentSystemServerNameT GetCurrentSystemServerName
+
+#endif // #ifdef UNICODE
+
+#endif // #ifdef DEBUG_UNICODE_MACROS
+
+#endif // #ifdef __cplusplus
+
+#endif // #ifndef __DBGENG_H__
diff --git a/src/ToolBox/SOS/Strike/inc/dbghelp.h b/src/ToolBox/SOS/Strike/inc/dbghelp.h
new file mode 100644
index 0000000000..8075929bf0
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/dbghelp.h
@@ -0,0 +1,4540 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++ BUILD Version: 0000 Increment this if a change has global effects
+
+
+
+Module Name:
+
+ dbghelp.h
+
+Abstract:
+
+ This module defines the prototypes and constants required for the image
+ help routines.
+
+ Contains debugging support routines that are redistributable.
+
+Revision History:
+
+--*/
+
+#ifndef _DBGHELP_
+#define _DBGHELP_
+
+#if _MSC_VER > 1020
+#pragma once
+#endif
+
+
+// As a general principal always call the 64 bit version
+// of every API, if a choice exists. The 64 bit version
+// works great on 32 bit platforms, and is forward
+// compatible to 64 bit platforms.
+
+#ifdef _WIN64
+#ifndef _IMAGEHLP64
+#define _IMAGEHLP64
+#endif
+#endif
+
+// For those without specstrings.h
+// Since there are different versions of this header, I need to
+// individually test each item and define it if it is not around.
+
+#ifndef __in
+ #define __in
+#endif
+#ifndef __out
+ #define __out
+#endif
+#ifndef __inout
+ #define __inout
+#endif
+#ifndef __in_opt
+ #define __in_opt
+#endif
+#ifndef __out_opt
+ #define __out_opt
+#endif
+#ifndef __inout_opt
+ #define __inout_opt
+#endif
+#ifndef __in_ecount
+ #define __in_ecount(x)
+#endif
+#ifndef __out_ecount
+ #define __out_ecount(x)
+#endif
+#ifndef __inout_ecount
+ #define __inout_ecount(x)
+#endif
+#ifndef __in_bcount
+ #define __in_bcount(x)
+#endif
+#ifndef __out_bcount
+ #define __out_bcount(x)
+#endif
+#ifndef __inout_bcount
+ #define __inout_bcount(x)
+#endif
+#ifndef __out_xcount
+ #define __out_xcount(x)
+#endif
+#ifndef __deref_opt_out
+ #define __deref_opt_out
+#endif
+#ifndef __deref_out
+ #define __deref_out
+#endif
+#ifndef __out_ecount_opt
+ #define __out_ecount_opt(x)
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _IMAGEHLP_SOURCE_
+ #define IMAGEAPI __stdcall
+ #define DBHLP_DEPRECIATED
+#else
+ #define IMAGEAPI DECLSPEC_IMPORT __stdcall
+ #if (_MSC_VER >= 1300) && !defined(MIDL_PASS)
+ #define DBHLP_DEPRECIATED __declspec(deprecated)
+ #else
+ #define DBHLP_DEPRECIATED
+ #endif
+#endif
+
+#define DBHLPAPI IMAGEAPI
+
+#define IMAGE_SEPARATION (64*1024)
+
+// Observant readers may notice that 2 new fields,
+// 'fReadOnly' and 'Version' have been added to
+// the LOADED_IMAGE structure after 'fDOSImage'.
+// This does not change the size of the structure
+// from previous headers. That is because while
+// 'fDOSImage' is a byte, it is padded by the
+// compiler to 4 bytes. So the 2 new fields are
+// slipped into the extra space.
+
+typedef struct _LOADED_IMAGE {
+ PSTR ModuleName;
+ HANDLE hFile;
+ PUCHAR MappedAddress;
+#ifdef _IMAGEHLP64
+ PIMAGE_NT_HEADERS64 FileHeader;
+#else
+ PIMAGE_NT_HEADERS32 FileHeader;
+#endif
+ PIMAGE_SECTION_HEADER LastRvaSection;
+ ULONG NumberOfSections;
+ PIMAGE_SECTION_HEADER Sections;
+ ULONG Characteristics;
+ BOOLEAN fSystemImage;
+ BOOLEAN fDOSImage;
+ BOOLEAN fReadOnly;
+ UCHAR Version;
+ LIST_ENTRY Links;
+ ULONG SizeOfImage;
+} LOADED_IMAGE, *PLOADED_IMAGE;
+
+#define MAX_SYM_NAME 2000
+
+
+// Error codes set by dbghelp functions. Call GetLastError
+// to see them.
+// Dbghelp also sets error codes found in winerror.h
+
+#define ERROR_IMAGE_NOT_STRIPPED 0x8800 // the image is not stripped. No dbg file available.
+#define ERROR_NO_DBG_POINTER 0x8801 // image is stripped but there is no pointer to a dbg file
+#define ERROR_NO_PDB_POINTER 0x8802 // image does not point to a pdb file
+
+typedef BOOL
+(CALLBACK *PFIND_DEBUG_FILE_CALLBACK)(
+ __in HANDLE FileHandle,
+ __in PCSTR FileName,
+ __in PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindDebugInfoFile(
+ __in HANDLE hProcess,
+ __in PCSTR FileName,
+ __out_ecount(MAX_PATH + 1) PSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACK Callback,
+ __in_opt PVOID CallerData
+ );
+
+typedef BOOL
+(CALLBACK *PFIND_DEBUG_FILE_CALLBACKW)(
+ __in HANDLE FileHandle,
+ __in PCWSTR FileName,
+ __in PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindDebugInfoFileW(
+ __in HANDLE hProcess,
+ __in PCWSTR FileName,
+ __out_ecount(MAX_PATH + 1) PWSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACKW Callback,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFile (
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR DebugFilePath
+ );
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFileEx (
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACK Callback,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFileExW (
+ __in PCWSTR FileName,
+ __in PCWSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PWSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACKW Callback,
+ __in_opt PVOID CallerData
+ );
+
+typedef BOOL
+(CALLBACK *PFINDFILEINPATHCALLBACK)(
+ PCSTR filename,
+ PVOID context
+ );
+
+BOOL
+IMAGEAPI
+SymFindFileInPath(
+ __in HANDLE hprocess,
+ __in_opt PCSTR SearchPath,
+ __in PCSTR FileName,
+ __in_opt PVOID id,
+ __in DWORD two,
+ __in DWORD three,
+ __in DWORD flags,
+ __out_ecount(MAX_PATH + 1) PSTR FoundFile,
+ __in_opt PFINDFILEINPATHCALLBACK callback,
+ __in_opt PVOID context
+ );
+
+typedef BOOL
+(CALLBACK *PFINDFILEINPATHCALLBACKW)(
+ __in PCWSTR filename,
+ __in PVOID context
+ );
+
+BOOL
+IMAGEAPI
+SymFindFileInPathW(
+ __in HANDLE hprocess,
+ __in_opt PCWSTR SearchPath,
+ __in PCWSTR FileName,
+ __in_opt PVOID id,
+ __in DWORD two,
+ __in DWORD three,
+ __in DWORD flags,
+ __out_ecount(MAX_PATH + 1) PWSTR FoundFile,
+ __in_opt PFINDFILEINPATHCALLBACKW callback,
+ __in_opt PVOID context
+ );
+
+typedef BOOL
+(CALLBACK *PFIND_EXE_FILE_CALLBACK)(
+ __in HANDLE FileHandle,
+ __in PCSTR FileName,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindExecutableImage(
+ __in HANDLE hProcess,
+ __in PCSTR FileName,
+ __out_ecount(MAX_PATH + 1) PSTR ImageFilePath,
+ __in PFIND_EXE_FILE_CALLBACK Callback,
+ __in PVOID CallerData
+ );
+
+typedef BOOL
+(CALLBACK *PFIND_EXE_FILE_CALLBACKW)(
+ __in HANDLE FileHandle,
+ __in PCWSTR FileName,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindExecutableImageW(
+ __in HANDLE hProcess,
+ __in PCWSTR FileName,
+ __out_ecount(MAX_PATH + 1) PWSTR ImageFilePath,
+ __in PFIND_EXE_FILE_CALLBACKW Callback,
+ __in PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindExecutableImage(
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR ImageFilePath
+ );
+
+HANDLE
+IMAGEAPI
+FindExecutableImageEx(
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR ImageFilePath,
+ __in_opt PFIND_EXE_FILE_CALLBACK Callback,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindExecutableImageExW(
+ __in PCWSTR FileName,
+ __in PCWSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PWSTR ImageFilePath,
+ __in_opt PFIND_EXE_FILE_CALLBACKW Callback,
+ __in PVOID CallerData
+ );
+
+PIMAGE_NT_HEADERS
+IMAGEAPI
+ImageNtHeader (
+ __in PVOID Base
+ );
+
+PVOID
+IMAGEAPI
+ImageDirectoryEntryToDataEx (
+ __in PVOID Base,
+ __in BOOLEAN MappedAsImage,
+ __in USHORT DirectoryEntry,
+ __out PULONG Size,
+ __out_opt PIMAGE_SECTION_HEADER *FoundHeader
+ );
+
+PVOID
+IMAGEAPI
+ImageDirectoryEntryToData (
+ __in PVOID Base,
+ __in BOOLEAN MappedAsImage,
+ __in USHORT DirectoryEntry,
+ __out PULONG Size
+ );
+
+PIMAGE_SECTION_HEADER
+IMAGEAPI
+ImageRvaToSection(
+ __in PIMAGE_NT_HEADERS NtHeaders,
+ __in PVOID Base,
+ __in ULONG Rva
+ );
+
+PVOID
+IMAGEAPI
+ImageRvaToVa(
+ __in PIMAGE_NT_HEADERS NtHeaders,
+ __in PVOID Base,
+ __in ULONG Rva,
+ __in_opt OUT PIMAGE_SECTION_HEADER *LastRvaSection
+ );
+
+#ifndef _WIN64
+// This api won't be ported to Win64 - Fix your code.
+
+typedef struct _IMAGE_DEBUG_INFORMATION {
+ LIST_ENTRY List;
+ DWORD ReservedSize;
+ PVOID ReservedMappedBase;
+ USHORT ReservedMachine;
+ USHORT ReservedCharacteristics;
+ DWORD ReservedCheckSum;
+ DWORD ImageBase;
+ DWORD SizeOfImage;
+
+ DWORD ReservedNumberOfSections;
+ PIMAGE_SECTION_HEADER ReservedSections;
+
+ DWORD ReservedExportedNamesSize;
+ PSTR ReservedExportedNames;
+
+ DWORD ReservedNumberOfFunctionTableEntries;
+ PIMAGE_FUNCTION_ENTRY ReservedFunctionTableEntries;
+ DWORD ReservedLowestFunctionStartingAddress;
+ DWORD ReservedHighestFunctionEndingAddress;
+
+ DWORD ReservedNumberOfFpoTableEntries;
+ PFPO_DATA ReservedFpoTableEntries;
+
+ DWORD SizeOfCoffSymbols;
+ PIMAGE_COFF_SYMBOLS_HEADER CoffSymbols;
+
+ DWORD ReservedSizeOfCodeViewSymbols;
+ PVOID ReservedCodeViewSymbols;
+
+ PSTR ImageFilePath;
+ PSTR ImageFileName;
+ PSTR ReservedDebugFilePath;
+
+ DWORD ReservedTimeDateStamp;
+
+ BOOL ReservedRomImage;
+ PIMAGE_DEBUG_DIRECTORY ReservedDebugDirectory;
+ DWORD ReservedNumberOfDebugDirectories;
+
+ DWORD ReservedOriginalFunctionTableBaseAddress;
+
+ DWORD Reserved[ 2 ];
+
+} IMAGE_DEBUG_INFORMATION, *PIMAGE_DEBUG_INFORMATION;
+
+
+PIMAGE_DEBUG_INFORMATION
+IMAGEAPI
+MapDebugInformation(
+ __in_opt HANDLE FileHandle,
+ __in PCSTR FileName,
+ __in_opt PCSTR SymbolPath,
+ __in ULONG ImageBase
+ );
+
+BOOL
+IMAGEAPI
+UnmapDebugInformation(
+ __out_xcount(unknown) PIMAGE_DEBUG_INFORMATION DebugInfo
+ );
+
+#endif
+
+BOOL
+IMAGEAPI
+SearchTreeForFile(
+ __in PCSTR RootPath,
+ __in PCSTR InputPathName,
+ __out_ecount(MAX_PATH + 1) PSTR OutputPathBuffer
+ );
+
+BOOL
+IMAGEAPI
+SearchTreeForFileW(
+ __in PCWSTR RootPath,
+ __in PCWSTR InputPathName,
+ __out_ecount(MAX_PATH + 1) PWSTR OutputPathBuffer
+ );
+
+typedef BOOL
+(CALLBACK *PENUMDIRTREE_CALLBACK)(
+ __in PCSTR FilePath,
+ __in_opt PVOID CallerData
+ );
+
+BOOL
+IMAGEAPI
+EnumDirTree(
+ __in_opt HANDLE hProcess,
+ __in PCSTR RootPath,
+ __in PCSTR InputPathName,
+ __out_ecount_opt(MAX_PATH + 1) PSTR OutputPathBuffer,
+ __in_opt PENUMDIRTREE_CALLBACK cb,
+ __in_opt PVOID data
+ );
+
+typedef BOOL
+(CALLBACK *PENUMDIRTREE_CALLBACKW)(
+ __in PCWSTR FilePath,
+ __in_opt PVOID CallerData
+ );
+
+BOOL
+IMAGEAPI
+EnumDirTreeW(
+ __in_opt HANDLE hProcess,
+ __in PCWSTR RootPath,
+ __in PCWSTR InputPathName,
+ __out_ecount_opt(MAX_PATH + 1) PWSTR OutputPathBuffer,
+ __in_opt PENUMDIRTREE_CALLBACKW cb,
+ __in_opt PVOID data
+ );
+
+BOOL
+IMAGEAPI
+MakeSureDirectoryPathExists(
+ __in PCSTR DirPath
+ );
+
+//
+// UnDecorateSymbolName Flags
+//
+
+#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration
+#define UNDNAME_NO_LEADING_UNDERSCORES (0x0001) // Remove leading underscores from MS extended keywords
+#define UNDNAME_NO_MS_KEYWORDS (0x0002) // Disable expansion of MS extended keywords
+#define UNDNAME_NO_FUNCTION_RETURNS (0x0004) // Disable expansion of return type for primary declaration
+#define UNDNAME_NO_ALLOCATION_MODEL (0x0008) // Disable expansion of the declaration model
+#define UNDNAME_NO_ALLOCATION_LANGUAGE (0x0010) // Disable expansion of the declaration language specifier
+#define UNDNAME_NO_MS_THISTYPE (0x0020) // NYI Disable expansion of MS keywords on the 'this' type for primary declaration
+#define UNDNAME_NO_CV_THISTYPE (0x0040) // NYI Disable expansion of CV modifiers on the 'this' type for primary declaration
+#define UNDNAME_NO_THISTYPE (0x0060) // Disable all modifiers on the 'this' type
+#define UNDNAME_NO_ACCESS_SPECIFIERS (0x0080) // Disable expansion of access specifiers for members
+#define UNDNAME_NO_THROW_SIGNATURES (0x0100) // Disable expansion of 'throw-signatures' for functions and pointers to functions
+#define UNDNAME_NO_MEMBER_TYPE (0x0200) // Disable expansion of 'static' or 'virtual'ness of members
+#define UNDNAME_NO_RETURN_UDT_MODEL (0x0400) // Disable expansion of MS model for UDT returns
+#define UNDNAME_32_BIT_DECODE (0x0800) // Undecorate 32-bit decorated names
+#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration;
+ // return just [scope::]name. Does expand template params
+#define UNDNAME_NO_ARGUMENTS (0x2000) // Don't undecorate arguments to function
+#define UNDNAME_NO_SPECIAL_SYMS (0x4000) // Don't undecorate special names (v-table, vcall, vector xxx, metatype, etc)
+
+DWORD
+IMAGEAPI
+WINAPI
+UnDecorateSymbolName(
+ __in PCSTR name,
+ __out_ecount(maxStringLength) PSTR outputString,
+ __in DWORD maxStringLength,
+ __in DWORD flags
+ );
+
+DWORD
+IMAGEAPI
+WINAPI
+UnDecorateSymbolNameW(
+ __in PCWSTR name,
+ __out_ecount(maxStringLength) PWSTR outputString,
+ __in DWORD maxStringLength,
+ __in DWORD flags
+ );
+
+//
+// these values are used for synthesized file types
+// that can be passed in as image headers instead of
+// the standard ones from ntimage.h
+//
+
+#define DBHHEADER_DEBUGDIRS 0x1
+#define DBHHEADER_CVMISC 0x2
+
+typedef struct _MODLOAD_DATA {
+ DWORD ssize; // size of this struct
+ DWORD ssig; // signature identifying the passed data
+ PVOID data; // pointer to passed data
+ DWORD size; // size of passed data
+ DWORD flags; // options
+} MODLOAD_DATA, *PMODLOAD_DATA;
+
+typedef struct _MODLOAD_CVMISC {
+ DWORD oCV; // ofset to the codeview record
+ size_t cCV; // size of the codeview record
+ DWORD oMisc; // offset to the misc record
+ size_t cMisc; // size of the misc record
+ DWORD dtImage; // datetime stamp of the image
+ DWORD cImage; // size of the image
+} MODLOAD_CVMISC, *PMODLOAD_CVMISC;
+
+//
+// StackWalking API
+//
+
+typedef enum {
+ AddrMode1616,
+ AddrMode1632,
+ AddrModeReal,
+ AddrModeFlat
+} ADDRESS_MODE;
+
+typedef struct _tagADDRESS64 {
+ DWORD64 Offset;
+ WORD Segment;
+ ADDRESS_MODE Mode;
+} ADDRESS64, *LPADDRESS64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define ADDRESS ADDRESS64
+#define LPADDRESS LPADDRESS64
+#else
+typedef struct _tagADDRESS {
+ DWORD Offset;
+ WORD Segment;
+ ADDRESS_MODE Mode;
+} ADDRESS, *LPADDRESS;
+
+__inline
+void
+Address32To64(
+ __in LPADDRESS a32,
+ __out LPADDRESS64 a64
+ )
+{
+ a64->Offset = (ULONG64)(LONG64)(LONG)a32->Offset;
+ a64->Segment = a32->Segment;
+ a64->Mode = a32->Mode;
+}
+
+__inline
+void
+Address64To32(
+ __in LPADDRESS64 a64,
+ __out LPADDRESS a32
+ )
+{
+ a32->Offset = (ULONG)a64->Offset;
+ a32->Segment = a64->Segment;
+ a32->Mode = a64->Mode;
+}
+#endif
+
+//
+// This structure is included in the STACKFRAME structure,
+// and is used to trace through usermode callbacks in a thread's
+// kernel stack. The values must be copied by the kernel debugger
+// from the DBGKD_GET_VERSION and WAIT_STATE_CHANGE packets.
+//
+
+//
+// New KDHELP structure for 64 bit system support.
+// This structure is preferred in new code.
+//
+typedef struct _KDHELP64 {
+
+ //
+ // address of kernel thread object, as provided in the
+ // WAIT_STATE_CHANGE packet.
+ //
+ DWORD64 Thread;
+
+ //
+ // offset in thread object to pointer to the current callback frame
+ // in kernel stack.
+ //
+ DWORD ThCallbackStack;
+
+ //
+ // offset in thread object to pointer to the current callback backing
+ // store frame in kernel stack.
+ //
+ DWORD ThCallbackBStore;
+
+ //
+ // offsets to values in frame:
+ //
+ // address of next callback frame
+ DWORD NextCallback;
+
+ // address of saved frame pointer (if applicable)
+ DWORD FramePointer;
+
+
+ //
+ // Address of the kernel function that calls out to user mode
+ //
+ DWORD64 KiCallUserMode;
+
+ //
+ // Address of the user mode dispatcher function
+ //
+ DWORD64 KeUserCallbackDispatcher;
+
+ //
+ // Lowest kernel mode address
+ //
+ DWORD64 SystemRangeStart;
+
+ //
+ // Address of the user mode exception dispatcher function.
+ // Added in API version 10.
+ //
+ DWORD64 KiUserExceptionDispatcher;
+
+ //
+ // Stack bounds, added in API version 11.
+ //
+ DWORD64 StackBase;
+ DWORD64 StackLimit;
+
+ DWORD64 Reserved[5];
+
+} KDHELP64, *PKDHELP64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define KDHELP KDHELP64
+#define PKDHELP PKDHELP64
+#else
+typedef struct _KDHELP {
+
+ //
+ // address of kernel thread object, as provided in the
+ // WAIT_STATE_CHANGE packet.
+ //
+ DWORD Thread;
+
+ //
+ // offset in thread object to pointer to the current callback frame
+ // in kernel stack.
+ //
+ DWORD ThCallbackStack;
+
+ //
+ // offsets to values in frame:
+ //
+ // address of next callback frame
+ DWORD NextCallback;
+
+ // address of saved frame pointer (if applicable)
+ DWORD FramePointer;
+
+ //
+ // Address of the kernel function that calls out to user mode
+ //
+ DWORD KiCallUserMode;
+
+ //
+ // Address of the user mode dispatcher function
+ //
+ DWORD KeUserCallbackDispatcher;
+
+ //
+ // Lowest kernel mode address
+ //
+ DWORD SystemRangeStart;
+
+ //
+ // offset in thread object to pointer to the current callback backing
+ // store frame in kernel stack.
+ //
+ DWORD ThCallbackBStore;
+
+ //
+ // Address of the user mode exception dispatcher function.
+ // Added in API version 10.
+ //
+ DWORD KiUserExceptionDispatcher;
+
+ //
+ // Stack bounds, added in API version 11.
+ //
+ DWORD StackBase;
+ DWORD StackLimit;
+
+ DWORD Reserved[5];
+
+} KDHELP, *PKDHELP;
+
+__inline
+void
+KdHelp32To64(
+ __in PKDHELP p32,
+ __out PKDHELP64 p64
+ )
+{
+ p64->Thread = p32->Thread;
+ p64->ThCallbackStack = p32->ThCallbackStack;
+ p64->NextCallback = p32->NextCallback;
+ p64->FramePointer = p32->FramePointer;
+ p64->KiCallUserMode = p32->KiCallUserMode;
+ p64->KeUserCallbackDispatcher = p32->KeUserCallbackDispatcher;
+ p64->SystemRangeStart = p32->SystemRangeStart;
+ p64->KiUserExceptionDispatcher = p32->KiUserExceptionDispatcher;
+ p64->StackBase = p32->StackBase;
+ p64->StackLimit = p32->StackLimit;
+}
+#endif
+
+typedef struct _tagSTACKFRAME64 {
+ ADDRESS64 AddrPC; // program counter
+ ADDRESS64 AddrReturn; // return address
+ ADDRESS64 AddrFrame; // frame pointer
+ ADDRESS64 AddrStack; // stack pointer
+ ADDRESS64 AddrBStore; // backing store pointer
+ PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
+ DWORD64 Params[4]; // possible arguments to the function
+ BOOL Far; // WOW far call
+ BOOL Virtual; // is this a virtual frame?
+ DWORD64 Reserved[3];
+ KDHELP64 KdHelp;
+} STACKFRAME64, *LPSTACKFRAME64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define STACKFRAME STACKFRAME64
+#define LPSTACKFRAME LPSTACKFRAME64
+#else
+typedef struct _tagSTACKFRAME {
+ ADDRESS AddrPC; // program counter
+ ADDRESS AddrReturn; // return address
+ ADDRESS AddrFrame; // frame pointer
+ ADDRESS AddrStack; // stack pointer
+ PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
+ DWORD Params[4]; // possible arguments to the function
+ BOOL Far; // WOW far call
+ BOOL Virtual; // is this a virtual frame?
+ DWORD Reserved[3];
+ KDHELP KdHelp;
+ ADDRESS AddrBStore; // backing store pointer
+} STACKFRAME, *LPSTACKFRAME;
+#endif
+
+
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
+ __in HANDLE hProcess,
+ __in DWORD64 qwBaseAddress,
+ __out_bcount(nSize) PVOID lpBuffer,
+ __in DWORD nSize,
+ __out LPDWORD lpNumberOfBytesRead
+ );
+
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
+ __in HANDLE ahProcess,
+ __in DWORD64 AddrBase
+ );
+
+typedef
+DWORD64
+(__stdcall *PGET_MODULE_BASE_ROUTINE64)(
+ __in HANDLE hProcess,
+ __in DWORD64 Address
+ );
+
+typedef
+DWORD64
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __in LPADDRESS64 lpaddr
+ );
+
+BOOL
+IMAGEAPI
+StackWalk64(
+ __in DWORD MachineType,
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __inout LPSTACKFRAME64 StackFrame,
+ __inout PVOID ContextRecord,
+ __in_opt PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
+ __in_opt PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
+ __in_opt PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
+ __in_opt PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+#define PREAD_PROCESS_MEMORY_ROUTINE PREAD_PROCESS_MEMORY_ROUTINE64
+#define PFUNCTION_TABLE_ACCESS_ROUTINE PFUNCTION_TABLE_ACCESS_ROUTINE64
+#define PGET_MODULE_BASE_ROUTINE PGET_MODULE_BASE_ROUTINE64
+#define PTRANSLATE_ADDRESS_ROUTINE PTRANSLATE_ADDRESS_ROUTINE64
+
+#define StackWalk StackWalk64
+
+#else
+
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE)(
+ __in HANDLE hProcess,
+ __in DWORD lpBaseAddress,
+ __out_bcount(nSize) PVOID lpBuffer,
+ __in DWORD nSize,
+ __out PDWORD lpNumberOfBytesRead
+ );
+
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE)(
+ __in HANDLE hProcess,
+ __in DWORD AddrBase
+ );
+
+typedef
+DWORD
+(__stdcall *PGET_MODULE_BASE_ROUTINE)(
+ __in HANDLE hProcess,
+ __in DWORD Address
+ );
+
+typedef
+DWORD
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE)(
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __out LPADDRESS lpaddr
+ );
+
+BOOL
+IMAGEAPI
+StackWalk(
+ DWORD MachineType,
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __inout LPSTACKFRAME StackFrame,
+ __inout PVOID ContextRecord,
+ __in_opt PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
+ __in_opt PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
+ __in_opt PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
+ __in_opt PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
+ );
+
+#endif
+
+
+#define API_VERSION_NUMBER 11
+
+typedef struct API_VERSION {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ USHORT Revision;
+ USHORT Reserved;
+} API_VERSION, *LPAPI_VERSION;
+
+LPAPI_VERSION
+IMAGEAPI
+ImagehlpApiVersion(
+ VOID
+ );
+
+LPAPI_VERSION
+IMAGEAPI
+ImagehlpApiVersionEx(
+ __in LPAPI_VERSION AppVersion
+ );
+
+DWORD
+IMAGEAPI
+GetTimestampForLoadedLibrary(
+ __in HMODULE Module
+ );
+
+//
+// typedefs for function pointers
+//
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACK64)(
+ __in PCSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACKW64)(
+ __in PCWSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACK64)(
+ __in PCSTR ModuleName,
+ __in DWORD64 ModuleBase,
+ __in ULONG ModuleSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACKW64)(
+ __in PCWSTR ModuleName,
+ __in DWORD64 ModuleBase,
+ __in ULONG ModuleSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK64)(
+ __in PCSTR SymbolName,
+ __in DWORD64 SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK64W)(
+ __in PCWSTR SymbolName,
+ __in DWORD64 SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYMBOL_REGISTERED_CALLBACK64)(
+ __in HANDLE hProcess,
+ __in ULONG ActionCode,
+ __in_opt ULONG64 CallbackData,
+ __in_opt ULONG64 UserContext
+ );
+
+typedef
+PVOID
+(CALLBACK *PSYMBOL_FUNCENTRY_CALLBACK)(
+ __in HANDLE hProcess,
+ __in DWORD AddrBase,
+ __in_opt PVOID UserContext
+ );
+
+typedef
+PVOID
+(CALLBACK *PSYMBOL_FUNCENTRY_CALLBACK64)(
+ __in HANDLE hProcess,
+ __in ULONG64 AddrBase,
+ __in ULONG64 UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+#define PSYM_ENUMMODULES_CALLBACK PSYM_ENUMMODULES_CALLBACK64
+#define PSYM_ENUMSYMBOLS_CALLBACK PSYM_ENUMSYMBOLS_CALLBACK64
+#define PSYM_ENUMSYMBOLS_CALLBACKW PSYM_ENUMSYMBOLS_CALLBACK64W
+#define PENUMLOADED_MODULES_CALLBACK PENUMLOADED_MODULES_CALLBACK64
+#define PSYMBOL_REGISTERED_CALLBACK PSYMBOL_REGISTERED_CALLBACK64
+#define PSYMBOL_FUNCENTRY_CALLBACK PSYMBOL_FUNCENTRY_CALLBACK64
+
+#else
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACK)(
+ __in PCSTR ModuleName,
+ __in ULONG BaseOfDll,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK)(
+ __in PCSTR SymbolName,
+ __in ULONG SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACKW)(
+ __in PCWSTR SymbolName,
+ __in ULONG SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACK)(
+ __in PCSTR ModuleName,
+ __in ULONG ModuleBase,
+ __in ULONG ModuleSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYMBOL_REGISTERED_CALLBACK)(
+ __in HANDLE hProcess,
+ __in ULONG ActionCode,
+ __in_opt PVOID CallbackData,
+ __in_opt PVOID UserContext
+ );
+
+#endif
+
+
+// values found in SYMBOL_INFO.Tag
+//
+// This was taken from cvconst.h and should
+// not override any values found there.
+//
+// #define _NO_CVCONST_H_ if you don't
+// have access to that file...
+
+#ifdef _NO_CVCONST_H
+
+// DIA enums
+
+enum SymTagEnum
+{
+ SymTagNull,
+ SymTagExe,
+ SymTagCompiland,
+ SymTagCompilandDetails,
+ SymTagCompilandEnv,
+ SymTagFunction,
+ SymTagBlock,
+ SymTagData,
+ SymTagAnnotation,
+ SymTagLabel,
+ SymTagPublicSymbol,
+ SymTagUDT,
+ SymTagEnum,
+ SymTagFunctionType,
+ SymTagPointerType,
+ SymTagArrayType,
+ SymTagBaseType,
+ SymTagTypedef,
+ SymTagBaseClass,
+ SymTagFriend,
+ SymTagFunctionArgType,
+ SymTagFuncDebugStart,
+ SymTagFuncDebugEnd,
+ SymTagUsingNamespace,
+ SymTagVTableShape,
+ SymTagVTable,
+ SymTagCustom,
+ SymTagThunk,
+ SymTagCustomType,
+ SymTagManagedType,
+ SymTagDimension,
+ SymTagMax
+};
+
+#endif
+
+//
+// flags found in SYMBOL_INFO.Flags
+//
+
+#define SYMFLAG_VALUEPRESENT 0x00000001
+#define SYMFLAG_REGISTER 0x00000008
+#define SYMFLAG_REGREL 0x00000010
+#define SYMFLAG_FRAMEREL 0x00000020
+#define SYMFLAG_PARAMETER 0x00000040
+#define SYMFLAG_LOCAL 0x00000080
+#define SYMFLAG_CONSTANT 0x00000100
+#define SYMFLAG_EXPORT 0x00000200
+#define SYMFLAG_FORWARDER 0x00000400
+#define SYMFLAG_FUNCTION 0x00000800
+#define SYMFLAG_VIRTUAL 0x00001000
+#define SYMFLAG_THUNK 0x00002000
+#define SYMFLAG_TLSREL 0x00004000
+#define SYMFLAG_SLOT 0x00008000
+#define SYMFLAG_ILREL 0x00010000
+#define SYMFLAG_METADATA 0x00020000
+#define SYMFLAG_CLR_TOKEN 0x00040000
+
+// this resets SymNext/Prev to the beginning
+// of the module passed in the address field
+
+#define SYMFLAG_RESET 0x80000000
+
+//
+// symbol type enumeration
+//
+typedef enum {
+ SymNone = 0,
+ SymCoff,
+ SymCv,
+ SymPdb,
+ SymExport,
+ SymDeferred,
+ SymSym, // .sym file
+ SymDia,
+ SymVirtual,
+ NumSymTypes
+} SYM_TYPE;
+
+//
+// symbol data structure
+//
+
+typedef struct _IMAGEHLP_SYMBOL64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
+ DWORD64 Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ CHAR Name[1]; // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
+
+typedef struct _IMAGEHLP_SYMBOL64_PACKAGE {
+ IMAGEHLP_SYMBOL64 sym;
+ CHAR name[MAX_SYM_NAME + 1];
+} IMAGEHLP_SYMBOL64_PACKAGE, *PIMAGEHLP_SYMBOL64_PACKAGE;
+
+typedef struct _IMAGEHLP_SYMBOLW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOLW64)
+ DWORD64 Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ WCHAR Name[1]; // symbol name (null terminated string)
+} IMAGEHLP_SYMBOLW64, *PIMAGEHLP_SYMBOLW64;
+
+typedef struct _IMAGEHLP_SYMBOLW64_PACKAGE {
+ IMAGEHLP_SYMBOLW64 sym;
+ WCHAR name[MAX_SYM_NAME + 1];
+} IMAGEHLP_SYMBOLW64_PACKAGE, *PIMAGEHLP_SYMBOLW64_PACKAGE;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+ #define IMAGEHLP_SYMBOL IMAGEHLP_SYMBOL64
+ #define PIMAGEHLP_SYMBOL PIMAGEHLP_SYMBOL64
+ #define IMAGEHLP_SYMBOL_PACKAGE IMAGEHLP_SYMBOL64_PACKAGE
+ #define PIMAGEHLP_SYMBOL_PACKAGE PIMAGEHLP_SYMBOL64_PACKAGE
+ #define IMAGEHLP_SYMBOLW IMAGEHLP_SYMBOLW64
+ #define PIMAGEHLP_SYMBOLW PIMAGEHLP_SYMBOLW64
+ #define IMAGEHLP_SYMBOLW_PACKAGE IMAGEHLP_SYMBOLW64_PACKAGE
+ #define PIMAGEHLP_SYMBOLW_PACKAGE PIMAGEHLP_SYMBOLW64_PACKAGE
+
+#else
+
+ typedef struct _IMAGEHLP_SYMBOL {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL)
+ DWORD Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ CHAR Name[1]; // symbol name (null terminated string)
+ } IMAGEHLP_SYMBOL, *PIMAGEHLP_SYMBOL;
+
+ typedef struct _IMAGEHLP_SYMBOL_PACKAGE {
+ IMAGEHLP_SYMBOL sym;
+ CHAR name[MAX_SYM_NAME + 1];
+ } IMAGEHLP_SYMBOL_PACKAGE, *PIMAGEHLP_SYMBOL_PACKAGE;
+
+ typedef struct _IMAGEHLP_SYMBOLW {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOLW)
+ DWORD Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ WCHAR Name[1]; // symbol name (null terminated string)
+ } IMAGEHLP_SYMBOLW, *PIMAGEHLP_SYMBOLW;
+
+ typedef struct _IMAGEHLP_SYMBOLW_PACKAGE {
+ IMAGEHLP_SYMBOLW sym;
+ WCHAR name[MAX_SYM_NAME + 1];
+ } IMAGEHLP_SYMBOLW_PACKAGE, *PIMAGEHLP_SYMBOLW_PACKAGE;
+
+#endif
+
+//
+// module data structure
+//
+
+typedef struct _IMAGEHLP_MODULE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+ // new elements: 07-Jun-2002
+ CHAR LoadedPdbName[256]; // pdb file name
+ DWORD CVSig; // Signature of the CV record in the debug directories
+ CHAR CVData[MAX_PATH * 3]; // Contents of the CV record
+ DWORD PdbSig; // Signature of PDB
+ GUID PdbSig70; // Signature of PDB (VC 7 and up)
+ DWORD PdbAge; // DBI age of pdb
+ BOOL PdbUnmatched; // loaded an unmatched pdb
+ BOOL DbgUnmatched; // loaded an unmatched dbg
+ BOOL LineNumbers; // we have line number information
+ BOOL GlobalSymbols; // we have internal symbol information
+ BOOL TypeInfo; // we have type information
+ // new elements: 17-Dec-2003
+ BOOL SourceIndexed; // pdb supports source server
+ BOOL Publics; // contains public symbols
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
+
+typedef struct _IMAGEHLP_MODULEW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ WCHAR ModuleName[32]; // module name
+ WCHAR ImageName[256]; // image name
+ // new elements: 07-Jun-2002
+ WCHAR LoadedImageName[256]; // symbol file name
+ WCHAR LoadedPdbName[256]; // pdb file name
+ DWORD CVSig; // Signature of the CV record in the debug directories
+ WCHAR CVData[MAX_PATH * 3]; // Contents of the CV record
+ DWORD PdbSig; // Signature of PDB
+ GUID PdbSig70; // Signature of PDB (VC 7 and up)
+ DWORD PdbAge; // DBI age of pdb
+ BOOL PdbUnmatched; // loaded an unmatched pdb
+ BOOL DbgUnmatched; // loaded an unmatched dbg
+ BOOL LineNumbers; // we have line number information
+ BOOL GlobalSymbols; // we have internal symbol information
+ BOOL TypeInfo; // we have type information
+ // new elements: 17-Dec-2003
+ BOOL SourceIndexed; // pdb supports source server
+ BOOL Publics; // contains public symbols
+} IMAGEHLP_MODULEW64, *PIMAGEHLP_MODULEW64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_MODULE IMAGEHLP_MODULE64
+#define PIMAGEHLP_MODULE PIMAGEHLP_MODULE64
+#define IMAGEHLP_MODULEW IMAGEHLP_MODULEW64
+#define PIMAGEHLP_MODULEW PIMAGEHLP_MODULEW64
+#else
+typedef struct _IMAGEHLP_MODULE {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE)
+ DWORD BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULE, *PIMAGEHLP_MODULE;
+
+typedef struct _IMAGEHLP_MODULEW {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE)
+ DWORD BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ WCHAR ModuleName[32]; // module name
+ WCHAR ImageName[256]; // image name
+ WCHAR LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULEW, *PIMAGEHLP_MODULEW;
+#endif
+
+//
+// source file line data structure
+//
+
+typedef struct _IMAGEHLP_LINE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
+
+typedef struct _IMAGEHLP_LINEW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PWSTR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINEW64, *PIMAGEHLP_LINEW64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_LINE IMAGEHLP_LINE64
+#define PIMAGEHLP_LINE PIMAGEHLP_LINE64
+#else
+typedef struct _IMAGEHLP_LINE {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD Address; // first instruction of line
+} IMAGEHLP_LINE, *PIMAGEHLP_LINE;
+
+typedef struct _IMAGEHLP_LINEW {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINEW, *PIMAGEHLP_LINEW;
+#endif
+
+//
+// source file structure
+//
+
+typedef struct _SOURCEFILE {
+ DWORD64 ModBase; // base address of loaded module
+ PCHAR FileName; // full filename of source
+} SOURCEFILE, *PSOURCEFILE;
+
+typedef struct _SOURCEFILEW {
+ DWORD64 ModBase; // base address of loaded module
+ PWSTR FileName; // full filename of source
+} SOURCEFILEW, *PSOURCEFILEW;
+
+//
+// data structures used for registered symbol callbacks
+//
+
+#define CBA_DEFERRED_SYMBOL_LOAD_START 0x00000001
+#define CBA_DEFERRED_SYMBOL_LOAD_COMPLETE 0x00000002
+#define CBA_DEFERRED_SYMBOL_LOAD_FAILURE 0x00000003
+#define CBA_SYMBOLS_UNLOADED 0x00000004
+#define CBA_DUPLICATE_SYMBOL 0x00000005
+#define CBA_READ_MEMORY 0x00000006
+#define CBA_DEFERRED_SYMBOL_LOAD_CANCEL 0x00000007
+#define CBA_SET_OPTIONS 0x00000008
+#define CBA_EVENT 0x00000010
+#define CBA_DEFERRED_SYMBOL_LOAD_PARTIAL 0x00000020
+#define CBA_DEBUG_INFO 0x10000000
+#define CBA_SRCSRV_INFO 0x20000000
+#define CBA_SRCSRV_EVENT 0x40000000
+
+typedef struct _IMAGEHLP_CBA_READ_MEMORY {
+ DWORD64 addr; // address to read from
+ PVOID buf; // buffer to read to
+ DWORD bytes; // amount of bytes to read
+ DWORD *bytesread; // pointer to store amount of bytes read
+} IMAGEHLP_CBA_READ_MEMORY, *PIMAGEHLP_CBA_READ_MEMORY;
+
+enum {
+ sevInfo = 0,
+ sevProblem,
+ sevAttn,
+ sevFatal,
+ sevMax // unused
+};
+
+#define EVENT_SRCSPEW_START 100
+#define EVENT_SRCSPEW 100
+#define EVENT_SRCSPEW_END 199
+
+typedef struct _IMAGEHLP_CBA_EVENT {
+ DWORD severity; // values from sevInfo to sevFatal
+ DWORD code; // numerical code IDs the error
+ PCHAR desc; // may contain a text description of the error
+ PVOID object; // value dependant upon the error code
+} IMAGEHLP_CBA_EVENT, *PIMAGEHLP_CBA_EVENT;
+
+typedef struct _IMAGEHLP_CBA_EVENTW {
+ DWORD severity; // values from sevInfo to sevFatal
+ DWORD code; // numerical code IDs the error
+ PCWSTR desc; // may contain a text description of the error
+ PVOID object; // value dependant upon the error code
+} IMAGEHLP_CBA_EVENTW, *PIMAGEHLP_CBA_EVENTW;
+
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOAD64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD CheckSum; // checksum from the pe header
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ CHAR FileName[MAX_PATH]; // symbols file or image name
+ BOOLEAN Reparse; // load failure reparse
+ HANDLE hFile; // file handle, if passed
+ DWORD Flags; //
+} IMAGEHLP_DEFERRED_SYMBOL_LOAD64, *PIMAGEHLP_DEFERRED_SYMBOL_LOAD64;
+
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOADW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOADW64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD CheckSum; // checksum from the pe header
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ WCHAR FileName[MAX_PATH + 1]; // symbols file or image name
+ BOOLEAN Reparse; // load failure reparse
+ HANDLE hFile; // file handle, if passed
+ DWORD Flags; //
+} IMAGEHLP_DEFERRED_SYMBOL_LOADW64, *PIMAGEHLP_DEFERRED_SYMBOL_LOADW64;
+
+#define DSLFLAG_MISMATCHED_PDB 0x1
+#define DSLFLAG_MISMATCHED_DBG 0x2
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_DEFERRED_SYMBOL_LOAD IMAGEHLP_DEFERRED_SYMBOL_LOAD64
+#define PIMAGEHLP_DEFERRED_SYMBOL_LOAD PIMAGEHLP_DEFERRED_SYMBOL_LOAD64
+#else
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOAD {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD)
+ DWORD BaseOfImage; // base load address of module
+ DWORD CheckSum; // checksum from the pe header
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ CHAR FileName[MAX_PATH]; // symbols file or image name
+ BOOLEAN Reparse; // load failure reparse
+ HANDLE hFile; // file handle, if passed
+} IMAGEHLP_DEFERRED_SYMBOL_LOAD, *PIMAGEHLP_DEFERRED_SYMBOL_LOAD;
+#endif
+
+typedef struct _IMAGEHLP_DUPLICATE_SYMBOL64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DUPLICATE_SYMBOL64)
+ DWORD NumberOfDups; // number of duplicates in the Symbol array
+ PIMAGEHLP_SYMBOL64 Symbol; // array of duplicate symbols
+ DWORD SelectedSymbol; // symbol selected (-1 to start)
+} IMAGEHLP_DUPLICATE_SYMBOL64, *PIMAGEHLP_DUPLICATE_SYMBOL64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_DUPLICATE_SYMBOL IMAGEHLP_DUPLICATE_SYMBOL64
+#define PIMAGEHLP_DUPLICATE_SYMBOL PIMAGEHLP_DUPLICATE_SYMBOL64
+#else
+typedef struct _IMAGEHLP_DUPLICATE_SYMBOL {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DUPLICATE_SYMBOL)
+ DWORD NumberOfDups; // number of duplicates in the Symbol array
+ PIMAGEHLP_SYMBOL Symbol; // array of duplicate symbols
+ DWORD SelectedSymbol; // symbol selected (-1 to start)
+} IMAGEHLP_DUPLICATE_SYMBOL, *PIMAGEHLP_DUPLICATE_SYMBOL;
+#endif
+
+// If dbghelp ever needs to display graphical UI, it will use this as the parent window.
+
+BOOL
+IMAGEAPI
+SymSetParentWindow(
+ __in HWND hwnd
+ );
+
+PCHAR
+IMAGEAPI
+SymSetHomeDirectory(
+ __in_opt HANDLE hProcess,
+ __in_opt PCSTR dir
+ );
+
+PWSTR
+IMAGEAPI
+SymSetHomeDirectoryW(
+ __in_opt HANDLE hProcess,
+ __in_opt PCWSTR dir
+ );
+
+PCHAR
+IMAGEAPI
+SymGetHomeDirectory(
+ __in DWORD type,
+ __out_ecount(size) PSTR dir,
+ __in size_t size
+ );
+
+PWSTR
+IMAGEAPI
+SymGetHomeDirectoryW(
+ __in DWORD type,
+ __out_ecount(size) PWSTR dir,
+ __in size_t size
+ );
+
+typedef enum {
+ hdBase = 0, // root directory for dbghelp
+ hdSym, // where symbols are stored
+ hdSrc, // where source is stored
+ hdMax // end marker
+};
+
+typedef struct _OMAP {
+ ULONG rva;
+ ULONG rvaTo;
+} OMAP, *POMAP;
+
+BOOL
+IMAGEAPI
+SymGetOmaps(
+ __in HANDLE hProcess,
+ __in DWORD64 BaseOfDll,
+ __out POMAP *OmapTo,
+ __out PDWORD64 cOmapTo,
+ __out POMAP *OmapFrom,
+ __out PDWORD64 cOmapFrom
+ );
+
+//
+// options that are set/returned by SymSetOptions() & SymGetOptions()
+// these are used as a mask
+//
+#define SYMOPT_CASE_INSENSITIVE 0x00000001
+#define SYMOPT_UNDNAME 0x00000002
+#define SYMOPT_DEFERRED_LOADS 0x00000004
+#define SYMOPT_NO_CPP 0x00000008
+#define SYMOPT_LOAD_LINES 0x00000010
+#define SYMOPT_OMAP_FIND_NEAREST 0x00000020
+#define SYMOPT_LOAD_ANYTHING 0x00000040
+#define SYMOPT_IGNORE_CVREC 0x00000080
+#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
+#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
+#define SYMOPT_EXACT_SYMBOLS 0x00000400
+#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
+#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
+#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
+#define SYMOPT_PUBLICS_ONLY 0x00004000
+#define SYMOPT_NO_PUBLICS 0x00008000
+#define SYMOPT_AUTO_PUBLICS 0x00010000
+#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
+#define SYMOPT_SECURE 0x00040000
+#define SYMOPT_NO_PROMPTS 0x00080000
+#define SYMOPT_OVERWRITE 0x00100000
+#define SYMOPT_IGNORE_IMAGEDIR 0x00200000
+#define SYMOPT_FLAT_DIRECTORY 0x00400000
+#define SYMOPT_FAVOR_COMPRESSED 0x00800000
+#define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000
+#define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000
+
+#define SYMOPT_DEBUG 0x80000000
+
+DWORD
+IMAGEAPI
+SymSetOptions(
+ __in DWORD SymOptions
+ );
+
+DWORD
+IMAGEAPI
+SymGetOptions(
+ VOID
+ );
+
+BOOL
+IMAGEAPI
+SymCleanup(
+ __in HANDLE hProcess
+ );
+
+BOOL
+IMAGEAPI
+SymMatchString(
+ __in PCSTR string,
+ __in PCSTR expression,
+ __in BOOL fCase
+ );
+
+BOOL
+IMAGEAPI
+SymMatchStringA(
+ __in PCSTR string,
+ __in PCSTR expression,
+ __in BOOL fCase
+ );
+
+BOOL
+IMAGEAPI
+SymMatchStringW(
+ __in PCWSTR string,
+ __in PCWSTR expression,
+ __in BOOL fCase
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSOURCEFILES_CALLBACK)(
+ __in PSOURCEFILE pSourceFile,
+ __in_opt PVOID UserContext
+ );
+
+// for backwards compatibility - don't use this
+#define PSYM_ENUMSOURCFILES_CALLBACK PSYM_ENUMSOURCEFILES_CALLBACK
+
+BOOL
+IMAGEAPI
+SymEnumSourceFiles(
+ __in HANDLE hProcess,
+ __in ULONG64 ModBase,
+ __in_opt PCSTR Mask,
+ __in PSYM_ENUMSOURCEFILES_CALLBACK cbSrcFiles,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSOURCEFILES_CALLBACKW)(
+ __in PSOURCEFILEW pSourceFile,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSourceFilesW(
+ __in HANDLE hProcess,
+ __in ULONG64 ModBase,
+ __in_opt PCWSTR Mask,
+ __in PSYM_ENUMSOURCEFILES_CALLBACKW cbSrcFiles,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumerateModules64(
+ __in HANDLE hProcess,
+ __in PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumerateModulesW64(
+ __in HANDLE hProcess,
+ __in PSYM_ENUMMODULES_CALLBACKW64 EnumModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymEnumerateModules SymEnumerateModules64
+#else
+BOOL
+IMAGEAPI
+SymEnumerateModules(
+ __in HANDLE hProcess,
+ __in PSYM_ENUMMODULES_CALLBACK EnumModulesCallback,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModulesEx(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModulesExW(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACKW64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModules64(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModulesW64(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACKW64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define EnumerateLoadedModules EnumerateLoadedModules64
+#else
+BOOL
+IMAGEAPI
+EnumerateLoadedModules(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACK EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+PVOID
+IMAGEAPI
+SymFunctionTableAccess64(
+ __in HANDLE hProcess,
+ __in DWORD64 AddrBase
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymFunctionTableAccess SymFunctionTableAccess64
+#else
+PVOID
+IMAGEAPI
+SymFunctionTableAccess(
+ __in HANDLE hProcess,
+ __in DWORD AddrBase
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetUnwindInfo(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __out_bcount_opt(*Size) PVOID Buffer,
+ __inout PULONG Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfo64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out PIMAGEHLP_MODULE64 ModuleInfo
+ );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfoW64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out PIMAGEHLP_MODULEW64 ModuleInfo
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetModuleInfo SymGetModuleInfo64
+#define SymGetModuleInfoW SymGetModuleInfoW64
+#else
+BOOL
+IMAGEAPI
+SymGetModuleInfo(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PIMAGEHLP_MODULE ModuleInfo
+ );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfoW(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PIMAGEHLP_MODULEW ModuleInfo
+ );
+#endif
+
+DWORD64
+IMAGEAPI
+SymGetModuleBase64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetModuleBase SymGetModuleBase64
+#else
+DWORD
+IMAGEAPI
+SymGetModuleBase(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr
+ );
+#endif
+
+typedef struct _SRCCODEINFO {
+ DWORD SizeOfStruct; // set to sizeof(SRCCODEINFO)
+ PVOID Key; // not used
+ DWORD64 ModBase; // base address of module this applies to
+ CHAR Obj[MAX_PATH + 1]; // the object file within the module
+ CHAR FileName[MAX_PATH + 1]; // full filename
+ DWORD LineNumber; // line number in file
+ DWORD64 Address; // first instruction of line
+} SRCCODEINFO, *PSRCCODEINFO;
+
+typedef struct _SRCCODEINFOW {
+ DWORD SizeOfStruct; // set to sizeof(SRCCODEINFO)
+ PVOID Key; // not used
+ DWORD64 ModBase; // base address of module this applies to
+ WCHAR Obj[MAX_PATH + 1]; // the object file within the module
+ WCHAR FileName[MAX_PATH + 1]; // full filename
+ DWORD LineNumber; // line number in file
+ DWORD64 Address; // first instruction of line
+} SRCCODEINFOW, *PSRCCODEINFOW;
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMLINES_CALLBACK)(
+ __in PSRCCODEINFO LineInfo,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumLines(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR Obj,
+ __in_opt PCSTR File,
+ __in PSYM_ENUMLINES_CALLBACK EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMLINES_CALLBACKW)(
+ __in PSRCCODEINFOW LineInfo,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumLinesW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR Obj,
+ __in_opt PCWSTR File,
+ __in PSYM_ENUMLINES_CALLBACKW EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromAddr64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINE64 Line64
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromAddrW64(
+ __in HANDLE hProcess,
+ __in DWORD64 dwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINEW64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSourceLines(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR Obj,
+ __in_opt PCSTR File,
+ __in_opt DWORD Line,
+ __in DWORD Flags,
+ __in PSYM_ENUMLINES_CALLBACK EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSourceLinesW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR Obj,
+ __in_opt PCWSTR File,
+ __in_opt DWORD Line,
+ __in DWORD Flags,
+ __in PSYM_ENUMLINES_CALLBACKW EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+// flags for SymEnumSourceLines
+
+#define ESLFLAG_FULLPATH 0x1
+#define ESLFLAG_NEAREST 0x2
+#define ESLFLAG_PREV 0x4
+#define ESLFLAG_NEXT 0x8
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineFromAddr SymGetLineFromAddr64
+#define SymGetLineFromAddrW SymGetLineFromAddrW64
+#else
+BOOL
+IMAGEAPI
+SymGetLineFromAddr(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINE Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromAddrW(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINEW Line
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLineFromName64(
+ __in HANDLE hProcess,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR FileName,
+ __in DWORD dwLineNumber,
+ __out PLONG plDisplacement,
+ __inout PIMAGEHLP_LINE64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromNameW64(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR ModuleName,
+ __in_opt PCWSTR FileName,
+ __in DWORD dwLineNumber,
+ __out PLONG plDisplacement,
+ __inout PIMAGEHLP_LINEW64 Line
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineFromName SymGetLineFromName64
+#else
+BOOL
+IMAGEAPI
+SymGetLineFromName(
+ __in HANDLE hProcess,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR FileName,
+ __in DWORD dwLineNumber,
+ __out PLONG plDisplacement,
+ __inout PIMAGEHLP_LINE Line
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLineNext64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineNextW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW64 Line
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineNext SymGetLineNext64
+#else
+BOOL
+IMAGEAPI
+SymGetLineNext(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineNextW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW Line
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLinePrev64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLinePrevW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW64 Line
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLinePrev SymGetLinePrev64
+#else
+BOOL
+IMAGEAPI
+SymGetLinePrev(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLinePrevW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW Line
+ );
+#endif
+
+ULONG
+IMAGEAPI
+SymGetFileLineOffsets64(
+ __in HANDLE hProcess,
+ __in_opt PCSTR ModuleName,
+ __in PCSTR FileName,
+ __out_ecount(BufferLines) PDWORD64 Buffer,
+ __in ULONG BufferLines
+ );
+
+BOOL
+IMAGEAPI
+SymMatchFileName(
+ __in PCSTR FileName,
+ __in PCSTR Match,
+ __deref_opt_out PSTR *FileNameStop,
+ __deref_opt_out PSTR *MatchStop
+ );
+
+BOOL
+IMAGEAPI
+SymMatchFileNameW(
+ __in PCWSTR FileName,
+ __in PCWSTR Match,
+ __deref_opt_out PWSTR *FileNameStop,
+ __deref_opt_out PWSTR *MatchStop
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFile(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR Params,
+ __in PCSTR FileSpec,
+ __out_ecount(Size) PSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR Params,
+ __in PCWSTR FileSpec,
+ __out_ecount(Size) PWSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileToken(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in PCSTR FileSpec,
+ __deref_out PVOID *Token,
+ __out DWORD *Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileTokenW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in PCWSTR FileSpec,
+ __deref_out PVOID *Token,
+ __out DWORD *Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileFromToken(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCSTR Params,
+ __out_ecount(Size) PSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileFromTokenW(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCWSTR Params,
+ __out_ecount(Size) PWSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceVarFromToken(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCSTR Params,
+ __in PCSTR VarName,
+ __out_ecount(Size) PSTR Value,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceVarFromTokenW(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCWSTR Params,
+ __in PCWSTR VarName,
+ __out_ecount(Size) PWSTR Value,
+ __in DWORD Size
+ );
+
+typedef BOOL (CALLBACK *PENUMSOURCEFILETOKENSCALLBACK)(__in PVOID token, __in size_t size);
+
+BOOL
+IMAGEAPI
+SymEnumSourceFileTokens(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in PENUMSOURCEFILETOKENSCALLBACK Callback
+ );
+
+BOOL
+IMAGEAPI
+SymInitialize(
+ __in HANDLE hProcess,
+ __in_opt PCSTR UserSearchPath,
+ __in BOOL fInvadeProcess
+ );
+
+BOOL
+IMAGEAPI
+SymInitializeW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR UserSearchPath,
+ __in BOOL fInvadeProcess
+ );
+
+BOOL
+IMAGEAPI
+SymGetSearchPath(
+ __in HANDLE hProcess,
+ __out_ecount(SearchPathLength) PSTR SearchPath,
+ __in DWORD SearchPathLength
+ );
+
+BOOL
+IMAGEAPI
+SymGetSearchPathW(
+ __in HANDLE hProcess,
+ __out_ecount(SearchPathLength) PWSTR SearchPath,
+ __in DWORD SearchPathLength
+ );
+
+BOOL
+IMAGEAPI
+SymSetSearchPath(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SearchPath
+ );
+
+BOOL
+IMAGEAPI
+SymSetSearchPathW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SearchPath
+ );
+
+#define SLMFLAG_VIRTUAL 0x1
+#define SLMFLAG_ALT_INDEX 0x2
+#define SLMFLAG_NO_SYMBOLS 0x4
+
+DWORD64
+IMAGEAPI
+SymLoadModuleEx(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCSTR ImageName,
+ __in_opt PCSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in DWORD DllSize,
+ __in_opt PMODLOAD_DATA Data,
+ __in_opt DWORD Flags
+ );
+
+DWORD64
+IMAGEAPI
+SymLoadModuleExW(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCWSTR ImageName,
+ __in_opt PCWSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in DWORD DllSize,
+ __in_opt PMODLOAD_DATA Data,
+ __in_opt DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymUnloadModule64(
+ __in HANDLE hProcess,
+ __in DWORD64 BaseOfDll
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymUnloadModule SymUnloadModule64
+#else
+BOOL
+IMAGEAPI
+SymUnloadModule(
+ __in HANDLE hProcess,
+ __in DWORD BaseOfDll
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymUnDName64(
+ __in PIMAGEHLP_SYMBOL64 sym, // Symbol to undecorate
+ __out_ecount(UnDecNameLength) PSTR UnDecName, // Buffer to store undecorated name in
+ __in DWORD UnDecNameLength // Size of the buffer
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymUnDName SymUnDName64
+#else
+BOOL
+IMAGEAPI
+SymUnDName(
+ __in PIMAGEHLP_SYMBOL sym, // Symbol to undecorate
+ __out_ecount(UnDecNameLength) PSTR UnDecName, // Buffer to store undecorated name in
+ __in DWORD UnDecNameLength // Size of the buffer
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymRegisterCallback64(
+ __in HANDLE hProcess,
+ __in PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
+ __in ULONG64 UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymRegisterCallbackW64(
+ __in HANDLE hProcess,
+ __in PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
+ __in ULONG64 UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymRegisterFunctionEntryCallback64(
+ __in HANDLE hProcess,
+ __in PSYMBOL_FUNCENTRY_CALLBACK64 CallbackFunction,
+ __in ULONG64 UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymRegisterCallback SymRegisterCallback64
+#define SymRegisterFunctionEntryCallback SymRegisterFunctionEntryCallback64
+#else
+BOOL
+IMAGEAPI
+SymRegisterCallback(
+ __in HANDLE hProcess,
+ __in PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymRegisterFunctionEntryCallback(
+ __in HANDLE hProcess,
+ __in PSYMBOL_FUNCENTRY_CALLBACK CallbackFunction,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+
+typedef struct _IMAGEHLP_SYMBOL_SRC {
+ DWORD sizeofstruct;
+ DWORD type;
+ char file[MAX_PATH];
+} IMAGEHLP_SYMBOL_SRC, *PIMAGEHLP_SYMBOL_SRC;
+
+typedef struct _MODULE_TYPE_INFO { // AKA TYPTYP
+ USHORT dataLength;
+ USHORT leaf;
+ BYTE data[1];
+} MODULE_TYPE_INFO, *PMODULE_TYPE_INFO;
+
+typedef struct _SYMBOL_INFO {
+ ULONG SizeOfStruct;
+ ULONG TypeIndex; // Type Index of symbol
+ ULONG64 Reserved[2];
+ ULONG Index;
+ ULONG Size;
+ ULONG64 ModBase; // Base Address of module comtaining this symbol
+ ULONG Flags;
+ ULONG64 Value; // Value of symbol, ValuePresent should be 1
+ ULONG64 Address; // Address of symbol including base address of module
+ ULONG Register; // register holding value or pointer to value
+ ULONG Scope; // scope of the symbol
+ ULONG Tag; // pdb classification
+ ULONG NameLen; // Actual length of name
+ ULONG MaxNameLen;
+ CHAR Name[1]; // Name of symbol
+} SYMBOL_INFO, *PSYMBOL_INFO;
+
+typedef struct _SYMBOL_INFO_PACKAGE {
+ SYMBOL_INFO si;
+ CHAR name[MAX_SYM_NAME + 1];
+} SYMBOL_INFO_PACKAGE, *PSYMBOL_INFO_PACKAGE;
+
+typedef struct _SYMBOL_INFOW {
+ ULONG SizeOfStruct;
+ ULONG TypeIndex; // Type Index of symbol
+ ULONG64 Reserved[2];
+ ULONG Index;
+ ULONG Size;
+ ULONG64 ModBase; // Base Address of module comtaining this symbol
+ ULONG Flags;
+ ULONG64 Value; // Value of symbol, ValuePresent should be 1
+ ULONG64 Address; // Address of symbol including base address of module
+ ULONG Register; // register holding value or pointer to value
+ ULONG Scope; // scope of the symbol
+ ULONG Tag; // pdb classification
+ ULONG NameLen; // Actual length of name
+ ULONG MaxNameLen;
+ WCHAR Name[1]; // Name of symbol
+} SYMBOL_INFOW, *PSYMBOL_INFOW;
+
+typedef struct _SYMBOL_INFO_PACKAGEW {
+ SYMBOL_INFOW si;
+ WCHAR name[MAX_SYM_NAME + 1];
+} SYMBOL_INFO_PACKAGEW, *PSYMBOL_INFO_PACKAGEW;
+
+typedef struct _IMAGEHLP_STACK_FRAME
+{
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 BackingStoreOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[5];
+ BOOL Virtual;
+ ULONG Reserved2;
+} IMAGEHLP_STACK_FRAME, *PIMAGEHLP_STACK_FRAME;
+
+typedef VOID IMAGEHLP_CONTEXT, *PIMAGEHLP_CONTEXT;
+
+
+BOOL
+IMAGEAPI
+SymSetContext(
+ __in HANDLE hProcess,
+ __in PIMAGEHLP_STACK_FRAME StackFrame,
+ __in_opt PIMAGEHLP_CONTEXT Context
+ );
+
+BOOL
+IMAGEAPI
+SymSetScopeFromAddr(
+ __in HANDLE hProcess,
+ __in ULONG64 Address
+ );
+
+BOOL
+IMAGEAPI
+SymSetScopeFromIndex(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMPROCESSES_CALLBACK)(
+ __in HANDLE hProcess,
+ __in PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumProcesses(
+ __in PSYM_ENUMPROCESSES_CALLBACK EnumProcessesCallback,
+ __in PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymFromAddr(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __out_opt PDWORD64 Displacement,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromAddrW(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __out_opt PDWORD64 Displacement,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromToken(
+ __in HANDLE hProcess,
+ __in DWORD64 Base,
+ __in DWORD Token,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromTokenW(
+ __in HANDLE hProcess,
+ __in DWORD64 Base,
+ __in DWORD Token,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymNext(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFO si
+ );
+
+BOOL
+IMAGEAPI
+SymNextW(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFOW siw
+ );
+
+BOOL
+IMAGEAPI
+SymPrev(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFO si
+ );
+
+BOOL
+IMAGEAPI
+SymPrevW(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFOW siw
+ );
+
+// While SymFromName will provide a symbol from a name,
+// SymEnumSymbols can provide the same matching information
+// for ALL symbols with a matching name, even regular
+// expressions. That way you can search across modules
+// and differentiate between identically named symbols.
+
+BOOL
+IMAGEAPI
+SymFromName(
+ __in HANDLE hProcess,
+ __in PCSTR Name,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromNameW(
+ __in HANDLE hProcess,
+ __in PCWSTR Name,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACK)(
+ __in PSYMBOL_INFO pSymInfo,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbols(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCSTR Mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACKW)(
+ __in PSYMBOL_INFOW pSymInfo,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbolsW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCWSTR Mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbolsForAddr(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbolsForAddrW(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+#define SYMSEARCH_MASKOBJS 0x01 // used internally to implement other APIs
+#define SYMSEARCH_RECURSE 0X02 // recurse scopes
+#define SYMSEARCH_GLOBALSONLY 0X04 // search only for global symbols
+#define SYMSEARCH_ALLITEMS 0X08 // search for everything in the pdb, not just normal scoped symbols
+
+BOOL
+IMAGEAPI
+SymSearch(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt DWORD Index,
+ __in_opt DWORD SymTag,
+ __in_opt PCSTR Mask,
+ __in_opt DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext,
+ __in DWORD Options
+ );
+
+BOOL
+IMAGEAPI
+SymSearchW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt DWORD Index,
+ __in_opt DWORD SymTag,
+ __in_opt PCWSTR Mask,
+ __in_opt DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext,
+ __in DWORD Options
+ );
+
+BOOL
+IMAGEAPI
+SymGetScope(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetScopeW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromIndex(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromIndexW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+typedef enum _IMAGEHLP_SYMBOL_TYPE_INFO {
+ TI_GET_SYMTAG,
+ TI_GET_SYMNAME,
+ TI_GET_LENGTH,
+ TI_GET_TYPE,
+ TI_GET_TYPEID,
+ TI_GET_BASETYPE,
+ TI_GET_ARRAYINDEXTYPEID,
+ TI_FINDCHILDREN,
+ TI_GET_DATAKIND,
+ TI_GET_ADDRESSOFFSET,
+ TI_GET_OFFSET,
+ TI_GET_VALUE,
+ TI_GET_COUNT,
+ TI_GET_CHILDRENCOUNT,
+ TI_GET_BITPOSITION,
+ TI_GET_VIRTUALBASECLASS,
+ TI_GET_VIRTUALTABLESHAPEID,
+ TI_GET_VIRTUALBASEPOINTEROFFSET,
+ TI_GET_CLASSPARENTID,
+ TI_GET_NESTED,
+ TI_GET_SYMINDEX,
+ TI_GET_LEXICALPARENT,
+ TI_GET_ADDRESS,
+ TI_GET_THISADJUST,
+ TI_GET_UDTKIND,
+ TI_IS_EQUIV_TO,
+ TI_GET_CALLING_CONVENTION,
+ TI_IS_CLOSE_EQUIV_TO,
+ TI_GTIEX_REQS_VALID,
+ TI_GET_VIRTUALBASEOFFSET,
+ TI_GET_VIRTUALBASEDISPINDEX,
+ TI_GET_IS_REFERENCE,
+ TI_GET_INDIRECTVIRTUALBASECLASS,
+ IMAGEHLP_SYMBOL_TYPE_INFO_MAX,
+} IMAGEHLP_SYMBOL_TYPE_INFO;
+
+typedef struct _TI_FINDCHILDREN_PARAMS {
+ ULONG Count;
+ ULONG Start;
+ ULONG ChildId[1];
+} TI_FINDCHILDREN_PARAMS;
+
+BOOL
+IMAGEAPI
+SymGetTypeInfo(
+ __in HANDLE hProcess,
+ __in DWORD64 ModBase,
+ __in ULONG TypeId,
+ __in IMAGEHLP_SYMBOL_TYPE_INFO GetType,
+ __out PVOID pInfo
+ );
+
+#define IMAGEHLP_GET_TYPE_INFO_UNCACHED 0x00000001
+#define IMAGEHLP_GET_TYPE_INFO_CHILDREN 0x00000002
+
+typedef struct _IMAGEHLP_GET_TYPE_INFO_PARAMS {
+ IN ULONG SizeOfStruct;
+ IN ULONG Flags;
+ IN ULONG NumIds;
+ IN PULONG TypeIds;
+ IN ULONG64 TagFilter;
+ IN ULONG NumReqs;
+ IN IMAGEHLP_SYMBOL_TYPE_INFO* ReqKinds;
+ IN PULONG_PTR ReqOffsets;
+ IN PULONG ReqSizes;
+ IN ULONG_PTR ReqStride;
+ IN ULONG_PTR BufferSize;
+ OUT PVOID Buffer;
+ OUT ULONG EntriesMatched;
+ OUT ULONG EntriesFilled;
+ OUT ULONG64 TagsFound;
+ OUT ULONG64 AllReqsValid;
+ IN ULONG NumReqsValid;
+ OUT PULONG64 ReqsValid OPTIONAL;
+} IMAGEHLP_GET_TYPE_INFO_PARAMS, *PIMAGEHLP_GET_TYPE_INFO_PARAMS;
+
+BOOL
+IMAGEAPI
+SymGetTypeInfoEx(
+ __in HANDLE hProcess,
+ __in DWORD64 ModBase,
+ __inout PIMAGEHLP_GET_TYPE_INFO_PARAMS Params
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypes(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypesW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypesByName(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCSTR mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypesByNameW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCWSTR mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymGetTypeFromName(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCSTR Name,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetTypeFromNameW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCWSTR Name,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymAddSymbol(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Size,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymAddSymbolW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCWSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Size,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymDeleteSymbol(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymDeleteSymbolW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCWSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymRefreshModuleList(
+ __in HANDLE hProcess
+ );
+
+BOOL
+IMAGEAPI
+SymAddSourceStream(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR StreamFile,
+ __in_bcount_opt(Size) PBYTE Buffer,
+ __in size_t Size
+ );
+
+typedef BOOL (WINAPI *SYMADDSOURCESTREAM)(HANDLE, ULONG64, PCSTR, PBYTE, size_t);
+
+BOOL
+IMAGEAPI
+SymAddSourceStreamA(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR StreamFile,
+ __in_bcount_opt(Size) PBYTE Buffer,
+ __in size_t Size
+ );
+
+typedef BOOL (WINAPI *SYMADDSOURCESTREAMA)(HANDLE, ULONG64, PCSTR, PBYTE, size_t);
+
+BOOL
+IMAGEAPI
+SymAddSourceStreamW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR FileSpec,
+ __in_bcount_opt(Size) PBYTE Buffer,
+ __in size_t Size
+ );
+
+BOOL
+IMAGEAPI
+SymSrvIsStoreW(
+ __in_opt HANDLE hProcess,
+ __in PCWSTR path
+ );
+
+BOOL
+IMAGEAPI
+SymSrvIsStore(
+ __in_opt HANDLE hProcess,
+ __in PCSTR path
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvDeltaName(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SymPath,
+ __in PCSTR Type,
+ __in PCSTR File1,
+ __in PCSTR File2
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvDeltaNameW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR Type,
+ __in PCWSTR File1,
+ __in PCWSTR File2
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvGetSupplement(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SymPath,
+ __in PCSTR Node,
+ __in PCSTR File
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvGetSupplementW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR Node,
+ __in PCWSTR File
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexes(
+ __in PCSTR File,
+ __out GUID *Id,
+ __out PDWORD Val1,
+ __out_opt PDWORD Val2,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexesW(
+ __in PCWSTR File,
+ __out GUID *Id,
+ __out PDWORD Val1,
+ __out_opt PDWORD Val2,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexStringW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SrvPath,
+ __in PCWSTR File,
+ __out_ecount(Size) PWSTR Index,
+ __in size_t Size,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexString(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SrvPath,
+ __in PCSTR File,
+ __out_ecount(Size) PSTR Index,
+ __in size_t Size,
+ __in DWORD Flags
+ );
+
+typedef struct {
+ DWORD sizeofstruct;
+ char file[MAX_PATH +1];
+ BOOL stripped;
+ DWORD timestamp;
+ DWORD size;
+ char dbgfile[MAX_PATH +1];
+ char pdbfile[MAX_PATH + 1];
+ GUID guid;
+ DWORD sig;
+ DWORD age;
+} SYMSRV_INDEX_INFO, *PSYMSRV_INDEX_INFO;
+
+typedef struct {
+ DWORD sizeofstruct;
+ WCHAR file[MAX_PATH +1];
+ BOOL stripped;
+ DWORD timestamp;
+ DWORD size;
+ WCHAR dbgfile[MAX_PATH +1];
+ WCHAR pdbfile[MAX_PATH + 1];
+ GUID guid;
+ DWORD sig;
+ DWORD age;
+} SYMSRV_INDEX_INFOW, *PSYMSRV_INDEX_INFOW;
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexInfo(
+ __in PCSTR File,
+ __out PSYMSRV_INDEX_INFO Info,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexInfoW(
+ __in PCWSTR File,
+ __out PSYMSRV_INDEX_INFOW Info,
+ __in DWORD Flags
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvStoreSupplement(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SrvPath,
+ __in PCSTR Node,
+ __in PCSTR File,
+ __in DWORD Flags
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvStoreSupplementW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR Node,
+ __in PCWSTR File,
+ __in DWORD Flags
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvStoreFile(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SrvPath,
+ __in PCSTR File,
+ __in DWORD Flags
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvStoreFileW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SrvPath,
+ __in PCWSTR File,
+ __in DWORD Flags
+ );
+
+// used by SymGetSymbolFile's "Type" parameter
+
+typedef enum {
+ sfImage = 0,
+ sfDbg,
+ sfPdb,
+ sfMpd,
+ sfMax
+};
+
+BOOL
+IMAGEAPI
+SymGetSymbolFile(
+ __in_opt HANDLE hProcess,
+ __in_opt PCSTR SymPath,
+ __in PCSTR ImageFile,
+ __in DWORD Type,
+ __out_ecount(cSymbolFile) PSTR SymbolFile,
+ __in size_t cSymbolFile,
+ __out_ecount(cDbgFile) PSTR DbgFile,
+ __in size_t cDbgFile
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymbolFileW(
+ __in_opt HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR ImageFile,
+ __in DWORD Type,
+ __out_ecount(cSymbolFile) PWSTR SymbolFile,
+ __in size_t cSymbolFile,
+ __out_ecount(cDbgFile) PWSTR DbgFile,
+ __in size_t cDbgFile
+ );
+
+//
+// Full user-mode dump creation.
+//
+
+typedef BOOL (WINAPI *PDBGHELP_CREATE_USER_DUMP_CALLBACK)(
+ __in DWORD DataType,
+ __in PVOID* Data,
+ __out LPDWORD DataLength,
+ __in_opt PVOID UserData
+ );
+
+BOOL
+WINAPI
+DbgHelpCreateUserDump(
+ __in_opt LPCSTR FileName,
+ __in PDBGHELP_CREATE_USER_DUMP_CALLBACK Callback,
+ __in_opt PVOID UserData
+ );
+
+BOOL
+WINAPI
+DbgHelpCreateUserDumpW(
+ __in_opt LPCWSTR FileName,
+ __in PDBGHELP_CREATE_USER_DUMP_CALLBACK Callback,
+ __in_opt PVOID UserData
+ );
+
+// -----------------------------------------------------------------
+// The following 4 legacy APIs are fully supported, but newer
+// ones are recommended. SymFromName and SymFromAddr provide
+// much more detailed info on the returned symbol.
+
+BOOL
+IMAGEAPI
+SymGetSymFromAddr64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out_opt PDWORD64 pdwDisplacement,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymFromAddr SymGetSymFromAddr64
+#else
+BOOL
+IMAGEAPI
+SymGetSymFromAddr(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out_opt PDWORD pdwDisplacement,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+#endif
+
+// While following two APIs will provide a symbol from a name,
+// SymEnumSymbols can provide the same matching information
+// for ALL symbols with a matching name, even regular
+// expressions. That way you can search across modules
+// and differentiate between identically named symbols.
+
+BOOL
+IMAGEAPI
+SymGetSymFromName64(
+ __in HANDLE hProcess,
+ __in PCSTR Name,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymFromName SymGetSymFromName64
+#else
+BOOL
+IMAGEAPI
+SymGetSymFromName(
+ __in HANDLE hProcess,
+ __in PCSTR Name,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+#endif
+
+
+// Symbol server exports
+
+typedef BOOL (WINAPI *PSYMBOLSERVERPROC)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPROCA)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPROCW)(PCWSTR, PCWSTR, PVOID, DWORD, DWORD, PWSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERBYINDEXPROC)(PCSTR, PCSTR, PCSTR, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERBYINDEXPROCA)(PCSTR, PCSTR, PCSTR, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERBYINDEXPROCW)(PCWSTR, PCWSTR, PCWSTR, PWSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVEROPENPROC)(VOID);
+typedef BOOL (WINAPI *PSYMBOLSERVERCLOSEPROC)(VOID);
+typedef BOOL (WINAPI *PSYMBOLSERVERSETOPTIONSPROC)(UINT_PTR, ULONG64);
+typedef BOOL (WINAPI *PSYMBOLSERVERSETOPTIONSWPROC)(UINT_PTR, ULONG64);
+typedef BOOL (CALLBACK WINAPI *PSYMBOLSERVERCALLBACKPROC)(UINT_PTR action, ULONG64 data, ULONG64 context);
+typedef UINT_PTR (WINAPI *PSYMBOLSERVERGETOPTIONSPROC)();
+typedef BOOL (WINAPI *PSYMBOLSERVERPINGPROC)(PCSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPINGPROCA)(PCSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPINGPROCW)(PCWSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETVERSION)(LPAPI_VERSION);
+typedef BOOL (WINAPI *PSYMBOLSERVERDELTANAME)(PCSTR, PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, PSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERDELTANAMEW)(PCWSTR, PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, PWSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETSUPPLEMENT)(PCSTR, PCSTR, PCSTR, PSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETSUPPLEMENTW)(PCWSTR, PCWSTR, PCWSTR, PWSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTORESUPPLEMENT)(PCSTR, PCSTR, PCSTR, PSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTORESUPPLEMENTW)(PCWSTR, PCWSTR, PCWSTR, PWSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETINDEXSTRING)(PVOID, DWORD, DWORD, PSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETINDEXSTRINGW)(PVOID, DWORD, DWORD, PWSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTOREFILE)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTOREFILEW)(PCWSTR, PCWSTR, PVOID, DWORD, DWORD, PWSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERISSTORE)(PCSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERISSTOREW)(PCWSTR);
+typedef DWORD (WINAPI *PSYMBOLSERVERVERSION)();
+typedef BOOL (CALLBACK WINAPI *PSYMBOLSERVERMESSAGEPROC)(UINT_PTR action, ULONG64 data, ULONG64 context);
+
+#define SYMSRV_VERSION 2
+
+#define SSRVOPT_CALLBACK 0x00000001
+#define SSRVOPT_DWORD 0x00000002
+#define SSRVOPT_DWORDPTR 0x00000004
+#define SSRVOPT_GUIDPTR 0x00000008
+#define SSRVOPT_OLDGUIDPTR 0x00000010
+#define SSRVOPT_UNATTENDED 0x00000020
+#define SSRVOPT_NOCOPY 0x00000040
+#define SSRVOPT_GETPATH 0x00000040
+#define SSRVOPT_PARENTWIN 0x00000080
+#define SSRVOPT_PARAMTYPE 0x00000100
+#define SSRVOPT_SECURE 0x00000200
+#define SSRVOPT_TRACE 0x00000400
+#define SSRVOPT_SETCONTEXT 0x00000800
+#define SSRVOPT_PROXY 0x00001000
+#define SSRVOPT_DOWNSTREAM_STORE 0x00002000
+#define SSRVOPT_OVERWRITE 0x00004000
+#define SSRVOPT_RESETTOU 0x00008000
+#define SSRVOPT_CALLBACKW 0x00010000
+#define SSRVOPT_FLAT_DEFAULT_STORE 0x00020000
+#define SSRVOPT_PROXYW 0x00040000
+#define SSRVOPT_MESSAGE 0x00080000
+#define SSRVOPT_SERVICE 0x00100000 // deprecated
+#define SSRVOPT_FAVOR_COMPRESSED 0x00200000
+#define SSRVOPT_STRING 0x00400000
+#define SSRVOPT_WINHTTP 0x00800000
+#define SSRVOPT_WININET 0x01000000
+
+#define SSRVOPT_MAX 0x0100000
+
+#define SSRVOPT_RESET ((ULONG_PTR)-1)
+
+
+#define NUM_SSRVOPTS 30
+
+#define SSRVACTION_TRACE 1
+#define SSRVACTION_QUERYCANCEL 2
+#define SSRVACTION_EVENT 3
+#define SSRVACTION_EVENTW 4
+#define SSRVACTION_SIZE 5
+
+#define SYMSTOREOPT_COMPRESS 0x01
+#define SYMSTOREOPT_OVERWRITE 0x02
+#define SYMSTOREOPT_RETURNINDEX 0x04
+#define SYMSTOREOPT_POINTER 0x08
+#define SYMSTOREOPT_ALT_INDEX 0x10
+#define SYMSTOREOPT_UNICODE 0x20
+#define SYMSTOREOPT_PASS_IF_EXISTS 0x40
+
+#ifdef DBGHELP_TRANSLATE_TCHAR
+ #define SymInitialize SymInitializeW
+ #define SymAddSymbol SymAddSymbolW
+ #define SymDeleteSymbol SymDeleteSymbolW
+ #define SearchTreeForFile SearchTreeForFileW
+ #define UnDecorateSymbolName UnDecorateSymbolNameW
+ #define SymGetLineFromName64 SymGetLineFromNameW64
+ #define SymGetLineFromAddr64 SymGetLineFromAddrW64
+ #define SymGetLineNext64 SymGetLineNextW64
+ #define SymGetLinePrev64 SymGetLinePrevW64
+ #define SymFromName SymFromNameW
+ #define SymFindExecutableImage SymFindExecutableImageW
+ #define FindExecutableImageEx FindExecutableImageExW
+ #define SymSearch SymSearchW
+ #define SymEnumLines SymEnumLinesW
+ #define SymEnumSourceLines SymEnumSourceLinesW
+ #define SymGetTypeFromName SymGetTypeFromNameW
+ #define SymEnumSymbolsForAddr SymEnumSymbolsForAddrW
+ #define SymFromAddr SymFromAddrW
+ #define SymMatchString SymMatchStringW
+ #define SymEnumSourceFiles SymEnumSourceFilesW
+ #define SymEnumSymbols SymEnumSymbolsW
+ #define SymLoadModuleEx SymLoadModuleExW
+ #define SymSetSearchPath SymSetSearchPathW
+ #define SymGetSearchPath SymGetSearchPathW
+ #define EnumDirTree EnumDirTreeW
+ #define SymFromToken SymFromTokenW
+ #define SymFromIndex SymFromIndexW
+ #define SymGetScope SymGetScopeW
+ #define SymNext SymNextW
+ #define SymPrev SymPrevW
+ #define SymEnumTypes SymEnumTypesW
+ #define SymEnumTypesByName SymEnumTypesByNameW
+ #define SymRegisterCallback64 SymRegisterCallbackW64
+ #define SymFindDebugInfoFile SymFindDebugInfoFileW
+ #define FindDebugInfoFileEx FindDebugInfoFileExW
+ #define SymFindFileInPath SymFindFileInPathW
+ #define SymEnumerateModules64 SymEnumerateModulesW64
+ #define SymSetHomeDirectory SymSetHomeDirectoryW
+ #define SymGetHomeDirectory SymGetHomeDirectoryW
+ #define SymGetSourceFile SymGetSourceFileW
+ #define SymGetSourceFileToken SymGetSourceFileTokenW
+ #define SymGetSourceFileFromToken SymGetSourceFileFromTokenW
+ #define SymGetSourceVarFromToken SymGetSourceVarFromTokenW
+ #define SymGetSourceFileToken SymGetSourceFileTokenW
+ #define SymGetFileLineOffsets64 SymGetFileLineOffsetsW64
+ #define SymFindFileInPath SymFindFileInPathW
+ #define SymMatchFileName SymMatchFileNameW
+ #define SymGetSourceFileFromToken SymGetSourceFileFromTokenW
+ #define SymGetSourceVarFromToken SymGetSourceVarFromTokenW
+ #define SymGetModuleInfo64 SymGetModuleInfoW64
+ #define SymSrvIsStore SymSrvIsStoreW
+ #define SymSrvDeltaName SymSrvDeltaNameW
+ #define SymSrvGetSupplement SymSrvGetSupplementW
+ #define SymSrvStoreSupplement SymSrvStoreSupplementW
+ #define SymSrvGetFileIndexes SymSrvGetFileIndexes
+ #define SymSrvGetFileIndexString SymSrvGetFileIndexStringW
+ #define SymSrvStoreFile SymSrvStoreFileW
+ #define SymGetSymbolFile SymGetSymbolFileW
+ #define EnumerateLoadedModules64 EnumerateLoadedModulesW64
+ #define EnumerateLoadedModulesEx EnumerateLoadedModulesExW
+ #define SymSrvGetFileIndexInfo SymSrvGetFileIndexInfoW
+
+ #define IMAGEHLP_LINE64 IMAGEHLP_LINEW64
+ #define PIMAGEHLP_LINE64 PIMAGEHLP_LINEW64
+ #define SYMBOL_INFO SYMBOL_INFOW
+ #define PSYMBOL_INFO PSYMBOL_INFOW
+ #define SYMBOL_INFO_PACKAGE SYMBOL_INFO_PACKAGEW
+ #define PSYMBOL_INFO_PACKAGE PSYMBOL_INFO_PACKAGEW
+ #define FIND_EXE_FILE_CALLBACK FIND_EXE_FILE_CALLBACKW
+ #define PFIND_EXE_FILE_CALLBACK PFIND_EXE_FILE_CALLBACKW
+ #define SYM_ENUMERATESYMBOLS_CALLBACK SYM_ENUMERATESYMBOLS_CALLBACKW
+ #define PSYM_ENUMERATESYMBOLS_CALLBACK PSYM_ENUMERATESYMBOLS_CALLBACKW
+ #define SRCCODEINFO SRCCODEINFOW
+ #define PSRCCODEINFO PSRCCODEINFOW
+ #define SOURCEFILE SOURCEFILEW
+ #define PSOURCEFILE PSOURCEFILEW
+ #define SYM_ENUMSOURECFILES_CALLBACK SYM_ENUMSOURCEFILES_CALLBACKW
+ #define PSYM_ENUMSOURCEFILES_CALLBACK PSYM_ENUMSOURECFILES_CALLBACKW
+ #define IMAGEHLP_CBA_EVENT IMAGEHLP_CBA_EVENTW
+ #define PIMAGEHLP_CBA_EVENT PIMAGEHLP_CBA_EVENTW
+ #define PENUMDIRTREE_CALLBACK PENUMDIRTREE_CALLBACKW
+ #define IMAGEHLP_DEFERRED_SYMBOL_LOAD64 IMAGEHLP_DEFERRED_SYMBOL_LOADW64
+ #define PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 PIMAGEHLP_DEFERRED_SYMBOL_LOADW64
+ #define PFIND_DEBUG_FILE_CALLBACK PFIND_DEBUG_FILE_CALLBACKW
+ #define PFINDFILEINPATHCALLBACK PFINDFILEINPATHCALLBACKW
+ #define IMAGEHLP_MODULE64 IMAGEHLP_MODULEW64
+ #define PIMAGEHLP_MODULE64 PIMAGEHLP_MODULEW64
+ #define SYMSRV_INDEX_INFO SYMSRV_INDEX_INFOW
+ #define PSYMSRV_INDEX_INFO PSYMSRV_INDEX_INFOW
+
+ #define PSYMBOLSERVERPROC PSYMBOLSERVERPROCW
+ #define PSYMBOLSERVERPINGPROC PSYMBOLSERVERPINGPROCW
+#endif
+
+// -----------------------------------------------------------------
+// The following APIs exist only for backwards compatibility
+// with a pre-release version documented in an MSDN release.
+
+// You should use SymFindFileInPath if you want to maintain
+// future compatibility.
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+FindFileInPath(
+ __in HANDLE hprocess,
+ __in PCSTR SearchPath,
+ __in PCSTR FileName,
+ __in PVOID id,
+ __in DWORD two,
+ __in DWORD three,
+ __in DWORD flags,
+ __out_ecount(MAX_PATH + 1) PSTR FilePath
+ );
+
+// You should use SymFindFileInPath if you want to maintain
+// future compatibility.
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+FindFileInSearchPath(
+ __in HANDLE hprocess,
+ __in PCSTR SearchPath,
+ __in PCSTR FileName,
+ __in DWORD one,
+ __in DWORD two,
+ __in DWORD three,
+ __out_ecount(MAX_PATH + 1) PSTR FilePath
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumSym(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbols64(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbolsW64(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACK64W EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymEnumerateSymbols SymEnumerateSymbols64
+#define SymEnumerateSymbolsW SymEnumerateSymbolsW64
+#else
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbols(
+ __in HANDLE hProcess,
+ __in ULONG BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbolsW(
+ __in HANDLE hProcess,
+ __in ULONG BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+// use SymLoadModuleEx
+
+DWORD64
+IMAGEAPI
+SymLoadModule64(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCSTR ImageName,
+ __in_opt PCSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in DWORD SizeOfDll
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymLoadModule SymLoadModule64
+#else
+DWORD
+IMAGEAPI
+SymLoadModule(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCSTR ImageName,
+ __in_opt PCSTR ModuleName,
+ __in DWORD BaseOfDll,
+ __in DWORD SizeOfDll
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetSymNext64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymNextW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW64 Symbol
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymNext SymGetSymNext64
+#define SymGetSymNextW SymGetSymNextW64
+#else
+BOOL
+IMAGEAPI
+SymGetSymNext(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymNextW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW Symbol
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetSymPrev64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymPrevW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW64 Symbol
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymPrev SymGetSymPrev64
+#define SymGetSymPrevW SymGetSymPrevW64
+#else
+BOOL
+IMAGEAPI
+SymGetSymPrev(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymPrevW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW Symbol
+ );
+#endif
+
+
+// These values should not be used.
+// They have been replaced by SYMFLAG_ values.
+
+#define SYMF_OMAP_GENERATED 0x00000001
+#define SYMF_OMAP_MODIFIED 0x00000002
+#define SYMF_REGISTER 0x00000008
+#define SYMF_REGREL 0x00000010
+#define SYMF_FRAMEREL 0x00000020
+#define SYMF_PARAMETER 0x00000040
+#define SYMF_LOCAL 0x00000080
+#define SYMF_CONSTANT 0x00000100
+#define SYMF_EXPORT 0x00000200
+#define SYMF_FORWARDER 0x00000400
+#define SYMF_FUNCTION 0x00000800
+#define SYMF_VIRTUAL 0x00001000
+#define SYMF_THUNK 0x00002000
+#define SYMF_TLSREL 0x00004000
+
+// These values should also not be used.
+// They have been replaced by SYMFLAG_ values.
+
+#define IMAGEHLP_SYMBOL_INFO_VALUEPRESENT 1
+#define IMAGEHLP_SYMBOL_INFO_REGISTER SYMF_REGISTER // 0x0008
+#define IMAGEHLP_SYMBOL_INFO_REGRELATIVE SYMF_REGREL // 0x0010
+#define IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE SYMF_FRAMEREL // 0x0020
+#define IMAGEHLP_SYMBOL_INFO_PARAMETER SYMF_PARAMETER // 0x0040
+#define IMAGEHLP_SYMBOL_INFO_LOCAL SYMF_LOCAL // 0x0080
+#define IMAGEHLP_SYMBOL_INFO_CONSTANT SYMF_CONSTANT // 0x0100
+#define IMAGEHLP_SYMBOL_FUNCTION SYMF_FUNCTION // 0x0800
+#define IMAGEHLP_SYMBOL_VIRTUAL SYMF_VIRTUAL // 0x1000
+#define IMAGEHLP_SYMBOL_THUNK SYMF_THUNK // 0x2000
+#define IMAGEHLP_SYMBOL_INFO_TLSRELATIVE SYMF_TLSREL // 0x4000
+
+
+#include <pshpack4.h>
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+#pragma warning(disable:4200) /* Zero length array */
+#pragma warning(disable:4201) /* Nameless struct/union */
+#endif
+#endif
+
+#define MINIDUMP_SIGNATURE ('PMDM')
+#define MINIDUMP_VERSION (42899)
+typedef DWORD RVA;
+typedef ULONG64 RVA64;
+
+typedef struct _MINIDUMP_LOCATION_DESCRIPTOR {
+ ULONG32 DataSize;
+ RVA Rva;
+} MINIDUMP_LOCATION_DESCRIPTOR;
+
+typedef struct _MINIDUMP_LOCATION_DESCRIPTOR64 {
+ ULONG64 DataSize;
+ RVA64 Rva;
+} MINIDUMP_LOCATION_DESCRIPTOR64;
+
+
+typedef struct _MINIDUMP_MEMORY_DESCRIPTOR {
+ ULONG64 StartOfMemoryRange;
+ MINIDUMP_LOCATION_DESCRIPTOR Memory;
+} MINIDUMP_MEMORY_DESCRIPTOR, *PMINIDUMP_MEMORY_DESCRIPTOR;
+
+// DESCRIPTOR64 is used for full-memory minidumps where
+// all of the raw memory is laid out sequentially at the
+// end of the dump. There is no need for individual RVAs
+// as the RVA is the base RVA plus the sum of the preceeding
+// data blocks.
+typedef struct _MINIDUMP_MEMORY_DESCRIPTOR64 {
+ ULONG64 StartOfMemoryRange;
+ ULONG64 DataSize;
+} MINIDUMP_MEMORY_DESCRIPTOR64, *PMINIDUMP_MEMORY_DESCRIPTOR64;
+
+
+typedef struct _MINIDUMP_HEADER {
+ ULONG32 Signature;
+ ULONG32 Version;
+ ULONG32 NumberOfStreams;
+ RVA StreamDirectoryRva;
+ ULONG32 CheckSum;
+ union {
+ ULONG32 Reserved;
+ ULONG32 TimeDateStamp;
+ };
+ ULONG64 Flags;
+} MINIDUMP_HEADER, *PMINIDUMP_HEADER;
+
+//
+// The MINIDUMP_HEADER field StreamDirectoryRva points to
+// an array of MINIDUMP_DIRECTORY structures.
+//
+
+typedef struct _MINIDUMP_DIRECTORY {
+ ULONG32 StreamType;
+ MINIDUMP_LOCATION_DESCRIPTOR Location;
+} MINIDUMP_DIRECTORY, *PMINIDUMP_DIRECTORY;
+
+
+typedef struct _MINIDUMP_STRING {
+ ULONG32 Length; // Length in bytes of the string
+ WCHAR Buffer [0]; // Variable size buffer
+} MINIDUMP_STRING, *PMINIDUMP_STRING;
+
+
+
+//
+// The MINIDUMP_DIRECTORY field StreamType may be one of the following types.
+// Types will be added in the future, so if a program reading the minidump
+// header encounters a stream type it does not understand it should ignore
+// the data altogether. Any tag above LastReservedStream will not be used by
+// the system and is reserved for program-specific information.
+//
+
+typedef enum _MINIDUMP_STREAM_TYPE {
+
+ UnusedStream = 0,
+ ReservedStream0 = 1,
+ ReservedStream1 = 2,
+ ThreadListStream = 3,
+ ModuleListStream = 4,
+ MemoryListStream = 5,
+ ExceptionStream = 6,
+ SystemInfoStream = 7,
+ ThreadExListStream = 8,
+ Memory64ListStream = 9,
+ CommentStreamA = 10,
+ CommentStreamW = 11,
+ HandleDataStream = 12,
+ FunctionTableStream = 13,
+ UnloadedModuleListStream = 14,
+ MiscInfoStream = 15,
+ MemoryInfoListStream = 16,
+ ThreadInfoListStream = 17,
+ HandleOperationListStream = 18,
+
+ ceStreamNull = 0x8000,
+ ceStreamSystemInfo = 0x8001,
+ ceStreamException = 0x8002,
+ ceStreamModuleList = 0x8003,
+ ceStreamProcessList = 0x8004,
+ ceStreamThreadList = 0x8005,
+ ceStreamThreadContextList = 0x8006,
+ ceStreamThreadCallStackList = 0x8007,
+ ceStreamMemoryVirtualList = 0x8008,
+ ceStreamMemoryPhysicalList = 0x8009,
+ ceStreamBucketParameters = 0x800A,
+
+ LastReservedStream = 0xffff
+
+} MINIDUMP_STREAM_TYPE;
+
+
+//
+// The minidump system information contains processor and
+// Operating System specific information.
+//
+
+//
+// CPU information is obtained from one of two places.
+//
+// 1) On x86 computers, CPU_INFORMATION is obtained from the CPUID
+// instruction. You must use the X86 portion of the union for X86
+// computers.
+//
+// 2) On non-x86 architectures, CPU_INFORMATION is obtained by calling
+// IsProcessorFeatureSupported().
+//
+
+typedef union _CPU_INFORMATION {
+
+ //
+ // X86 platforms use CPUID function to obtain processor information.
+ //
+
+ struct {
+
+ //
+ // CPUID Subfunction 0, register EAX (VendorId [0]),
+ // EBX (VendorId [1]) and ECX (VendorId [2]).
+ //
+
+ ULONG32 VendorId [ 3 ];
+
+ //
+ // CPUID Subfunction 1, register EAX
+ //
+
+ ULONG32 VersionInformation;
+
+ //
+ // CPUID Subfunction 1, register EDX
+ //
+
+ ULONG32 FeatureInformation;
+
+
+ //
+ // CPUID, Subfunction 80000001, register EBX. This will only
+ // be obtained if the vendor id is "AuthenticAMD".
+ //
+
+ ULONG32 AMDExtendedCpuFeatures;
+
+ } X86CpuInfo;
+
+ //
+ // Non-x86 platforms use processor feature flags.
+ //
+
+ struct {
+
+ ULONG64 ProcessorFeatures [ 2 ];
+
+ } OtherCpuInfo;
+
+} CPU_INFORMATION, *PCPU_INFORMATION;
+
+typedef struct _MINIDUMP_SYSTEM_INFO {
+
+ //
+ // ProcessorArchitecture, ProcessorLevel and ProcessorRevision are all
+ // taken from the SYSTEM_INFO structure obtained by GetSystemInfo( ).
+ //
+
+ USHORT ProcessorArchitecture;
+ USHORT ProcessorLevel;
+ USHORT ProcessorRevision;
+
+ union {
+ USHORT Reserved0;
+ struct {
+ UCHAR NumberOfProcessors;
+ UCHAR ProductType;
+ };
+ };
+
+ //
+ // MajorVersion, MinorVersion, BuildNumber, PlatformId and
+ // CSDVersion are all taken from the OSVERSIONINFO structure
+ // returned by GetVersionEx( ).
+ //
+
+ ULONG32 MajorVersion;
+ ULONG32 MinorVersion;
+ ULONG32 BuildNumber;
+ ULONG32 PlatformId;
+
+ //
+ // RVA to a CSDVersion string in the string table.
+ //
+
+ RVA CSDVersionRva;
+
+ union {
+ ULONG32 Reserved1;
+ struct {
+ USHORT SuiteMask;
+ USHORT Reserved2;
+ };
+ };
+
+ CPU_INFORMATION Cpu;
+
+} MINIDUMP_SYSTEM_INFO, *PMINIDUMP_SYSTEM_INFO;
+
+
+//
+// The minidump thread contains standard thread
+// information plus an RVA to the memory for this
+// thread and an RVA to the CONTEXT structure for
+// this thread.
+//
+
+
+//
+// ThreadId must be 4 bytes on all architectures.
+//
+#ifndef FEATURE_PAL
+static_assert (sizeof ( ((PPROCESS_INFORMATION)0)->dwThreadId ) == 4, "ThreadId must be 4 bytes on all architectures.");
+#else
+typedef int VS_FIXEDFILEINFO;
+#endif
+
+
+typedef struct _MINIDUMP_THREAD {
+ ULONG32 ThreadId;
+ ULONG32 SuspendCount;
+ ULONG32 PriorityClass;
+ ULONG32 Priority;
+ ULONG64 Teb;
+ MINIDUMP_MEMORY_DESCRIPTOR Stack;
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+} MINIDUMP_THREAD, *PMINIDUMP_THREAD;
+
+//
+// The thread list is a container of threads.
+//
+
+typedef struct _MINIDUMP_THREAD_LIST {
+ ULONG32 NumberOfThreads;
+ MINIDUMP_THREAD Threads [0];
+} MINIDUMP_THREAD_LIST, *PMINIDUMP_THREAD_LIST;
+
+
+typedef struct _MINIDUMP_THREAD_EX {
+ ULONG32 ThreadId;
+ ULONG32 SuspendCount;
+ ULONG32 PriorityClass;
+ ULONG32 Priority;
+ ULONG64 Teb;
+ MINIDUMP_MEMORY_DESCRIPTOR Stack;
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+ MINIDUMP_MEMORY_DESCRIPTOR BackingStore;
+} MINIDUMP_THREAD_EX, *PMINIDUMP_THREAD_EX;
+
+//
+// The thread list is a container of threads.
+//
+
+typedef struct _MINIDUMP_THREAD_EX_LIST {
+ ULONG32 NumberOfThreads;
+ MINIDUMP_THREAD_EX Threads [0];
+} MINIDUMP_THREAD_EX_LIST, *PMINIDUMP_THREAD_EX_LIST;
+
+
+//
+// The MINIDUMP_EXCEPTION is the same as EXCEPTION on Win64.
+//
+
+typedef struct _MINIDUMP_EXCEPTION {
+ ULONG32 ExceptionCode;
+ ULONG32 ExceptionFlags;
+ ULONG64 ExceptionRecord;
+ ULONG64 ExceptionAddress;
+ ULONG32 NumberParameters;
+ ULONG32 __unusedAlignment;
+ ULONG64 ExceptionInformation [ EXCEPTION_MAXIMUM_PARAMETERS ];
+} MINIDUMP_EXCEPTION, *PMINIDUMP_EXCEPTION;
+
+
+//
+// The exception information stream contains the id of the thread that caused
+// the exception (ThreadId), the exception record for the exception
+// (ExceptionRecord) and an RVA to the thread context where the exception
+// occured.
+//
+
+typedef struct MINIDUMP_EXCEPTION_STREAM {
+ ULONG32 ThreadId;
+ ULONG32 __alignment;
+ MINIDUMP_EXCEPTION ExceptionRecord;
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+} MINIDUMP_EXCEPTION_STREAM, *PMINIDUMP_EXCEPTION_STREAM;
+
+
+//
+// The MINIDUMP_MODULE contains information about a
+// a specific module. It includes the CheckSum and
+// the TimeDateStamp for the module so the module
+// can be reloaded during the analysis phase.
+//
+
+typedef struct _MINIDUMP_MODULE {
+ ULONG64 BaseOfImage;
+ ULONG32 SizeOfImage;
+ ULONG32 CheckSum;
+ ULONG32 TimeDateStamp;
+ RVA ModuleNameRva;
+ VS_FIXEDFILEINFO VersionInfo;
+ MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
+ MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
+ ULONG64 Reserved0; // Reserved for future use.
+ ULONG64 Reserved1; // Reserved for future use.
+} MINIDUMP_MODULE, *PMINIDUMP_MODULE;
+
+
+//
+// The minidump module list is a container for modules.
+//
+
+typedef struct _MINIDUMP_MODULE_LIST {
+ ULONG32 NumberOfModules;
+ MINIDUMP_MODULE Modules [ 0 ];
+} MINIDUMP_MODULE_LIST, *PMINIDUMP_MODULE_LIST;
+
+
+//
+// Memory Ranges
+//
+
+typedef struct _MINIDUMP_MEMORY_LIST {
+ ULONG32 NumberOfMemoryRanges;
+ MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges [0];
+} MINIDUMP_MEMORY_LIST, *PMINIDUMP_MEMORY_LIST;
+
+typedef struct _MINIDUMP_MEMORY64_LIST {
+ ULONG64 NumberOfMemoryRanges;
+ RVA64 BaseRva;
+ MINIDUMP_MEMORY_DESCRIPTOR64 MemoryRanges [0];
+} MINIDUMP_MEMORY64_LIST, *PMINIDUMP_MEMORY64_LIST;
+
+
+//
+// Support for user supplied exception information.
+//
+
+typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
+ DWORD ThreadId;
+ PEXCEPTION_POINTERS ExceptionPointers;
+ BOOL ClientPointers;
+} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
+
+typedef struct _MINIDUMP_EXCEPTION_INFORMATION64 {
+ DWORD ThreadId;
+ ULONG64 ExceptionRecord;
+ ULONG64 ContextRecord;
+ BOOL ClientPointers;
+} MINIDUMP_EXCEPTION_INFORMATION64, *PMINIDUMP_EXCEPTION_INFORMATION64;
+
+
+//
+// Support for capturing system handle state at the time of the dump.
+//
+
+// Per-handle object information varies according to
+// the OS, the OS version, the processor type and
+// so on. The minidump gives a minidump identifier
+// to each possible data format for identification
+// purposes but does not control nor describe the actual data.
+typedef enum _MINIDUMP_HANDLE_OBJECT_INFORMATION_TYPE {
+ MiniHandleObjectInformationNone,
+ MiniThreadInformation1,
+ MiniMutantInformation1,
+ MiniMutantInformation2,
+ MiniProcessInformation1,
+ MiniProcessInformation2,
+ MiniHandleObjectInformationTypeMax
+} MINIDUMP_HANDLE_OBJECT_INFORMATION_TYPE;
+
+typedef struct _MINIDUMP_HANDLE_OBJECT_INFORMATION {
+ RVA NextInfoRva;
+ ULONG32 InfoType;
+ ULONG32 SizeOfInfo;
+ // Raw information follows.
+} MINIDUMP_HANDLE_OBJECT_INFORMATION;
+
+typedef struct _MINIDUMP_HANDLE_DESCRIPTOR {
+ ULONG64 Handle;
+ RVA TypeNameRva;
+ RVA ObjectNameRva;
+ ULONG32 Attributes;
+ ULONG32 GrantedAccess;
+ ULONG32 HandleCount;
+ ULONG32 PointerCount;
+} MINIDUMP_HANDLE_DESCRIPTOR, *PMINIDUMP_HANDLE_DESCRIPTOR;
+
+typedef struct _MINIDUMP_HANDLE_DESCRIPTOR_2 {
+ ULONG64 Handle;
+ RVA TypeNameRva;
+ RVA ObjectNameRva;
+ ULONG32 Attributes;
+ ULONG32 GrantedAccess;
+ ULONG32 HandleCount;
+ ULONG32 PointerCount;
+ RVA ObjectInfoRva;
+ ULONG32 Reserved0;
+} MINIDUMP_HANDLE_DESCRIPTOR_2, *PMINIDUMP_HANDLE_DESCRIPTOR_2;
+
+// The latest MINIDUMP_HANDLE_DESCRIPTOR definition.
+typedef MINIDUMP_HANDLE_DESCRIPTOR_2 MINIDUMP_HANDLE_DESCRIPTOR_N;
+typedef MINIDUMP_HANDLE_DESCRIPTOR_N *PMINIDUMP_HANDLE_DESCRIPTOR_N;
+
+typedef struct _MINIDUMP_HANDLE_DATA_STREAM {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfDescriptor;
+ ULONG32 NumberOfDescriptors;
+ ULONG32 Reserved;
+} MINIDUMP_HANDLE_DATA_STREAM, *PMINIDUMP_HANDLE_DATA_STREAM;
+
+// Some operating systems can track the last operations
+// performed on a handle. For example, Application Verifier
+// can enable this for some versions of Windows. The
+// handle operation list collects handle operations
+// known for the dump target.
+// Each entry is an AVRF_HANDLE_OPERATION.
+typedef struct _MINIDUMP_HANDLE_OPERATION_LIST {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfEntry;
+ ULONG32 NumberOfEntries;
+ ULONG32 Reserved;
+} MINIDUMP_HANDLE_OPERATION_LIST, *PMINIDUMP_HANDLE_OPERATION_LIST;
+
+
+//
+// Support for capturing dynamic function table state at the time of the dump.
+//
+
+typedef struct _MINIDUMP_FUNCTION_TABLE_DESCRIPTOR {
+ ULONG64 MinimumAddress;
+ ULONG64 MaximumAddress;
+ ULONG64 BaseAddress;
+ ULONG32 EntryCount;
+ ULONG32 SizeOfAlignPad;
+} MINIDUMP_FUNCTION_TABLE_DESCRIPTOR, *PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR;
+
+typedef struct _MINIDUMP_FUNCTION_TABLE_STREAM {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfDescriptor;
+ ULONG32 SizeOfNativeDescriptor;
+ ULONG32 SizeOfFunctionEntry;
+ ULONG32 NumberOfDescriptors;
+ ULONG32 SizeOfAlignPad;
+} MINIDUMP_FUNCTION_TABLE_STREAM, *PMINIDUMP_FUNCTION_TABLE_STREAM;
+
+
+//
+// The MINIDUMP_UNLOADED_MODULE contains information about a
+// a specific module that was previously loaded but no
+// longer is. This can help with diagnosing problems where
+// callers attempt to call code that is no longer loaded.
+//
+
+typedef struct _MINIDUMP_UNLOADED_MODULE {
+ ULONG64 BaseOfImage;
+ ULONG32 SizeOfImage;
+ ULONG32 CheckSum;
+ ULONG32 TimeDateStamp;
+ RVA ModuleNameRva;
+} MINIDUMP_UNLOADED_MODULE, *PMINIDUMP_UNLOADED_MODULE;
+
+
+//
+// The minidump unloaded module list is a container for unloaded modules.
+//
+
+typedef struct _MINIDUMP_UNLOADED_MODULE_LIST {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfEntry;
+ ULONG32 NumberOfEntries;
+} MINIDUMP_UNLOADED_MODULE_LIST, *PMINIDUMP_UNLOADED_MODULE_LIST;
+
+
+//
+// The miscellaneous information stream contains a variety
+// of small pieces of information. A member is valid if
+// it's within the available size and its corresponding
+// bit is set.
+//
+
+#define MINIDUMP_MISC1_PROCESS_ID 0x00000001
+#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002
+#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004
+
+typedef struct _MINIDUMP_MISC_INFO {
+ ULONG32 SizeOfInfo;
+ ULONG32 Flags1;
+ ULONG32 ProcessId;
+ ULONG32 ProcessCreateTime;
+ ULONG32 ProcessUserTime;
+ ULONG32 ProcessKernelTime;
+} MINIDUMP_MISC_INFO, *PMINIDUMP_MISC_INFO;
+
+typedef struct _MINIDUMP_MISC_INFO_2 {
+ ULONG32 SizeOfInfo;
+ ULONG32 Flags1;
+ ULONG32 ProcessId;
+ ULONG32 ProcessCreateTime;
+ ULONG32 ProcessUserTime;
+ ULONG32 ProcessKernelTime;
+ ULONG32 ProcessorMaxMhz;
+ ULONG32 ProcessorCurrentMhz;
+ ULONG32 ProcessorMhzLimit;
+ ULONG32 ProcessorMaxIdleState;
+ ULONG32 ProcessorCurrentIdleState;
+} MINIDUMP_MISC_INFO_2, *PMINIDUMP_MISC_INFO_2;
+
+// The latest MINIDUMP_MISC_INFO definition.
+typedef MINIDUMP_MISC_INFO_2 MINIDUMP_MISC_INFO_N;
+typedef MINIDUMP_MISC_INFO_N* PMINIDUMP_MISC_INFO_N;
+
+
+//
+// The memory information stream contains memory region
+// description information. This stream corresponds to
+// what VirtualQuery would return for the process the
+// dump was created for.
+//
+
+typedef struct _MINIDUMP_MEMORY_INFO {
+ ULONG64 BaseAddress;
+ ULONG64 AllocationBase;
+ ULONG32 AllocationProtect;
+ ULONG32 __alignment1;
+ ULONG64 RegionSize;
+ ULONG32 State;
+ ULONG32 Protect;
+ ULONG32 Type;
+ ULONG32 __alignment2;
+} MINIDUMP_MEMORY_INFO, *PMINIDUMP_MEMORY_INFO;
+
+typedef struct _MINIDUMP_MEMORY_INFO_LIST {
+ ULONG SizeOfHeader;
+ ULONG SizeOfEntry;
+ ULONG64 NumberOfEntries;
+} MINIDUMP_MEMORY_INFO_LIST, *PMINIDUMP_MEMORY_INFO_LIST;
+
+
+//
+// The memory information stream contains memory region
+// description information. This stream corresponds to
+// what VirtualQuery would return for the process the
+// dump was created for.
+//
+
+// Thread dump writer status flags.
+#define MINIDUMP_THREAD_INFO_ERROR_THREAD 0x00000001
+#define MINIDUMP_THREAD_INFO_WRITING_THREAD 0x00000002
+#define MINIDUMP_THREAD_INFO_EXITED_THREAD 0x00000004
+#define MINIDUMP_THREAD_INFO_INVALID_INFO 0x00000008
+#define MINIDUMP_THREAD_INFO_INVALID_CONTEXT 0x00000010
+#define MINIDUMP_THREAD_INFO_INVALID_TEB 0x00000020
+
+typedef struct _MINIDUMP_THREAD_INFO {
+ ULONG32 ThreadId;
+ ULONG32 DumpFlags;
+ ULONG32 DumpError;
+ ULONG32 ExitStatus;
+ ULONG64 CreateTime;
+ ULONG64 ExitTime;
+ ULONG64 KernelTime;
+ ULONG64 UserTime;
+ ULONG64 StartAddress;
+ ULONG64 Affinity;
+} MINIDUMP_THREAD_INFO, *PMINIDUMP_THREAD_INFO;
+
+typedef struct _MINIDUMP_THREAD_INFO_LIST {
+ ULONG SizeOfHeader;
+ ULONG SizeOfEntry;
+ ULONG NumberOfEntries;
+} MINIDUMP_THREAD_INFO_LIST, *PMINIDUMP_THREAD_INFO_LIST;
+
+
+//
+// Support for arbitrary user-defined information.
+//
+
+typedef struct _MINIDUMP_USER_RECORD {
+ ULONG32 Type;
+ MINIDUMP_LOCATION_DESCRIPTOR Memory;
+} MINIDUMP_USER_RECORD, *PMINIDUMP_USER_RECORD;
+
+
+typedef struct _MINIDUMP_USER_STREAM {
+ ULONG32 Type;
+ ULONG BufferSize;
+ PVOID Buffer;
+
+} MINIDUMP_USER_STREAM, *PMINIDUMP_USER_STREAM;
+
+
+typedef struct _MINIDUMP_USER_STREAM_INFORMATION {
+ ULONG UserStreamCount;
+ PMINIDUMP_USER_STREAM UserStreamArray;
+} MINIDUMP_USER_STREAM_INFORMATION, *PMINIDUMP_USER_STREAM_INFORMATION;
+
+//
+// Callback support.
+//
+
+typedef enum _MINIDUMP_CALLBACK_TYPE {
+ ModuleCallback,
+ ThreadCallback,
+ ThreadExCallback,
+ IncludeThreadCallback,
+ IncludeModuleCallback,
+ MemoryCallback,
+ CancelCallback,
+ WriteKernelMinidumpCallback,
+ KernelMinidumpStatusCallback,
+ RemoveMemoryCallback,
+ IncludeVmRegionCallback,
+ IoStartCallback,
+ IoWriteAllCallback,
+ IoFinishCallback,
+ ReadMemoryFailureCallback,
+ SecondaryFlagsCallback,
+} MINIDUMP_CALLBACK_TYPE;
+
+
+typedef struct _MINIDUMP_THREAD_CALLBACK {
+ ULONG ThreadId;
+ HANDLE ThreadHandle;
+ CONTEXT Context;
+ ULONG SizeOfContext;
+ ULONG64 StackBase;
+ ULONG64 StackEnd;
+} MINIDUMP_THREAD_CALLBACK, *PMINIDUMP_THREAD_CALLBACK;
+
+
+typedef struct _MINIDUMP_THREAD_EX_CALLBACK {
+ ULONG ThreadId;
+ HANDLE ThreadHandle;
+ CONTEXT Context;
+ ULONG SizeOfContext;
+ ULONG64 StackBase;
+ ULONG64 StackEnd;
+ ULONG64 BackingStoreBase;
+ ULONG64 BackingStoreEnd;
+} MINIDUMP_THREAD_EX_CALLBACK, *PMINIDUMP_THREAD_EX_CALLBACK;
+
+
+typedef struct _MINIDUMP_INCLUDE_THREAD_CALLBACK {
+ ULONG ThreadId;
+} MINIDUMP_INCLUDE_THREAD_CALLBACK, *PMINIDUMP_INCLUDE_THREAD_CALLBACK;
+
+
+typedef enum _THREAD_WRITE_FLAGS {
+ ThreadWriteThread = 0x0001,
+ ThreadWriteStack = 0x0002,
+ ThreadWriteContext = 0x0004,
+ ThreadWriteBackingStore = 0x0008,
+ ThreadWriteInstructionWindow = 0x0010,
+ ThreadWriteThreadData = 0x0020,
+ ThreadWriteThreadInfo = 0x0040,
+} THREAD_WRITE_FLAGS;
+
+typedef struct _MINIDUMP_MODULE_CALLBACK {
+ PWCHAR FullPath;
+ ULONG64 BaseOfImage;
+ ULONG SizeOfImage;
+ ULONG CheckSum;
+ ULONG TimeDateStamp;
+ VS_FIXEDFILEINFO VersionInfo;
+ PVOID CvRecord;
+ ULONG SizeOfCvRecord;
+ PVOID MiscRecord;
+ ULONG SizeOfMiscRecord;
+} MINIDUMP_MODULE_CALLBACK, *PMINIDUMP_MODULE_CALLBACK;
+
+
+typedef struct _MINIDUMP_INCLUDE_MODULE_CALLBACK {
+ ULONG64 BaseOfImage;
+} MINIDUMP_INCLUDE_MODULE_CALLBACK, *PMINIDUMP_INCLUDE_MODULE_CALLBACK;
+
+
+typedef enum _MODULE_WRITE_FLAGS {
+ ModuleWriteModule = 0x0001,
+ ModuleWriteDataSeg = 0x0002,
+ ModuleWriteMiscRecord = 0x0004,
+ ModuleWriteCvRecord = 0x0008,
+ ModuleReferencedByMemory = 0x0010,
+ ModuleWriteTlsData = 0x0020,
+ ModuleWriteCodeSegs = 0x0040,
+} MODULE_WRITE_FLAGS;
+
+
+typedef struct _MINIDUMP_IO_CALLBACK {
+ HANDLE Handle;
+ ULONG64 Offset;
+ PVOID Buffer;
+ ULONG BufferBytes;
+} MINIDUMP_IO_CALLBACK, *PMINIDUMP_IO_CALLBACK;
+
+
+typedef struct _MINIDUMP_READ_MEMORY_FAILURE_CALLBACK
+{
+ ULONG64 Offset;
+ ULONG Bytes;
+ HRESULT FailureStatus;
+} MINIDUMP_READ_MEMORY_FAILURE_CALLBACK,
+ *PMINIDUMP_READ_MEMORY_FAILURE_CALLBACK;
+
+
+typedef struct _MINIDUMP_CALLBACK_INPUT {
+ ULONG ProcessId;
+ HANDLE ProcessHandle;
+ ULONG CallbackType;
+ union {
+ HRESULT Status;
+ MINIDUMP_THREAD_CALLBACK Thread;
+ MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
+ MINIDUMP_MODULE_CALLBACK Module;
+ MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
+ MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
+ MINIDUMP_IO_CALLBACK Io;
+ MINIDUMP_READ_MEMORY_FAILURE_CALLBACK ReadMemoryFailure;
+ ULONG SecondaryFlags;
+ };
+} MINIDUMP_CALLBACK_INPUT, *PMINIDUMP_CALLBACK_INPUT;
+
+typedef struct _MINIDUMP_CALLBACK_OUTPUT {
+ union {
+ ULONG ModuleWriteFlags;
+ ULONG ThreadWriteFlags;
+ ULONG SecondaryFlags;
+ struct {
+ ULONG64 MemoryBase;
+ ULONG MemorySize;
+ };
+ struct {
+ BOOL CheckCancel;
+ BOOL Cancel;
+ };
+ HANDLE Handle;
+ struct {
+ MINIDUMP_MEMORY_INFO VmRegion;
+ BOOL Continue;
+ };
+ HRESULT Status;
+ };
+} MINIDUMP_CALLBACK_OUTPUT, *PMINIDUMP_CALLBACK_OUTPUT;
+
+
+//
+// A normal minidump contains just the information
+// necessary to capture stack traces for all of the
+// existing threads in a process.
+//
+// A minidump with data segments includes all of the data
+// sections from loaded modules in order to capture
+// global variable contents. This can make the dump much
+// larger if many modules have global data.
+//
+// A minidump with full memory includes all of the accessible
+// memory in the process and can be very large. A minidump
+// with full memory always has the raw memory data at the end
+// of the dump so that the initial structures in the dump can
+// be mapped directly without having to include the raw
+// memory information.
+//
+// Stack and backing store memory can be filtered to remove
+// data unnecessary for stack walking. This can improve
+// compression of stacks and also deletes data that may
+// be private and should not be stored in a dump.
+// Memory can also be scanned to see what modules are
+// referenced by stack and backing store memory to allow
+// omission of other modules to reduce dump size.
+// In either of these modes the ModuleReferencedByMemory flag
+// is set for all modules referenced before the base
+// module callbacks occur.
+//
+// On some operating systems a list of modules that were
+// recently unloaded is kept in addition to the currently
+// loaded module list. This information can be saved in
+// the dump if desired.
+//
+// Stack and backing store memory can be scanned for referenced
+// pages in order to pick up data referenced by locals or other
+// stack memory. This can increase the size of a dump significantly.
+//
+// Module paths may contain undesired information such as user names
+// or other important directory names so they can be stripped. This
+// option reduces the ability to locate the proper image later
+// and should only be used in certain situations.
+//
+// Complete operating system per-process and per-thread information can
+// be gathered and stored in the dump.
+//
+// The virtual address space can be scanned for various types
+// of memory to be included in the dump.
+//
+// Code which is concerned with potentially private information
+// getting into the minidump can set a flag that automatically
+// modifies all existing and future flags to avoid placing
+// unnecessary data in the dump. Basic data, such as stack
+// information, will still be included but optional data, such
+// as indirect memory, will not.
+//
+// When doing a full memory dump it's possible to store all
+// of the enumerated memory region descriptive information
+// in a memory information stream.
+//
+// Additional thread information beyond the basic thread
+// structure can be collected if desired.
+//
+// A minidump with code segments includes all of the code
+// and code-related sections from loaded modules in order
+// to capture executable content.
+//
+// MiniDumpWithoutAuxiliaryState turns off any secondary,
+// auxiliary-supported memory gathering.
+//
+// MiniDumpWithFullAuxiliaryState asks any present auxiliary
+// data providers to include all of their state in the dump.
+// The exact set of what is provided depends on the auxiliary.
+// This can be quite large.
+//
+
+typedef enum _MINIDUMP_TYPE {
+ MiniDumpNormal = 0x00000000,
+ MiniDumpWithDataSegs = 0x00000001,
+ MiniDumpWithFullMemory = 0x00000002,
+ MiniDumpWithHandleData = 0x00000004,
+ MiniDumpFilterMemory = 0x00000008,
+ MiniDumpScanMemory = 0x00000010,
+ MiniDumpWithUnloadedModules = 0x00000020,
+ MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
+ MiniDumpFilterModulePaths = 0x00000080,
+ MiniDumpWithProcessThreadData = 0x00000100,
+ MiniDumpWithPrivateReadWriteMemory = 0x00000200,
+ MiniDumpWithoutOptionalData = 0x00000400,
+ MiniDumpWithFullMemoryInfo = 0x00000800,
+ MiniDumpWithThreadInfo = 0x00001000,
+ MiniDumpWithCodeSegs = 0x00002000,
+ MiniDumpWithoutAuxiliaryState = 0x00004000,
+ MiniDumpWithFullAuxiliaryState = 0x00008000,
+
+ MiniDumpValidTypeFlags = 0x0000ffff,
+} MINIDUMP_TYPE;
+
+//
+// In addition to the primary flags provided to
+// MiniDumpWriteDump there are additional, less
+// frequently used options queried via the secondary
+// flags callback.
+//
+// MiniSecondaryWithoutPowerInfo suppresses the minidump
+// query that retrieves processor power information for
+// MINIDUMP_MISC_INFO.
+//
+
+typedef enum _MINIDUMP_SECONDARY_FLAGS {
+ MiniSecondaryWithoutPowerInfo = 0x00000001,
+
+ MiniSecondaryValidFlags = 0x00000001,
+} MINIDUMP_SECONDARY_FLAGS;
+
+
+//
+// The minidump callback should modify the FieldsToWrite parameter to reflect
+// what portions of the specified thread or module should be written to the
+// file.
+//
+
+typedef
+BOOL
+(WINAPI * MINIDUMP_CALLBACK_ROUTINE) (
+ IN PVOID CallbackParam,
+ IN CONST PMINIDUMP_CALLBACK_INPUT CallbackInput,
+ IN OUT PMINIDUMP_CALLBACK_OUTPUT CallbackOutput
+ );
+
+typedef struct _MINIDUMP_CALLBACK_INFORMATION {
+ MINIDUMP_CALLBACK_ROUTINE CallbackRoutine;
+ PVOID CallbackParam;
+} MINIDUMP_CALLBACK_INFORMATION, *PMINIDUMP_CALLBACK_INFORMATION;
+
+
+
+//++
+//
+// PVOID
+// RVA_TO_ADDR(
+// PVOID Mapping,
+// ULONG Rva
+// )
+//
+// Routine Description:
+//
+// Map an RVA that is contained within a mapped file to it's associated
+// flat address.
+//
+// Arguments:
+//
+// Mapping - Base address of mapped file containing the RVA.
+//
+// Rva - An Rva to fixup.
+//
+// Return Values:
+//
+// A pointer to the desired data.
+//
+//--
+
+#define RVA_TO_ADDR(Mapping,Rva) ((PVOID)(((ULONG_PTR) (Mapping)) + (Rva)))
+
+BOOL
+WINAPI
+MiniDumpWriteDump(
+ IN HANDLE hProcess,
+ IN DWORD ProcessId,
+ IN HANDLE hFile,
+ IN MINIDUMP_TYPE DumpType,
+ IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
+ IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
+ IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
+ );
+
+BOOL
+WINAPI
+MiniDumpReadDumpStream(
+ IN PVOID BaseOfDump,
+ IN ULONG StreamNumber,
+ OUT PMINIDUMP_DIRECTORY * Dir, OPTIONAL
+ OUT PVOID * StreamPointer, OPTIONAL
+ OUT ULONG * StreamSize OPTIONAL
+ );
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#else
+#pragma warning(default:4200) /* Zero length array */
+#pragma warning(default:4201) /* Nameless struct/union */
+#endif
+#endif
+#endif
+
+#include <poppack.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // _DBGHELP_
diff --git a/src/ToolBox/SOS/Strike/inc/wdbgexts.h b/src/ToolBox/SOS/Strike/inc/wdbgexts.h
new file mode 100644
index 0000000000..5e0e8c3adf
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/wdbgexts.h
@@ -0,0 +1,2807 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ wdbgexts.h
+
+Abstract:
+
+ This file contains the necessary prototypes and data types for a user
+ to write a debugger extension DLL. This header file is also included
+ by the NT debuggers (WINDBG & KD).
+
+ This header file must be included after "windows.h" and "dbghelp.h".
+
+ Please see the NT DDK documentation for specific information about
+ how to write your own debugger extension DLL.
+
+Environment:
+
+ Win32 only.
+
+Revision History:
+
+--*/
+
+#ifndef _WDBGEXTS_
+#define _WDBGEXTS_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+#ifndef FEATURE_PAL
+#pragma warning(disable:4115 4201 4204 4214 4221)
+#endif
+
+// Maximum value of MAXIMUM_PROCESSORS for all platforms.
+#define CROSS_PLATFORM_MAXIMUM_PROCESSORS 256
+
+#if !defined(WDBGAPI)
+#define WDBGAPI __stdcall
+#endif
+
+#if !defined(WDBGAPIV)
+#define WDBGAPIV __cdecl
+#endif
+
+#ifndef _WINDEF_
+typedef CONST void *LPCVOID;
+#endif
+
+#ifndef _ULONGLONG_
+typedef unsigned __int64 ULONGLONG;
+typedef ULONGLONG *PULONGLONG;
+#endif
+
+#ifndef __field_ecount_opt
+// Should include SpecStrings.h to get proper definitions.
+#define __field_ecount_opt(x)
+#endif
+
+#define WDBGEXTS_MAXSIZE_T ((SIZE_T)~((SIZE_T)0))
+
+typedef
+VOID
+(WDBGAPIV*PWINDBG_OUTPUT_ROUTINE)(
+ PCSTR lpFormat,
+ ...
+ );
+
+typedef
+ULONG_PTR
+(WDBGAPI*PWINDBG_GET_EXPRESSION)(
+ PCSTR lpExpression
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_GET_EXPRESSION32)(
+ PCSTR lpExpression
+ );
+
+typedef
+ULONG64
+(WDBGAPI*PWINDBG_GET_EXPRESSION64)(
+ PCSTR lpExpression
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_GET_SYMBOL)(
+ PVOID offset,
+ PCHAR pchBuffer,
+ ULONG_PTR *pDisplacement
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_GET_SYMBOL32)(
+ ULONG offset,
+ PCHAR pchBuffer,
+ PULONG pDisplacement
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_GET_SYMBOL64)(
+ ULONG64 offset,
+ PCHAR pchBuffer,
+ PULONG64 pDisplacement
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_DISASM)(
+ ULONG_PTR *lpOffset,
+ PCSTR lpBuffer,
+ ULONG fShowEffectiveAddress
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_DISASM32)(
+ ULONG *lpOffset,
+ PCSTR lpBuffer,
+ ULONG fShowEffectiveAddress
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_DISASM64)(
+ ULONG64 *lpOffset,
+ PCSTR lpBuffer,
+ ULONG fShowEffectiveAddress
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_CHECK_CONTROL_C)(
+ VOID
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_READ_PROCESS_MEMORY_ROUTINE)(
+ ULONG_PTR offset,
+ PVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesRead
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_READ_PROCESS_MEMORY_ROUTINE32)(
+ ULONG offset,
+ PVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesRead
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_READ_PROCESS_MEMORY_ROUTINE64)(
+ ULONG64 offset,
+ PVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesRead
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE)(
+ ULONG_PTR offset,
+ LPCVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesWritten
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE32)(
+ ULONG offset,
+ LPCVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesWritten
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE64)(
+ ULONG64 offset,
+ LPCVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesWritten
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_GET_THREAD_CONTEXT_ROUTINE)(
+ ULONG Processor,
+ PCONTEXT lpContext,
+ ULONG cbSizeOfContext
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_SET_THREAD_CONTEXT_ROUTINE)(
+ ULONG Processor,
+ PCONTEXT lpContext,
+ ULONG cbSizeOfContext
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_IOCTL_ROUTINE)(
+ USHORT IoctlType,
+ PVOID lpvData,
+ ULONG cbSize
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_OLDKD_READ_PHYSICAL_MEMORY)(
+ ULONGLONG address,
+ PVOID buffer,
+ ULONG count,
+ PULONG bytesread
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_OLDKD_WRITE_PHYSICAL_MEMORY)(
+ ULONGLONG address,
+ PVOID buffer,
+ ULONG length,
+ PULONG byteswritten
+ );
+
+
+typedef struct _EXTSTACKTRACE {
+ ULONG FramePointer;
+ ULONG ProgramCounter;
+ ULONG ReturnAddress;
+ ULONG Args[4];
+} EXTSTACKTRACE, *PEXTSTACKTRACE;
+
+typedef struct _EXTSTACKTRACE32 {
+ ULONG FramePointer;
+ ULONG ProgramCounter;
+ ULONG ReturnAddress;
+ ULONG Args[4];
+} EXTSTACKTRACE32, *PEXTSTACKTRACE32;
+
+typedef struct _EXTSTACKTRACE64 {
+ ULONG64 FramePointer;
+ ULONG64 ProgramCounter;
+ ULONG64 ReturnAddress;
+ ULONG64 Args[4];
+} EXTSTACKTRACE64, *PEXTSTACKTRACE64;
+
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_STACKTRACE_ROUTINE)(
+ ULONG FramePointer,
+ ULONG StackPointer,
+ ULONG ProgramCounter,
+ PEXTSTACKTRACE StackFrames,
+ ULONG Frames
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_STACKTRACE_ROUTINE32)(
+ ULONG FramePointer,
+ ULONG StackPointer,
+ ULONG ProgramCounter,
+ PEXTSTACKTRACE32 StackFrames,
+ ULONG Frames
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_STACKTRACE_ROUTINE64)(
+ ULONG64 FramePointer,
+ ULONG64 StackPointer,
+ ULONG64 ProgramCounter,
+ PEXTSTACKTRACE64 StackFrames,
+ ULONG Frames
+ );
+
+typedef struct _WINDBG_EXTENSION_APIS {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL lpGetSymbolRoutine;
+ PWINDBG_DISASM lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE lpReadProcessMemoryRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE lpWriteProcessMemoryRoutine;
+ PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;
+ PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;
+ PWINDBG_IOCTL_ROUTINE lpIoctlRoutine;
+ PWINDBG_STACKTRACE_ROUTINE lpStackTraceRoutine;
+} WINDBG_EXTENSION_APIS, *PWINDBG_EXTENSION_APIS;
+
+typedef struct _WINDBG_EXTENSION_APIS32 {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION32 lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL32 lpGetSymbolRoutine;
+ PWINDBG_DISASM32 lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE32 lpReadProcessMemoryRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE32 lpWriteProcessMemoryRoutine;
+ PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;
+ PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;
+ PWINDBG_IOCTL_ROUTINE lpIoctlRoutine;
+ PWINDBG_STACKTRACE_ROUTINE32 lpStackTraceRoutine;
+} WINDBG_EXTENSION_APIS32, *PWINDBG_EXTENSION_APIS32;
+
+typedef struct _WINDBG_EXTENSION_APIS64 {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION64 lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL64 lpGetSymbolRoutine;
+ PWINDBG_DISASM64 lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE64 lpReadProcessMemoryRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE64 lpWriteProcessMemoryRoutine;
+ PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;
+ PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;
+ PWINDBG_IOCTL_ROUTINE lpIoctlRoutine;
+ PWINDBG_STACKTRACE_ROUTINE64 lpStackTraceRoutine;
+} WINDBG_EXTENSION_APIS64, *PWINDBG_EXTENSION_APIS64;
+
+
+typedef struct _WINDBG_OLD_EXTENSION_APIS {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL lpGetSymbolRoutine;
+ PWINDBG_DISASM lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+} WINDBG_OLD_EXTENSION_APIS, *PWINDBG_OLD_EXTENSION_APIS;
+
+typedef struct _WINDBG_OLDKD_EXTENSION_APIS {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION32 lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL32 lpGetSymbolRoutine;
+ PWINDBG_DISASM32 lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE32 lpReadVirtualMemRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE32 lpWriteVirtualMemRoutine;
+ PWINDBG_OLDKD_READ_PHYSICAL_MEMORY lpReadPhysicalMemRoutine;
+ PWINDBG_OLDKD_WRITE_PHYSICAL_MEMORY lpWritePhysicalMemRoutine;
+} WINDBG_OLDKD_EXTENSION_APIS, *PWINDBG_OLDKD_EXTENSION_APIS;
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_OLD_EXTENSION_ROUTINE)(
+ ULONG dwCurrentPc,
+ PWINDBG_EXTENSION_APIS lpExtensionApis,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_ROUTINE)(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ ULONG dwCurrentPc,
+ ULONG dwProcessor,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_ROUTINE32)(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ ULONG dwCurrentPc,
+ ULONG dwProcessor,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_ROUTINE64)(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ ULONG64 dwCurrentPc,
+ ULONG dwProcessor,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_OLDKD_EXTENSION_ROUTINE)(
+ ULONG dwCurrentPc,
+ PWINDBG_OLDKD_EXTENSION_APIS lpExtensionApis,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_DLL_INIT)(
+ PWINDBG_EXTENSION_APIS lpExtensionApis,
+ USHORT MajorVersion,
+ USHORT MinorVersion
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_DLL_INIT32)(
+ PWINDBG_EXTENSION_APIS32 lpExtensionApis,
+ USHORT MajorVersion,
+ USHORT MinorVersion
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_DLL_INIT64)(
+ PWINDBG_EXTENSION_APIS64 lpExtensionApis,
+ USHORT MajorVersion,
+ USHORT MinorVersion
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_CHECK_VERSION)(
+ VOID
+ );
+
+#define EXT_API_VERSION_NUMBER 5
+#define EXT_API_VERSION_NUMBER32 5
+#define EXT_API_VERSION_NUMBER64 6
+
+typedef struct EXT_API_VERSION {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ USHORT Revision;
+ USHORT Reserved;
+} EXT_API_VERSION, *LPEXT_API_VERSION;
+
+typedef
+LPEXT_API_VERSION
+(WDBGAPI*PWINDBG_EXTENSION_API_VERSION)(
+ VOID
+ );
+
+#define IG_KD_CONTEXT 1
+#define IG_READ_CONTROL_SPACE 2
+#define IG_WRITE_CONTROL_SPACE 3
+#define IG_READ_IO_SPACE 4
+#define IG_WRITE_IO_SPACE 5
+#define IG_READ_PHYSICAL 6
+#define IG_WRITE_PHYSICAL 7
+#define IG_READ_IO_SPACE_EX 8
+#define IG_WRITE_IO_SPACE_EX 9
+#define IG_KSTACK_HELP 10 // obsolete
+#define IG_SET_THREAD 11
+#define IG_READ_MSR 12
+#define IG_WRITE_MSR 13
+#define IG_GET_DEBUGGER_DATA 14
+#define IG_GET_KERNEL_VERSION 15
+#define IG_RELOAD_SYMBOLS 16
+#define IG_GET_SET_SYMPATH 17
+#define IG_GET_EXCEPTION_RECORD 18
+#define IG_IS_PTR64 19
+#define IG_GET_BUS_DATA 20
+#define IG_SET_BUS_DATA 21
+#define IG_DUMP_SYMBOL_INFO 22
+#define IG_LOWMEM_CHECK 23
+#define IG_SEARCH_MEMORY 24
+#define IG_GET_CURRENT_THREAD 25
+#define IG_GET_CURRENT_PROCESS 26
+#define IG_GET_TYPE_SIZE 27
+#define IG_GET_CURRENT_PROCESS_HANDLE 28
+#define IG_GET_INPUT_LINE 29
+#define IG_GET_EXPRESSION_EX 30
+#define IG_TRANSLATE_VIRTUAL_TO_PHYSICAL 31
+#define IG_GET_CACHE_SIZE 32
+#define IG_READ_PHYSICAL_WITH_FLAGS 33
+#define IG_WRITE_PHYSICAL_WITH_FLAGS 34
+#define IG_POINTER_SEARCH_PHYSICAL 35
+#define IG_OBSOLETE_PLACEHOLDER_36 36
+#define IG_GET_THREAD_OS_INFO 37
+#define IG_GET_CLR_DATA_INTERFACE 38
+#define IG_MATCH_PATTERN_A 39
+#define IG_FIND_FILE 40
+#define IG_TYPED_DATA_OBSOLETE 41
+#define IG_QUERY_TARGET_INTERFACE 42
+#define IG_TYPED_DATA 43
+#define IG_DISASSEMBLE_BUFFER 44
+#define IG_GET_ANY_MODULE_IN_RANGE 45
+
+#define IG_GET_TEB_ADDRESS 128
+#define IG_GET_PEB_ADDRESS 129
+
+typedef struct _PROCESSORINFO {
+ USHORT Processor; // current processor
+ USHORT NumberProcessors; // total number of processors
+} PROCESSORINFO, *PPROCESSORINFO;
+
+typedef struct _READCONTROLSPACE {
+ USHORT Processor;
+ ULONG Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} READCONTROLSPACE, *PREADCONTROLSPACE;
+
+typedef struct _READCONTROLSPACE32 {
+ USHORT Processor;
+ ULONG Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} READCONTROLSPACE32, *PREADCONTROLSPACE32;
+
+typedef struct _READCONTROLSPACE64 {
+ USHORT Processor;
+ ULONG64 Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} READCONTROLSPACE64, *PREADCONTROLSPACE64;
+
+typedef struct _IOSPACE {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+} IOSPACE, *PIOSPACE;
+
+typedef struct _IOSPACE32 {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+} IOSPACE32, *PIOSPACE32;
+
+typedef struct _IOSPACE64 {
+ ULONG64 Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+} IOSPACE64, *PIOSPACE64;
+
+typedef struct _IOSPACE_EX {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+ ULONG InterfaceType;
+ ULONG BusNumber;
+ ULONG AddressSpace;
+} IOSPACE_EX, *PIOSPACE_EX;
+
+typedef struct _IOSPACE_EX32 {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+ ULONG InterfaceType;
+ ULONG BusNumber;
+ ULONG AddressSpace;
+} IOSPACE_EX32, *PIOSPACE_EX32;
+
+typedef struct _IOSPACE_EX64 {
+ ULONG64 Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+ ULONG InterfaceType;
+ ULONG BusNumber;
+ ULONG AddressSpace;
+} IOSPACE_EX64, *PIOSPACE_EX64;
+
+typedef struct _GETSETBUSDATA {
+ ULONG BusDataType;
+ ULONG BusNumber;
+ ULONG SlotNumber;
+ PVOID Buffer;
+ ULONG Offset;
+ ULONG Length;
+} BUSDATA, *PBUSDATA;
+
+typedef struct _SEARCHMEMORY {
+ ULONG64 SearchAddress;
+ ULONG64 SearchLength;
+ ULONG64 FoundAddress;
+ ULONG PatternLength;
+ PVOID Pattern;
+} SEARCHMEMORY, *PSEARCHMEMORY;
+
+typedef struct _PHYSICAL {
+ ULONGLONG Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} PHYSICAL, *PPHYSICAL;
+
+#define PHYS_FLAG_DEFAULT 0
+#define PHYS_FLAG_CACHED 1
+#define PHYS_FLAG_UNCACHED 2
+#define PHYS_FLAG_WRITE_COMBINED 3
+
+typedef struct _PHYSICAL_WITH_FLAGS {
+ ULONGLONG Address;
+ ULONG BufLen;
+ ULONG Flags;
+ UCHAR Buf[1];
+} PHYSICAL_WITH_FLAGS, *PPHYSICAL_WITH_FLAGS;
+
+typedef struct _READ_WRITE_MSR {
+ ULONG Msr;
+ LONGLONG Value;
+} READ_WRITE_MSR, *PREAD_WRITE_MSR;
+
+typedef struct _GET_SET_SYMPATH {
+ PCSTR Args; // args to !reload command
+ PSTR Result; // returns new path
+ int Length; // Length of result buffer
+} GET_SET_SYMPATH, *PGET_SET_SYMPATH;
+
+typedef struct _GET_TEB_ADDRESS {
+ ULONGLONG Address;
+} GET_TEB_ADDRESS, *PGET_TEB_ADDRESS;
+
+typedef struct _GET_PEB_ADDRESS {
+ ULONG64 CurrentThread;
+ ULONGLONG Address;
+} GET_PEB_ADDRESS, *PGET_PEB_ADDRESS;
+
+typedef struct _GET_CURRENT_THREAD_ADDRESS {
+ ULONG Processor;
+ ULONG64 Address;
+} GET_CURRENT_THREAD_ADDRESS, *PGET_CURRENT_THREAD_ADDRESS;
+
+typedef struct _GET_CURRENT_PROCESS_ADDRESS {
+ ULONG Processor;
+ ULONG64 CurrentThread;
+ ULONG64 Address;
+} GET_CURRENT_PROCESS_ADDRESS, *PGET_CURRENT_PROCESS_ADDRESS;
+
+typedef struct _GET_INPUT_LINE {
+ PCSTR Prompt;
+ PSTR Buffer;
+ ULONG BufferSize;
+ ULONG InputSize;
+} GET_INPUT_LINE, *PGET_INPUT_LINE;
+
+typedef struct _GET_EXPRESSION_EX {
+ PCSTR Expression;
+ PCSTR Remainder;
+ ULONG64 Value;
+} GET_EXPRESSION_EX, *PGET_EXPRESSION_EX;
+
+typedef struct _TRANSLATE_VIRTUAL_TO_PHYSICAL {
+ ULONG64 Virtual;
+ ULONG64 Physical;
+} TRANSLATE_VIRTUAL_TO_PHYSICAL, *PTRANSLATE_VIRTUAL_TO_PHYSICAL;
+
+#define PTR_SEARCH_PHYS_ALL_HITS 0x00000001
+#define PTR_SEARCH_PHYS_PTE 0x00000002
+#define PTR_SEARCH_PHYS_RANGE_CHECK_ONLY 0x00000004
+
+#define PTR_SEARCH_PHYS_SIZE_SHIFT 3
+#define PTR_SEARCH_PHYS_SIZE_MASK (0xf << PTR_SEARCH_PHYS_SIZE_SHIFT)
+
+#define PTR_SEARCH_NO_SYMBOL_CHECK 0x80000000
+
+typedef struct _POINTER_SEARCH_PHYSICAL {
+ IN ULONG64 Offset;
+ IN ULONG64 Length;
+ IN ULONG64 PointerMin;
+ IN ULONG64 PointerMax;
+ IN ULONG Flags;
+ OUT PULONG64 MatchOffsets;
+ IN ULONG MatchOffsetsSize;
+ OUT ULONG MatchOffsetsCount;
+} POINTER_SEARCH_PHYSICAL, *PPOINTER_SEARCH_PHYSICAL;
+
+typedef struct _WDBGEXTS_THREAD_OS_INFO {
+ // System thread ID input.
+ ULONG ThreadId;
+
+ //
+ // Output information.
+ //
+
+ // Exit status is STILL_ACTIVE by default.
+ ULONG ExitStatus;
+ // Priority class is zero if not known.
+ ULONG PriorityClass;
+ // Priority defaults to normal.
+ ULONG Priority;
+ // Times can be zero if not known.
+ ULONG64 CreateTime;
+ ULONG64 ExitTime;
+ ULONG64 KernelTime;
+ ULONG64 UserTime;
+ // Start offset is zero if not known.
+ ULONG64 StartOffset;
+ // Affinity is zero if not known.
+ ULONG64 Affinity;
+} WDBGEXTS_THREAD_OS_INFO, *PWDBGEXTS_THREAD_OS_INFO;
+
+typedef struct _WDBGEXTS_CLR_DATA_INTERFACE {
+ // Interface requested.
+ const IID* Iid;
+ // Interface pointer return.
+ PVOID Iface;
+} WDBGEXTS_CLR_DATA_INTERFACE, *PWDBGEXTS_CLR_DATA_INTERFACE;
+
+typedef struct _EXT_MATCH_PATTERN_A {
+ IN PCSTR Str;
+ IN PCSTR Pattern;
+ IN ULONG CaseSensitive;
+} EXT_MATCH_PATTERN_A, *PEXT_MATCH_PATTERN_A;
+
+#define EXT_FIND_FILE_ALLOW_GIVEN_PATH 0x00000001
+
+typedef struct _EXT_FIND_FILE {
+ IN PCWSTR FileName;
+ IN ULONG64 IndexedSize;
+ IN ULONG ImageTimeDateStamp;
+ // Pass zero to ignore.
+ IN ULONG ImageCheckSum;
+ IN OPTIONAL PVOID ExtraInfo;
+ IN ULONG ExtraInfoSize;
+ IN ULONG Flags;
+ // Free with UnmapViewOfFile.
+ OUT PVOID FileMapping;
+ OUT ULONG64 FileMappingSize;
+ // Free with CloseHandle.
+ OUT HANDLE FileHandle;
+ // Must be at least MAX_PATH characters if set.
+ OUT OPTIONAL PWSTR FoundFileName;
+ OUT ULONG FoundFileNameChars;
+} EXT_FIND_FILE, *PEXT_FIND_FILE;
+
+#define DEBUG_TYPED_DATA_IS_IN_MEMORY 0x00000001
+#define DEBUG_TYPED_DATA_PHYSICAL_DEFAULT 0x00000002
+#define DEBUG_TYPED_DATA_PHYSICAL_CACHED 0x00000004
+#define DEBUG_TYPED_DATA_PHYSICAL_UNCACHED 0x00000006
+#define DEBUG_TYPED_DATA_PHYSICAL_WRITE_COMBINED 0x00000008
+
+// Mask for all physical flags.
+#define DEBUG_TYPED_DATA_PHYSICAL_MEMORY 0x0000000e
+
+typedef struct _DEBUG_TYPED_DATA
+{
+ ULONG64 ModBase;
+ ULONG64 Offset;
+ ULONG64 EngineHandle;
+ ULONG64 Data;
+ ULONG Size;
+ ULONG Flags;
+ ULONG TypeId;
+ ULONG BaseTypeId;
+ ULONG Tag;
+ ULONG Register;
+ ULONG64 Internal[9];
+} DEBUG_TYPED_DATA, *PDEBUG_TYPED_DATA;
+
+typedef enum _EXT_TDOP {
+ EXT_TDOP_COPY,
+ EXT_TDOP_RELEASE,
+ EXT_TDOP_SET_FROM_EXPR,
+ EXT_TDOP_SET_FROM_U64_EXPR,
+ EXT_TDOP_GET_FIELD,
+ EXT_TDOP_EVALUATE,
+ EXT_TDOP_GET_TYPE_NAME,
+ EXT_TDOP_OUTPUT_TYPE_NAME,
+ EXT_TDOP_OUTPUT_SIMPLE_VALUE,
+ EXT_TDOP_OUTPUT_FULL_VALUE,
+ EXT_TDOP_HAS_FIELD,
+ EXT_TDOP_GET_FIELD_OFFSET,
+ EXT_TDOP_GET_ARRAY_ELEMENT,
+ EXT_TDOP_GET_DEREFERENCE,
+ EXT_TDOP_GET_TYPE_SIZE,
+ EXT_TDOP_OUTPUT_TYPE_DEFINITION,
+ EXT_TDOP_GET_POINTER_TO,
+ EXT_TDOP_SET_FROM_TYPE_ID_AND_U64,
+ EXT_TDOP_SET_PTR_FROM_TYPE_ID_AND_U64,
+
+ EXT_TDOP_COUNT
+} EXT_TDOP;
+
+// EXT_TDF physical flags must match DEBUG_TYPED.
+#define EXT_TDF_PHYSICAL_DEFAULT 0x00000002
+#define EXT_TDF_PHYSICAL_CACHED 0x00000004
+#define EXT_TDF_PHYSICAL_UNCACHED 0x00000006
+#define EXT_TDF_PHYSICAL_WRITE_COMBINED 0x00000008
+#define EXT_TDF_PHYSICAL_MEMORY 0x0000000e
+
+// NOTE: Every DEBUG_TYPED_DATA should be released
+// via EXT_TDOP_RELEASE when it is no longer needed.
+typedef struct _EXT_TYPED_DATA {
+ IN EXT_TDOP Operation;
+ IN ULONG Flags;
+ IN DEBUG_TYPED_DATA InData;
+ OUT DEBUG_TYPED_DATA OutData;
+ IN ULONG InStrIndex;
+ IN ULONG In32;
+ OUT ULONG Out32;
+ IN ULONG64 In64;
+ OUT ULONG64 Out64;
+ OUT ULONG StrBufferIndex;
+ IN ULONG StrBufferChars;
+ OUT ULONG StrCharsNeeded;
+ IN OUT ULONG DataBufferIndex;
+ IN ULONG DataBufferBytes;
+ OUT ULONG DataBytesNeeded;
+ OUT HRESULT Status;
+ // Must be zeroed.
+ ULONG64 Reserved[8];
+} EXT_TYPED_DATA, *PEXT_TYPED_DATA;
+
+typedef struct _WDBGEXTS_QUERY_INTERFACE {
+ // Interface requested.
+ const IID* Iid;
+ // Interface pointer return.
+ PVOID Iface;
+} WDBGEXTS_QUERY_INTERFACE, *PWDBGEXTS_QUERY_INTERFACE;
+
+#define WDBGEXTS_ADDRESS_DEFAULT 0x00000000
+#define WDBGEXTS_ADDRESS_SEG16 0x00000001
+#define WDBGEXTS_ADDRESS_SEG32 0x00000002
+#define WDBGEXTS_ADDRESS_RESERVED0 0x80000000
+
+typedef struct _WDBGEXTS_DISASSEMBLE_BUFFER {
+ IN ULONG64 InOffset;
+ OUT ULONG64 OutOffset;
+ // AddrFlags are from above.
+ IN ULONG AddrFlags;
+ // FormatFlags are from dbgeng's DEBUG_DISASM_*.
+ IN ULONG FormatFlags;
+ IN ULONG DataBufferBytes;
+ IN ULONG DisasmBufferChars;
+ IN OPTIONAL PVOID DataBuffer;
+ OUT PWSTR DisasmBuffer;
+ IN ULONG64 Reserved0[3];
+} WDBGEXTS_DISASSEMBLE_BUFFER, *PWDBGEXTS_DISASSEMBLE_BUFFER;
+
+typedef struct _WDBGEXTS_MODULE_IN_RANGE {
+ IN ULONG64 Start;
+ // Inclusive ending offset.
+ IN ULONG64 End;
+ OUT ULONG64 FoundModBase;
+ OUT ULONG FoundModSize;
+} WDBGEXTS_MODULE_IN_RANGE, *PWDBGEXTS_MODULE_IN_RANGE;
+
+//
+// If DBGKD_VERS_FLAG_DATA is set in Flags, info should be retrieved from
+// the KDDEBUGGER_DATA block rather than from the DBGKD_GET_VERSION
+// packet. The data will remain in the version packet for a while to
+// reduce compatibility problems.
+//
+
+#define DBGKD_VERS_FLAG_MP 0x0001 // kernel is MP built
+#define DBGKD_VERS_FLAG_DATA 0x0002 // DebuggerDataList is valid
+#define DBGKD_VERS_FLAG_PTR64 0x0004 // native pointers are 64 bits
+#define DBGKD_VERS_FLAG_NOMM 0x0008 // No MM - don't decode PTEs
+#define DBGKD_VERS_FLAG_HSS 0x0010 // hardware stepping support
+#define DBGKD_VERS_FLAG_PARTITIONS 0x0020 // multiple OS partitions exist
+
+#define KDBG_TAG 'GBDK'
+
+//
+// KD version MajorVersion high-byte identifiers.
+//
+
+typedef enum _DBGKD_MAJOR_TYPES
+{
+ DBGKD_MAJOR_NT,
+ DBGKD_MAJOR_XBOX,
+ DBGKD_MAJOR_BIG,
+ DBGKD_MAJOR_EXDI,
+ DBGKD_MAJOR_NTBD,
+ DBGKD_MAJOR_EFI,
+ DBGKD_MAJOR_TNT,
+ DBGKD_MAJOR_SINGULARITY,
+ DBGKD_MAJOR_HYPERVISOR,
+ DBGKD_MAJOR_COUNT
+} DBGKD_MAJOR_TYPES;
+
+#define DBGKD_MAJOR_TYPE(MajorVersion) \
+ ((DBGKD_MAJOR_TYPES)((MajorVersion) >> 8))
+
+
+// **********************************************************************
+// DO NOT CHANGE THESE 32 BIT STRUCTURES!
+// ONLY MAKE CHAGES TO THE 64 BIT VERSION BELOW!!
+// **********************************************************************
+
+//
+// The following structure has changed in more than pointer size.
+//
+// This is the version packet for pre-NT5 Beta 2 systems.
+// For now, it is also still used on x86
+//
+typedef struct _DBGKD_GET_VERSION32 {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ USHORT ProtocolVersion;
+ USHORT Flags;
+ ULONG KernBase;
+ ULONG PsLoadedModuleList;
+
+ USHORT MachineType;
+
+ //
+ // help for walking stacks with user callbacks:
+ //
+
+ //
+ // The address of the thread structure is provided in the
+ // WAIT_STATE_CHANGE packet. This is the offset from the base of
+ // the thread structure to the pointer to the kernel stack frame
+ // for the currently active usermode callback.
+ //
+
+ USHORT ThCallbackStack; // offset in thread data
+
+ //
+ // these values are offsets into that frame:
+ //
+
+ USHORT NextCallback; // saved pointer to next callback frame
+ USHORT FramePointer; // saved frame pointer
+
+ //
+ // Address of the kernel callout routine.
+ //
+
+ ULONG KiCallUserMode; // kernel routine
+
+ //
+ // Address of the usermode entry point for callbacks.
+ //
+
+ ULONG KeUserCallbackDispatcher; // address in ntdll
+
+ //
+ // DbgBreakPointWithStatus is a function which takes a ULONG argument
+ // and hits a breakpoint. This field contains the address of the
+ // breakpoint instruction. When the debugger sees a breakpoint
+ // at this address, it may retrieve the argument from the first
+ // argument register, or on x86 the eax register.
+ //
+
+ ULONG BreakpointWithStatus; // address of breakpoint
+
+ //
+ // Components may register a debug data block for use by
+ // debugger extensions. This is the address of the list head.
+ //
+
+ ULONG DebuggerDataList;
+
+} DBGKD_GET_VERSION32, *PDBGKD_GET_VERSION32;
+
+
+//
+// This is the debugger data packet for pre NT5 Beta 2 systems.
+// For now, it is still used on x86
+//
+
+typedef struct _DBGKD_DEBUG_DATA_HEADER32 {
+
+ LIST_ENTRY32 List;
+ ULONG OwnerTag;
+ ULONG Size;
+
+} DBGKD_DEBUG_DATA_HEADER32, *PDBGKD_DEBUG_DATA_HEADER32;
+
+typedef struct _KDDEBUGGER_DATA32 {
+
+ DBGKD_DEBUG_DATA_HEADER32 Header;
+ ULONG KernBase;
+ ULONG BreakpointWithStatus; // address of breakpoint
+ ULONG SavedContext;
+ USHORT ThCallbackStack; // offset in thread data
+ USHORT NextCallback; // saved pointer to next callback frame
+ USHORT FramePointer; // saved frame pointer
+ USHORT PaeEnabled:1;
+ ULONG KiCallUserMode; // kernel routine
+ ULONG KeUserCallbackDispatcher; // address in ntdll
+
+ ULONG PsLoadedModuleList;
+ ULONG PsActiveProcessHead;
+ ULONG PspCidTable;
+
+ ULONG ExpSystemResourcesList;
+ ULONG ExpPagedPoolDescriptor;
+ ULONG ExpNumberOfPagedPools;
+
+ ULONG KeTimeIncrement;
+ ULONG KeBugCheckCallbackListHead;
+ ULONG KiBugcheckData;
+
+ ULONG IopErrorLogListHead;
+
+ ULONG ObpRootDirectoryObject;
+ ULONG ObpTypeObjectType;
+
+ ULONG MmSystemCacheStart;
+ ULONG MmSystemCacheEnd;
+ ULONG MmSystemCacheWs;
+
+ ULONG MmPfnDatabase;
+ ULONG MmSystemPtesStart;
+ ULONG MmSystemPtesEnd;
+ ULONG MmSubsectionBase;
+ ULONG MmNumberOfPagingFiles;
+
+ ULONG MmLowestPhysicalPage;
+ ULONG MmHighestPhysicalPage;
+ ULONG MmNumberOfPhysicalPages;
+
+ ULONG MmMaximumNonPagedPoolInBytes;
+ ULONG MmNonPagedSystemStart;
+ ULONG MmNonPagedPoolStart;
+ ULONG MmNonPagedPoolEnd;
+
+ ULONG MmPagedPoolStart;
+ ULONG MmPagedPoolEnd;
+ ULONG MmPagedPoolInformation;
+ ULONG MmPageSize;
+
+ ULONG MmSizeOfPagedPoolInBytes;
+
+ ULONG MmTotalCommitLimit;
+ ULONG MmTotalCommittedPages;
+ ULONG MmSharedCommit;
+ ULONG MmDriverCommit;
+ ULONG MmProcessCommit;
+ ULONG MmPagedPoolCommit;
+ ULONG MmExtendedCommit;
+
+ ULONG MmZeroedPageListHead;
+ ULONG MmFreePageListHead;
+ ULONG MmStandbyPageListHead;
+ ULONG MmModifiedPageListHead;
+ ULONG MmModifiedNoWritePageListHead;
+ ULONG MmAvailablePages;
+ ULONG MmResidentAvailablePages;
+
+ ULONG PoolTrackTable;
+ ULONG NonPagedPoolDescriptor;
+
+ ULONG MmHighestUserAddress;
+ ULONG MmSystemRangeStart;
+ ULONG MmUserProbeAddress;
+
+ ULONG KdPrintCircularBuffer;
+ ULONG KdPrintCircularBufferEnd;
+ ULONG KdPrintWritePointer;
+ ULONG KdPrintRolloverCount;
+
+ ULONG MmLoadedUserImageList;
+
+} KDDEBUGGER_DATA32, *PKDDEBUGGER_DATA32;
+
+// **********************************************************************
+//
+// DO NOT CHANGE KDDEBUGGER_DATA32!!
+// ONLY MAKE CHANGES TO KDDEBUGGER_DATA64!!!
+//
+// **********************************************************************
+
+
+enum
+{
+ DBGKD_SIMULATION_NONE,
+ DBGKD_SIMULATION_EXDI
+};
+
+#define KD_SECONDARY_VERSION_DEFAULT 0
+
+#define KD_SECONDARY_VERSION_AMD64_OBSOLETE_CONTEXT_1 0
+#define KD_SECONDARY_VERSION_AMD64_OBSOLETE_CONTEXT_2 1
+#define KD_SECONDARY_VERSION_AMD64_CONTEXT 2
+
+#ifdef _AMD64_
+#define CURRENT_KD_SECONDARY_VERSION \
+ KD_SECONDARY_VERSION_AMD64_CONTEXT
+#else
+#define CURRENT_KD_SECONDARY_VERSION KD_SECONDARY_VERSION_DEFAULT
+#endif
+
+typedef struct _DBGKD_GET_VERSION64 {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ UCHAR ProtocolVersion;
+ UCHAR KdSecondaryVersion; // Cannot be 'A' for compat with dump header
+ USHORT Flags;
+ USHORT MachineType;
+
+ //
+ // Protocol command support descriptions.
+ // These allow the debugger to automatically
+ // adapt to different levels of command support
+ // in different kernels.
+ //
+
+ // One beyond highest packet type understood, zero based.
+ UCHAR MaxPacketType;
+ // One beyond highest state change understood, zero based.
+ UCHAR MaxStateChange;
+ // One beyond highest state manipulate message understood, zero based.
+ UCHAR MaxManipulate;
+
+ // Kind of execution environment the kernel is running in,
+ // such as a real machine or a simulator. Written back
+ // by the simulation if one exists.
+ UCHAR Simulation;
+
+ USHORT Unused[1];
+
+ ULONG64 KernBase;
+ ULONG64 PsLoadedModuleList;
+
+ //
+ // Components may register a debug data block for use by
+ // debugger extensions. This is the address of the list head.
+ //
+ // There will always be an entry for the debugger.
+ //
+
+ ULONG64 DebuggerDataList;
+
+} DBGKD_GET_VERSION64, *PDBGKD_GET_VERSION64;
+
+
+//
+// This structure is used by the debugger for all targets
+// It is the same size as DBGKD_DATA_HEADER on all systems
+//
+typedef struct _DBGKD_DEBUG_DATA_HEADER64 {
+
+ //
+ // Link to other blocks
+ //
+
+ LIST_ENTRY64 List;
+
+ //
+ // This is a unique tag to identify the owner of the block.
+ // If your component only uses one pool tag, use it for this, too.
+ //
+
+ ULONG OwnerTag;
+
+ //
+ // This must be initialized to the size of the data block,
+ // including this structure.
+ //
+
+ ULONG Size;
+
+} DBGKD_DEBUG_DATA_HEADER64, *PDBGKD_DEBUG_DATA_HEADER64;
+
+
+//
+// This structure is the same size on all systems. The only field
+// which must be translated by the debugger is Header.List.
+//
+
+//
+// DO NOT ADD OR REMOVE FIELDS FROM THE MIDDLE OF THIS STRUCTURE!!!
+//
+// If you remove a field, replace it with an "unused" placeholder.
+// Do not reuse fields until there has been enough time for old debuggers
+// and extensions to age out.
+//
+typedef struct _KDDEBUGGER_DATA64 {
+
+ DBGKD_DEBUG_DATA_HEADER64 Header;
+
+ //
+ // Base address of kernel image
+ //
+
+ ULONG64 KernBase;
+
+ //
+ // DbgBreakPointWithStatus is a function which takes an argument
+ // and hits a breakpoint. This field contains the address of the
+ // breakpoint instruction. When the debugger sees a breakpoint
+ // at this address, it may retrieve the argument from the first
+ // argument register, or on x86 the eax register.
+ //
+
+ ULONG64 BreakpointWithStatus; // address of breakpoint
+
+ //
+ // Address of the saved context record during a bugcheck
+ //
+ // N.B. This is an automatic in KeBugcheckEx's frame, and
+ // is only valid after a bugcheck.
+ //
+
+ ULONG64 SavedContext;
+
+ //
+ // help for walking stacks with user callbacks:
+ //
+
+ //
+ // The address of the thread structure is provided in the
+ // WAIT_STATE_CHANGE packet. This is the offset from the base of
+ // the thread structure to the pointer to the kernel stack frame
+ // for the currently active usermode callback.
+ //
+
+ USHORT ThCallbackStack; // offset in thread data
+
+ //
+ // these values are offsets into that frame:
+ //
+
+ USHORT NextCallback; // saved pointer to next callback frame
+ USHORT FramePointer; // saved frame pointer
+
+ //
+ // pad to a quad boundary
+ //
+ USHORT PaeEnabled:1;
+
+ //
+ // Address of the kernel callout routine.
+ //
+
+ ULONG64 KiCallUserMode; // kernel routine
+
+ //
+ // Address of the usermode entry point for callbacks.
+ //
+
+ ULONG64 KeUserCallbackDispatcher; // address in ntdll
+
+
+ //
+ // Addresses of various kernel data structures and lists
+ // that are of interest to the kernel debugger.
+ //
+
+ ULONG64 PsLoadedModuleList;
+ ULONG64 PsActiveProcessHead;
+ ULONG64 PspCidTable;
+
+ ULONG64 ExpSystemResourcesList;
+ ULONG64 ExpPagedPoolDescriptor;
+ ULONG64 ExpNumberOfPagedPools;
+
+ ULONG64 KeTimeIncrement;
+ ULONG64 KeBugCheckCallbackListHead;
+ ULONG64 KiBugcheckData;
+
+ ULONG64 IopErrorLogListHead;
+
+ ULONG64 ObpRootDirectoryObject;
+ ULONG64 ObpTypeObjectType;
+
+ ULONG64 MmSystemCacheStart;
+ ULONG64 MmSystemCacheEnd;
+ ULONG64 MmSystemCacheWs;
+
+ ULONG64 MmPfnDatabase;
+ ULONG64 MmSystemPtesStart;
+ ULONG64 MmSystemPtesEnd;
+ ULONG64 MmSubsectionBase;
+ ULONG64 MmNumberOfPagingFiles;
+
+ ULONG64 MmLowestPhysicalPage;
+ ULONG64 MmHighestPhysicalPage;
+ ULONG64 MmNumberOfPhysicalPages;
+
+ ULONG64 MmMaximumNonPagedPoolInBytes;
+ ULONG64 MmNonPagedSystemStart;
+ ULONG64 MmNonPagedPoolStart;
+ ULONG64 MmNonPagedPoolEnd;
+
+ ULONG64 MmPagedPoolStart;
+ ULONG64 MmPagedPoolEnd;
+ ULONG64 MmPagedPoolInformation;
+ ULONG64 MmPageSize;
+
+ ULONG64 MmSizeOfPagedPoolInBytes;
+
+ ULONG64 MmTotalCommitLimit;
+ ULONG64 MmTotalCommittedPages;
+ ULONG64 MmSharedCommit;
+ ULONG64 MmDriverCommit;
+ ULONG64 MmProcessCommit;
+ ULONG64 MmPagedPoolCommit;
+ ULONG64 MmExtendedCommit;
+
+ ULONG64 MmZeroedPageListHead;
+ ULONG64 MmFreePageListHead;
+ ULONG64 MmStandbyPageListHead;
+ ULONG64 MmModifiedPageListHead;
+ ULONG64 MmModifiedNoWritePageListHead;
+ ULONG64 MmAvailablePages;
+ ULONG64 MmResidentAvailablePages;
+
+ ULONG64 PoolTrackTable;
+ ULONG64 NonPagedPoolDescriptor;
+
+ ULONG64 MmHighestUserAddress;
+ ULONG64 MmSystemRangeStart;
+ ULONG64 MmUserProbeAddress;
+
+ ULONG64 KdPrintCircularBuffer;
+ ULONG64 KdPrintCircularBufferEnd;
+ ULONG64 KdPrintWritePointer;
+ ULONG64 KdPrintRolloverCount;
+
+ ULONG64 MmLoadedUserImageList;
+
+ // NT 5.1 Addition
+
+ ULONG64 NtBuildLab;
+ ULONG64 KiNormalSystemCall;
+
+ // NT 5.0 hotfix addition
+
+ ULONG64 KiProcessorBlock;
+ ULONG64 MmUnloadedDrivers;
+ ULONG64 MmLastUnloadedDriver;
+ ULONG64 MmTriageActionTaken;
+ ULONG64 MmSpecialPoolTag;
+ ULONG64 KernelVerifier;
+ ULONG64 MmVerifierData;
+ ULONG64 MmAllocatedNonPagedPool;
+ ULONG64 MmPeakCommitment;
+ ULONG64 MmTotalCommitLimitMaximum;
+ ULONG64 CmNtCSDVersion;
+
+ // NT 5.1 Addition
+
+ ULONG64 MmPhysicalMemoryBlock;
+ ULONG64 MmSessionBase;
+ ULONG64 MmSessionSize;
+ ULONG64 MmSystemParentTablePage;
+
+ // Server 2003 addition
+
+ ULONG64 MmVirtualTranslationBase;
+
+ USHORT OffsetKThreadNextProcessor;
+ USHORT OffsetKThreadTeb;
+ USHORT OffsetKThreadKernelStack;
+ USHORT OffsetKThreadInitialStack;
+
+ USHORT OffsetKThreadApcProcess;
+ USHORT OffsetKThreadState;
+ USHORT OffsetKThreadBStore;
+ USHORT OffsetKThreadBStoreLimit;
+
+ USHORT SizeEProcess;
+ USHORT OffsetEprocessPeb;
+ USHORT OffsetEprocessParentCID;
+ USHORT OffsetEprocessDirectoryTableBase;
+
+ USHORT SizePrcb;
+ USHORT OffsetPrcbDpcRoutine;
+ USHORT OffsetPrcbCurrentThread;
+ USHORT OffsetPrcbMhz;
+
+ USHORT OffsetPrcbCpuType;
+ USHORT OffsetPrcbVendorString;
+ USHORT OffsetPrcbProcStateContext;
+ USHORT OffsetPrcbNumber;
+
+ USHORT SizeEThread;
+
+ ULONG64 KdPrintCircularBufferPtr;
+ ULONG64 KdPrintBufferSize;
+
+ ULONG64 KeLoaderBlock;
+
+ USHORT SizePcr;
+ USHORT OffsetPcrSelfPcr;
+ USHORT OffsetPcrCurrentPrcb;
+ USHORT OffsetPcrContainedPrcb;
+
+ USHORT OffsetPcrInitialBStore;
+ USHORT OffsetPcrBStoreLimit;
+ USHORT OffsetPcrInitialStack;
+ USHORT OffsetPcrStackLimit;
+
+ USHORT OffsetPrcbPcrPage;
+ USHORT OffsetPrcbProcStateSpecialReg;
+ USHORT GdtR0Code;
+ USHORT GdtR0Data;
+
+ USHORT GdtR0Pcr;
+ USHORT GdtR3Code;
+ USHORT GdtR3Data;
+ USHORT GdtR3Teb;
+
+ USHORT GdtLdt;
+ USHORT GdtTss;
+ USHORT Gdt64R3CmCode;
+ USHORT Gdt64R3CmTeb;
+
+ ULONG64 IopNumTriageDumpDataBlocks;
+ ULONG64 IopTriageDumpDataBlocks;
+
+ // Longhorn addition
+
+ ULONG64 VfCrashDataBlock;
+ ULONG64 MmBadPagesDetected;
+ ULONG64 MmZeroedPageSingleBitErrorsDetected;
+
+
+} KDDEBUGGER_DATA64, *PKDDEBUGGER_DATA64;
+
+
+
+/************************************
+
+ Type Dump Ioctl
+
+*************************************/
+
+
+//
+// Fields are not indented if this is set
+//
+#define DBG_DUMP_NO_INDENT 0x00000001
+//
+// Offsets are not printed if this is set
+//
+#define DBG_DUMP_NO_OFFSET 0x00000002
+//
+// Verbose output
+//
+#define DBG_DUMP_VERBOSE 0x00000004
+//
+// Callback is done for each of fields
+//
+#define DBG_DUMP_CALL_FOR_EACH 0x00000008
+//
+// A list of type is dumped, listLink should have info about next element pointer
+//
+#define DBG_DUMP_LIST 0x00000020
+//
+// Nothing is printed if this is set (only callbacks and data copies done)
+//
+#define DBG_DUMP_NO_PRINT 0x00000040
+//
+// Ioctl returns the size as usual, but will not do field prints/callbacks if this is set
+//
+#define DBG_DUMP_GET_SIZE_ONLY 0x00000080
+//
+// Specifies how much deep into structs we can go
+//
+#define DBG_DUMP_RECUR_LEVEL(l) ((l & 0xf) << 8)
+//
+// No newlines are printed after each field
+//
+#define DBG_DUMP_COMPACT_OUT 0x00002000
+//
+// An array of type is dumped, number of elements can be specified in listLink->size
+//
+#define DBG_DUMP_ARRAY 0x00008000
+//
+// The specified addr value is actually the address of field listLink->fName
+//
+#define DBG_DUMP_ADDRESS_OF_FIELD 0x00010000
+
+//
+// The specified addr value is actually the adress at the end of type
+//
+#define DBG_DUMP_ADDRESS_AT_END 0x00020000
+
+//
+// This could be used to copy only the primitive types like ULONG, PVOID etc.
+// - will not work with structures/unions
+//
+#define DBG_DUMP_COPY_TYPE_DATA 0x00040000
+//
+// Flag to allow read directly from physical memory
+//
+#define DBG_DUMP_READ_PHYSICAL 0x00080000
+//
+// This causes a function type to be dumped in format function(arg1, arg2, ...)
+//
+#define DBG_DUMP_FUNCTION_FORMAT 0x00100000
+//
+// This recurses on a struct but doesn't expand pointers
+//
+#define DBG_DUMP_BLOCK_RECURSE 0x00200000
+//
+// Match the type size to resolve ambiguity in case multiple matches with same name are available
+//
+#define DBG_DUMP_MATCH_SIZE 0x00400000
+
+//
+// Obsolete defs
+//
+#define DBG_RETURN_TYPE 0
+#define DBG_RETURN_SUBTYPES 0
+#define DBG_RETURN_TYPE_VALUES 0
+
+//
+// Dump and callback optons for fields - Options used in FIELD_INFO.fOptions
+//
+
+//
+// Callback is done before printing the field if this is set
+//
+#define DBG_DUMP_FIELD_CALL_BEFORE_PRINT 0x00000001
+//
+// No callback is done
+//
+#define DBG_DUMP_FIELD_NO_CALLBACK_REQ 0x00000002
+//
+// Subfields of the fields are processesed
+//
+#define DBG_DUMP_FIELD_RECUR_ON_THIS 0x00000004
+//
+// fName must match completely for the field to be dumped instead just a prefix
+// match by default
+//
+#define DBG_DUMP_FIELD_FULL_NAME 0x00000008
+//
+// This causes array elements of an array field to be printed
+//
+#define DBG_DUMP_FIELD_ARRAY 0x00000010
+//
+// The data of the field is copied into fieldCallBack
+//
+#define DBG_DUMP_FIELD_COPY_FIELD_DATA 0x00000020
+//
+// In callback or when Ioctl returns, the FIELD_INFO.address has the address of field.
+// If no address is supplied for the type, it contains total offset of the field.
+//
+#define DBG_DUMP_FIELD_RETURN_ADDRESS 0x00001000
+//
+// Return the offset and size in bits instead of bytes is case of Bitfield
+//
+#define DBG_DUMP_FIELD_SIZE_IN_BITS 0x00002000
+//
+// Nothing is printed for field if this is set (only callbacks and data copies done)
+//
+#define DBG_DUMP_FIELD_NO_PRINT 0x00004000
+//
+// If the field is a pointer, it is dumped as a string, ANSI, WCHAR, MULTI or GUID
+// depending on following options
+//
+#define DBG_DUMP_FIELD_DEFAULT_STRING 0x00010000
+#define DBG_DUMP_FIELD_WCHAR_STRING 0x00020000
+#define DBG_DUMP_FIELD_MULTI_STRING 0x00040000
+#define DBG_DUMP_FIELD_GUID_STRING 0x00080000
+
+
+//
+// Error status returned on TYPE DUMP Ioctl failure
+//
+#define MEMORY_READ_ERROR 0x01
+#define SYMBOL_TYPE_INDEX_NOT_FOUND 0x02
+#define SYMBOL_TYPE_INFO_NOT_FOUND 0x03
+#define FIELDS_DID_NOT_MATCH 0x04
+#define NULL_SYM_DUMP_PARAM 0x05
+#define NULL_FIELD_NAME 0x06
+#define INCORRECT_VERSION_INFO 0x07
+#define EXIT_ON_CONTROLC 0x08
+#define CANNOT_ALLOCATE_MEMORY 0x09
+#define INSUFFICIENT_SPACE_TO_COPY 0x0a
+#define ADDRESS_TYPE_INDEX_NOT_FOUND 0x0b
+
+
+//////////////////////////////////////////////////////////////////////////*/
+
+
+typedef
+ULONG
+(WDBGAPI*PSYM_DUMP_FIELD_CALLBACK)(
+ struct _FIELD_INFO *pField,
+ PVOID UserContext
+ );
+
+typedef struct _FIELD_INFO {
+ PUCHAR fName; // Name of the field
+ PUCHAR printName; // Name to be printed at dump
+ ULONG size; // Size of the field
+ ULONG fOptions; // Dump Options for the field
+ ULONG64 address; // address of the field
+ union {
+ PVOID fieldCallBack; // Return info or callBack routine for the field
+ PVOID pBuffer; // the type data is copied into this
+ };
+ ULONG TypeId; // OUT Type index of the field
+ ULONG FieldOffset; // OUT Offset of field inside struct
+ ULONG BufferSize; // size of buffer used with DBG_DUMP_FIELD_COPY_FIELD_DATA
+ struct _BitField {
+ USHORT Position; // OUT set to start position for bitfield
+ USHORT Size; // OUT set to size for bitfields
+ } BitField;
+ ULONG fPointer:2; // OUT set to 1 for pointers, 3 for 64bit pointers
+ ULONG fArray:1; // OUT set to 1 for array types
+ ULONG fStruct:1; // OUT set to 1 for struct/class tyoes
+ ULONG fConstant:1; // OUT set to 1 for constants (enumerate as fields)
+ ULONG fStatic:1; // OUT set to 1 for statics (class/struct static members)
+ ULONG Reserved:26; // unused
+} FIELD_INFO, *PFIELD_INFO;
+
+typedef struct _SYM_DUMP_PARAM {
+ ULONG size; // size of this struct
+ PUCHAR sName; // type name
+ ULONG Options; // Dump options
+ ULONG64 addr; // Address to take data for type
+ PFIELD_INFO listLink; // fName here would be used to do list dump
+ union {
+ PVOID Context; // Usercontext passed to CallbackRoutine
+ PVOID pBuffer; // the type data is copied into this
+ };
+ PSYM_DUMP_FIELD_CALLBACK CallbackRoutine;
+ // Routine called back
+ ULONG nFields; // # elements in Fields
+ __field_ecount_opt(nFields) PFIELD_INFO Fields; // Used to return information about field
+ ULONG64 ModBase; // OUT Module base address containing type
+ ULONG TypeId; // OUT Type index of the symbol
+ ULONG TypeSize; // OUT Size of type
+ ULONG BufferSize; // IN size of buffer (used with DBG_DUMP_COPY_TYPE_DATA)
+ ULONG fPointer:2; // OUT set to 1 for pointers, 3 for 64bit pointers
+ ULONG fArray:1; // OUT set to 1 for array types
+ ULONG fStruct:1; // OUT set to 1 for struct/class tyoes
+ ULONG fConstant:1; // OUT set to 1 for constant types (unused)
+ ULONG Reserved:27; // unused
+} SYM_DUMP_PARAM, *PSYM_DUMP_PARAM;
+
+#ifdef __cplusplus
+#define CPPMOD extern "C"
+#else
+#define CPPMOD
+#endif
+
+
+#ifndef NOEXTAPI
+
+#if defined(KDEXT_64BIT)
+#define WINDBG_EXTENSION_APIS WINDBG_EXTENSION_APIS64
+#define PWINDBG_EXTENSION_APIS PWINDBG_EXTENSION_APIS64
+#define PWINDBG_EXTENSION_ROUTINE PWINDBG_EXTENSION_ROUTINE64
+#define DECLARE_API(s) DECLARE_API64(s)
+#elif defined(KDEXT_32BIT)
+#define WINDBG_EXTENSION_APIS WINDBG_EXTENSION_APIS32
+#define PWINDBG_EXTENSION_APIS PWINDBG_EXTENSION_APIS32
+#define PWINDBG_EXTENSION_ROUTINE PWINDBG_EXTENSION_ROUTINE32
+#define DECLARE_API(s) DECLARE_API32(s)
+#else
+#define DECLARE_API(s) \
+ CPPMOD VOID \
+ s( \
+ HANDLE hCurrentProcess, \
+ HANDLE hCurrentThread, \
+ ULONG dwCurrentPc, \
+ ULONG dwProcessor, \
+ PCSTR args \
+ )
+#endif
+
+#define DECLARE_API32(s) \
+ CPPMOD VOID \
+ s( \
+ HANDLE hCurrentProcess, \
+ HANDLE hCurrentThread, \
+ ULONG dwCurrentPc, \
+ ULONG dwProcessor, \
+ PCSTR args \
+ )
+
+#define DECLARE_API64(s) \
+ CPPMOD VOID \
+ s( \
+ HANDLE hCurrentProcess, \
+ HANDLE hCurrentThread, \
+ ULONG64 dwCurrentPc, \
+ ULONG dwProcessor, \
+ PCSTR args \
+ )
+
+
+extern WINDBG_EXTENSION_APIS ExtensionApis;
+
+
+#define dprintf (ExtensionApis.lpOutputRoutine)
+#define GetExpression (ExtensionApis.lpGetExpressionRoutine)
+#define CheckControlC (ExtensionApis.lpCheckControlCRoutine)
+#define GetContext (ExtensionApis.lpGetThreadContextRoutine)
+#define SetContext (ExtensionApis.lpSetThreadContextRoutine)
+#define Ioctl (ExtensionApis.lpIoctlRoutine)
+#define Disasm (ExtensionApis.lpDisasmRoutine)
+#define GetSymbol (ExtensionApis.lpGetSymbolRoutine)
+#define ReadMemory (ExtensionApis.lpReadProcessMemoryRoutine)
+#define WriteMemory (ExtensionApis.lpWriteProcessMemoryRoutine)
+#define StackTrace (ExtensionApis.lpStackTraceRoutine)
+
+
+#define GetKdContext(ppi) \
+ Ioctl( IG_KD_CONTEXT, (PVOID)ppi, sizeof(*ppi) )
+
+
+//
+// BOOL
+// GetDebuggerData(
+// ULONG Tag,
+// PVOID Buf,
+// ULONG Size
+// )
+//
+
+#define GetDebuggerData(TAG, BUF, SIZE) \
+ ( (((PDBGKD_DEBUG_DATA_HEADER64)(BUF))->OwnerTag = (TAG)), \
+ (((PDBGKD_DEBUG_DATA_HEADER64)(BUF))->Size = (SIZE)), \
+ Ioctl( IG_GET_DEBUGGER_DATA, (PVOID)(BUF), (SIZE) ) )
+
+// Check if LocalAlloc is prototyped
+//#ifdef _WINBASE_
+
+#ifndef FEATURE_PAL
+__inline VOID
+ReadPhysical(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ PULONG sizer
+ )
+{
+ PPHYSICAL phy = NULL;
+ *sizer = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ Ioctl( IG_READ_PHYSICAL, (PVOID)phy, sizeof(*phy) + size );
+ *sizer = phy->BufLen;
+ CopyMemory( buf, phy->Buf, *sizer );
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+WritePhysical(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ PULONG sizew
+ )
+{
+ PPHYSICAL phy = NULL;
+ *sizew = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ CopyMemory( phy->Buf, buf, size );
+ Ioctl( IG_WRITE_PHYSICAL, (PVOID)phy, sizeof(*phy) + size );
+ *sizew = phy->BufLen;
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+ReadPhysicalWithFlags(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ ULONG flags,
+ PULONG sizer
+ )
+{
+ PPHYSICAL_WITH_FLAGS phy = NULL;
+ *sizer = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL_WITH_FLAGS)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ phy->Flags = flags;
+ Ioctl( IG_READ_PHYSICAL_WITH_FLAGS, (PVOID)phy, sizeof(*phy) + size );
+ *sizer = phy->BufLen;
+ CopyMemory( buf, phy->Buf, *sizer );
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+WritePhysicalWithFlags(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ ULONG flags,
+ PULONG sizew
+ )
+{
+ PPHYSICAL_WITH_FLAGS phy = NULL;
+ *sizew = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL_WITH_FLAGS)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ phy->Flags = flags;
+ CopyMemory( phy->Buf, buf, size );
+ Ioctl( IG_WRITE_PHYSICAL_WITH_FLAGS, (PVOID)phy, sizeof(*phy) + size );
+ *sizew = phy->BufLen;
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+ReadMsr(
+ ULONG MsrReg,
+ ULONGLONG *MsrValue
+ )
+{
+ READ_WRITE_MSR msr;
+
+ msr.Msr = MsrReg;
+ Ioctl( IG_READ_MSR, (PVOID)&msr, sizeof(msr) );
+
+ *MsrValue = msr.Value;
+}
+
+__inline VOID
+WriteMsr(
+ ULONG MsrReg,
+ ULONGLONG MsrValue
+ )
+{
+ READ_WRITE_MSR msr;
+
+ msr.Msr = MsrReg;
+ msr.Value = MsrValue;
+ Ioctl( IG_WRITE_MSR, (PVOID)&msr, sizeof(msr) );
+}
+
+__inline VOID
+SetThreadForOperation(
+ ULONG_PTR * Thread
+ )
+{
+ Ioctl(IG_SET_THREAD, (PVOID)Thread, sizeof(PULONG));
+}
+
+__inline VOID
+SetThreadForOperation32(
+ ULONG Thread
+ )
+{
+ Ioctl(IG_SET_THREAD, (PVOID)LongToPtr(Thread), sizeof(ULONG));
+}
+
+__inline VOID
+SetThreadForOperation64(
+ PULONG64 Thread
+ )
+{
+ Ioctl(IG_SET_THREAD, (PVOID)Thread, sizeof(ULONG64));
+}
+
+
+__inline VOID
+ReadControlSpace(
+ USHORT processor,
+ ULONG address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ Ioctl( IG_READ_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ CopyMemory( buf, prc->Buf, size );
+ LocalFree( prc );
+ }
+}
+
+__inline VOID
+ReadControlSpace32(
+ USHORT processor,
+ ULONG address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE32 prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE32)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ Ioctl( IG_READ_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ CopyMemory( buf, prc->Buf, size );
+ LocalFree( prc );
+ }
+}
+
+#define ReadTypedControlSpace32( _Proc, _Addr, _Buf ) \
+ ReadControlSpace64( (USHORT)(_Proc), (ULONG)(_Addr), (PVOID)&(_Buf), (ULONG)sizeof(_Buf) )
+
+__inline VOID
+ReadControlSpace64(
+ USHORT processor,
+ ULONG64 address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE64 prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE64)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ Ioctl( IG_READ_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ CopyMemory( buf, prc->Buf, size );
+ LocalFree( prc );
+ }
+}
+
+#define ReadTypedControlSpace64( _Proc, _Addr, _Buf ) \
+ ReadControlSpace64( (USHORT)(_Proc), (ULONG64)(_Addr), (PVOID)&(_Buf), (ULONG)sizeof(_Buf) )
+
+__inline VOID
+WriteControlSpace(
+ USHORT processor,
+ ULONG address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE64 prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE64)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ CopyMemory( prc->Buf, buf, size );
+ Ioctl( IG_WRITE_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ LocalFree( prc );
+ }
+}
+
+// #endif // _WINBASE_
+
+__inline VOID
+ReadIoSpace(
+ ULONG address,
+ PULONG data,
+ PULONG size
+ )
+{
+ IOSPACE is;
+ is.Address = address;
+ is.Length = *size;
+ Ioctl( IG_READ_IO_SPACE, (PVOID)&is, sizeof(is) );
+ memcpy(data, &is.Data, is.Length);
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpace32(
+ ULONG address,
+ PULONG data,
+ PULONG size
+ )
+{
+ IOSPACE32 is;
+ is.Address = address;
+ is.Length = *size;
+ Ioctl( IG_READ_IO_SPACE, (PVOID)&is, sizeof(is) );
+ memcpy(data, &is.Data, is.Length);
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpace64(
+ ULONG64 address,
+ PULONG data,
+ PULONG size
+ )
+{
+ IOSPACE64 is;
+ is.Address = address;
+ is.Length = *size;
+ Ioctl( IG_READ_IO_SPACE, (PVOID)&is, sizeof(is) );
+ memcpy(data, &is.Data, is.Length);
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpace(
+ ULONG address,
+ ULONG data,
+ PULONG size
+ )
+{
+ IOSPACE is;
+ is.Address = (ULONG)address;
+ is.Length = *size;
+ is.Data = data;
+ Ioctl( IG_WRITE_IO_SPACE, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpace32(
+ ULONG address,
+ ULONG data,
+ PULONG size
+ )
+{
+ IOSPACE32 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ Ioctl( IG_WRITE_IO_SPACE, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpace64(
+ ULONG64 address,
+ ULONG data,
+ PULONG size
+ )
+{
+ IOSPACE64 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ Ioctl( IG_WRITE_IO_SPACE, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpaceEx(
+ ULONG address,
+ PULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX is;
+ is.Address = (ULONG)address;
+ is.Length = *size;
+ is.Data = 0;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_READ_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *data = is.Data;
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpaceEx32(
+ ULONG address,
+ PULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX32 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = 0;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_READ_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *data = is.Data;
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpaceEx64(
+ ULONG64 address,
+ PULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX64 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = 0;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_READ_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *data = is.Data;
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpaceEx(
+ ULONG address,
+ ULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX is;
+ is.Address = (ULONG)address;
+ is.Length = *size;
+ is.Data = data;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_WRITE_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpaceEx32(
+ ULONG address,
+ ULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX32 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_WRITE_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpaceEx64(
+ ULONG64 address,
+ ULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX64 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_WRITE_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+ReloadSymbols(
+ IN PSTR Arg OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Calls the debugger to reload symbols.
+
+Arguments:
+
+ Args - Supplies the tail of a !reload command string.
+
+ !reload [flags] [module[=address]]
+ flags: /n do not load from usermode list
+ /u unload symbols, no reload
+ /v verbose
+
+ A value of NULL is equivalent to an empty string
+
+Return Value:
+
+ None
+
+--*/
+{
+ Ioctl(IG_RELOAD_SYMBOLS, (PVOID)Arg, Arg?((ULONG)strlen(Arg)+1):0);
+}
+
+__inline VOID
+GetSetSympath(
+ IN PSTR Arg,
+ OUT PSTR Result OPTIONAL,
+ IN int Length
+ )
+/*++
+
+Routine Description:
+
+ Calls the debugger to set or retrieve symbol search path.
+
+Arguments:
+
+ Arg - Supplies new search path. If Arg is NULL or string is empty,
+ the search path is not changed and the current setting is
+ returned in Result. When the symbol search path is changed,
+ a call to ReloadSymbols is made implicitly.
+
+ Result - OPTIONAL Returns the symbol search path setting.
+
+ Length - Supplies the size of the buffer supplied by Result.
+
+Return Value:
+
+ None
+
+--*/
+{
+ GET_SET_SYMPATH gss;
+ gss.Args = Arg;
+ gss.Result = Result;
+ gss.Length = Length;
+ Ioctl(IG_GET_SET_SYMPATH, (PVOID)&gss, sizeof(gss));
+}
+
+#if defined(KDEXT_64BIT)
+
+__inline
+ULONG
+IsPtr64(
+ void
+ )
+{
+ ULONG flag;
+ ULONG dw;
+
+ if (Ioctl(IG_IS_PTR64, &dw, sizeof(dw))) {
+ flag = ((dw != 0) ? 1 : 0);
+ } else {
+ flag = 0;
+ }
+ return flag;
+}
+
+__inline
+ULONG
+ReadListEntry(
+ ULONG64 Address,
+ PLIST_ENTRY64 List
+ )
+{
+ ULONG cb;
+ if (IsPtr64()) {
+ return (ReadMemory(Address, (PVOID)List, sizeof(*List), &cb) &&
+ cb == sizeof(*List));
+ } else {
+ LIST_ENTRY32 List32;
+ ULONG Status;
+ Status = ReadMemory(Address,
+ (PVOID)&List32,
+ sizeof(List32),
+ &cb);
+ if (Status && cb == sizeof(List32)) {
+ List->Flink = (ULONG64)(LONG64)(LONG)List32.Flink;
+ List->Blink = (ULONG64)(LONG64)(LONG)List32.Blink;
+ return 1;
+ }
+ return 0;
+ }
+}
+
+__inline
+ULONG
+ReadPointer(
+ ULONG64 Address,
+ PULONG64 Pointer
+ )
+{
+ ULONG cb;
+ if (IsPtr64()) {
+ return (ReadMemory(Address, (PVOID)Pointer, sizeof(*Pointer), &cb) &&
+ cb == sizeof(*Pointer));
+ } else {
+ ULONG Pointer32;
+ ULONG Status;
+ Status = ReadMemory(Address,
+ (PVOID)&Pointer32,
+ sizeof(Pointer32),
+ &cb);
+ if (Status && cb == sizeof(Pointer32)) {
+ *Pointer = (ULONG64)(LONG64)(LONG)Pointer32;
+ return 1;
+ }
+ return 0;
+ }
+}
+
+__inline
+ULONG
+WritePointer(
+ ULONG64 Address,
+ ULONG64 Pointer
+ )
+{
+ ULONG cb;
+ if (IsPtr64()) {
+ return (WriteMemory(Address, &Pointer, sizeof(Pointer), &cb) &&
+ cb == sizeof(Pointer));
+ } else {
+ ULONG Pointer32 = (ULONG)Pointer;
+ ULONG Status;
+ Status = WriteMemory(Address,
+ &Pointer32,
+ sizeof(Pointer32),
+ &cb);
+ return (Status && cb == sizeof(Pointer32)) ? 1 : 0;
+ }
+}
+
+/**
+ This does Ioctl call for type info and returns size of the type on success.
+
+ **/
+__inline
+ULONG
+GetTypeSize (
+ IN LPCSTR Type
+ )
+{
+#ifndef FEATURE_PAL
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), (PUCHAR)Type, DBG_DUMP_NO_PRINT | DBG_DUMP_GET_SIZE_ONLY, 0,
+ NULL, NULL, NULL, 0, NULL
+ };
+
+ return Ioctl( IG_GET_TYPE_SIZE, &Sym, Sym.size );
+#else
+ return (ULONG)~0;
+#endif
+}
+
+/**
+ GetFieldData
+
+ Copies the value of the specified field into pOutValue assuming TypeAddress
+ points to start of the type in debugee.
+
+ If the Field is NULL and the size of Type is <= 8 Whole type value is read into
+ pOutValue. This is to allow to read in primitive types suchas ULONG, PVOID etc.
+
+ If address is zero this considers Type a global variable.
+
+ It raises an exception if OutSize is less than size to be copied.
+
+ Returns 0 on success, errorvalue (defined with SYM_DUMP_PARAM) otherwise.
+
+ **/
+__inline
+ULONG
+GetFieldData (
+ IN ULONG64 TypeAddress,
+ IN LPCSTR Type,
+ IN LPCSTR Field,
+ IN ULONG OutSize,
+ OUT PVOID pOutValue
+ )
+{
+#ifndef FEATURE_PAL
+ FIELD_INFO flds = {(PUCHAR)Field, NULL, 0, DBG_DUMP_FIELD_FULL_NAME | DBG_DUMP_FIELD_COPY_FIELD_DATA | DBG_DUMP_FIELD_RETURN_ADDRESS, 0, pOutValue};
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), (PUCHAR)Type, DBG_DUMP_NO_PRINT, TypeAddress,
+ NULL, NULL, NULL, 1, &flds
+ };
+ ULONG RetVal;
+
+ if (!Field) {
+ Sym.nFields =0; Sym.Options |= DBG_DUMP_COPY_TYPE_DATA;
+ Sym.Context = pOutValue;
+ }
+
+ ZeroMemory(pOutValue, OutSize);
+ RetVal = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
+
+ if (OutSize < ((Field == NULL) ? 8 : flds.size)) {
+ // Fail
+ dprintf("Not enough space to read %s-%s\n", Type, Field);
+ RaiseException((DWORD)EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
+ return 0;
+ }
+ return RetVal;
+#else
+ return (ULONG)~0;
+#endif
+}
+
+//
+// Typecast the buffer where value is to be read
+//
+#define GetFieldValue(Addr, Type, Field, OutValue) \
+ GetFieldData(Addr, Type, Field, sizeof(OutValue), (PVOID) &(OutValue))
+
+//
+// Used to read in value of a short (<= 8 bytes) fields
+//
+__inline
+ULONG64
+GetShortField (
+ IN ULONG64 TypeAddress,
+ IN LPCSTR Name,
+ IN USHORT StoreAddress
+ )
+{
+#ifndef FEATURE_PAL
+ static ULONG64 SavedAddress;
+ static PUCHAR SavedName;
+ static ULONG ReadPhysical;
+ FIELD_INFO flds = {(PUCHAR) Name, NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL};
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), SavedName, DBG_DUMP_NO_PRINT | ((StoreAddress & 2) ? DBG_DUMP_READ_PHYSICAL : 0),
+ SavedAddress, NULL, NULL, NULL, 1, &flds
+ };
+
+
+ if (StoreAddress) {
+ Sym.sName = (PUCHAR) Name;
+ Sym.nFields = 0;
+ SavedName = (PUCHAR) Name;
+ Sym.addr = SavedAddress = TypeAddress;
+ ReadPhysical = (StoreAddress & 2);
+ return SavedAddress ? Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size ) : MEMORY_READ_ERROR; // zero on success
+ } else {
+ Sym.Options |= ReadPhysical ? DBG_DUMP_READ_PHYSICAL : 0;
+ }
+
+ if (!Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size )) {
+ return flds.address;
+ }
+ return 0;
+#else
+ return (ULONG64)~0;
+#endif
+}
+
+//
+// Stores the address and type name for future reads
+//
+#define InitTypeRead(Addr, Type) GetShortField(Addr, #Type, 1)
+#define InitTypeStrRead(Addr, TypeStr) GetShortField(Addr, TypeStr, 1)
+
+//
+// Stores the address and type name for future reads
+//
+#define InitTypeReadPhysical(Addr, Type) GetShortField(Addr, #Type, 3)
+#define InitTypeStrReadPhysical(Addr, TypeStr) GetShortField(Addr, TypeStr, 3)
+
+//
+// Returns the field's value as ULONG64 if size of field is <= sizeof (ULONG64)
+//
+#define ReadField(Field) GetShortField(0, #Field, 0)
+#define ReadFieldStr(FieldStr) GetShortField(0, FieldStr, 0)
+
+//
+// Read in a pointer value
+//
+__inline
+ULONG
+ReadPtr(
+ ULONG64 Addr,
+ PULONG64 pPointer
+ )
+{
+ return !ReadPointer(Addr, pPointer);
+}
+
+/*
+ * ListType
+ *
+ * Routine ListType gives a callback on each element in the list of Type.
+ *
+ * Type : Name of the type to be listed
+ *
+ * NextPointer : Name of field which gives address of next element in list
+ *
+ * Context, CallbackRoutine :
+ * Context and the callback routine. The address field in PFIELD_INFO
+ * parameter of callback contains the address of next Type element in list.
+ *
+ * Address, ListByFieldAddress :
+ * if ListByFieldAddress is 0, Adress is the address of first element of Type List.
+ *
+ * Lists by LIST_ENTRY are also handled implicitly (by Ioctl). If the NextPointer
+ * is a pointer to LIST_ENTRY type, the type address is properly calculated by
+ * subtracting the offsets.
+ *
+ * If ListByFieldAddress is 1, the Address is considered to be the address of field
+ * "NextPointer" of the first Type element and first element address is derived
+ * from it.
+ *
+ */
+
+__inline
+ULONG
+ListType (
+ IN LPCSTR Type,
+ IN ULONG64 Address,
+ IN USHORT ListByFieldAddress,
+ IN LPCSTR NextPointer,
+ IN PVOID Context,
+ IN PSYM_DUMP_FIELD_CALLBACK CallbackRoutine
+ )
+{
+#ifndef FEATURE_PAL
+ FIELD_INFO flds = {(PUCHAR)NextPointer, NULL, 0, 0, 0, NULL};
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), (PUCHAR) Type, DBG_DUMP_NO_PRINT | DBG_DUMP_LIST, Address,
+ &flds, Context, CallbackRoutine, 0, NULL
+ };
+
+ if (ListByFieldAddress==1) {
+ //
+ // Address is the address of "NextPointer"
+ //
+ Sym.Options |= DBG_DUMP_ADDRESS_OF_FIELD;
+ }
+
+ return Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
+#else
+ return (ULONG)~0;
+#endif
+}
+
+
+/**
+
+ Routine to get offset of a "Field" of "Type" on a debugee machine. This uses
+ Ioctl call for type info.
+ Returns 0 on success, Ioctl error value otherwise.
+
+ **/
+
+__inline
+ULONG
+GetFieldOffset (
+ IN LPCSTR Type,
+ IN LPCSTR Field,
+ OUT PULONG pOffset
+ )
+{
+#ifndef FEATURE_PAL
+ FIELD_INFO flds = {
+ (PUCHAR)Field,
+ (PUCHAR)"",
+ 0,
+ DBG_DUMP_FIELD_FULL_NAME | DBG_DUMP_FIELD_RETURN_ADDRESS,
+ 0,
+ NULL};
+
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM),
+ (PUCHAR)Type,
+ DBG_DUMP_NO_PRINT,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ 1,
+ &flds
+ };
+
+ ULONG Err;
+
+ Sym.nFields = 1;
+ Err = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
+ *pOffset = (ULONG) (flds.address - Sym.addr);
+ return Err;
+#else
+ return (ULONG)~0;
+#endif
+}
+
+
+#endif // defined(KDEXT_64BIT)
+
+__inline VOID
+ GetCurrentProcessHandle(
+ PHANDLE hp
+ )
+{
+ Ioctl(IG_GET_CURRENT_PROCESS_HANDLE, hp, sizeof(HANDLE));
+}
+
+__inline VOID
+ GetTebAddress(
+ PULONGLONG Address
+ )
+{
+ GET_TEB_ADDRESS gpt;
+ gpt.Address = 0;
+ Ioctl(IG_GET_TEB_ADDRESS, (PVOID)&gpt, sizeof(gpt));
+ *Address = gpt.Address;
+}
+
+__inline VOID
+ GetPebAddress(
+ ULONG64 CurrentThread,
+ PULONGLONG Address
+ )
+{
+ GET_PEB_ADDRESS gpt;
+ gpt.CurrentThread = CurrentThread;
+ gpt.Address = 0;
+ Ioctl(IG_GET_PEB_ADDRESS, (PVOID)&gpt, sizeof(gpt));
+ *Address = gpt.Address;
+}
+
+__inline VOID
+ GetCurrentThreadAddr(
+ DWORD Processor,
+ PULONG64 Address
+ )
+{
+ GET_CURRENT_THREAD_ADDRESS ct;
+ ct.Processor = Processor;
+ Ioctl(IG_GET_CURRENT_THREAD, (PVOID)&ct, sizeof(ct));
+ *Address = ct.Address;
+}
+
+__inline VOID
+ GetCurrentProcessAddr(
+ DWORD Processor,
+ ULONG64 CurrentThread,
+ PULONG64 Address
+ )
+{
+ GET_CURRENT_PROCESS_ADDRESS cp;
+ cp.Processor = Processor;
+ cp.CurrentThread = CurrentThread;
+ Ioctl(IG_GET_CURRENT_PROCESS, (PVOID)&cp, sizeof(cp));
+ *Address = cp.Address;
+}
+
+__inline VOID
+SearchMemory(
+ ULONG64 SearchAddress,
+ ULONG64 SearchLength,
+ ULONG PatternLength,
+ PVOID Pattern,
+ PULONG64 FoundAddress
+ )
+{
+ SEARCHMEMORY sm;
+ sm.SearchAddress = SearchAddress;
+ sm.SearchLength = SearchLength;
+ sm.FoundAddress = 0;
+ sm.PatternLength = PatternLength;
+ sm.Pattern = Pattern;
+ Ioctl(IG_SEARCH_MEMORY, (PVOID)&sm, sizeof(sm));
+ *FoundAddress = sm.FoundAddress;
+}
+
+__inline ULONG
+GetInputLine(
+ PCSTR Prompt,
+ PSTR Buffer,
+ ULONG BufferSize
+ )
+{
+ GET_INPUT_LINE InLine;
+ InLine.Prompt = Prompt;
+ InLine.Buffer = Buffer;
+ InLine.BufferSize = BufferSize;
+ if (Ioctl(IG_GET_INPUT_LINE, (PVOID)&InLine, sizeof(InLine)))
+ {
+ return InLine.InputSize;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+__inline BOOL
+GetExpressionEx(
+ PCSTR Expression,
+ ULONG64* Value,
+ PCSTR* Remainder
+ )
+{
+ GET_EXPRESSION_EX Expr;
+ Expr.Expression = Expression;
+ if (Ioctl(IG_GET_EXPRESSION_EX, (PVOID)&Expr, sizeof(Expr)))
+ {
+ *Value = Expr.Value;
+
+ if (Remainder != NULL)
+ {
+ *Remainder = Expr.Remainder;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+__inline BOOL
+TranslateVirtualToPhysical(
+ ULONG64 Virtual,
+ ULONG64* Physical
+ )
+{
+ TRANSLATE_VIRTUAL_TO_PHYSICAL VToP;
+ VToP.Virtual = Virtual;
+ if (Ioctl(IG_TRANSLATE_VIRTUAL_TO_PHYSICAL, (PVOID)&VToP, sizeof(VToP)))
+ {
+ *Physical = VToP.Physical;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+__inline BOOL
+GetDebuggerCacheSize(
+ OUT PULONG64 CacheSize
+ )
+{
+ return Ioctl(IG_GET_CACHE_SIZE, (PVOID) CacheSize, sizeof(ULONG64));
+}
+
+__inline BOOL
+ExtMatchPatternA(
+ IN PCSTR Str,
+ IN PCSTR Pattern,
+ IN BOOL CaseSensitive
+ )
+{
+ EXT_MATCH_PATTERN_A Args;
+
+ Args.Str = Str;
+ Args.Pattern = Pattern;
+ Args.CaseSensitive = CaseSensitive;
+ return Ioctl(IG_MATCH_PATTERN_A, (PVOID)&Args, sizeof(Args));
+}
+
+#endif // FEATURE_PAL
+
+#endif
+
+#ifndef FEATURE_PAL
+#pragma warning(default:4115 4201 4204 4214 4221)
+#endif
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _WDBGEXTS_
diff --git a/src/ToolBox/SOS/Strike/metadata.cpp b/src/ToolBox/SOS/Strike/metadata.cpp
new file mode 100644
index 0000000000..073b979baa
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/metadata.cpp
@@ -0,0 +1,1041 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#include "strike.h"
+#include "util.h"
+#include "genericstackprobe.h"
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a TypeDef using *
+* metadata API. *
+* *
+\**********************************************************************/
+// Caller should guard against exception
+// !!! mdName should have at least mdNameLen WCHAR
+static HRESULT NameForTypeDef_s(mdTypeDef tkTypeDef, IMetaDataImport *pImport,
+ __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
+{
+ DWORD flags;
+ ULONG nameLen;
+
+ HRESULT hr = pImport->GetTypeDefProps(tkTypeDef, mdName,
+ mdNameLen, &nameLen,
+ &flags, NULL);
+ if (hr != S_OK) {
+ return hr;
+ }
+
+ if (!IsTdNested(flags)) {
+ return hr;
+ }
+ mdTypeDef tkEnclosingClass;
+ hr = pImport->GetNestedClassProps(tkTypeDef, &tkEnclosingClass);
+ if (hr != S_OK) {
+ return hr;
+ }
+ WCHAR *name = (WCHAR*)_alloca((nameLen+1)*sizeof(WCHAR));
+ wcscpy_s (name, nameLen+1, mdName);
+ hr = NameForTypeDef_s(tkEnclosingClass,pImport,mdName, capacity_mdName);
+ if (hr != S_OK) {
+ return hr;
+ }
+ size_t Len = _wcslen (mdName);
+ if (Len < mdNameLen-2) {
+ mdName[Len++] = L'+';
+ mdName[Len] = L'\0';
+ }
+ Len = mdNameLen-1 - Len;
+ if (Len > nameLen) {
+ Len = nameLen;
+ }
+ wcsncat_s (mdName,capacity_mdName,name,Len);
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a TypeDef using *
+* metadata API. *
+* *
+\**********************************************************************/
+// Caller should guard against exception
+// !!! mdName should have at least mdNameLen WCHAR
+/*
+static HRESULT NameForTypeDefNew(mdTypeDef tkTypeDef, IMDInternalImport *pImport,
+ WCHAR *mdName)
+{
+ DWORD flags;
+ ULONG nameLen;
+ char *name = (char *)_alloca((mdNameLen+1)*sizeof(char));
+ char *namesp = (char *)_alloca((mdNameLen+1)*sizeof(char));
+
+ HRESULT hr = pImport->GetNameOfTypeDef(tkTypeDef, name, namesp);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ strcpy (namesp, ".");
+ strcpy (namesp, name);
+ MultiByteToWideChar (CP_ACP,0,namesp,-1,mdName,mdNameLen);
+ return hr;
+}
+*/
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the Module MD Importer given the name of the Module. *
+* *
+\**********************************************************************/
+IMetaDataImport* MDImportForModule(DacpModuleData* pModule)
+{
+ IMetaDataImport *pRet = NULL;
+ ToRelease<IXCLRDataModule> module;
+ HRESULT hr = g_sos->GetModule(pModule->Address, &module);
+
+ if (SUCCEEDED(hr))
+ hr = module->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pRet);
+
+ if (SUCCEEDED(hr))
+ return pRet;
+
+ return NULL;
+}
+
+IMetaDataImport* MDImportForModule(DWORD_PTR pModule)
+{
+ DacpModuleData moduleData;
+ if(moduleData.Request(g_sos, TO_CDADDR(pModule))==S_OK)
+ return MDImportForModule(&moduleData);
+ else
+ return NULL;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the name for a metadata token given an importer. *
+* *
+\**********************************************************************/
+HRESULT NameForToken_s(mdTypeDef mb, IMetaDataImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+ mdName[0] = L'\0';
+ if ((mb & 0xff000000) != mdtTypeDef
+ && (mb & 0xff000000) != mdtFieldDef
+ && (mb & 0xff000000) != mdtMethodDef)
+ {
+ //ExtOut("unsupported\n");
+ return E_FAIL;
+ }
+
+ HRESULT hr = E_FAIL;
+
+ PAL_CPP_TRY
+ {
+ static WCHAR name[MAX_CLASSNAME_LENGTH];
+ if ((mb & 0xff000000) == mdtTypeDef)
+ {
+ hr = NameForTypeDef_s (mb, pImport, mdName, capacity_mdName);
+ }
+ else if ((mb & 0xff000000) == mdtFieldDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+ hr = pImport->GetMemberProps(mb, &mdClass,
+ name, sizeof(name)/sizeof(WCHAR)-1, &size,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDef_s (mdClass, pImport, mdName, capacity_mdName);
+ wcscat_s (mdName, capacity_mdName, W("."));
+ }
+ name[size] = L'\0';
+ wcscat_s (mdName, capacity_mdName, name);
+ }
+ }
+ else if ((mb & 0xff000000) == mdtMethodDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+ hr = pImport->GetMethodProps(mb, &mdClass,
+ name, sizeof(name)/sizeof(WCHAR)-1, &size,
+ NULL, NULL, NULL, NULL, NULL);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDef_s (mdClass, pImport, mdName, capacity_mdName);
+ wcscat_s (mdName, capacity_mdName, W("."));
+ }
+ name[size] = L'\0';
+ wcscat_s (mdName, capacity_mdName, name);
+ }
+ }
+ else
+ {
+ ExtOut ("Unsupported token type\n");
+ hr = E_FAIL;
+ }
+ }
+ PAL_CPP_CATCH_ALL
+ {
+ hr = E_FAIL;
+ }
+ PAL_CPP_ENDTRY
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the name for a metadata token given an importer. *
+* *
+\**********************************************************************/
+/*
+HRESULT NameForTokenNew(mdTypeDef mb, IMDInternalImport *pImport, WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+
+ // TODO: Change calls to use the secure versions (string as well as to functions defined here)
+ // Simply uncommenting this function will not result in a clean compile
+ // --chirayuk @ 11/23/2004
+
+ mdName[0] = L'\0';
+ if ((mb & 0xff000000) != mdtTypeDef
+ && (mb & 0xff000000) != mdtFieldDef
+ && (mb & 0xff000000) != mdtMethodDef)
+ {
+ //ExtOut("unsupported\n");
+ return E_FAIL;
+ }
+
+ HRESULT hr;
+
+ __try
+ {
+ static WCHAR name[MAX_CLASSNAME_LENGTH];
+ if (TypeFromToken(mb) == mdtTypeDef)
+ {
+ hr = NameForTypeDefNew (mb, pImport, mdName);
+ }
+ else if (TypeFromToken(mb) == mdtFieldDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+ MultiByteToWideChar (CP_ACP,0,pImport->GetNameOfFieldDef(mb),-1,name,MAX_CLASSNAME_LENGTH);
+
+ hr = pImport->GetParentToken (mb, &mdClass);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDefNew (mdClass, pImport, mdName);
+ _wcscat (mdName, W("."));
+ }
+ name[size] = L'\0';
+ _wcscat (mdName, name);
+ }
+ }
+ else if (TypeFromToken(mb) == mdtMethodDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+
+ MultiByteToWideChar (CP_ACP,0,pImport->GetNameOfMethodDef(mb),-1,name,MAX_CLASSNAME_LENGTH);
+ hr = pImport->GetParentToken (mb, &mdClass);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDefNew (mdClass, pImport, mdName);
+ _wcscat (mdName, W("."));
+ }
+ name[size] = L'\0';
+ _wcscat (mdName, name);
+ }
+ }
+ else
+ {
+ ExtOut ("Unsupported token type\n");
+ hr = E_FAIL;
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ //ExtOut("Metadata operation failure\n");
+ hr = E_FAIL;
+ }
+ return hr;
+}
+*/
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a metadata token *
+* using metadata API. *
+* *
+\**********************************************************************/
+void NameForToken_s(DWORD_PTR ModuleAddr, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+ DacpModuleData ModuleData;
+ mdName[0] = L'\0';
+ if(ModuleData.Request(g_sos, TO_CDADDR(ModuleAddr))==S_OK)
+ NameForToken_s(&ModuleData,mb,mdName,capacity_mdName,bClassName);
+}
+
+BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb)
+{
+ DacpModuleData ModuleData;
+ if(ModuleData.Request(g_sos, TO_CDADDR(ModuleAddr))==S_OK)
+ {
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&ModuleData);
+ if (pImport)
+ {
+ if (pImport->IsValidToken (mb))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+ mdName[0] = L'\0';
+ HRESULT hr = 0;
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(pModule);
+ if (pImport)
+ {
+ hr = NameForToken_s (mb, pImport, mdName, capacity_mdName, bClassName);
+ }
+
+ if (!pImport || !SUCCEEDED (hr))
+ {
+ const SIZE_T capacity_moduleName = mdNameLen+19;
+ LPWSTR moduleName = (LPWSTR)alloca(capacity_moduleName * sizeof(WCHAR)); // for the "Dynamic Module In " below
+ FileNameForModule(pModule,moduleName);
+ if (moduleName[0] == L'\0') {
+ DacpAssemblyData assembly;
+ assembly.Request(g_sos,pModule->Assembly);
+ if (assembly.isDynamic) {
+ wcscpy_s(moduleName, capacity_moduleName, W("Dynamic "));
+ }
+ wcscat_s (moduleName, capacity_moduleName, W("Module in "));
+ if(g_sos->GetAssemblyName(pModule->Assembly, mdNameLen, g_mdName, NULL)==S_OK)
+ {
+ wcscat_s(moduleName, capacity_moduleName, g_mdName);
+ }
+ }
+ swprintf_s (mdName, capacity_mdName,
+ W(" mdToken: %08x (%ws)"),
+ mb,
+ moduleName[0] ? moduleName : W("Unknown Module") );
+ }
+}
+
+#define STRING_BUFFER_LEN 1024
+
+class MDInfo
+{
+public:
+ MDInfo (DWORD_PTR ModuleAddr)
+ {
+ m_pImport = MDImportForModule(ModuleAddr);
+ if (!m_pImport)
+ ExtOut("Unable to get IMetaDataImport for module %p\n", ModuleAddr);
+ m_pSigBuf = NULL;
+ }
+
+ MDInfo (IMetaDataImport * pImport)
+ {
+ m_pImport = pImport;
+ m_pImport->AddRef();
+ m_pSigBuf = NULL;
+ }
+
+ void GetMethodName(mdTypeDef token, CQuickBytes *fullName);
+ GetSignatureStringResults GetMethodSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName);
+ GetSignatureStringResults GetSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName);
+
+ LPCWSTR TypeDefName(mdTypeDef inTypeDef);
+ LPCWSTR TypeRefName(mdTypeRef tr);
+ LPCWSTR TypeDeforRefName(mdToken inToken);
+private:
+ // helper to init signature buffer
+ void InitSigBuffer()
+ {
+ ((LPWSTR)m_pSigBuf->Ptr())[0] = L'\0';
+ }
+
+ HRESULT AddToSigBuffer(LPCWSTR string);
+
+ HRESULT GetFullNameForMD(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, LONG *plSigBlobRemaining OPTIONAL);
+ HRESULT GetOneElementType(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, ULONG *pcb);
+
+ ToRelease<IMetaDataImport> m_pImport;
+ // Signature buffer.
+ CQuickBytes *m_pSigBuf;
+
+ // temporary buffer for TypeDef or TypeRef name. Consume immediately
+ // because other functions may overwrite it.
+ static WCHAR m_szTempBuf[MAX_CLASSNAME_LENGTH];
+
+ static WCHAR m_szName[MAX_CLASSNAME_LENGTH];
+};
+
+WCHAR MDInfo::m_szTempBuf[MAX_CLASSNAME_LENGTH];
+WCHAR MDInfo::m_szName[MAX_CLASSNAME_LENGTH];
+
+GetSignatureStringResults GetMethodSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString)
+{
+ MDInfo mdInfo(dwModuleAddr);
+
+ return mdInfo.GetMethodSignature(pbSigBlob, ulSigBlob, sigString);
+}
+
+
+GetSignatureStringResults GetSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString)
+{
+ MDInfo mdInfo(dwModuleAddr);
+
+ return mdInfo.GetSignature(pbSigBlob, ulSigBlob, sigString);
+}
+
+void GetMethodName(mdMethodDef methodDef, IMetaDataImport * pImport, CQuickBytes *fullName)
+{
+ MDInfo mdInfo(pImport);
+
+ mdInfo.GetMethodName(methodDef, fullName);
+}
+
+
+// Tables for mapping element type to text
+const WCHAR *g_wszMapElementType[] =
+{
+ W("End"), // 0x0
+ W("Void"), // 0x1
+ W("Boolean"),
+ W("Char"),
+ W("I1"),
+ W("UI1"),
+ W("I2"), // 0x6
+ W("UI2"),
+ W("I4"),
+ W("UI4"),
+ W("I8"),
+ W("UI8"),
+ W("R4"),
+ W("R8"),
+ W("String"),
+ W("Ptr"), // 0xf
+ W("ByRef"), // 0x10
+ W("ValueClass"),
+ W("Class"),
+ W("CopyCtor"),
+ W("MDArray"), // 0x14
+ W("GENArray"),
+ W("TypedByRef"),
+ W("VALUEARRAY"),
+ W("I"),
+ W("U"),
+ W("R"), // 0x1a
+ W("FNPTR"),
+ W("Object"),
+ W("SZArray"),
+ W("GENERICArray"),
+ W("CMOD_REQD"),
+ W("CMOD_OPT"),
+ W("INTERNAL"),
+};
+
+const WCHAR *g_wszCalling[] =
+{
+ W("[DEFAULT]"),
+ W("[C]"),
+ W("[STDCALL]"),
+ W("[THISCALL]"),
+ W("[FASTCALL]"),
+ W("[VARARG]"),
+ W("[FIELD]"),
+ W("[LOCALSIG]"),
+ W("[PROPERTY]"),
+ W("[UNMANAGED]"),
+};
+
+void MDInfo::GetMethodName(mdTypeDef token, CQuickBytes *fullName)
+{
+ if (m_pImport == NULL) {
+ return;
+ }
+
+ HRESULT hr;
+ mdTypeDef memTypeDef;
+ ULONG nameLen;
+ DWORD flags;
+ PCCOR_SIGNATURE pbSigBlob;
+ ULONG ulSigBlob;
+ ULONG ulCodeRVA;
+ ULONG ulImplFlags;
+
+ m_pSigBuf = fullName;
+ InitSigBuffer();
+
+ WCHAR szFunctionName[1024];
+
+ hr = m_pImport->GetMethodProps(token, &memTypeDef,
+ szFunctionName, _countof(szFunctionName), &nameLen,
+ &flags, &pbSigBlob, &ulSigBlob, &ulCodeRVA, &ulImplFlags);
+ if (FAILED (hr))
+ {
+ return;
+ }
+
+ szFunctionName[nameLen] = L'\0';
+ m_szName[0] = L'\0';
+ if (memTypeDef != mdTypeDefNil)
+ {
+ hr = NameForTypeDef_s (memTypeDef, m_pImport, m_szName, _countof(m_szName));
+ if (SUCCEEDED (hr)) {
+ wcscat_s (m_szName, _countof(m_szName), W("."));
+ }
+ }
+ wcscat_s (m_szName, _countof(m_szName), szFunctionName);
+
+ LONG lSigBlobRemaining;
+ hr = GetFullNameForMD(pbSigBlob, ulSigBlob, &lSigBlobRemaining);
+
+ // We should have consumed all signature blob. If not, dump the sig in hex.
+ // Also dump in hex if so requested.
+ if (lSigBlobRemaining != 0)
+ {
+ // Did we not consume enough, or try to consume too much?
+ if (lSigBlobRemaining < 0)
+ ExtOut("ERROR IN SIGNATURE: Signature should be larger.\n");
+ else
+ ExtOut("ERROR IN SIGNATURE: Not all of signature blob was consumed. %d byte(s) remain\n", lSigBlobRemaining);
+ }
+
+ if (FAILED(hr))
+ ExtOut("ERROR!! Bad signature blob value!");
+}
+
+
+GetSignatureStringResults MDInfo::GetMethodSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName)
+{
+ if (!m_pImport)
+ return GSS_ERROR;
+
+ m_pSigBuf = fullName;
+ InitSigBuffer();
+
+ m_szName[0] = '\0';
+
+ LONG lSigBlobRemaining;
+ if (FAILED(GetFullNameForMD(pbSigBlob, ulSigBlob, &lSigBlobRemaining)))
+ return GSS_ERROR;
+
+ if (lSigBlobRemaining < 0)
+ return GSS_INSUFFICIENT_DATA;
+
+ return GSS_SUCCESS;
+}
+
+
+GetSignatureStringResults MDInfo::GetSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName)
+{
+ if (!m_pImport)
+ return GSS_ERROR;
+
+ m_pSigBuf = fullName;
+ InitSigBuffer();
+
+ m_szName[0] = '\0';
+
+ ULONG cb;
+ if (FAILED(GetOneElementType(pbSigBlob, ulSigBlob, &cb)))
+ {
+ if (cb > ulSigBlob)
+ return GSS_INSUFFICIENT_DATA;
+ else
+ return GSS_ERROR;
+ }
+
+ return GSS_SUCCESS;
+}
+
+
+inline bool isCallConv(unsigned sigByte, CorCallingConvention conv)
+{
+ return ((sigByte & IMAGE_CEE_CS_CALLCONV_MASK) == (unsigned) conv);
+}
+
+#ifndef IfFailGoto
+#define IfFailGoto(EXPR, LABEL) \
+do { hr = (EXPR); if(FAILED(hr)) { goto LABEL; } } while (0)
+#endif
+
+#ifndef IfFailGo
+#define IfFailGo(EXPR) IfFailGoto(EXPR, ErrExit)
+#endif
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0)
+#endif
+
+#ifndef _ASSERTE
+#define _ASSERTE(expr)
+#endif
+
+HRESULT MDInfo::GetFullNameForMD(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, LONG *plSigBlobRemaining)
+{
+ ULONG cbCur = 0;
+ ULONG cb;
+ ULONG ulData = NULL;
+ ULONG ulArgs;
+ HRESULT hr = NOERROR;
+
+ cb = CorSigUncompressData(pbSigBlob, &ulData);
+
+ // 0 is a valid calling convention byte (IMAGE_CEE_CS_CALLCONV_DEFAULT w/ no flags)
+ //if (ulData == NULL)
+ // goto ErrExit;
+
+ AddToSigBuffer (g_wszCalling[ulData & IMAGE_CEE_CS_CALLCONV_MASK]);
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+
+ if (ulData & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ AddToSigBuffer ( W(" [hasThis]"));
+ if (ulData & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ AddToSigBuffer ( W(" [explicit]"));
+
+ AddToSigBuffer (W(" "));
+ if ( isCallConv(ulData,IMAGE_CEE_CS_CALLCONV_FIELD) )
+ {
+ // display field type
+ if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb)))
+ goto ErrExit;
+ AddToSigBuffer ( W(" "));
+ AddToSigBuffer ( m_szName);
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+ }
+ else
+ {
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulArgs);
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+
+ if (ulData != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG)
+ {
+ // display return type when it is not a local varsig
+ if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb)))
+ goto ErrExit;
+ AddToSigBuffer (W(" "));
+ AddToSigBuffer (m_szName);
+ AddToSigBuffer ( W("("));
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+ }
+
+ ULONG i = 0;
+ while (i < ulArgs && ulSigBlob > 0)
+ {
+ ULONG ulDataUncompress;
+
+ // Handle the sentinal for varargs because it isn't counted in the args.
+ CorSigUncompressData(&pbSigBlob[cbCur], &ulDataUncompress);
+ ++i;
+
+ if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb)))
+ goto ErrExit;
+ if (i != ulArgs) {
+ AddToSigBuffer ( W(","));
+ }
+ if (cb>ulSigBlob)
+ goto ErrExit;
+
+ cbCur += cb;
+ ulSigBlob -= cb;
+ }
+ AddToSigBuffer ( W(")"));
+ }
+
+ // Nothing consumed but not yet counted.
+ cb = 0;
+
+ErrExit:
+
+ if (plSigBlobRemaining)
+ *plSigBlobRemaining = (ulSigBlob - cb);
+
+ return hr;
+}
+
+LPCWSTR MDInfo::TypeDefName(mdTypeDef inTypeDef)
+{
+ if (m_pImport == NULL) {
+ return W("");
+ }
+
+ HRESULT hr;
+
+ hr = m_pImport->GetTypeDefProps(
+ // [IN] The import scope.
+ inTypeDef, // [IN] TypeDef token for inquiry.
+ m_szTempBuf, // [OUT] Put name here.
+ MAX_CLASSNAME_LENGTH , // [IN] size of name buffer in wide chars.
+ NULL, // [OUT] put size of name (wide chars) here.
+ NULL, // [OUT] Put flags here.
+ NULL); // [OUT] Put base class TypeDef/TypeRef here.
+
+ if (FAILED(hr)) return (W("NoName"));
+ return (m_szTempBuf);
+} // LPCWSTR MDInfo::TypeDefName()
+LPCWSTR MDInfo::TypeRefName(mdTypeRef tr)
+{
+ if (m_pImport == NULL) {
+ return W("");
+ }
+
+ HRESULT hr;
+
+ hr = m_pImport->GetTypeRefProps(
+ tr, // The class ref token.
+ NULL, // Resolution scope.
+ m_szTempBuf, // Put the name here.
+ MAX_CLASSNAME_LENGTH, // Size of the name buffer, wide chars.
+ NULL); // Put actual size of name here.
+ if (FAILED(hr)) return (W("NoName"));
+
+ return (m_szTempBuf);
+} // LPCWSTR MDInfo::TypeRefName()
+
+LPCWSTR MDInfo::TypeDeforRefName(mdToken inToken)
+{
+ if (RidFromToken(inToken))
+ {
+ if (TypeFromToken(inToken) == mdtTypeDef)
+ return (TypeDefName((mdTypeDef) inToken));
+ else if (TypeFromToken(inToken) == mdtTypeRef)
+ return (TypeRefName((mdTypeRef) inToken));
+ else
+ return (W("[InvalidReference]"));
+ }
+ else
+ return (W(""));
+} // LPCWSTR MDInfo::TypeDeforRefName()
+
+
+HRESULT MDInfo::AddToSigBuffer(LPCWSTR string)
+{
+ HRESULT hr;
+ IfFailRet(m_pSigBuf->ReSize((_wcslen((LPWSTR)m_pSigBuf->Ptr()) + _wcslen(string) + 1) * sizeof(WCHAR)));
+ wcscat_s((LPWSTR)m_pSigBuf->Ptr(), m_pSigBuf->Size()/sizeof(WCHAR),string);
+ return NOERROR;
+}
+
+HRESULT MDInfo::GetOneElementType(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, ULONG *pcb)
+{
+ HRESULT hr = S_OK; // A result.
+ ULONG cbCur = 0;
+ ULONG cb;
+ ULONG ulData;
+ ULONG ulTemp;
+ int iTemp = 0;
+ mdToken tk;
+ const size_t capacity_buffer = 9;
+
+ cb = CorSigUncompressData(pbSigBlob, &ulData);
+
+ if (cb == ULONG(-1)) {
+ hr = E_FAIL;
+ goto ErrExit;
+ }
+
+ cbCur += cb;
+
+ // Handle the modifiers.
+ if (ulData & ELEMENT_TYPE_MODIFIER)
+ {
+ if (ulData == ELEMENT_TYPE_SENTINEL)
+ IfFailGo(AddToSigBuffer(W("<ELEMENT_TYPE_SENTINEL> ")));
+ else if (ulData == ELEMENT_TYPE_PINNED)
+ IfFailGo(AddToSigBuffer(W("PINNED ")));
+ else
+ {
+ hr = E_FAIL;
+ goto ErrExit;
+ }
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ goto ErrExit;
+ }
+
+ // Handle the underlying element types.
+ if (ulData >= ELEMENT_TYPE_MAX)
+ {
+ hr = E_FAIL;
+ goto ErrExit;
+ }
+ while (ulData == ELEMENT_TYPE_PTR || ulData == ELEMENT_TYPE_BYREF)
+ {
+ IfFailGo(AddToSigBuffer(g_wszMapElementType[ulData]));
+ IfFailGo(AddToSigBuffer(W(" ")));
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ }
+
+ // Generics
+ if (ulData == ELEMENT_TYPE_VAR)
+ {
+ IfFailGo(AddToSigBuffer(W("__Canon")));
+
+ // The next byte represents which generic parameter is referred to. We
+ // do not currently use this information, so just bypass this byte.
+ cbCur++;
+
+ goto ErrExit;
+ }
+
+ // A generic instance, e.g. IEnumerable<String>
+ if (ulData == ELEMENT_TYPE_GENERICINST)
+ {
+ // Print out the base type.
+ IfFailGo(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb));
+ cbCur += cb;
+
+ // Get the number of generic arguments.
+ ULONG numParams = 0;
+ IfFailGo(CorSigUncompressData(&pbSigBlob[cbCur], 1, &numParams, &cb));
+ cbCur += cb;
+
+ // Print out the list of arguments
+ IfFailGo(AddToSigBuffer(W("<")));
+ for (ULONG i = 0; i < numParams; i++)
+ {
+ if (i > 0)
+ IfFailGo(AddToSigBuffer(W(",")));
+
+ IfFailGo(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb));
+ cbCur += cb;
+ }
+ IfFailGo(AddToSigBuffer(W(">")));
+ goto ErrExit;
+ }
+
+ // Past this point we must have something which directly maps to a value in g_wszMapElementType.
+ IfFailGo(AddToSigBuffer(g_wszMapElementType[ulData]));
+ if (CorIsPrimitiveType((CorElementType)ulData) ||
+ ulData == ELEMENT_TYPE_TYPEDBYREF ||
+ ulData == ELEMENT_TYPE_OBJECT ||
+ ulData == ELEMENT_TYPE_I ||
+ ulData == ELEMENT_TYPE_U)
+ {
+ // If this is a primitive type, we are done
+ goto ErrExit;
+ }
+
+ AddToSigBuffer(W(" "));
+ if (ulData == ELEMENT_TYPE_VALUETYPE ||
+ ulData == ELEMENT_TYPE_CLASS ||
+ ulData == ELEMENT_TYPE_CMOD_REQD ||
+ ulData == ELEMENT_TYPE_CMOD_OPT)
+ {
+ cb = CorSigUncompressToken(&pbSigBlob[cbCur], &tk);
+ cbCur += cb;
+
+ // get the name of type ref. Don't care if truncated
+ if (TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtTypeRef)
+ {
+ IfFailGo(AddToSigBuffer(TypeDeforRefName(tk)));
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tk) == mdtTypeSpec);
+ WCHAR buffer[capacity_buffer];
+ _itow_s (tk, buffer, capacity_buffer, 16);
+ IfFailGo(AddToSigBuffer(buffer));
+ }
+ if (ulData == ELEMENT_TYPE_CMOD_REQD ||
+ ulData == ELEMENT_TYPE_CMOD_OPT)
+ {
+ IfFailGo(AddToSigBuffer(W(" ")));
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ }
+
+ goto ErrExit;
+ }
+ if (ulData == ELEMENT_TYPE_SZARRAY)
+ {
+ // display the base type of SZARRAY or GENERICARRAY
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ goto ErrExit;
+ }
+ if (ulData == ELEMENT_TYPE_FNPTR)
+ {
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ if (ulData & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ IfFailGo(AddToSigBuffer(W("[explicit] ")));
+ if (ulData & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ IfFailGo(AddToSigBuffer(W("[hasThis] ")));
+
+ IfFailGo(AddToSigBuffer(g_wszCalling[ulData & IMAGE_CEE_CS_CALLCONV_MASK]));
+
+ // Get number of args
+ ULONG numArgs;
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &numArgs);
+ cbCur += cb;
+
+ // do return type
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+
+ IfFailGo(AddToSigBuffer(W("(")));
+ while (numArgs > 0)
+ {
+ if (cbCur > ulSigBlob)
+ goto ErrExit;
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ --numArgs;
+ if (numArgs > 0)
+ IfFailGo(AddToSigBuffer(W(",")));
+ }
+ IfFailGo(AddToSigBuffer(W(")")));
+ goto ErrExit;
+ }
+
+ if (ulData == ELEMENT_TYPE_INTERNAL)
+ {
+ IfFailGo(AddToSigBuffer(W("MT ")));
+
+ void *pvMethodTable;
+ cb = CorSigUncompressPointer(&pbSigBlob[cbCur], (void**)&pvMethodTable);
+ cbCur += cb;
+
+ const size_t capacity_szMethodTableValue = 10;
+ WCHAR szMethodTableValue[10];
+ itow_s_ptr((INT_PTR)pvMethodTable, szMethodTableValue, capacity_szMethodTableValue, 16);
+
+ IfFailGo(AddToSigBuffer(szMethodTableValue));
+ IfFailGo(AddToSigBuffer(W(" ")));
+
+ IfFailGo(g_sos->GetMethodTableName(TO_CDADDR(pvMethodTable), mdNameLen, g_mdName, NULL));
+ IfFailGo(AddToSigBuffer(g_mdName));
+
+ goto ErrExit;
+ }
+
+
+ if(ulData != ELEMENT_TYPE_ARRAY) return E_FAIL;
+
+ // display the base type of SDARRAY
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+
+ IfFailGo(AddToSigBuffer(W(" ")));
+ // display the rank of MDARRAY
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ WCHAR buffer[capacity_buffer];
+ _itow_s (ulData, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ if (ulData == 0)
+ // we are done if no rank specified
+ goto ErrExit;
+
+ IfFailGo(AddToSigBuffer(W(" ")));
+ // how many dimensions have size specified?
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ _itow_s (ulData, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ if (ulData == 0) {
+ IfFailGo(AddToSigBuffer(W(" ")));
+ }
+ while (ulData)
+ {
+
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulTemp);
+ _itow_s (ulTemp, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ IfFailGo(AddToSigBuffer(W(" ")));
+ cbCur += cb;
+ ulData--;
+ }
+ // how many dimensions have lower bounds specified?
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ _itow_s (ulData, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ while (ulData)
+ {
+
+ cb = CorSigUncompressSignedInt(&pbSigBlob[cbCur], &iTemp);
+ _itow_s (iTemp, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ IfFailGo(AddToSigBuffer(W(" ")));
+ cbCur += cb;
+ ulData--;
+ }
+
+ErrExit:
+ if (cbCur > ulSigBlob)
+ hr = E_FAIL;
+ *pcb = cbCur;
+ return hr;
+}
+
+//*****************************************************************************
+// Used when the method is tiny (< 64 bytes), and there are no local vars
+//*****************************************************************************
+typedef struct tagCOR_ILMETHOD_TINY : IMAGE_COR_ILMETHOD_TINY
+{
+ bool IsTiny() const { return((Flags_CodeSize & (CorILMethod_FormatMask >> 1)) == CorILMethod_TinyFormat); }
+ DWORD GetLocalVarSigTok() const { return(0); }
+} COR_ILMETHOD_TINY;
+
+
+//*****************************************************************************
+// This strucuture is the 'fat' layout, where no compression is attempted.
+// Note that this structure can be added on at the end, thus making it extensible
+//*****************************************************************************
+typedef struct tagCOR_ILMETHOD_FAT : IMAGE_COR_ILMETHOD_FAT
+{
+ bool IsFat() const { return((Flags & CorILMethod_FormatMask) == CorILMethod_FatFormat); }
+ mdToken GetLocalVarSigTok() const { return(LocalVarSigTok); }
+} COR_ILMETHOD_FAT;
diff --git a/src/ToolBox/SOS/Strike/ntinfo.h b/src/ToolBox/SOS/Strike/ntinfo.h
new file mode 100644
index 0000000000..f3e6e91ff0
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ntinfo.h
@@ -0,0 +1,193 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef _ntinfo_h__
+#define _ntinfo_h__
+
+//+---------------------------------------------------------------------------
+//
+// forward declarations (in order to avoid type casting when accessing
+// data members of the SOleTlsData structure).
+//
+//+---------------------------------------------------------------------------
+
+class CAptCallCtrl; // see callctrl.hxx
+class CSrvCallState; // see callctrl.hxx
+class CObjServer; // see sobjact.hxx
+class CSmAllocator; // see stg\h\smalloc.hxx
+class CMessageCall; // see call.hxx
+class CClientCall; // see call.hxx
+class CAsyncCall; // see call.hxx
+class CClipDataObject; // see ole232\clipbrd\clipdata.h
+class CSurrogatedObjectList; // see com\inc\comsrgt.hxx
+class CCtxCall; // see PSTable.hxx
+class CPolicySet; // see PSTable.hxx
+class CObjectContext; // see context.hxx
+class CComApartment; // see aprtmnt.hxx
+class ContextStackNode;
+//+-------------------------------------------------------------------
+//
+// Struct: CallEntry
+//
+// Synopsis: Call Table Entry.
+//
+//+-------------------------------------------------------------------
+typedef struct tagCallEntry
+{
+ void *pNext; // ptr to next entry
+ void *pvObject; // Entry object
+} CallEntry;
+
+//+---------------------------------------------------------------------------
+//
+// Enum: OLETLSFLAGS
+//
+// Synopsys: bit values for dwFlags field of SOleTlsData. If you just want
+// to store a BOOL in TLS, use this enum and the dwFlag field.
+//
+//+---------------------------------------------------------------------------
+typedef enum tagOLETLSFLAGS
+{
+ OLETLS_LOCALTID = 0x01, // This TID is in the current process.
+ OLETLS_UUIDINITIALIZED = 0x02, // This Logical thread is init'd.
+ OLETLS_INTHREADDETACH = 0x04, // This is in thread detach. Needed
+ // due to NT's special thread detach
+ // rules.
+ OLETLS_CHANNELTHREADINITIALZED = 0x08,// This channel has been init'd
+ OLETLS_WOWTHREAD = 0x10, // This thread is a 16-bit WOW thread.
+ OLETLS_THREADUNINITIALIZING = 0x20, // This thread is in CoUninitialize.
+ OLETLS_DISABLE_OLE1DDE = 0x40, // This thread can't use a DDE window.
+ OLETLS_APARTMENTTHREADED = 0x80, // This is an STA apartment thread
+ OLETLS_MULTITHREADED = 0x100, // This is an MTA apartment thread
+ OLETLS_IMPERSONATING = 0x200, // This thread is impersonating
+ OLETLS_DISABLE_EVENTLOGGER = 0x400, // Prevent recursion in event logger
+ OLETLS_INNEUTRALAPT = 0x800, // This thread is in the NTA
+ OLETLS_DISPATCHTHREAD = 0x1000, // This is a dispatch thread
+ OLETLS_HOSTTHREAD = 0x2000, // This is a host thread
+ OLETLS_ALLOWCOINIT = 0x4000, // This thread allows inits
+ OLETLS_PENDINGUNINIT = 0x8000, // This thread has pending uninit
+ OLETLS_FIRSTMTAINIT = 0x10000,// First thread to attempt an MTA init
+ OLETLS_FIRSTNTAINIT = 0x20000,// First thread to attempt an NTA init
+ OLETLS_APTINITIALIZING = 0x40000 // Apartment Object is initializing
+} OLETLSFLAGS;
+
+
+//+---------------------------------------------------------------------------
+//
+// Structure: SOleTlsData
+//
+// Synopsis: structure holding per thread state needed by OLE32
+//
+//+---------------------------------------------------------------------------
+typedef struct tagSOleTlsData
+{
+ // jsimmons 5/23/2001
+ // Alert Alert: nefarious folks (eg, URT) are looking in our TLS at
+ // various stuff. They expect that pCurrentCtx will be at a certain
+ // offset from the beginning of the tls struct. So don't add, delete, or
+ // move any members within this block.
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// ********* BEGIN "NO MUCKING AROUND" BLOCK *********
+/////////////////////////////////////////////////////////////////////////////////////////
+ // Docfile multiple allocator support
+ void *pvThreadBase; // per thread base pointer
+ CSmAllocator *pSmAllocator; // per thread docfile allocator
+
+ DWORD dwApartmentID; // Per thread "process ID"
+ DWORD dwFlags; // see OLETLSFLAGS above
+
+ LONG TlsMapIndex; // index in the global TLSMap
+ void **ppTlsSlot; // Back pointer to the thread tls slot
+ DWORD cComInits; // number of per-thread inits
+ DWORD cOleInits; // number of per-thread OLE inits
+
+ DWORD cCalls; // number of outstanding calls
+ CMessageCall *pCallInfo; // channel call info
+ CAsyncCall *pFreeAsyncCall; // ptr to available call object for this thread.
+ CClientCall *pFreeClientCall; // ptr to available call object for this thread.
+
+ CObjServer *pObjServer; // Activation Server Object for this apartment.
+ DWORD dwTIDCaller; // TID of current calling app
+ CObjectContext *pCurrentCtx; // Current context
+/////////////////////////////////////////////////////////////////////////////////////////
+// ********* END "NO MUCKING AROUND" BLOCK *********
+/////////////////////////////////////////////////////////////////////////////////////////
+
+ CObjectContext *pEmptyCtx; // Empty context
+
+ CObjectContext *pNativeCtx; // Native context
+ ULONGLONG ContextId; // Uniquely identifies the current context
+ CComApartment *pNativeApt; // Native apartment for the thread.
+ IUnknown *pCallContext; // call context object
+ CCtxCall *pCtxCall; // Context call object
+
+ CPolicySet *pPS; // Policy set
+ PVOID pvPendingCallsFront;// Per Apt pending async calls
+ PVOID pvPendingCallsBack;
+ CAptCallCtrl *pCallCtrl; // call control for RPC for this apartment
+
+ CSrvCallState *pTopSCS; // top server-side callctrl state
+ IMessageFilter *pMsgFilter; // temp storage for App MsgFilter
+ HWND hwndSTA; // STA server window same as poxid->hServerSTA
+ // ...needed on Win95 before oxid registration
+ LONG cORPCNestingLevel; // call nesting level (DBG only)
+
+ DWORD cDebugData; // count of bytes of debug data in call
+
+ UUID LogicalThreadId; // current logical thread id
+
+ HANDLE hThread; // Thread handle used for cancel
+ HANDLE hRevert; // Token before first impersonate.
+ IUnknown *pAsyncRelease; // Controlling unknown for async release
+ // DDE data
+ HWND hwndDdeServer; // Per thread Common DDE server
+
+ HWND hwndDdeClient; // Per thread Common DDE client
+ ULONG cServeDdeObjects; // non-zero if objects DDE should serve
+ // ClassCache data
+ LPVOID pSTALSvrsFront; // Chain of LServers registers in this thread if STA
+ // upper layer data
+ HWND hwndClip; // Clipboard window
+
+ IDataObject *pDataObjClip; // Current Clipboard DataObject
+ DWORD dwClipSeqNum; // Clipboard Sequence # for the above DataObject
+ DWORD fIsClipWrapper; // Did we hand out the wrapper Clipboard DataObject?
+ IUnknown *punkState; // Per thread "state" object
+ // cancel data
+ DWORD cCallCancellation; // count of CoEnableCallCancellation
+ // async sends data
+ DWORD cAsyncSends; // count of async sends outstanding
+
+ CAsyncCall* pAsyncCallList; // async calls outstanding
+ CSurrogatedObjectList *pSurrogateList; // Objects in the surrogate
+
+ LockEntry lockEntry; // Locks currently held by the thread
+ CallEntry CallEntry; // client-side call chain for this thread
+
+#ifdef WX86OLE
+ IUnknown *punkStateWx86; // Per thread "state" object for Wx86
+#endif
+ void *pDragCursors; // Per thread drag cursor table.
+
+ IUnknown *punkError; // Per thread error object.
+ ULONG cbErrorData; // Maximum size of error data.
+
+ IUnknown *punkActiveXSafetyProvider;
+
+#if DBG==1
+ LONG cTraceNestingLevel; // call nesting level for OLETRACE
+#endif
+
+ ContextStackNode* pContextStack;
+
+} SOleTlsData;
+
+#endif //_ntinfo_h__
+
diff --git a/src/ToolBox/SOS/Strike/platformspecific.h b/src/ToolBox/SOS/Strike/platformspecific.h
new file mode 100644
index 0000000000..fdbc5b52ca
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/platformspecific.h
@@ -0,0 +1,195 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+// Include platform specific declarations based on the target platform rather than the host platform.
+
+#ifndef __PLATFORM_SPECIFIC_INCLUDED
+#define __PLATFORM_SPECIFIC_INCLUDED
+
+// The main debugger code already has target platform definitions for CONTEXT.
+#include "../../../debug/inc/dbgtargetcontext.h"
+
+#ifndef FEATURE_PAL
+
+// The various OS structure definitions below tend to differ based soley on the size of pointers. DT_POINTER
+// is a type whose size matches that of the target platform. It's integral rather than point since it is never
+// legal to dereference one of these on the host.
+#ifdef _TARGET_WIN64_
+typedef ULONG64 DT_POINTER;
+#else
+typedef ULONG32 DT_POINTER;
+#endif
+
+struct DT_LIST_ENTRY
+{
+ DT_POINTER Flink;
+ DT_POINTER Blink;
+};
+
+struct DT_UNICODE_STRING
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ DT_POINTER Buffer;
+};
+
+#define DT_GDI_HANDLE_BUFFER_SIZE32 34
+#define DT_GDI_HANDLE_BUFFER_SIZE64 60
+
+#ifndef IMAGE_FILE_MACHINE_ARMNT
+#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
+#endif
+
+#ifndef IMAGE_FILE_MACHINE_ARM64
+#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
+#endif
+
+#ifdef _TARGET_WIN64_
+typedef ULONG DT_GDI_HANDLE_BUFFER[DT_GDI_HANDLE_BUFFER_SIZE64];
+#else
+typedef ULONG DT_GDI_HANDLE_BUFFER[DT_GDI_HANDLE_BUFFER_SIZE32];
+#endif
+
+struct DT_PEB
+{
+ BOOLEAN InheritedAddressSpace;
+ BOOLEAN ReadImageFileExecOptions;
+ BOOLEAN BeingDebugged;
+ BOOLEAN SpareBool;
+ DT_POINTER Mutant;
+ DT_POINTER ImageBaseAddress;
+ DT_POINTER Ldr;
+ DT_POINTER ProcessParameters;
+ DT_POINTER SubSystemData;
+ DT_POINTER ProcessHeap;
+ DT_POINTER FastPebLock;
+ DT_POINTER SparePtr1;
+ DT_POINTER SparePtr2;
+ ULONG EnvironmentUpdateCount;
+ DT_POINTER KernelCallbackTable;
+ ULONG SystemReserved[1];
+ struct _dummy {
+ ULONG ExecuteOptions : 2;
+ ULONG SpareBits : 30;
+ };
+ DT_POINTER FreeList;
+ ULONG TlsExpansionCounter;
+ DT_POINTER TlsBitmap;
+ ULONG TlsBitmapBits[2];
+ DT_POINTER ReadOnlySharedMemoryBase;
+ DT_POINTER ReadOnlySharedMemoryHeap;
+ DT_POINTER ReadOnlyStaticServerData;
+ DT_POINTER AnsiCodePageData;
+ DT_POINTER OemCodePageData;
+ DT_POINTER UnicodeCaseTableData;
+ ULONG NumberOfProcessors;
+ ULONG NtGlobalFlag;
+ LARGE_INTEGER CriticalSectionTimeout;
+ DT_POINTER HeapSegmentReserve;
+ DT_POINTER HeapSegmentCommit;
+ DT_POINTER HeapDeCommitTotalFreeThreshold;
+ DT_POINTER HeapDeCommitFreeBlockThreshold;
+ ULONG NumberOfHeaps;
+ ULONG MaximumNumberOfHeaps;
+ DT_POINTER ProcessHeaps;
+ DT_POINTER GdiSharedHandleTable;
+ DT_POINTER ProcessStarterHelper;
+ ULONG GdiDCAttributeList;
+ DT_POINTER LoaderLock;
+ ULONG OSMajorVersion;
+ ULONG OSMinorVersion;
+ USHORT OSBuildNumber;
+ USHORT OSCSDVersion;
+ ULONG OSPlatformId;
+ ULONG ImageSubsystem;
+ ULONG ImageSubsystemMajorVersion;
+ ULONG ImageSubsystemMinorVersion;
+ DT_POINTER ImageProcessAffinityMask;
+ DT_GDI_HANDLE_BUFFER GdiHandleBuffer;
+ DT_POINTER PostProcessInitRoutine;
+ DT_POINTER TlsExpansionBitmap;
+ ULONG TlsExpansionBitmapBits[32];
+ ULONG SessionId;
+ ULARGE_INTEGER AppCompatFlags;
+ ULARGE_INTEGER AppCompatFlagsUser;
+ DT_POINTER pShimData;
+ DT_POINTER AppCompatInfo;
+ DT_UNICODE_STRING CSDVersion;
+ DT_POINTER ActivationContextData;
+ DT_POINTER ProcessAssemblyStorageMap;
+ DT_POINTER SystemDefaultActivationContextData;
+ DT_POINTER SystemAssemblyStorageMap;
+ DT_POINTER MinimumStackCommit;
+ DT_POINTER FlsCallback;
+ DT_LIST_ENTRY FlsListHead;
+ DT_POINTER FlsBitmap;
+ ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)];
+ ULONG FlsHighIndex;
+};
+
+struct DT_PEB_LDR_DATA
+{
+ BYTE Reserved1[8];
+ DT_POINTER Reserved2[3];
+ DT_LIST_ENTRY InMemoryOrderModuleList;
+};
+
+struct DT_CURDIR
+{
+ DT_UNICODE_STRING DosPath;
+ DT_POINTER Handle;
+};
+
+struct DT_RTL_DRIVE_LETTER_CURDIR {
+ USHORT Flags;
+ USHORT Length;
+ ULONG TimeStamp;
+ STRING DosPath;
+};
+
+#define DT_RTL_MAX_DRIVE_LETTERS 32
+
+struct DT_RTL_USER_PROCESS_PARAMETERS
+{
+ ULONG MaximumLength;
+ ULONG Length;
+ ULONG Flags;
+ ULONG DebugFlags;
+ DT_POINTER ConsoleHandle;
+ ULONG ConsoleFlags;
+ DT_POINTER StandardInput;
+ DT_POINTER StandardOutput;
+ DT_POINTER StandardError;
+ DT_CURDIR CurrentDirectory;
+ DT_UNICODE_STRING DllPath;
+ DT_UNICODE_STRING ImagePathName;
+ DT_UNICODE_STRING CommandLine;
+ DT_POINTER Environment;
+ ULONG StartingX;
+ ULONG StartingY;
+ ULONG CountX;
+ ULONG CountY;
+ ULONG CountCharsX;
+ ULONG CountCharsY;
+ ULONG FillAttribute;
+ ULONG WindowFlags;
+ ULONG ShowWindowFlags;
+ DT_UNICODE_STRING WindowTitle;
+ DT_UNICODE_STRING DesktopInfo;
+ DT_UNICODE_STRING ShellInfo;
+ DT_UNICODE_STRING RuntimeData;
+ DT_RTL_DRIVE_LETTER_CURDIR CurrentDirectores[ DT_RTL_MAX_DRIVE_LETTERS ];
+};
+
+#endif // !FEATURE_PAL
+
+#define DT_OS_PAGE_SIZE 4096
+
+#endif // !__PLATFORM_SPECIFIC_INCLUDED
diff --git a/src/ToolBox/SOS/Strike/sildasm.cpp b/src/ToolBox/SOS/Strike/sildasm.cpp
new file mode 100644
index 0000000000..6bd3bb4801
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sildasm.cpp
@@ -0,0 +1,1090 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+//
+// disasm.cpp : Defines the entry point for the console application.
+//
+#ifndef FEATURE_PAL
+#include <tchar.h>
+#endif
+#include "strike.h"
+#include "util.h"
+#include "strsafe.h"
+//#ifndef FEATURE_PAL
+//#include "gcinfo.h"
+//#endif
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+
+#include "cor.h"
+#include "dacprivate.h"
+
+#include "openum.h"
+
+#include "sos_md.h"
+
+#define SOS_INCLUDE 1
+#include "corhlpr.h"
+#include "corhlpr.cpp"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+#undef printf
+#define printf ExtOut
+
+// typedef unsigned char BYTE;
+struct OpCode
+{
+ int code;
+ const char *name;
+ int args;
+ BYTE b1;
+ BYTE b2;
+
+ unsigned int getCode() {
+ if (b1==0xFF) return b2;
+ else return (0xFE00 | b2);
+ }
+};
+
+#define OPCODES_LENGTH 0x122
+
+#undef OPDEF
+#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) {c, s, args, s1, s2},
+static OpCode opcodes[] =
+{
+#include "opcode.def"
+};
+
+static ULONG position = 0;
+static BYTE *pBuffer = NULL;
+
+// The UNALIGNED is because on IA64 alignment rules would prevent
+// us from reading a pointer from an unaligned source.
+template <typename T>
+T readData ( ) {
+ T val = *((T UNALIGNED*)(pBuffer+position));
+ position += sizeof(T);
+ return val;
+}
+
+unsigned int readOpcode()
+{
+ unsigned int c = readData<BYTE>();
+ if (c == 0xFE)
+ {
+ c = readData<BYTE>();
+ c |= 0x100;
+ }
+ return c;
+}
+
+void DisassembleToken(IMetaDataImport *i,
+ DWORD token)
+{
+ HRESULT hr;
+
+ switch (TypeFromToken(token))
+ {
+ default:
+ printf("<unknown token type %08x>", TypeFromToken(token));
+ break;
+
+ case mdtTypeDef:
+ {
+ ULONG cLen;
+ WCHAR szName[50];
+
+ hr = i->GetTypeDefProps(token, szName, 49, &cLen, NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type def>"));
+
+ printf("%S", szName);
+ }
+ break;
+
+ case mdtTypeRef:
+ {
+ ULONG cLen;
+ WCHAR szName[50];
+
+ hr = i->GetTypeRefProps(token, NULL, szName, 49, &cLen);
+
+ if (FAILED(hr))
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type ref>"));
+
+ printf("%S", szName);
+ }
+ break;
+
+ case mdtFieldDef:
+ {
+ ULONG cLen;
+ WCHAR szFieldName[50];
+ WCHAR szClassName[50];
+ mdTypeDef mdClass;
+
+ hr = i->GetFieldProps(token, &mdClass, szFieldName, 49, &cLen,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szFieldName, COUNTOF(szFieldName), W("<unknown field def>"));
+
+ hr = i->GetTypeDefProps(mdClass, szClassName, 49, &cLen,
+ NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szClassName, COUNTOF(szClassName), W("<unknown type def>"));
+
+ printf("%S::%S", szClassName, szFieldName);
+ }
+ break;
+
+ case mdtMethodDef:
+ {
+ ULONG cLen;
+ WCHAR szFieldName[50];
+ WCHAR szClassName[50];
+ mdTypeDef mdClass;
+
+ hr = i->GetMethodProps(token, &mdClass, szFieldName, 49, &cLen,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szFieldName, COUNTOF(szFieldName), W("<unknown method def>"));
+
+ hr = i->GetTypeDefProps(mdClass, szClassName, 49, &cLen,
+ NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szClassName, COUNTOF(szClassName), W("<unknown type def>"));
+
+ printf("%S::%S", szClassName, szFieldName);
+ }
+ break;
+
+ case mdtMemberRef:
+ {
+ mdTypeRef cr = mdTypeRefNil;
+ LPCWSTR pMemberName;
+ WCHAR memberName[50];
+ ULONG memberNameLen;
+
+ hr = i->GetMemberRefProps(token, &cr, memberName, 49,
+ &memberNameLen, NULL, NULL);
+
+ if (FAILED(hr))
+ {
+ pMemberName = W("<unknown member ref>");
+ }
+ else
+ pMemberName = memberName;
+
+ ULONG cLen;
+ WCHAR szName[50];
+
+ if(TypeFromToken(cr) == mdtTypeRef)
+ {
+ if (FAILED(i->GetTypeRefProps(cr, NULL, szName, 50, &cLen)))
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type ref>"));
+ }
+ }
+ else if(TypeFromToken(cr) == mdtTypeDef)
+ {
+ if (FAILED(i->GetTypeDefProps(cr, szName, 49, &cLen,
+ NULL, NULL)))
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type def>"));
+ }
+ }
+ else if(TypeFromToken(cr) == mdtTypeSpec)
+ {
+ IMDInternalImport *pIMDI = NULL;
+ if (SUCCEEDED(GetMDInternalFromImport(i, &pIMDI)))
+ {
+ CQuickBytes out;
+ ULONG cSig;
+ PCCOR_SIGNATURE sig;
+ if (FAILED(pIMDI->GetSigFromToken(cr, &cSig, &sig)))
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<Invalid record>"));
+ }
+ else
+ {
+ PrettyPrintType(sig, &out, pIMDI);
+ MultiByteToWideChar (CP_ACP, 0, asString(&out), -1, szName, 50);
+ }
+
+ pIMDI->Release();
+ }
+ else
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type spec>"));
+ }
+ }
+ else
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type token>"));
+ }
+
+ printf("%S::%S ", szName, pMemberName);
+ }
+ break;
+ }
+}
+
+ULONG GetILSize(DWORD_PTR ilAddr)
+{
+ ULONG uRet = 0;
+
+ // workaround: read enough bytes at ilAddr to presumably get the entire header.
+ // Could be error prone.
+
+ static BYTE headerArray[1024];
+ HRESULT Status = g_ExtData->ReadVirtual(TO_CDADDR(ilAddr), headerArray, sizeof(headerArray), NULL);
+ if (SUCCEEDED(Status))
+ {
+ COR_ILMETHOD_DECODER header((COR_ILMETHOD *)headerArray);
+ // uRet = header.GetHeaderSize();
+ uRet = header.GetOnDiskSize((COR_ILMETHOD *)headerArray);
+ }
+
+ return uRet;
+}
+
+HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr)
+{
+ HRESULT Status = S_OK;
+
+ ULONG Size = GetILSize(ilAddr);
+ if (Size == 0)
+ {
+ ExtOut("error decoding IL\n");
+ return Status;
+ }
+
+ ExtOut("ilAddr = %p\n", SOS_PTR(ilAddr));
+
+ // Read the memory into a local buffer
+ ArrayHolder<BYTE> pArray = new BYTE[Size];
+ Status = g_ExtData->ReadVirtual(TO_CDADDR(ilAddr), pArray, Size, NULL);
+ if (Status != S_OK)
+ {
+ ExtOut("Failed to read memory\n");
+ return Status;
+ }
+
+ DecodeIL(pImport, pArray, Size);
+
+ return Status;
+}
+
+void DecodeIL(IMetaDataImport *pImport, BYTE *buffer, ULONG bufSize)
+{
+ // First decode the header
+ COR_ILMETHOD *pHeader = (COR_ILMETHOD *) buffer;
+ COR_ILMETHOD_DECODER header(pHeader);
+
+ // Set globals
+ position = 0;
+ pBuffer = (BYTE *) header.Code;
+
+ UINT indentCount = 0;
+ ULONG endCodePosition = header.GetCodeSize();
+ while(position < endCodePosition)
+ {
+ for (unsigned e=0;e<header.EHCount();e++)
+ {
+ IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehBuff;
+ const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo;
+
+ ehInfo = header.EH->EHClause(e,&ehBuff);
+ if (ehInfo->TryOffset == position)
+ {
+ printf ("%*s.try\n%*s{\n", indentCount, "", indentCount, "");
+ indentCount+=2;
+ }
+ else if ((ehInfo->TryOffset + ehInfo->TryLength) == position)
+ {
+ indentCount-=2;
+ printf("%*s} // end .try\n", indentCount, "");
+ }
+
+ if (ehInfo->HandlerOffset == position)
+ {
+ if (ehInfo->Flags == COR_ILEXCEPTION_CLAUSE_FINALLY)
+ printf("%*s.finally\n%*s{\n", indentCount, "", indentCount, "");
+ else
+ printf("%*s.catch\n%*s{\n", indentCount, "", indentCount, "");
+
+ indentCount+=2;
+ }
+ else if ((ehInfo->HandlerOffset + ehInfo->HandlerLength) == position)
+ {
+ indentCount-=2;
+
+ if (ehInfo->Flags == COR_ILEXCEPTION_CLAUSE_FINALLY)
+ printf("%*s} // end .finally\n", indentCount, "");
+ else
+ printf("%*s} // end .catch\n", indentCount, "");
+ }
+ }
+
+ printf("%*sIL_%04x: ", indentCount, "", position);
+ unsigned int c = readOpcode();
+ OpCode opcode = opcodes[c];
+ printf("%s ", opcode.name);
+
+ switch(opcode.args)
+ {
+ case InlineNone: break;
+
+ case ShortInlineVar:
+ printf("VAR OR ARG %d",readData<BYTE>()); break;
+ case InlineVar:
+ printf("VAR OR ARG %d",readData<WORD>()); break;
+ case InlineI:
+ printf("%d",readData<LONG>());
+ break;
+ case InlineR:
+ printf("%f",readData<double>());
+ break;
+ case InlineBrTarget:
+ printf("IL_%04x",readData<LONG>() + position); break;
+ case ShortInlineBrTarget:
+ printf("IL_%04x",readData<BYTE>() + position); break;
+ case InlineI8:
+ printf("%ld", readData<__int64>()); break;
+
+ case InlineMethod:
+ case InlineField:
+ case InlineType:
+ case InlineTok:
+ case InlineSig:
+ {
+ LONG l = readData<LONG>();
+ if (pImport != NULL)
+ {
+ DisassembleToken(pImport, l);
+ }
+ else
+ {
+ printf("TOKEN %x", l);
+ }
+ break;
+ }
+
+ case InlineString:
+ {
+ LONG l = readData<LONG>();
+
+ ULONG numChars;
+ WCHAR str[84];
+
+ if ((pImport != NULL) && (pImport->GetUserString((mdString) l, str, 80, &numChars) == S_OK))
+ {
+ if (numChars < 80)
+ str[numChars] = 0;
+ wcscpy_s(&str[79], 4, W("..."));
+ WCHAR* ptr = str;
+ while(*ptr != 0) {
+ if (*ptr < 0x20 || * ptr >= 0x80) {
+ *ptr = '.';
+ }
+ ptr++;
+ }
+
+ printf("\"%S\"", str);
+ }
+ else
+ {
+ printf("STRING %x", l);
+ }
+ break;
+ }
+
+ case InlineSwitch:
+ {
+ LONG cases = readData<LONG>();
+ LONG *pArray = new LONG[cases];
+ LONG i=0;
+ for(i=0;i<cases;i++)
+ {
+ pArray[i] = readData<LONG>();
+ }
+ printf("(");
+ for(i=0;i<cases;i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("IL_%04x",pArray[i] + position);
+ }
+ printf(")");
+ delete [] pArray;
+ break;
+ }
+ case ShortInlineI:
+ printf("%d", readData<BYTE>()); break;
+ case ShortInlineR:
+ printf("%f", readData<float>()); break;
+ default: printf("Error, unexpected opcode type\n"); break;
+ }
+
+ printf("\n");
+ }
+}
+
+DWORD_PTR GetObj(DacpObjectData& tokenArray, UINT item)
+{
+ if (item < tokenArray.dwNumComponents)
+ {
+ DWORD_PTR dwAddr = (DWORD_PTR) (tokenArray.ArrayDataPtr + tokenArray.dwComponentSize*item);
+ DWORD_PTR objPtr;
+ if (SUCCEEDED(MOVE(objPtr, dwAddr)))
+ {
+ return objPtr;
+ }
+ }
+ return NULL;
+}
+
+
+void DisassembleToken(DacpObjectData& tokenArray,
+ DWORD token)
+{
+ switch (TypeFromToken(token))
+ {
+ default:
+ printf("<unknown token type (token=%08x)>", token);
+ break;
+
+ case mdtTypeDef:
+ {
+ DWORD_PTR runtimeTypeHandle = GetObj(tokenArray, RidFromToken(token));
+
+ DWORD_PTR runtimeType = NULL;
+ MOVE(runtimeType, runtimeTypeHandle + sizeof(DWORD_PTR));
+
+ int offset = GetObjFieldOffset(runtimeType, W("m_handle"));
+
+ DWORD_PTR methodTable = NULL;
+ MOVE(methodTable, runtimeType + offset);
+
+ if (NameForMT_s(methodTable, g_mdName,mdNameLen))
+ {
+ printf("%x \"%S\"", token, g_mdName);
+ }
+ else
+ {
+ printf("<invalid MethodTable>");
+ }
+ }
+ break;
+
+ case mdtSignature:
+ case mdtTypeRef:
+ {
+ printf ("%x (%p)", token, SOS_PTR(GetObj(tokenArray, RidFromToken(token))));
+ }
+ break;
+
+ case mdtFieldDef:
+ {
+ printf ("%x (%p)", token, SOS_PTR(GetObj(tokenArray, RidFromToken(token))));
+ }
+ break;
+
+ case mdtMethodDef:
+ {
+ CLRDATA_ADDRESS runtimeMethodHandle = GetObj(tokenArray, RidFromToken(token));
+ int offset = GetObjFieldOffset(runtimeMethodHandle, W("m_value"));
+
+ TADDR runtimeMethodInfo = NULL;
+ MOVE(runtimeMethodInfo, runtimeMethodHandle+offset);
+
+ offset = GetObjFieldOffset(runtimeMethodInfo, W("m_handle"));
+
+ TADDR methodDesc = NULL;
+ MOVE(methodDesc, runtimeMethodInfo+offset);
+
+ NameForMD_s((DWORD_PTR)methodDesc, g_mdName, mdNameLen);
+ printf ("%x %S", token, g_mdName);
+ }
+ break;
+
+ case mdtMemberRef:
+ {
+ printf ("%x (%p)", token, SOS_PTR(GetObj(tokenArray, RidFromToken(token))));
+ }
+ break;
+ case mdtString:
+ {
+ DWORD_PTR strObj = GetObj(tokenArray, RidFromToken(token));
+ printf ("%x \"", token);
+ StringObjectContent (strObj, FALSE, 40);
+ printf ("\"");
+ }
+ break;
+ }
+}
+
+// Similar to the function above. TODO: factor them together before checkin.
+void DecodeDynamicIL(BYTE *data, ULONG Size, DacpObjectData& tokenArray)
+{
+ // There is no header for this dynamic guy.
+ // Set globals
+ position = 0;
+ pBuffer = data;
+
+ // At this time no exception information will be displayed (fix soon)
+ UINT indentCount = 0;
+ ULONG endCodePosition = Size;
+ while(position < endCodePosition)
+ {
+ printf("%*sIL_%04x: ", indentCount, "", position);
+ unsigned int c = readOpcode();
+ OpCode opcode = opcodes[c];
+ printf("%s ", opcode.name);
+
+ switch(opcode.args)
+ {
+ case InlineNone: break;
+
+ case ShortInlineVar:
+ printf("VAR OR ARG %d",readData<BYTE>()); break;
+ case InlineVar:
+ printf("VAR OR ARG %d",readData<WORD>()); break;
+ case InlineI:
+ printf("%d",readData<LONG>());
+ break;
+ case InlineR:
+ printf("%f",readData<double>());
+ break;
+ case InlineBrTarget:
+ printf("IL_%04x",readData<LONG>() + position); break;
+ case ShortInlineBrTarget:
+ printf("IL_%04x",readData<BYTE>() + position); break;
+ case InlineI8:
+ printf("%ld", readData<__int64>()); break;
+
+ case InlineMethod:
+ case InlineField:
+ case InlineType:
+ case InlineTok:
+ case InlineSig:
+ case InlineString:
+ {
+ LONG l = readData<LONG>();
+ DisassembleToken(tokenArray, l);
+ break;
+ }
+
+ case InlineSwitch:
+ {
+ LONG cases = readData<LONG>();
+ LONG *pArray = new LONG[cases];
+ LONG i=0;
+ for(i=0;i<cases;i++)
+ {
+ pArray[i] = readData<LONG>();
+ }
+ printf("(");
+ for(i=0;i<cases;i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("IL_%04x",pArray[i] + position);
+ }
+ printf(")");
+ delete [] pArray;
+ break;
+ }
+ case ShortInlineI:
+ printf("%d", readData<BYTE>()); break;
+ case ShortInlineR:
+ printf("%f", readData<float>()); break;
+ default: printf("Error, unexpected opcode type\n"); break;
+ }
+
+ printf("\n");
+ }
+}
+
+
+
+/******************************************************************************/
+// CQuickBytes utilities
+static char* asString(CQuickBytes *out) {
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + 1);
+ char* cur = &((char*) out->Ptr())[oldSize];
+ *cur = 0;
+ out->ReSize(oldSize); // Don't count the null character
+ return((char*) out->Ptr());
+}
+
+static void appendStr(CQuickBytes *out, const char* str, unsigned len=-1) {
+ if(len == (unsigned)(-1)) len = (unsigned)strlen(str);
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + len);
+ char* cur = &((char*) out->Ptr())[oldSize];
+ memcpy(cur, str, len);
+ // Note no trailing null!
+}
+
+static void appendChar(CQuickBytes *out, char chr) {
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + 1);
+ ((char*) out->Ptr())[oldSize] = chr;
+ // Note no trailing null!
+}
+
+static void insertStr(CQuickBytes *out, const char* str) {
+ unsigned len = (unsigned)strlen(str);
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + len);
+ char* cur = &((char*) out->Ptr())[len];
+ memmove(cur,out->Ptr(),oldSize);
+ memcpy(out->Ptr(), str, len);
+ // Note no trailing null!
+}
+
+static void appendStrNum(CQuickBytes *out, int num) {
+ char buff[16];
+ sprintf_s(buff, COUNTOF(buff), "%d", num);
+ appendStr(out, buff);
+}
+
+
+//PrettyPrinting type names
+PCCOR_SIGNATURE PrettyPrintType(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI, // ptr to IMDInternal class with ComSig
+ DWORD formatFlags /*= formatILDasm*/)
+{
+ mdToken tk;
+ const char* str;
+ int typ;
+ CQuickBytes tmp;
+ CQuickBytes Appendix;
+ BOOL Reiterate;
+ int n;
+
+ do {
+ Reiterate = FALSE;
+ switch(typ = *typePtr++) {
+ case ELEMENT_TYPE_VOID :
+ str = "void"; goto APPEND;
+ case ELEMENT_TYPE_BOOLEAN :
+ str = "bool"; goto APPEND;
+ case ELEMENT_TYPE_CHAR :
+ str = "char"; goto APPEND;
+ case ELEMENT_TYPE_I1 :
+ str = "int8"; goto APPEND;
+ case ELEMENT_TYPE_U1 :
+ str = "uint8"; goto APPEND;
+ case ELEMENT_TYPE_I2 :
+ str = "int16"; goto APPEND;
+ case ELEMENT_TYPE_U2 :
+ str = "uint16"; goto APPEND;
+ case ELEMENT_TYPE_I4 :
+ str = "int32"; goto APPEND;
+ case ELEMENT_TYPE_U4 :
+ str = "uint32"; goto APPEND;
+ case ELEMENT_TYPE_I8 :
+ str = "int64"; goto APPEND;
+ case ELEMENT_TYPE_U8 :
+ str = "uint64"; goto APPEND;
+ case ELEMENT_TYPE_R4 :
+ str = "float32"; goto APPEND;
+ case ELEMENT_TYPE_R8 :
+ str = "float64"; goto APPEND;
+ case ELEMENT_TYPE_U :
+ str = "native uint"; goto APPEND;
+ case ELEMENT_TYPE_I :
+ str = "native int"; goto APPEND;
+ case ELEMENT_TYPE_OBJECT :
+ str = "object"; goto APPEND;
+ case ELEMENT_TYPE_STRING :
+ str = "string"; goto APPEND;
+ case ELEMENT_TYPE_TYPEDBYREF :
+ str = "typedref"; goto APPEND;
+ APPEND:
+ appendStr(out, (char*)str);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE :
+ if ((formatFlags & FormatKwInNames) != 0)
+ str = "valuetype ";
+ else str = "";
+ goto DO_CLASS;
+ case ELEMENT_TYPE_CLASS :
+ if ((formatFlags & FormatKwInNames) != 0)
+ str = "class ";
+ else str = "";
+ goto DO_CLASS;
+
+ DO_CLASS:
+ appendStr(out, (char*)str);
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ if(IsNilToken(tk))
+ {
+ appendStr(out, "[ERROR! NIL TOKEN]");
+ }
+ else PrettyPrintClass(out, tk, pIMDI, formatFlags);
+ break;
+
+ case ELEMENT_TYPE_SZARRAY :
+ insertStr(&Appendix,"[]");
+ Reiterate = TRUE;
+ break;
+
+ case ELEMENT_TYPE_ARRAY :
+ {
+ typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags);
+ unsigned rank = CorSigUncompressData(typePtr);
+ // <TODO> what is the syntax for the rank 0 case? </TODO>
+ if (rank == 0) {
+ appendStr(out, "[BAD: RANK == 0!]");
+ }
+ else {
+ _ASSERTE(rank != 0);
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:22009) // "Suppress PREfast warnings about integer overflow"
+// PREFAST warns about using _alloca in a loop. However when we're in this switch case we do NOT
+// set Reiterate to true, so we only execute through the loop once!
+#pragma warning(disable:6263) // "Suppress PREfast warnings about stack overflow due to _alloca in a loop."
+#endif
+ int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
+ int* sizes = &lowerBounds[rank];
+ memset(lowerBounds, 0, sizeof(int)*2*rank);
+
+ unsigned numSizes = CorSigUncompressData(typePtr);
+ _ASSERTE(numSizes <= rank);
+ unsigned i;
+ for(i =0; i < numSizes; i++)
+ sizes[i] = CorSigUncompressData(typePtr);
+
+ unsigned numLowBounds = CorSigUncompressData(typePtr);
+ _ASSERTE(numLowBounds <= rank);
+ for(i = 0; i < numLowBounds; i++)
+ typePtr+=CorSigUncompressSignedInt(typePtr,&lowerBounds[i]);
+
+ appendChar(out, '[');
+ if (rank == 1 && numSizes == 0 && numLowBounds == 0)
+ appendStr(out, "...");
+ else {
+ for(i = 0; i < rank; i++)
+ {
+ //if (sizes[i] != 0 || lowerBounds[i] != 0)
+ {
+ if (lowerBounds[i] == 0 && i < numSizes)
+ appendStrNum(out, sizes[i]);
+ else
+ {
+ if(i < numLowBounds)
+ {
+ appendStrNum(out, lowerBounds[i]);
+ appendStr(out, "...");
+ if (/*sizes[i] != 0 && */i < numSizes)
+ appendStrNum(out, lowerBounds[i] + sizes[i] - 1);
+ }
+ }
+ }
+ if (i < rank-1)
+ appendChar(out, ',');
+ }
+ }
+ appendChar(out, ']');
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ }
+ } break;
+
+ case ELEMENT_TYPE_VAR :
+ appendChar(out, '!');
+ n = CorSigUncompressData(typePtr);
+ appendStrNum(out, n);
+ break;
+
+ case ELEMENT_TYPE_MVAR :
+ appendChar(out, '!');
+ appendChar(out, '!');
+ n = CorSigUncompressData(typePtr);
+ appendStrNum(out, n);
+ break;
+
+ case ELEMENT_TYPE_FNPTR :
+ appendStr(out, "method ");
+ appendStr(out, "METHOD"); // was: typePtr = PrettyPrintSignature(typePtr, 0x7FFF, "*", out, pIMDI, NULL);
+ break;
+
+ case ELEMENT_TYPE_GENERICINST :
+ {
+ typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags);
+ if ((formatFlags & FormatSignature) == 0)
+ break;
+
+ if ((formatFlags & FormatAngleBrackets) != 0)
+ appendStr(out, "<");
+ else
+ appendStr(out,"[");
+ unsigned numArgs = CorSigUncompressData(typePtr);
+ bool needComma = false;
+ while(numArgs--)
+ {
+ if (needComma)
+ appendChar(out, ',');
+ typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags);
+ needComma = true;
+ }
+ if ((formatFlags & FormatAngleBrackets) != 0)
+ appendStr(out, ">");
+ else
+ appendStr(out,"]");
+ break;
+ }
+
+ case ELEMENT_TYPE_PINNED :
+ str = " pinned"; goto MODIFIER;
+ case ELEMENT_TYPE_PTR :
+ str = "*"; goto MODIFIER;
+ case ELEMENT_TYPE_BYREF :
+ str = "&"; goto MODIFIER;
+ MODIFIER:
+ insertStr(&Appendix, str);
+ Reiterate = TRUE;
+ break;
+
+ default:
+ case ELEMENT_TYPE_SENTINEL :
+ case ELEMENT_TYPE_END :
+ //_ASSERTE(!"Unknown Type");
+ if(typ)
+ {
+ char sz[64];
+ sprintf_s(sz,COUNTOF(sz),"/* UNKNOWN TYPE (0x%X)*/",typ);
+ appendStr(out, sz);
+ }
+ break;
+ } // end switch
+ } while(Reiterate);
+ if (Appendix.Size() > 0)
+ appendStr(out,asString(&Appendix));
+
+ return(typePtr);
+}
+
+// Protection against null names, used by ILDASM/SOS
+const char *const szStdNamePrefix[] = {"MO","TR","TD","","FD","","MD","","PA","II","MR","","CA","","PE","","","SG","","","EV",
+"","","PR","","","MOR","TS","","","","","AS","","","AR","","","FL","ET","MAR"};
+
+#define MAKE_NAME_IF_NONE(psz, tk) { if(!(psz && *psz)) { char* sz = (char*)_alloca(16); \
+sprintf_s(sz,16,"$%s$%X",szStdNamePrefix[tk>>24],tk&0x00FFFFFF); psz = sz; } }
+
+const char* PrettyPrintClass(
+ CQuickBytes *out, // where to put the pretty printed string
+ mdToken tk, // The class token to look up
+ IMDInternalImport *pIMDI, // ptr to IMDInternalImport class with ComSig
+ DWORD formatFlags /*= formatILDasm*/)
+{
+ if(tk == mdTokenNil) // Zero resolution scope for "somewhere here" TypeRefs
+ {
+ appendStr(out,"[*]");
+ return(asString(out));
+ }
+ if (!pIMDI->IsValidToken(tk))
+ {
+ char str[1024];
+ sprintf_s(str,COUNTOF(str)," [ERROR: INVALID TOKEN 0x%8.8X] ",tk);
+ appendStr(out, str);
+ return(asString(out));
+ }
+ switch (TypeFromToken(tk))
+ {
+ case mdtTypeRef:
+ case mdtTypeDef:
+ {
+ const char *nameSpace = 0;
+ const char *name = 0;
+ mdToken tkEncloser = mdTokenNil;
+
+ if (TypeFromToken(tk) == mdtTypeRef)
+ {
+ if (((formatFlags & FormatAssembly) && FAILED(pIMDI->GetResolutionScopeOfTypeRef(tk, &tkEncloser))) ||
+ FAILED(pIMDI->GetNameOfTypeRef(tk, &nameSpace, &name)))
+ {
+ char str[1024];
+ sprintf_s(str, COUNTOF(str), " [ERROR: Invalid TypeRef record 0x%8.8X] ", tk);
+ appendStr(out, str);
+ return asString(out);
+ }
+ }
+ else
+ {
+ if (((formatFlags & FormatNamespace) == 0) || FAILED(pIMDI->GetNestedClassProps(tk,&tkEncloser)))
+ {
+ tkEncloser = mdTypeDefNil;
+ }
+ if (FAILED(pIMDI->GetNameOfTypeDef(tk, &name, &nameSpace)))
+ {
+ char str[1024];
+ sprintf_s(str, COUNTOF(str), " [ERROR: Invalid TypeDef record 0x%8.8X] ", tk);
+ appendStr(out, str);
+ return asString(out);
+ }
+ }
+ MAKE_NAME_IF_NONE(name,tk);
+ if((tkEncloser == mdTokenNil) || RidFromToken(tkEncloser))
+ {
+ if (TypeFromToken(tkEncloser) == mdtTypeRef || TypeFromToken(tkEncloser) == mdtTypeDef)
+ {
+ PrettyPrintClass(out,tkEncloser,pIMDI, formatFlags);
+ if (formatFlags & FormatSlashSep)
+ appendChar(out, '/');
+ else
+ appendChar(out, '+');
+ //nameSpace = ""; //don't print namespaces for nested classes!
+ }
+ else if (formatFlags & FormatAssembly)
+ {
+ PrettyPrintClass(out,tkEncloser,pIMDI, formatFlags);
+ }
+ }
+ if(TypeFromToken(tk)==mdtTypeDef)
+ {
+ unsigned L = (unsigned)strlen(name)+1;
+ char* szFN = NULL;
+ if(((formatFlags & FormatNamespace) != 0) && nameSpace && *nameSpace)
+ {
+ const char* sz = nameSpace;
+ L+= (unsigned)strlen(sz)+1;
+ szFN = new char[L];
+ sprintf_s(szFN,L,"%s.",sz);
+ }
+ else
+ {
+ szFN = new char[L];
+ *szFN = 0;
+ }
+ strcat_s(szFN,L, name);
+ appendStr(out, szFN);
+ if (szFN) delete[] (szFN);
+ }
+ else
+ {
+ if (((formatFlags & FormatNamespace) != 0) && nameSpace && *nameSpace) {
+ appendStr(out, nameSpace);
+ appendChar(out, '.');
+ }
+
+ appendStr(out, name);
+ }
+ }
+ break;
+
+ case mdtAssemblyRef:
+ {
+ LPCSTR szName = NULL;
+ pIMDI->GetAssemblyRefProps(tk,NULL,NULL,&szName,NULL,NULL,NULL,NULL);
+ if(szName && *szName)
+ {
+ appendChar(out, '[');
+ appendStr(out, szName);
+ appendChar(out, ']');
+ }
+ }
+ break;
+ case mdtAssembly:
+ {
+ LPCSTR szName = NULL;
+ pIMDI->GetAssemblyProps(tk,NULL,NULL,NULL,&szName,NULL,NULL);
+ if(szName && *szName)
+ {
+ appendChar(out, '[');
+ appendStr(out, szName);
+ appendChar(out, ']');
+ }
+ }
+ break;
+ case mdtModuleRef:
+ {
+ LPCSTR szName = NULL;
+ pIMDI->GetModuleRefProps(tk,&szName);
+ if(szName && *szName)
+ {
+ appendChar(out, '[');
+ appendStr(out, ".module ");
+ appendStr(out, szName);
+ appendChar(out, ']');
+ }
+ }
+ break;
+
+ case mdtTypeSpec:
+ {
+ ULONG cSig;
+ PCCOR_SIGNATURE sig;
+ if (FAILED(pIMDI->GetSigFromToken(tk, &cSig, &sig)))
+ {
+ char str[128];
+ sprintf_s(str, COUNTOF(str), " [ERROR: Invalid token 0x%8.8X] ", tk);
+ appendStr(out, str);
+ }
+ else
+ {
+ PrettyPrintType(sig, out, pIMDI, formatFlags);
+ }
+ }
+ break;
+
+ case mdtModule:
+ break;
+
+ default:
+ {
+ char str[128];
+ sprintf_s(str,COUNTOF(str)," [ERROR: INVALID TOKEN TYPE 0x%8.8X] ",tk);
+ appendStr(out, str);
+ }
+ }
+ return(asString(out));
+}
+
+// This function takes a module and a token and prints the representation in the mdName buffer.
+void PrettyPrintClassFromToken(
+ TADDR moduleAddr, // the module containing the token
+ mdToken tok, // the class token to look up
+ __out_ecount(cbName) WCHAR* mdName, // where to put the pretty printed string
+ size_t cbName, // the capacity of the buffer
+ DWORD formatFlags /*= FormatCSharp*/)
+{
+ // set the default value
+ swprintf_s(mdName, cbName, W("token_0x%8.8X"), tok);
+
+ DacpModuleData dmd;
+ if (dmd.Request(g_sos, TO_CDADDR(moduleAddr)) != S_OK)
+ return;
+
+ ToRelease<IMetaDataImport> pImport(MDImportForModule(&dmd));
+ ToRelease<IMDInternalImport> pIMDI = NULL;
+
+ if ((pImport == NULL) || FAILED(GetMDInternalFromImport(pImport, &pIMDI)))
+ return;
+
+ CQuickBytes qb;
+ PrettyPrintClass(&qb, tok, pIMDI, formatFlags);
+ MultiByteToWideChar (CP_ACP, 0, asString(&qb), -1, mdName, (int) cbName);
+}
diff --git a/src/ToolBox/SOS/Strike/sos.cpp b/src/ToolBox/SOS/Strike/sos.cpp
new file mode 100644
index 0000000000..351199c058
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.cpp
@@ -0,0 +1,888 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "strike.h"
+#include "util.h"
+
+#include "sos.h"
+
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+#define _ASSERTE(a) {;}
+
+#include "gcdesc.h"
+
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+namespace sos
+{
+ template <class T>
+ static bool MemOverlap(T beg1, T end1, // first range
+ T beg2, T end2) // second range
+ {
+ if (beg2 >= beg1 && beg2 <= end1) // second range starts within first range
+ return true;
+ else if (end2 >= beg1 && end2 <= end1) // second range ends within first range
+ return true;
+ else if (beg1 >= beg2 && beg1 <= end2) // first range starts within second range
+ return true;
+ else if (end1 >= beg2 && end1 <= end2) // first range ends within second range
+ return true;
+ else
+ return false;
+ }
+
+
+ Object::Object(TADDR addr)
+ : mAddress(addr), mMT(0), mSize(~0), mPointers(false), mMTData(0), mTypeName(0)
+ {
+ if ((mAddress & ~ALIGNCONST) != mAddress)
+ sos::Throw<Exception>("Object %p is misaligned.", mAddress);
+ }
+
+ Object::Object(TADDR addr, TADDR mt)
+ : mAddress(addr), mMT(mt & ~3), mSize(~0), mPointers(false), mMTData(0), mTypeName(0)
+ {
+ if ((mAddress & ~ALIGNCONST) != mAddress)
+ sos::Throw<Exception>("Object %p is misaligned.", mAddress);
+ }
+
+
+ Object::Object(const Object &rhs)
+ : mAddress(rhs.mAddress), mMT(rhs.mMT), mSize(rhs.mSize), mPointers(rhs.mPointers), mMTData(rhs.mMTData), mTypeName(rhs.mTypeName)
+ {
+ rhs.mMTData = 0;
+ rhs.mTypeName = 0;
+ }
+
+ const Object &Object::operator=(TADDR addr)
+ {
+ if (mMTData)
+ delete mMTData;
+
+ if (mTypeName)
+ delete mTypeName;
+
+ mAddress = addr;
+ mMT = 0;
+ mSize = ~0;
+ mMTData = 0;
+ mTypeName = 0;
+
+ return *this;
+ }
+
+ bool Object::TryGetHeader(ULONG &outHeader) const
+ {
+ struct ObjectHeader
+ {
+ #ifdef _WIN64
+ ULONG _alignpad;
+ #endif
+ ULONG SyncBlockValue; // the Index and the Bits
+ };
+
+ ObjectHeader header;
+
+ if (SUCCEEDED(rvCache->Read(TO_TADDR(GetAddress() - sizeof(ObjectHeader)), &header, sizeof(ObjectHeader), NULL)))
+ {
+ outHeader = header.SyncBlockValue;
+ return true;
+ }
+
+ return false;
+ }
+
+
+ ULONG Object::GetHeader() const
+ {
+ ULONG toReturn = 0;
+ if (!TryGetHeader(toReturn))
+ sos::Throw<DataRead>("Failed to get header for object %p.", GetAddress());
+
+ return toReturn;
+ }
+
+ TADDR Object::GetMT() const
+ {
+ if (mMT == NULL)
+ {
+ TADDR temp;
+ if (FAILED(MOVE(temp, mAddress)))
+ sos::Throw<DataRead>("Object %s has an invalid method table.", DMLListNearObj(mAddress));
+
+ if (temp == NULL)
+ sos::Throw<HeapCorruption>("Object %s has an invalid method table.", DMLListNearObj(mAddress));
+
+ mMT = temp & ~3;
+ }
+
+ return mMT;
+ }
+
+ TADDR Object::GetComponentMT() const
+ {
+ if (mMT != NULL && mMT != sos::MethodTable::GetArrayMT())
+ return NULL;
+
+ DacpObjectData objData;
+ if (FAILED(objData.Request(g_sos, TO_CDADDR(mAddress))))
+ sos::Throw<DataRead>("Failed to request object data for %s.", DMLListNearObj(mAddress));
+
+ if (mMT == NULL)
+ mMT = TO_TADDR(objData.MethodTable) & ~3;
+
+ return TO_TADDR(objData.ElementTypeHandle);
+ }
+
+ const WCHAR *Object::GetTypeName() const
+ {
+ if (mTypeName == NULL)
+ mTypeName = CreateMethodTableName(GetMT(), GetComponentMT());
+
+
+ if (mTypeName == NULL)
+ return W("<error>");
+
+ return mTypeName;
+ }
+
+ void Object::FillMTData() const
+ {
+ if (mMTData == NULL)
+ {
+ mMTData = new DacpMethodTableData;
+ if (FAILED(mMTData->Request(g_sos, GetMT())))
+ {
+ delete mMTData;
+ mMTData = NULL;
+ sos::Throw<DataRead>("Could not request method table data for object %p (MethodTable: %p).", mAddress, mMT);
+ }
+ }
+ }
+
+
+ void Object::CalculateSizeAndPointers() const
+ {
+ TADDR mt = GetMT();
+ MethodTableInfo* info = g_special_mtCache.Lookup((DWORD_PTR)mt);
+ if (!info->IsInitialized())
+ {
+ // this is the first time we see this method table, so we need to get the information
+ // from the target
+ FillMTData();
+
+ info->BaseSize = mMTData->BaseSize;
+ info->ComponentSize = mMTData->ComponentSize;
+ info->bContainsPointers = mMTData->bContainsPointers;
+ }
+
+ if (mSize == (size_t)~0)
+ {
+ mSize = info->BaseSize;
+ if (info->ComponentSize)
+ {
+ // this is an array, so the size has to include the size of the components. We read the number
+ // of components from the target and multiply by the component size to get the size.
+ mSize += info->ComponentSize * GetNumComponents(GetAddress());
+ }
+
+ // On x64 we do an optimization to save 4 bytes in almost every string we create.
+ #ifdef _WIN64
+ // Pad to min object size if necessary
+ if (mSize < min_obj_size)
+ mSize = min_obj_size;
+ #endif // _WIN64
+ }
+
+ mPointers = info->bContainsPointers != FALSE;
+ }
+
+ size_t Object::GetSize() const
+ {
+ if (mSize == (size_t)~0) // poison value
+ {
+ CalculateSizeAndPointers();
+ }
+
+ SOS_Assert(mSize != (size_t)~0);
+ return mSize;
+ }
+
+
+ bool Object::HasPointers() const
+ {
+ if (mSize == (size_t)~0)
+ CalculateSizeAndPointers();
+
+ SOS_Assert(mSize != (size_t)~0);
+ return mPointers;
+ }
+
+
+ bool Object::VerifyMemberFields(TADDR pMT, TADDR obj)
+ {
+ WORD numInstanceFields = 0;
+ return VerifyMemberFields(pMT, obj, numInstanceFields);
+ }
+
+
+ bool Object::VerifyMemberFields(TADDR pMT, TADDR obj, WORD &numInstanceFields)
+ {
+ DacpMethodTableData vMethTable;
+ if (FAILED(vMethTable.Request(g_sos, pMT)))
+ return false;
+
+ // Recursively verify the parent (this updates numInstanceFields)
+ if (vMethTable.ParentMethodTable)
+ {
+ if (!VerifyMemberFields(TO_TADDR(vMethTable.ParentMethodTable), obj, numInstanceFields))
+ return false;
+ }
+
+ DacpMethodTableFieldData vMethodTableFields;
+
+ // Verify all fields on the object.
+ CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
+ DacpFieldDescData vFieldDesc;
+
+ while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
+ {
+ CheckInterrupt();
+
+ if (FAILED(vFieldDesc.Request(g_sos, dwAddr)))
+ return false;
+
+ if (vFieldDesc.Type >= ELEMENT_TYPE_MAX)
+ return false;
+
+ dwAddr = vFieldDesc.NextField;
+
+ if (!vFieldDesc.bIsStatic)
+ {
+ numInstanceFields++;
+ TADDR dwTmp = TO_TADDR(obj + vFieldDesc.dwOffset + sizeof(BaseObject));
+ if (vFieldDesc.Type == ELEMENT_TYPE_CLASS)
+ {
+ // Is it a valid object?
+ if (FAILED(MOVE(dwTmp, dwTmp)))
+ return false;
+
+ if (dwTmp != NULL)
+ {
+ DacpObjectData objData;
+ if (FAILED(objData.Request(g_sos, TO_CDADDR(dwTmp))))
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool MethodTable::IsZombie(TADDR addr)
+ {
+ // Zombie objects are objects that reside in an unloaded AppDomain.
+ MethodTable mt = addr;
+ return _wcscmp(mt.GetName(), W("<Unloaded Type>")) == 0;
+ }
+
+ void MethodTable::Clear()
+ {
+ if (mName)
+ {
+ delete [] mName;
+ mName = NULL;
+ }
+ }
+
+ const WCHAR *MethodTable::GetName() const
+ {
+ if (mName == NULL)
+ mName = CreateMethodTableName(mMT);
+
+ if (mName == NULL)
+ return W("<error>");
+
+ return mName;
+ }
+
+ bool Object::IsValid(TADDR address, bool verifyFields)
+ {
+ DacpObjectData objectData;
+ if (FAILED(objectData.Request(g_sos, TO_CDADDR(address))))
+ return false;
+
+ if (verifyFields &&
+ objectData.MethodTable != g_special_usefulGlobals.FreeMethodTable &&
+ !MethodTable::IsZombie(TO_TADDR(objectData.MethodTable)))
+ {
+ return VerifyMemberFields(TO_TADDR(objectData.MethodTable), address);
+ }
+
+ return true;
+ }
+
+ bool Object::GetThinLock(ThinLockInfo &out) const
+ {
+ ULONG header = GetHeader();
+ if (header & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_SPIN_LOCK))
+ {
+ return false;
+ }
+
+ out.ThreadId = header & SBLK_MASK_LOCK_THREADID;
+ out.Recursion = (header & SBLK_MASK_LOCK_RECLEVEL) >> SBLK_RECLEVEL_SHIFT;
+
+ CLRDATA_ADDRESS threadPtr = NULL;
+ if (g_sos->GetThreadFromThinlockID(out.ThreadId, &threadPtr) != S_OK)
+ {
+ out.ThreadPtr = NULL;
+ }
+ else
+ {
+ out.ThreadPtr = TO_TADDR(threadPtr);
+ }
+
+ return out.ThreadId != 0 && out.ThreadPtr != NULL;
+ }
+
+ bool Object::GetStringData(__out_ecount(size) WCHAR *buffer, size_t size) const
+ {
+ SOS_Assert(IsString());
+ SOS_Assert(buffer);
+ SOS_Assert(size > 0);
+
+ return SUCCEEDED(g_sos->GetObjectStringData(mAddress, (ULONG32)size, buffer, NULL));
+ }
+
+ size_t Object::GetStringLength() const
+ {
+ SOS_Assert(IsString());
+
+ strobjInfo stInfo;
+ if (FAILED(MOVE(stInfo, mAddress)))
+ sos::Throw<DataRead>("Failed to read object data at %p.", mAddress);
+
+ // We get the method table for free here, if we don't have it already.
+ SOS_Assert((mMT == NULL) || (mMT == TO_TADDR(stInfo.methodTable)));
+ if (mMT == NULL)
+ mMT = TO_TADDR(stInfo.methodTable);
+
+ return (size_t)stInfo.m_StringLength;
+ }
+
+
+ RefIterator::RefIterator(TADDR obj, LinearReadCache *cache)
+ : mCache(cache), mGCDesc(0), mArrayOfVC(false), mDone(false), mBuffer(0), mCurrSeries(0),
+ i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
+ {
+ Init();
+ }
+
+ RefIterator::RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache)
+ : mCache(cache), mGCDesc(desc), mArrayOfVC(arrayOfVC), mDone(false), mBuffer(0), mCurrSeries(0),
+ i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
+ {
+ Init();
+ }
+
+ RefIterator::~RefIterator()
+ {
+ if (mBuffer)
+ delete [] mBuffer;
+ }
+
+ const RefIterator &RefIterator::operator++()
+ {
+ if (mDone)
+ Throw<Exception>("Attempt to move past the end of the iterator.");
+
+ if (!mArrayOfVC)
+ {
+ mCurr += sizeof(TADDR);
+ if (mCurr >= mStop)
+ {
+ mCurrSeries--;
+ if (mCurrSeries < mGCDesc->GetLowestSeries())
+ {
+ mDone = true;
+ }
+ else
+ {
+ mCurr = mObject + mCurrSeries->GetSeriesOffset();
+ mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
+ }
+ }
+ }
+ else
+ {
+ mCurr += sizeof(TADDR);
+ if (mCurr >= mStop)
+ {
+ int i_last = i;
+ i--;
+
+ if (i == mCount)
+ i = 0;
+
+ mCurr += mCurrSeries->val_serie[i_last].skip;
+ mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
+ }
+
+ if (mCurr >= mObject + mObjSize - plug_skew)
+ mDone = true;
+ }
+
+ return *this;
+ }
+
+ TADDR RefIterator::operator*() const
+ {
+ return ReadPointer(mCurr);
+ }
+
+ TADDR RefIterator::GetOffset() const
+ {
+ return mCurr - mObject;
+ }
+
+ void RefIterator::Init()
+ {
+ TADDR mt = ReadPointer(mObject);
+ BOOL bContainsPointers = FALSE;
+
+ if (!GetSizeEfficient(mObject, mt, FALSE, mObjSize, bContainsPointers))
+ Throw<DataRead>("Failed to get size of object.");
+
+ if (!bContainsPointers)
+ {
+ mDone = true;
+ return;
+ }
+
+ if (!mGCDesc)
+ {
+ int entries = 0;
+
+ if (FAILED(MOVE(entries, mt-sizeof(TADDR))))
+ Throw<DataRead>("Failed to request number of entries.");
+
+ // array of vc?
+ if (entries < 0)
+ {
+ entries = -entries;
+ mArrayOfVC = true;
+ }
+ else
+ {
+ mArrayOfVC = false;
+ }
+
+ size_t slots = 1 + entries * sizeof(CGCDescSeries)/sizeof(TADDR);
+
+ ArrayHolder<TADDR> buffer = new TADDR[slots];
+
+ ULONG fetched = 0;
+ CLRDATA_ADDRESS address = TO_CDADDR(mt - slots*sizeof(TADDR));
+ if (FAILED(g_ExtData->ReadVirtual(address, buffer, (ULONG)(slots*sizeof(TADDR)), &fetched)))
+ Throw<DataRead>("Failed to request GCDesc.");
+
+ mBuffer = buffer.Detach();
+ mGCDesc = (CGCDesc*)(mBuffer + slots);
+ }
+
+ mCurrSeries = mGCDesc->GetHighestSeries();
+
+ if (!mArrayOfVC)
+ {
+ mCurr = mObject + mCurrSeries->GetSeriesOffset();
+ mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
+ }
+ else
+ {
+ i = 0;
+ mCurr = mObject + mCurrSeries->startoffset;
+ mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
+ mCount = (int)mGCDesc->GetNumSeries();
+ }
+
+ if (mCurr == mStop)
+ operator++();
+ else if (mCurr >= mObject + mObjSize - plug_skew)
+ mDone = true;
+ }
+
+
+ const TADDR GCHeap::HeapStart = 0;
+ const TADDR GCHeap::HeapEnd = ~0;
+
+ ObjectIterator::ObjectIterator(const DacpGcHeapDetails *heap, int numHeaps, TADDR start, TADDR stop)
+ : bLarge(false), mCurrObj(0), mLastObj(0), mStart(start), mEnd(stop), mSegmentEnd(0), mHeaps(heap),
+ mNumHeaps(numHeaps), mCurrHeap(0)
+ {
+ mAllocInfo.Init();
+ SOS_Assert(numHeaps > 0);
+
+ TADDR segStart = TO_TADDR(mHeaps[0].generation_table[GetMaxGeneration()].start_segment);
+ if (FAILED(mSegment.Request(g_sos, segStart, mHeaps[0])))
+ sos::Throw<DataRead>("Could not request segment data at %p.", segStart);
+
+ mCurrObj = mStart < TO_TADDR(mSegment.mem) ? TO_TADDR(mSegment.mem) : mStart;
+ mSegmentEnd = (segStart == TO_TADDR(mHeaps[0].ephemeral_heap_segment)) ?
+ TO_TADDR(mHeaps[0].alloc_allocated) :
+ TO_TADDR(mSegment.allocated);
+
+ CheckSegmentRange();
+ }
+
+ bool ObjectIterator::NextSegment()
+ {
+ if (mCurrHeap >= mNumHeaps)
+ return false;
+
+ TADDR next = TO_TADDR(mSegment.next);
+ if (next == NULL)
+ {
+ if (bLarge)
+ {
+ mCurrHeap++;
+ if (mCurrHeap == mNumHeaps)
+ return false;
+
+ bLarge = false;
+ next = TO_TADDR(mHeaps[mCurrHeap].generation_table[GetMaxGeneration()].start_segment);
+ }
+ else
+ {
+ bLarge = true;
+ next = TO_TADDR(mHeaps[mCurrHeap].generation_table[GetMaxGeneration()+1].start_segment);
+ }
+ }
+
+ SOS_Assert(next != NULL);
+ if (FAILED(mSegment.Request(g_sos, next, mHeaps[mCurrHeap])))
+ sos::Throw<DataRead>("Failed to request segment data at %p.", next);
+
+ mLastObj = 0;
+ mCurrObj = mStart < TO_TADDR(mSegment.mem) ? TO_TADDR(mSegment.mem) : mStart;
+ mSegmentEnd = (next == TO_TADDR(mHeaps[mCurrHeap].ephemeral_heap_segment)) ?
+ TO_TADDR(mHeaps[mCurrHeap].alloc_allocated) :
+ TO_TADDR(mSegment.allocated);
+ return CheckSegmentRange();
+ }
+
+ bool ObjectIterator::CheckSegmentRange()
+ {
+ CheckInterrupt();
+
+ while (!MemOverlap(mStart, mEnd, TO_TADDR(mSegment.mem), mSegmentEnd))
+ if (!NextSegment())
+ return false;
+
+ // At this point we know that the current segment contains objects in
+ // the correct range. However, there's no telling if the user gave us
+ // a starting address that corresponds to an object. If mStart is a
+ // valid object, then we'll just start there. If it's not we'll need
+ // to walk the segment from the beginning to find the first aligned
+ // object on or after mStart.
+ if (mCurrObj == mStart && !Object::IsValid(mStart))
+ {
+ // It's possible mCurrObj will equal mStart after this. That's fine.
+ // It means that the starting object is corrupt (and we'll figure
+ // that when the user calls GetNext), or IsValid was wrong.
+ mLastObj = 0;
+ mCurrObj = TO_TADDR(mSegment.mem);
+ while (mCurrObj < mStart)
+ MoveToNextObject();
+ }
+
+ return true;
+ }
+
+
+
+ const Object &ObjectIterator::operator*() const
+ {
+ AssertSanity();
+ return mCurrObj;
+ }
+
+
+ const Object *ObjectIterator::operator->() const
+ {
+ AssertSanity();
+ return &mCurrObj;
+ }
+
+ //Object ObjectIterator::GetNext()
+ const ObjectIterator &ObjectIterator::operator++()
+ {
+ CheckInterrupt();
+
+ // Assert we aren't done walking the heap.
+ SOS_Assert(*this);
+ AssertSanity();
+
+ MoveToNextObject();
+ return *this;
+ }
+
+ void ObjectIterator::MoveToNextObjectCarefully()
+ {
+ CheckInterrupt();
+
+ SOS_Assert(*this);
+ AssertSanity();
+
+ // Move to NextObject won't generally throw unless it fails to request the
+ // MethodTable of the object. At which point we won't know how large the
+ // current object is, nor how to move past it. In this case we'll simply
+ // move to the next segment if possible to continue iterating from there.
+ try
+ {
+ MoveToNextObject();
+ }
+ catch(const sos::Exception &)
+ {
+ NextSegment();
+ }
+ }
+
+ void ObjectIterator::AssertSanity() const
+ {
+ // Assert that we are in a sane state. Function which call this assume two things:
+ // 1. That the current object is within the segment bounds.
+ // 2. That the current object is within the requested memory range.
+ SOS_Assert(mCurrObj >= TO_TADDR(mSegment.mem));
+ SOS_Assert(mCurrObj <= TO_TADDR(mSegmentEnd - Align(min_obj_size)));
+
+ SOS_Assert(mCurrObj >= mStart);
+ SOS_Assert(mCurrObj <= mEnd);
+ }
+
+ void ObjectIterator::MoveToNextObject()
+ {
+ // Object::GetSize can be unaligned, so we must align it ourselves.
+ size_t size = (bLarge ? AlignLarge(mCurrObj.GetSize()) : Align(mCurrObj.GetSize()));
+
+ mLastObj = mCurrObj;
+ mCurrObj = mCurrObj.GetAddress() + size;
+
+ if (!bLarge)
+ {
+ // Is this the end of an allocation context? We need to know this because there can be
+ // allocated memory at the end of an allocation context that doesn't yet contain any objects.
+ // This happens because we actually allocate a minimum amount of memory (the allocation quantum)
+ // whenever we need to get more memory. Typically, a single allocation request won't fill this
+ // block, so we'll fulfill subsequent requests out of the remainder of the block until it's
+ // depleted.
+ int i;
+ for (i = 0; i < mAllocInfo.num; i ++)
+ {
+ if (mCurrObj == TO_TADDR(mAllocInfo.array[i].alloc_ptr)) // end of objects in this context
+ {
+ // Set mCurrObj to point after the context (alloc_limit is the end of the allocation context).
+ mCurrObj = TO_TADDR(mAllocInfo.array[i].alloc_limit) + Align(min_obj_size);
+ break;
+ }
+ }
+
+ // We also need to look at the gen0 alloc context.
+ if (mCurrObj == TO_TADDR(mHeaps[mCurrHeap].generation_table[0].allocContextPtr))
+ mCurrObj = TO_TADDR(mHeaps[mCurrHeap].generation_table[0].allocContextLimit) + Align(min_obj_size);
+ }
+
+ if (mCurrObj > mEnd || mCurrObj >= mSegmentEnd)
+ NextSegment();
+ }
+
+ SyncBlkIterator::SyncBlkIterator()
+ : mCurr(1), mTotal(0)
+ {
+ // If DacpSyncBlockData::Request fails with the call "1", then it means
+ // there are no SyncBlocks in the process.
+ DacpSyncBlockData syncBlockData;
+ if (SUCCEEDED(syncBlockData.Request(g_sos, 1)))
+ mTotal = syncBlockData.SyncBlockCount;
+
+ mSyncBlk = mCurr;
+ }
+
+ GCHeap::GCHeap()
+ {
+ if (FAILED(mHeapData.Request(g_sos)))
+ sos::Throw<DataRead>("Failed to request GC heap data.");
+
+ if (mHeapData.bServerMode)
+ {
+ mNumHeaps = mHeapData.HeapCount;
+ DWORD dwAllocSize = 0;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), mNumHeaps, dwAllocSize))
+ sos::Throw<Exception>("Failed to get GCHeaps: Integer overflow.");
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (FAILED(g_sos->GetGCHeapList(mNumHeaps, heapAddrs, NULL)))
+ sos::Throw<DataRead>("Failed to get GCHeaps.");
+
+ mHeaps = new DacpGcHeapDetails[mNumHeaps];
+
+ for (int i = 0; i < mNumHeaps; i++)
+ if (FAILED(mHeaps[i].Request(g_sos, heapAddrs[i])))
+ sos::Throw<DataRead>("Failed to get GC heap details at %p.", heapAddrs[i]);
+ }
+ else
+ {
+ mHeaps = new DacpGcHeapDetails[1];
+ mNumHeaps = 1;
+
+ if (FAILED(mHeaps[0].Request(g_sos)))
+ sos::Throw<DataRead>("Failed to request GC details data.");
+ }
+ }
+
+ ObjectIterator GCHeap::WalkHeap(TADDR start, TADDR stop) const
+ {
+ return ObjectIterator(mHeaps, mNumHeaps, start, stop);
+ }
+
+ bool GCHeap::AreGCStructuresValid() const
+ {
+ return mHeapData.bGcStructuresValid != FALSE;
+ }
+
+ // SyncBlk class
+ SyncBlk::SyncBlk()
+ : mIndex(0)
+ {
+ }
+
+ SyncBlk::SyncBlk(int index)
+ : mIndex(index)
+ {
+ Init();
+ }
+
+ const SyncBlk &SyncBlk::operator=(int index)
+ {
+ mIndex = index;
+ Init();
+
+ return *this;
+ }
+
+ void SyncBlk::Init()
+ {
+ if (FAILED(mData.Request(g_sos, mIndex)))
+ sos::Throw<DataRead>("Failed to request SyncBlk at index %d.", mIndex);
+ }
+
+ TADDR SyncBlk::GetAddress() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.SyncBlockPointer);
+ }
+
+ TADDR SyncBlk::GetObject() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.Object);
+ }
+
+ int SyncBlk::GetIndex() const
+ {
+ return mIndex;
+ }
+
+ bool SyncBlk::IsFree() const
+ {
+ SOS_Assert(mIndex);
+ return mData.bFree != FALSE;
+ }
+
+ unsigned int SyncBlk::GetMonitorHeldCount() const
+ {
+ SOS_Assert(mIndex);
+ return mData.MonitorHeld;
+ }
+
+ unsigned int SyncBlk::GetRecursion() const
+ {
+ SOS_Assert(mIndex);
+ return mData.Recursion;
+ }
+
+ DWORD SyncBlk::GetCOMFlags() const
+ {
+ SOS_Assert(mIndex);
+ #ifdef FEATURE_COMINTEROP
+ return mData.COMFlags;
+ #else
+ return 0;
+ #endif
+ }
+
+ unsigned int SyncBlk::GetAdditionalThreadCount() const
+ {
+ SOS_Assert(mIndex);
+ return mData.AdditionalThreadCount;
+ }
+
+ TADDR SyncBlk::GetHoldingThread() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.HoldingThread);
+ }
+
+ TADDR SyncBlk::GetAppDomain() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.appDomainPtr);
+ }
+
+ void BuildTypeWithExtraInfo(TADDR addr, unsigned int size, __inout_ecount(size) WCHAR *buffer)
+ {
+ try
+ {
+ sos::Object obj(addr);
+ TADDR mtAddr = obj.GetMT();
+ bool isArray = sos::MethodTable::IsArrayMT(mtAddr);
+ bool isString = obj.IsString();
+
+ sos::MethodTable mt(isArray ? obj.GetComponentMT() : mtAddr);
+
+ if (isArray)
+ {
+ swprintf_s(buffer, size, W("%s[]"), mt.GetName());
+ }
+ else if (isString)
+ {
+ WCHAR str[32];
+ obj.GetStringData(str, _countof(str));
+
+ _snwprintf_s(buffer, size, _TRUNCATE, W("%s: \"%s\""), mt.GetName(), str);
+ }
+ else
+ {
+ _snwprintf_s(buffer, size, _TRUNCATE, W("%s"), mt.GetName());
+ }
+ }
+ catch (const sos::Exception &e)
+ {
+ int len = MultiByteToWideChar(CP_ACP, 0, e.what(), -1, NULL, 0);
+
+ ArrayHolder<WCHAR> tmp = new WCHAR[len];
+ MultiByteToWideChar(CP_ACP, 0, e.what(), -1, (WCHAR*)tmp, len);
+
+ swprintf_s(buffer, size, W("<invalid object: '%s'>"), (WCHAR*)tmp);
+ }
+ }
+}
diff --git a/src/ToolBox/SOS/Strike/sos.def b/src/ToolBox/SOS/Strike/sos.def
new file mode 100644
index 0000000000..c8d08e7319
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.def
@@ -0,0 +1,231 @@
+; Licensed to the .NET Foundation under one or more agreements.
+; The .NET Foundation licenses this file to you under the MIT license.
+; See the LICENSE file in the project root for more information.
+;
+LIBRARY STRIKE
+EXPORTS
+ AnalyzeOOM
+ analyzeoom=AnalyzeOOM
+ ao=AnalyzeOOM
+ ClrStack
+ clrstack=ClrStack
+ CLRStack=ClrStack
+ DumpArray
+ da=DumpArray
+ dumparray=DumpArray
+ DumpAssembly
+ dumpassembly=DumpAssembly
+ DumpClass
+ dumpclass=DumpClass
+ DumpDomain
+ dumpdomain=DumpDomain
+#ifdef TRACE_GC
+ DumpGCLog
+ dumpgclog=DumpGCLog
+ dlog=DumpGCLog
+#endif
+ DumpGCData
+ dumpgcdata=DumpGCData
+ dgc=DumpGCData
+ DumpGCConfigLog
+ dumpgcconfiglog=DumpGCConfigLog
+ dclog=DumpGCConfigLog
+ DumpHeap
+ dumpheap=DumpHeap
+ DumpIL
+ dumpil=DumpIL
+ DumpLog
+ dumplog=DumpLog
+ Dumplog=DumpLog
+ DumpMD
+ dumpmd=DumpMD
+ DumpModule
+ dumpmodule=DumpModule
+ DumpMT
+ dumpmt=DumpMT
+ DumpObj
+ do=DumpObj
+ dumpobj=DumpObj
+ DumpRuntimeTypes
+ dumpruntimetypes=DumpRuntimeTypes
+ Dumpruntimetypes=DumpRuntimeTypes
+ DumpSig
+ dumpsig=DumpSig
+ DumpSigElem
+ dumpsigelem=DumpSigElem
+ DumpStack
+ dumpstack=DumpStack
+ DumpStackObjects
+ dso=DumpStackObjects
+ dumpstackobjects=DumpStackObjects
+ DumpVC
+ dumpvc=DumpVC
+ EEHeap
+ eeheap=EEHeap
+ EEStack
+ eestack=EEStack
+ EHInfo
+ ehinfo=EHInfo
+ Ehinfo=EHInfo
+ FinalizeQueue
+ finalizequeue=FinalizeQueue
+ fq=FinalizeQueue
+ FindAppDomain
+ findappdomain=FindAppDomain
+ Findappdomain=FindAppDomain
+ FindRoots
+ findroots=FindRoots
+ GCHandles
+ gchandles=GCHandles
+ GCHeapStat
+ gcheapstat=GCHeapStat
+ GcHeapStat=GCHeapStat
+ heapstat=GCHeapStat
+ HeapStat=GCHeapStat
+ GCInfo
+ gcinfo=GCInfo
+ GCRoot
+ gcroot=GCRoot
+ GCWhere
+ gcwhere=GCWhere
+ GcWhere=GCWhere
+ Help
+ help=Help
+ HistClear
+ histclear=HistClear
+ HistInit
+ histinit=HistInit
+ HistObj
+ histobj=HistObj
+ HistObjFind
+ histobjfind=HistObjFind
+ hof=HistObjFind
+ HistRoot
+ histroot=HistRoot
+ HistStats
+ histstats=HistStats
+ IP2MD
+ ip2md=IP2MD
+ ListNearObj
+ listnearobj=ListNearObj
+ lno=ListNearObj
+ Name2EE
+ name2ee=Name2EE
+ ObjSize
+ objsize=ObjSize
+ PrintException
+ pe=PrintException
+ printexception=PrintException
+ Printexception=PrintException
+ SaveModule
+ savemodule=SaveModule
+ SOSFlush
+ sosflush=SOSFlush
+ StopOnException
+ soe=StopOnException
+ stoponexception=StopOnException
+ Stoponexception=StopOnException
+ SyncBlk
+ syncblk=SyncBlk
+ ThreadPool
+ threadpool=ThreadPool
+ tp=ThreadPool
+ Threads
+ t=Threads
+ threads=Threads
+ ThreadState
+ threadstate=ThreadState
+ Token2EE
+ token2ee=Token2EE
+ TraverseHeap
+ traverseheap=TraverseHeap
+ Traverseheap=TraverseHeap
+ u
+ U=u
+ VerifyHeap
+ verifyheap=VerifyHeap
+ Verifyheap=VerifyHeap
+ vh=VerifyHeap
+ VerifyObj
+ verifyobj=VerifyObj
+ vo=VerifyObj
+ VMMap
+ vmmap=VMMap
+ VMStat
+ vmstat=VMStat
+
+#ifdef FEATURE_COMINTEROP
+ COMState
+ comstate=COMState
+ RCWCleanupList
+ rcwcleanuplist=RCWCleanupList
+ Rcwcleanuplist=RCWCleanupList
+ DumpRCW
+ dumprcw=DumpRCW
+ Dumprcw=DumpRCW
+ DumpCCW
+ dumpccw=DumpCCW
+ Dumpccw=DumpCCW
+#endif
+
+#ifdef _DEBUG
+ DumpPermissionSet
+ dps=DumpPermissionSet
+ dumppermissionset=DumpPermissionSet
+ dbgout
+ filthint
+#endif
+
+#ifdef FEATURE_PAL
+ SetClrDebugDll
+ UnloadClrDebugDll
+#else
+ _EFN_GetManagedExcepStack
+ _EFN_GetManagedExcepStackW
+ _EFN_GetManagedObjectFieldInfo
+ _EFN_GetManagedObjectName
+ _EFN_StackTrace
+ bpmd
+ BPMD=bpmd
+ DebugExtensionInitialize
+ DebugExtensionNotify
+ DebugExtensionUninitialize
+ EEVersion
+ eeversion=EEVersion
+ GCHandleLeaks
+ gchandleleaks=GCHandleLeaks
+ Gchandleleaks=GCHandleLeaks
+ GCHandleleaks=GCHandleLeaks
+ HandleCLRN
+ MinidumpMode
+ minidumpmode=MinidumpMode
+ Minidumpmode=MinidumpMode
+ PathTo
+ pathto=PathTo
+ ProcInfo
+ procinfo=ProcInfo
+ VerifyStackTrace
+ WatsonBuckets
+#endif // !FEATURE_PAL
+
+
+#ifdef FEATURE_CORESYSTEM
+// Only documented for Apollo internal usage
+ Watch
+ watch=Watch
+ SuppressJitOptimization
+ suppressjitoptimization=SuppressJitOptimization
+ sjo=SuppressJitOptimization
+ SaveState
+ savestate=SaveState
+ StopOnCatch
+ stoponcatch=StopOnCatch
+
+// Undocumented commands - testing or very experimental
+ ExposeDML
+ exposeDML=ExposeDML
+ GetCodeTypeFlags
+ getCodeTypeFlags=GetCodeTypeFlags
+ TraceToCode
+ tracetocode=TraceToCode
+#endif
diff --git a/src/ToolBox/SOS/Strike/sos.h b/src/ToolBox/SOS/Strike/sos.h
new file mode 100644
index 0000000000..3778235964
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.h
@@ -0,0 +1,792 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#include "strike.h"
+#include "util.h"
+
+#ifndef SOS_Assert
+#ifdef _DEBUG
+#define SOS_Assert(x) do { if (!(x)) sos::Throw<sos::Exception>("SOS Assert Failure: %s\n", #x); } while(0)
+#else
+#define SOS_Assert(x) (void)0
+#endif
+#endif
+
+#ifdef throw
+#undef throw
+#endif
+
+#ifdef try
+#undef try
+#endif
+
+#ifdef catch
+#undef catch
+#endif
+
+class LinearReadCache;
+class CGCDesc;
+class CGCDescSeries;
+
+namespace sos
+{
+ class GCHeap;
+
+ /* The base SOS Exception. Note that most commands should not attempt to be
+ * resilient to exceptions thrown by most functions here. Instead a top level
+ * try/catch at the beginning of the command which prints out the exception's
+ * message should be sufficient.
+ * Note you should not throw these directly, instead use the sos::Throw function.
+ */
+ class Exception
+ {
+ public:
+ Exception(const char *format, va_list args)
+ {
+ vsprintf_s(mMsg, _countof(mMsg), format, args);
+
+ va_end(args);
+ }
+
+ inline virtual ~Exception() {}
+
+ // from std::exception
+ virtual const char *what() const
+ {
+ return mMsg;
+ }
+
+ const char *GetMesssage() const
+ {
+ return mMsg;
+ }
+
+ protected:
+ char mMsg[1024];
+ };
+
+ /* Thrown when we could not read data we expected out of the target process.
+ * This can be due to heap corruption, or it could just be an invalid pointer.
+ */
+ class DataRead : public Exception
+ {
+ public:
+ DataRead(const char *format, va_list args)
+ : Exception(format, args)
+ {
+ }
+ };
+
+ /* This is thrown when we detect heap corruption in the process.
+ */
+ class HeapCorruption : public Exception
+ {
+ public:
+ HeapCorruption(const char *format, va_list args)
+ : Exception(format, args)
+ {
+ }
+ };
+
+ // Internal helper method. Use SOS_Throw macros instead.
+ template <class T>
+ void Throw(const char *format, ...)
+ {
+ va_list args;
+ va_start(args, format);
+
+ throw T(format, args);
+ }
+
+ /* Checks to see if the user hit control-c. Throws an exception to escape SOS
+ * if so.
+ */
+ inline void CheckInterrupt()
+ {
+ if (g_ExtControl->GetInterrupt() == S_OK)
+ Throw<Exception>("User interrupt.");
+ }
+
+ /* ThinLock struct. Use Object::GetThinLock to fill the struct.
+ */
+ struct ThinLockInfo
+ {
+ int ThreadId;
+ TADDR ThreadPtr;
+ int Recursion;
+
+ ThinLockInfo()
+ : ThreadId(0), ThreadPtr(0), Recursion(0)
+ {
+ }
+ };
+
+ /* The MethodTable for an Object. The general pattern should be:
+ * MethodTable mt = someObject.GetMT();
+ */
+ class MethodTable
+ {
+ public:
+ /* Returns whether an object is from an AppDomain that has been unloaded.
+ * If so, we cannot validate the object's members.
+ * Params:
+ * mt - The address of the MethodTable to test for.
+ */
+ static bool IsZombie(TADDR mt);
+
+ /* Returns the method table for arrays.
+ */
+ inline static TADDR GetArrayMT()
+ {
+ return TO_TADDR(g_special_usefulGlobals.ArrayMethodTable);
+ }
+
+ /* Returns the method table for String objects.
+ */
+ inline static TADDR GetStringMT()
+ {
+ return TO_TADDR(g_special_usefulGlobals.StringMethodTable);
+ }
+
+ /* Returns the method table for Free objects.
+ */
+ inline static TADDR GetFreeMT()
+ {
+ return TO_TADDR(g_special_usefulGlobals.FreeMethodTable);
+ }
+
+ /* Returns true if the given method table is that of a Free object.
+ */
+ inline static bool IsFreeMT(TADDR mt)
+ {
+ return GetFreeMT() == mt;
+ }
+
+ /* Returns true if the given method table is that of an Array.
+ */
+ inline static bool IsArrayMT(TADDR mt)
+ {
+ return GetArrayMT() == mt;
+ }
+
+ /* Returns true if the given method table is that of a System.String object.
+ */
+ inline static bool IsStringMT(TADDR mt)
+ {
+ return GetStringMT() == mt;
+ }
+
+ inline static bool IsValid(TADDR mt)
+ {
+ DacpMethodTableData data;
+ return data.Request(g_sos, TO_CDADDR(mt)) == S_OK;
+ }
+
+ public:
+ MethodTable(TADDR mt)
+ : mMT(mt), mName(0)
+ {
+ }
+
+ MethodTable(const MethodTable &mt)
+ : mMT(mt.mMT), mName(mt.mName)
+ {
+ // Acquire the calculated mName field. Since we are making a copy, we will likely use
+ // the copy instead of the original.
+ mt.mName = NULL;
+ }
+
+ const MethodTable &operator=(const MethodTable &mt)
+ {
+ Clear();
+
+ // Acquire the calculated mName field. Since we are making a copy, we will likely use
+ // the copy instead of the original.
+ mMT = mt.mMT;
+ mName = mt.mName;
+ mt.mName = NULL;
+
+ return *this;
+ }
+
+ ~MethodTable()
+ {
+ Clear();
+ }
+
+ /* Returns the class name of this MethodTable. The pointer returned is
+ * valid through the lifetime of the MethodTable object and should not be
+ * freed.
+ */
+ const WCHAR *GetName() const;
+
+ private:
+ void Clear();
+
+ private:
+ TADDR mMT;
+ mutable WCHAR *mName;
+ };
+
+ /* This represents an object on the GC heap in the target process. This class
+ * represents a single object, and is immutable after construction. All
+ * information about this class is lazily evaluated, so it is entirely possible
+ * to get exceptions when calling any member function. If this is a concern,
+ * call validate before attempting to call any other method on this object.
+ */
+ class Object
+ {
+ public:
+ /* Attempts to determine if the target address points to a valid object.
+ * Note that this is a heuristic based check, so false positives could
+ * be possible.
+ * Params:
+ * address - The address of the object to inspect.
+ * verifyFields - Whether or not to validate that the fields the object
+ * points to are also valid. (If the object contains a
+ * corrupted pointer, passing true to this parameter will
+ * cause IsValid to return false.) In general passing
+ * true will make IsValid return less false positives.
+ */
+ static bool IsValid(TADDR address, bool verifyFields=false);
+
+ static int GetStringDataOffset()
+ {
+#ifndef _TARGET_WIN64_
+ return 8;
+#else
+ return 0xc;
+#endif
+ }
+
+ public:
+ /* Constructor. Use Object(TADDR, TADDR) instead if you know the method table.
+ * Parameters:
+ * addr - an address to an object on the managed heap
+ * Throws:
+ * Exception - if addr is misaligned.
+ */
+ Object(TADDR addr);
+
+ /* Constructor. Use this constructor if you already know the method table for
+ * the object in question. This will save a read if the method table is needed.
+ * Parameters:
+ * addr - an address to an object on the managed heap
+ * Throws:
+ * Exception - if addr is misaligned.
+ */
+ Object(TADDR addr, TADDR mt);
+
+ Object(const Object &rhs);
+
+ inline ~Object()
+ {
+ if (mMTData)
+ delete mMTData;
+
+ if (mTypeName)
+ delete mTypeName;
+ }
+
+ const Object &operator=(TADDR addr);
+
+ // Comparison operators. These compare the underlying address of
+ // the object to the parameter.
+ inline bool operator<=(TADDR addr) { return mAddress <= addr; }
+ inline bool operator>=(TADDR addr) { return mAddress >= addr; }
+ inline bool operator<(TADDR addr) { return mAddress < addr; }
+ inline bool operator>(TADDR addr) { return mAddress > addr; }
+ inline bool operator==(TADDR addr) { return mAddress == addr; }
+
+ /* Returns the target address of the object this represents.
+ */
+ inline TADDR GetAddress() const
+ {
+ return mAddress;
+ }
+
+ /* Returns the target address of the object this represents.
+ */
+ inline operator TADDR() const
+ {
+ return GetAddress();
+ }
+
+ /* Returns the object header for this object.
+ * Throws:
+ * DataRead - we failed to read the object header.
+ */
+ ULONG GetHeader() const;
+
+ /* Gets the header for the current object, does not throw any exception.
+ * Params:
+ * outHeader - filled with the header if this function was successful.
+ * Returns:
+ * True if we successfully read the object header, false otherwise.
+ */
+ bool TryGetHeader(ULONG &outHeader) const;
+
+ /* Returns the method table of the object this represents.
+ * Throws:
+ * DataRead - If we failed to read the method table from the address.
+ * This is usually indicative of heap corruption.
+ * HeapCorruption - If we successfully read the target method table
+ * but it is invalid. (We do not do a very deep
+ * verification here.)
+ */
+ TADDR GetMT() const;
+
+ /* Returns the component method table of the object. For example, if
+ * this object is an array, the method table will be the general array
+ * MT. Calling this function tells you what type of objects can be
+ * placed in the array.
+ * Throws:
+ * DataRead - If we failed to read the method table from the address.
+ * This is usually indicative of heap corruption.
+ * HeapCorruption - If we successfully read the target method table
+ * but it is invalid. (We do not do a very deep
+ * verification here.)
+ */
+ TADDR GetComponentMT() const;
+
+ /* Returns the size of the object this represents. Note that this size
+ * may not be pointer aligned.
+ * Throws:
+ * DataRead - If we failed to read the method table data (which contains
+ * the size of the object).
+ */
+ size_t GetSize() const;
+
+ /* Returns true if this object contains pointers to other objects.
+ * Throws:
+ * DataRead - if we failed to read out of the object's method table.
+ */
+ bool HasPointers() const;
+
+ /* Gets the thinlock information for this object.
+ * Params:
+ * out - The ThinLockInfo to be filled.
+ * Returns:
+ * True if the object has a thinlock, false otherwise. If this function
+ * returns false, then out will be untouched.
+ * Throws:
+ * DataRead - If we could not read the object header from the object.
+ */
+ bool GetThinLock(ThinLockInfo &out) const;
+
+ /* Returns true if this object is a Free object (meaning it points to free
+ * space in the GC heap.
+ * Throws:
+ * The same as GetMT().
+ */
+ inline bool IsFree() const
+ {
+ return GetMT() == MethodTable::GetFreeMT();
+ }
+
+ /* Returns true if this object is a string.
+ * Throws:
+ * The same as GetMT().
+ */
+ inline bool IsString() const
+ {
+ return GetMT() == MethodTable::GetStringMT();
+ }
+
+ /* Returns the length of the String, if this is a string object. This
+ * function assumes that you have called IsString first to ensure that
+ * the object is indeed a string.
+ * Throws:
+ * DataRead if we could not read the contents of the object.
+ */
+ size_t GetStringLength() const;
+
+ /* Fills the given buffer with the contents of the String. This
+ * function assumes you have called IsString first to ensure that this
+ * object is actually a System.String. This function does not throw,
+ * but the results are undefined if this object is not a string.
+ * Params:
+ * buffer - The buffer to fill with the string contents.
+ * size - The total size of the buffer.
+ * Returns:
+ * True if the string data was successfully requested and placed in
+ * buffer, false otherwise.
+ */
+ bool GetStringData(__out_ecount(size) WCHAR *buffer, size_t size) const;
+
+ /* Returns the name of the type of this object. E.g. System.String.
+ * Throws:
+ * DataRead if we could not read the contents of the object.
+ * Returns:
+ * A string containing the type of the object.
+ */
+ const WCHAR *GetTypeName() const;
+
+ private:
+ void FillMTData() const;
+ void CalculateSizeAndPointers() const;
+ static bool VerifyMemberFields(TADDR pMT, TADDR obj);
+ static bool VerifyMemberFields(TADDR pMT, TADDR obj, WORD &numInstanceFields);
+
+ protected:
+ // Conceptually, this class is never modified after you pass in the the object address.
+ // That is, there can't be anything the user does to point this object to a different
+ // object after construction. Since we lazy evaluate *everything*, we must be able to
+ // modify these variables. Hence they are mutable.
+ TADDR mAddress;
+ mutable TADDR mMT;
+ mutable size_t mSize;
+ mutable bool mPointers;
+ mutable DacpMethodTableData *mMTData;
+ mutable WCHAR *mTypeName;
+ };
+
+ /* Enumerates all the GC references (objects) contained in an object. This uses the GCDesc
+ * map exactly as the GC does.
+ */
+ class RefIterator
+ {
+ public:
+ RefIterator(TADDR obj, LinearReadCache *cache = NULL);
+ RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache = NULL);
+ ~RefIterator();
+
+ /* Moves to the next reference in the object.
+ */
+ const RefIterator &operator++();
+
+ /* Returns the address of the current reference.
+ */
+ TADDR operator*() const;
+
+ /* Gets the offset into the object where the current reference comes from.
+ */
+ TADDR GetOffset() const;
+
+ /* Returns true if there are more objects in the iteration, false otherwise.
+ * Used as:
+ * if (itr)
+ * ...
+ */
+ inline operator void *() const
+ {
+ return (void*)!mDone;
+ }
+
+ private:
+ void Init();
+ inline TADDR ReadPointer(TADDR addr) const
+ {
+ if (mCache)
+ {
+ if (!mCache->Read(addr, &addr, false))
+ Throw<DataRead>("Could not read address %p.", addr);
+ }
+ else
+ {
+ MOVE(addr, addr);
+ }
+
+ return addr;
+ }
+
+ private:
+ LinearReadCache *mCache;
+ CGCDesc *mGCDesc;
+ bool mArrayOfVC, mDone;
+
+ TADDR *mBuffer;
+ CGCDescSeries *mCurrSeries;
+
+ int i, mCount;
+
+ TADDR mCurr, mStop, mObject;
+ size_t mObjSize;
+ };
+
+
+ /* The Iterator used to walk the managed objects on the GC heap.
+ * The general usage pattern for this class is:
+ * for (ObjectIterator itr = gcheap.WalkHeap(); itr; ++itr)
+ * itr->SomeObjectMethod();
+ */
+ class ObjectIterator
+ {
+ friend class GCHeap;
+ public:
+
+ /* Returns the next object in the GCHeap. Note that you must ensure
+ * that there are more objects to walk before calling this function by
+ * checking "if (iterator)". If this function throws an exception,
+ * the the iterator is invalid, and should no longer be used to walk
+ * the heap. This should generally only happen if we cannot read the
+ * MethodTable of the object to move to the next object.
+ * Throws:
+ * DataRead
+ */
+ const ObjectIterator &operator++();
+
+ /* Dereference operator. This allows you to take a reference to the
+ * current object. Note the lifetime of this reference is valid for
+ * either the lifetime of the iterator or until you call operator++,
+ * whichever is shorter. For example.
+ * void Foo(const Object &param);
+ * void Bar(const ObjectIterator &itr)
+ * {
+ * Foo(*itr);
+ * }
+ */
+ const Object &operator*() const;
+
+ /* Returns a pointer to the current Object to call members on it.
+ * The usage pattern for the iterator is to simply use operator->
+ * to call methods on the Object it points to without taking a
+ * direct reference to the underlying Object if at all possible.
+ */
+ const Object *operator->() const;
+
+ /* Returns false when the iterator has reached the end of the managed
+ * heap.
+ */
+ inline operator void *() const
+ {
+ return (void*)(SIZE_T)(mCurrHeap == mNumHeaps ? 0 : 1);
+ }
+
+ /* Do not use.
+ * TODO: Replace this functionality with int Object::GetGeneration().
+ */
+ bool IsCurrObjectOnLOH() const
+ {
+ SOS_Assert(*this);
+ return bLarge;
+ }
+
+ /* Verifies the current object. Returns true if the current object is valid.
+ * Returns false and fills 'buffer' with the reason the object is corrupted.
+ * This is a deeper validation than Object::IsValid as it checks the card
+ * table entires for the object in addition to the rest of the references.
+ * This function does not throw exceptions.
+ * Params:
+ * buffer - out buffer that is filled if and only if this function returns
+ * false.
+ * size - the total size of the buffer
+ * Returns:
+ * True if the object is valid, false otherwise.
+ */
+ bool Verify(__out_ecount(size) char *buffer, size_t size) const;
+
+ /* The same as Verify(char*, size_t), except it does not write out the failure
+ * reason to a provided buffer.
+ * See:
+ * ObjectIterator::Verify(char *, size_t)
+ */
+ bool Verify() const;
+
+ /* Attempts to move to the next object (similar to ObjectIterator++), but
+ * attempts to recover from any heap corruption by skipping to the next
+ * segment. If Verify returns false, meaning it detected heap corruption
+ * at the current object, you can use MoveToNextObjectCarefully instead of
+ * ObjectIterator++ to attempt to keep reading from the heap. If possible,
+ * this function attempts to move to the next object in the same segment,
+ * but if that's not possible then it skips to the next segment and
+ * continues from there.
+ * Note:
+ * This function can throw, and if it does then the iterator is no longer
+ * in a valid state. No further attempts to move to the next object will
+ * be possible.
+ * Throws:
+ * DataRead - if the heap is corrupted and it's not possible to continue
+ * walking the heap
+ */
+ void MoveToNextObjectCarefully();
+
+ private:
+ ObjectIterator(const DacpGcHeapDetails *heap, int numHeaps, TADDR start, TADDR stop);
+
+ bool VerifyObjectMembers(__out_ecount(size) char *buffer, size_t size) const;
+ void BuildError(__out_ecount(count) char *out, size_t count, const char *format, ...) const;
+
+ void AssertSanity() const;
+ bool NextSegment();
+ bool CheckSegmentRange();
+ void MoveToNextObject();
+
+ private:
+ DacpHeapSegmentData mSegment;
+ bool bLarge;
+ Object mCurrObj;
+ TADDR mLastObj, mStart, mEnd, mSegmentEnd;
+ AllocInfo mAllocInfo;
+ const DacpGcHeapDetails *mHeaps;
+ int mNumHeaps;
+ int mCurrHeap;
+ };
+
+ /* Reprensents an entry in the sync block table.
+ */
+ class SyncBlk
+ {
+ friend class SyncBlkIterator;
+ public:
+ /* Constructor.
+ * Params:
+ * index - the index of the syncblk entry you wish to inspect.
+ * This should be in range [1, MaxEntries], but in general
+ * you should always use the SyncBlk iterator off of GCHeap
+ * and not construct these directly.
+ * Throws:
+ * DataRead - if we could not read the syncblk entry for the given index.
+ */
+ explicit SyncBlk(int index);
+
+ /* Returns whether or not the current entry is a "Free" SyncBlk table entry
+ * or not. This should be called *before* any other function here.
+ */
+ bool IsFree() const;
+
+ /* Returns the address of this syncblk entry (generally for display purposes).
+ */
+ TADDR GetAddress() const;
+
+ /* Returns the address of the object which this is syncblk is pointing to.
+ */
+ TADDR GetObject() const;
+
+ /* Returns the index of this entry.
+ */
+ int GetIndex() const;
+
+ /* Returns the COMFlags for the SyncBlk object. The return value of this
+ * function is undefined if FEATURE_COMINTEROP is not defined, so you should
+ * #ifdef the calling region yourself.
+ */
+ DWORD GetCOMFlags() const;
+
+ unsigned int GetMonitorHeldCount() const;
+ unsigned int GetRecursion() const;
+ unsigned int GetAdditionalThreadCount() const;
+
+ /* Returns the thread which holds this monitor (this is the clr!Thread object).
+ */
+ TADDR GetHoldingThread() const;
+ TADDR GetAppDomain() const;
+
+ private:
+ /* Copy constructor unimplemented due to how expensive this is. Use references
+ * instead.
+ */
+ SyncBlk(const SyncBlk &rhs);
+ SyncBlk();
+ void Init();
+ const SyncBlk &operator=(int index);
+
+ private:
+ int mIndex;
+ DacpSyncBlockData mData;
+ };
+
+ /* An iterator over syncblks. The common usage for this class is:
+ * for (SyncBlkIterator itr; itr; ++itr)
+ * itr->SomeSyncBlkFunction();
+ */
+ class SyncBlkIterator
+ {
+ public:
+ SyncBlkIterator();
+
+ /* Moves to the next SyncBlk in the table.
+ */
+ inline const SyncBlkIterator &operator++()
+ {
+ SOS_Assert(mCurr <= mTotal);
+ mSyncBlk = ++mCurr;
+
+ return *this;
+ }
+
+ inline const SyncBlk &operator*() const
+ {
+ SOS_Assert(mCurr <= mTotal);
+ return mSyncBlk;
+ }
+
+ inline const SyncBlk *operator->() const
+ {
+ SOS_Assert(mCurr <= mTotal);
+ return &mSyncBlk;
+ }
+
+ inline operator void *() const
+ {
+ return (void*)(SIZE_T)(mCurr <= mTotal ? 1 : 0);
+ }
+
+ private:
+ int mCurr, mTotal;
+ SyncBlk mSyncBlk;
+ };
+
+ /* An class which contains information about the GCHeap.
+ */
+ class GCHeap
+ {
+ public:
+ static const TADDR HeapStart; // A constant signifying the start of the GC heap.
+ static const TADDR HeapEnd; // A constant signifying the end of the GC heap.
+
+ public:
+ /* Constructor.
+ * Throws:
+ * DataRead
+ */
+ GCHeap();
+
+ /* Returns an ObjectIterator which allows you to walk the objects on the managed heap.
+ * This ObjectIterator is valid for the duration of the GCHeap's lifetime. Note that
+ * if you specify an address at which you wish to start walking the heap it need
+ * not point directly to a managed object. However, if it does not, WalkHeap
+ * will need to walk the segment that address resides in to find the first object
+ * after that address, and if it encounters any heap corruption along the way,
+ * it may be impossible to walk the heap from the address specified.
+ *
+ * Params:
+ * start - The starting address at which you want to start walking the heap.
+ * This need not point directly to an object on the heap.
+ * end - The ending address at which you want to stop walking the heap. This
+ * need not point directly to an object on the heap.
+ * validate - Whether or not you wish to validate the GC heap as you walk it.
+ * Throws:
+ * DataRead
+ */
+ ObjectIterator WalkHeap(TADDR start = HeapStart, TADDR stop = HeapEnd) const;
+
+ /* Returns true if the GC Heap structures are in a valid state for traversal.
+ * Returns false if not (e.g. if we are in the middle of a relocation).
+ */
+ bool AreGCStructuresValid() const;
+
+ private:
+ DacpGcHeapDetails *mHeaps;
+ DacpGcHeapData mHeapData;
+ int mNumHeaps;
+ };
+
+ // convenience functions
+ /* A temporary wrapper function for Object::IsValid. There are too many locations
+ * in SOS which need to use IsObject but have a wide variety of internal
+ * representations for an object address. Until it can all be unified as TADDR,
+ * this is what they will use.
+ */
+ template <class T>
+ bool IsObject(T addr, bool verifyFields=false)
+ {
+ return Object::IsValid(TO_TADDR(addr), verifyFields);
+ }
+
+
+ void BuildTypeWithExtraInfo(TADDR addr, unsigned int size, __inout_ecount(size) WCHAR *buffer);
+}
diff --git a/src/ToolBox/SOS/Strike/sos.targets b/src/ToolBox/SOS/Strike/sos.targets
new file mode 100644
index 0000000000..3a8185c6da
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.targets
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <Import Project="$(ClrBase)\src\debug\SetDebugTargetLocal.props" />
+ <Import Project="$(ClrBase)\src\debug\XPlatCommon.props" />
+
+ <!-- Handle cross platform debugging: SOS is different from the DAC -->
+ <!-- in that one binary includes code targeting multiple platforms. -->
+ <!-- This means that there may be more than one SosTarget msbuild -->
+ <!-- symbol or SOS_TARGET_XYZ conditional defined at the same time. -->
+ <!-- The next section is what determines what code is built in which-->
+ <!-- binary. -->
+ <PropertyGroup>
+ <!-- Code level conditional compilation symbols to support x-plat -->
+ <CDefines Condition="'$(BuildArchitecture)' == 'i386'">$(CDefines);SOS_TARGET_X86=1</CDefines>
+ <CDefines Condition="'$(BuildArchitecture)' == 'arm' or '$(BuildArchitecture)' == 'i386'">$(CDefines);SOS_TARGET_ARM=1</CDefines>
+ <CDefines Condition="'$(BuildArchitecture)' == 'amd64'">$(CDefines);SOS_TARGET_AMD64=1</CDefines>
+ <CDefines Condition="'$(BuildArchitecture)' == 'amd64' or '$(BuildArchitecture)' == 'arm64'">$(CDefines);SOS_TARGET_ARM64=1</CDefines>
+
+ <CDefines Condition="'$(BuildArchitecture)' == 'amd64' or '$(BuildArchitecture)' == 'arm64'">$(CDefines);_TARGET_WIN64_=1</CDefines>
+ </PropertyGroup>
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <SosSourceDir>$(ClrBase)\src\toolbox\sos\strike</SosSourceDir>
+ <CompilerWarnings>
+ </CompilerWarnings>
+ <UserIncludes>
+ $(SosSourceDir)\inc;
+ $(UserIncludes);
+ $(Clrbase)\src\vm;
+ $(Clrbase)\src\inc;
+ $(Clrbase)\src\gcdump;
+ $(Clrbase)\src\Debug\shim;
+ $(Clrbase)\src\coreclr\hosts\inc
+ </UserIncludes>
+ <OutputName>SOS</OutputName>
+ <FileToMarkForSigning>$(BinariesDirectory)\$(OutputName).dll</FileToMarkForSigning>
+ <TargetType>DYNLINK</TargetType>
+ <LinkSubsystem>windows</LinkSubsystem>
+ <Nxcompat>true</Nxcompat>
+ <SynchronizePass2Drain>true</SynchronizePass2Drain>
+ <DllDef>$(IntermediateOutputDirectory)\SOS.def</DllDef>
+ <DllEntryPoint>_DllMainCRTStartup</DllEntryPoint>
+ <LinkNoLibraries>true</LinkNoLibraries>
+ <UseMsvcrt>false</UseMsvcrt>
+ <Ltcg>false</Ltcg>
+ <LinkUseCMT>true</LinkUseCMT>
+ <UseStl>true</UseStl>
+ <ClAdditionalOptions Condition="'$(DebugBuild)' == 'true'">$(ClAdditionalOptions) -DDEBUG -D_DEBUG</ClAdditionalOptions>
+ <ClAdditionalOptions Condition="!('$(DebugBuild)' == 'true')">$(ClAdditionalOptions) -DFAST=1</ClAdditionalOptions>
+ <ClOptimization Condition="!('$(DebugBuild)' == 'true')">MaxSpeed</ClOptimization>
+ <ClAdditionalOptions Condition="'$(FeatureCoreSystem)' == 'true'">$(ClAdditionalOptions) -DFEATURE_CORESYSTEM</ClAdditionalOptions>
+ <MscOptimizations Condition="!('$(DebugBuild)' == 'true')">
+ </MscOptimizations>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DSTRIKE -D_MT=1 -DORCAS=0 -DMDA_SUPPORTED -DDEBUGGING_SUPPORTED -DEnC_SUPPORTED -DPROFILING_SUPPORTED -DFEATURE_COMINTEROP -DFEATURE_COMINTEROP_UNMANAGED_ACTIVATION -DFEATURE_COMINTEROP_MANAGED_ACTIVATION -DFEATURE_COMINTEROP_APARTMENT_SUPPORT -DFEATURE_RWLOCK -DFEATURE_PREJIT -DFEATURE_STACK_PROBE -DFEATURE_SVR_GC -DFEATURE_CAS_POLICY -DFEATURE_CLICKONCE -DFEATURE_CRYPTO -DFEATURE_IMPERSONATION -DFEATURE_ISOSTORE -DFEATURE_MACL -DFEATURE_WATSON -DFEATURE_X509 -DFEATURE_X509_SECURESTRINGS -DFEATURE_COMINTEROP_REGISTRATION -DFEATURE_MIXEDMODE -DFEATURE_PERFMON -DFEATURE_REFLECTION_ONLY_LOAD -DFEATURE_FUSION -DFEATURE_SYNTHETIC_CULTURES -DFEATURE_SORT_TABLES -DFEATURE_CODEPAGES_FILE -DFEATURE_VALIDATOR -DFEATURE_WIN32_REGISTRY -DFEATURE_REMOTING -DFEATURE_SERIALIZATION -DFEATURE_ISYM_READER -DFEATURE_LOADER_OPTIMIZATION -DFEATURE_IPCMAN -DFEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED -DFEATURE_MULTIMODULE_ASSEMBLIES -DFEATURE_METHOD_RENTAL -DFEATURE_APTCA -DFEATURE_USE_LCID -DFEATURE_BCL_FORMATTING -DENABLE_DOWNLEVEL_FOR_NLS -DFEATURE_INCLUDE_ALL_INTERFACES -DFEATURE_NONGENERIC_COLLECTIONS -DFEATURE_APPDOMAINMANAGER_INITOPTIONS -DFEATURE_COMPRESSEDSTACK -DFEATURE_PLS -DFEATURE_SYNCHRONIZATIONCONTEXT -DFEATURE_SYNCHRONIZATIONCONTEXT_WAIT -DUEF_CHAINING_SUPPORTED -DFEATURE_LEAK_CULTURE_INFO -DFEATURE_UEF_CHAINMANAGER -DFEATURE_CORRUPTING_EXCEPTIONS -DFEATURE_APPDOMAIN_RESOURCE_MONITORING -DFEATURE_EXCEPTION_NOTIFICATIONS -DFEATURE_LEGACY_THREADPOOL -DFEATURE_INTEROP_DEBUGGING</ClAdditionalOptions>
+ <ExceptionHandling>$(Fullcxxeh)</ExceptionHandling>
+ <!-- As part of X-plat DAC feature work we decided to put x86 SOS inside binaries\x86 subdirectory.
+ The pdb file will be places following the same subdir rule SymbolsDir\x86.
+ -->
+ <BinplaceRoot Condition="'$(BuildX86Sos)' == 'true'">$(BinariesDirectory)\x86</BinplaceRoot>
+ <BinplaceRoot Condition="'$(BuildX64Sos)' == 'true'">$(BinariesDirectory)\x64</BinplaceRoot>
+ <!-- Inhibit default behaviour of copying sos.pdb to Symbols.pri dir. -->
+ <BinplaceSymbols Condition="'$(BuildX86Sos)' == 'true' or '$(BuildX64Sos)' == 'true'">false</BinplaceSymbols>
+ <Verdir>$(InternalPath)\NDP\inc</Verdir>
+ <Bindir>$(BinariesDirectory)</Bindir>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <TargetLib Include="$(SdkLibPath)\kernel32.lib" />
+ <TargetLib Include="$(SdkLibPath)\user32.lib" />
+ <TargetLib Include="$(SdkLibPath)\ole32.lib" />
+ <TargetLib Include="$(SdkLibPath)\oleaut32.lib" />
+ <TargetLib Include="$(SdkLibPath)\dbghelp.lib" />
+ <TargetLib Include="$(SdkLibPath)\uuid.lib" />
+ <TargetLib Include="$(SdkLibPath)\version.lib" />
+ <TargetLib Include="$(SdkLibPath)\dbgeng.lib" />
+ <TargetLib Include="$(SdkLibPath)\advapi32.lib" />
+ <TargetLib Include="$(SdkLibPath)\psapi.lib" />
+
+ <TargetLib Condition="'$(DebugBuild)' == 'true'" Include="$(VCPublicLibPath)\libcmtd.lib" />
+ <TargetLib Condition="'$(DebugBuild)' != 'true'" Include="$(VCPublicLibPath)\libcmt.lib" />
+
+ <TargetLib Condition="'$(BuildX86Sos)' != 'true' and '$(BuildX64Sos)' != 'true'" Include="$(ClrLibPath)\corguids.lib">
+ <ProjectReference>$(ClrSrcDirectory)inc\corguids.nativeproj</ProjectReference>
+ </TargetLib>
+ <TargetLib Condition="'$(BuildX86Sos)' == 'true'" Include="$(ClrLibPath)\corguids_x86.lib">
+ <ProjectReference>$(ClrSrcDirectory)incx86\corguids.nativeproj</ProjectReference>
+ </TargetLib>
+ <TargetLib Condition="'$(BuildX64Sos)' == 'true'" Include="$(ClrLibPath)\corguids_amd64.lib">
+ <ProjectReference>$(ClrSrcDirectory)incamd64\corguids.nativeproj</ProjectReference>
+ </TargetLib>
+
+ <TargetLib Include="$(ClrLibPath)\debugshim$(XPlatHostLibSuffix).lib">
+ <ProjectReference>$(ClrSrcDirectory)\Debug\shim\$(XPlatHostLibBuildDir)\debugshim.nativeproj</ProjectReference>
+ </TargetLib>
+ <TargetLib Include="$(ClrLibPath)\dbgutil$(XPlatHostLibSuffix).lib">
+ <ProjectReference>$(ClrSrcDirectory)\Debug\dbgutil\$(XPlatHostLibBuildDir)\dbgutil.nativeproj</ProjectReference>
+ </TargetLib>
+ </ItemGroup>
+ <ItemGroup>
+ <TargetLib Include="$(SdkLibPath)\ntdll.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <RCResourceFile Condition="'$(FeatureCoreSystem)'!='true'" Include="$(SosSourceDir)\Native.rc" />
+ <RCResourceFile Condition="'$(FeatureCoreSystem)'=='true'" Include="$(SosSourceDir)\ApolloNative.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <CppCompile Include="$(SosSourceDir)\disasm.cpp" />
+ <CppCompile Include="$(SosSourceDir)\dllsext.cpp" />
+ <CppCompile Include="$(SosSourceDir)\eeheap.cpp" />
+ <CppCompile Include="$(SosSourceDir)\EventCallbacks.cpp" />
+ <CppCompile Include="$(SosSourceDir)\ExpressionNode.cpp" />
+ <CppCompile Include="$(SosSourceDir)\exts.cpp" />
+ <CppCompile Include="$(SosSourceDir)\gchist.cpp" />
+ <CppCompile Include="$(SosSourceDir)\gcroot.cpp" />
+ <CppCompile Include="$(SosSourceDir)\metadata.cpp" />
+ <CppCompile Include="$(SosSourceDir)\sildasm.cpp" />
+ <CppCompile Include="$(SosSourceDir)\sos.cpp" />
+ <CppCompile Include="$(SosSourceDir)\stressLogDump.cpp" />
+ <CppCompile Include="$(SosSourceDir)\strike.cpp" />
+ <CppCompile Include="$(SosSourceDir)\util.cpp" />
+ <CppCompile Include="$(SosSourceDir)\vm.cpp" />
+ <CppCompile Include="$(SosSourceDir)\WatchCmd.cpp" />
+ <CppPreprocess Include="$(SosSourceDir)\SOS.def">
+ <FinalOutput>$(IntermediateOutputDirectory)\SOS.def</FinalOutput>
+ <AdditionalOptions>$(ClAdditionalOptions) /TC</AdditionalOptions>
+ </CppPreprocess>
+ </ItemGroup>
+ <ItemGroup>
+ <CppCompile Condition="'$(BuildArchitecture)' == 'i386'" Include="$(SosSourceDir)\disasmX86.cpp" />
+ <CppCompile Condition="'$(BuildArchitecture)' == 'amd64'" Include="$(SosSourceDir)\disasmX86.cpp" />
+ <CppCompile Condition="'$(BuildArchitecture)' == 'arm' or '$(BuildArchitecture)' == 'i386'" Include="$(SosSourceDir)\disasmARM.cpp" />
+ <CppCompile Condition="'$(BuildArchitecture)' == 'arm64' or '$(BuildArchitecture)' == 'amd64'" Include="$(SosSourceDir)\disasmARM64.cpp" />
+ <RotorX86Sources Include="$(SosSourceDir)\disasmX86.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <DataFile Include="$(SosSourceDir)\sos_stacktrace.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <PublishPartGenerated Include="$(SosSourceDir)\sos_stacktrace.h">
+ <Visibility>Intra</Visibility>
+ <FileType>Include</FileType>
+ </PublishPartGenerated>
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+
+ <PropertyGroup>
+ <BuildLinkedDependsOn>$(BuildLinkedDependsOn);GenLongNameSOS</BuildLinkedDependsOn>
+ </PropertyGroup>
+
+ <UsingTask TaskName="GetFileVersionTask" AssemblyFile="$(ClrSrcDirectory)dlls\mscordac\GetFileVersion.dll"/>
+
+ <Target Name="GenLongNameSOS"
+ Inputs="$(Verdir)\version.h;$(IntermediateOutputDirectory)\sos.dll"
+ Outputs="sos_upd">
+ <GetFileVersionTask FilePath="$(IntermediateOutputDirectory)\sos.dll">
+ <Output TaskParameter="FileVersion" PropertyName="SOSFileVersion"/>
+ </GetFileVersionTask>
+ <Exec Command="$(PerlCommand) -I$(DevDivToolsPath) $(ClrSrcDirectory)dlls\mscordac\Update.pl $(IntermediateOutputDirectory)\sos.dll sos $(HostMachineArch) $(_EnvironmentMachineArch) $(SOSFileVersion) $(Bindir) echo" StandardOutputImportance="Normal" />
+
+ </Target>
+
+</Project>
diff --git a/src/ToolBox/SOS/Strike/sos_md.h b/src/ToolBox/SOS/Strike/sos_md.h
new file mode 100644
index 0000000000..f1a34cd706
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos_md.h
@@ -0,0 +1,926 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#ifndef __SOS_MD_H__
+#define __SOS_MD_H__
+
+#define IfErrGoTo(s, label) { \
+ hresult = (s); \
+ if(FAILED(hresult)){ \
+ goto label; }}
+
+class CQuickBytes;
+
+// TODO: Cleanup code to allow SOS to directly include the metadata header files.
+//#include "MetaData.h"
+//#include "corpriv.h"
+
+/*
+ *
+ * Metadata definitions needed for PrettyPrint functions.
+ * The original definitions for the types and interfaces below exist in
+ * inc\MetaData.h and inc\CorPriv.h
+ * TODO:
+ * Cleanup code to allow SOS to directly include the metadata header files.
+ * Currently it's extremely difficult due to symbol redefinitions.
+ * Always keep the definitions below in sync with the originals.
+ * NOTES:
+ * Since SOS runs in a native debugger session, since it does not use EnC,
+ * and in roder to minimize the amount of duplication we changed the
+ * method definitions that deal with UTSemReadWrite* arguments to take
+ * void* arguments instead.
+ * Also, some of the interface methods take CQuickBytes as arguments.
+ * If these methods are ever used it becomes crucial to maintain binary
+ * compatibility b/w the CQuickBytes defined in SOS and the definition
+ * from the EE.
+ *
+ */
+typedef enum tagEnumType
+{
+ MDSimpleEnum = 0x0, // simple enumerator that doesn't allocate memory
+ MDDynamicArrayEnum = 0x2, // dynamic array that holds tokens
+ MDCustomEnum = 0x3, // Custom enumerator that doesnt work with the enum functions
+} EnumType;
+
+struct HENUMInternal
+{
+ DWORD m_tkKind; // kind of tables that the enum is holding the result
+ ULONG m_ulCount; // count of total entries holding by the enumerator
+
+ EnumType m_EnumType;
+
+ struct {
+ ULONG m_ulStart;
+ ULONG m_ulEnd;
+ ULONG m_ulCur;
+ } u;
+
+ // m_cursor will go away when we no longer support running EE with uncompressed
+ // format. WHEN WE REMOVE THIS, REMOVE ITS VESTIAGES FROM ZeroEnum as well
+ //
+ char m_cursor[32]; // cursor holding query result for read/write mode
+};
+
+typedef struct _MDDefaultValue
+{
+#if DBG_TARGET_BIGENDIAN
+ _MDDefaultValue(void)
+ {
+ m_bType = ELEMENT_TYPE_END;
+ }
+ ~_MDDefaultValue(void)
+ {
+ if (m_bType == ELEMENT_TYPE_STRING)
+ {
+ delete[] m_wzValue;
+ }
+ }
+#endif
+
+ // type of default value
+ BYTE m_bType; // CorElementType for the default value
+
+ // the default value
+ union
+ {
+ BOOL m_bValue; // ELEMENT_TYPE_BOOLEAN
+ CHAR m_cValue; // ELEMENT_TYPE_I1
+ BYTE m_byteValue; // ELEMENT_TYPE_UI1
+ SHORT m_sValue; // ELEMENT_TYPE_I2
+ USHORT m_usValue; // ELEMENT_TYPE_UI2
+ LONG m_lValue; // ELEMENT_TYPE_I4
+ ULONG m_ulValue; // ELEMENT_TYPE_UI4
+ LONGLONG m_llValue; // ELEMENT_TYPE_I8
+ ULONGLONG m_ullValue; // ELEMENT_TYPE_UI8
+ FLOAT m_fltValue; // ELEMENT_TYPE_R4
+ DOUBLE m_dblValue; // ELEMENT_TYPE_R8
+ LPCWSTR m_wzValue; // ELEMENT_TYPE_STRING
+ IUnknown *m_unkValue; // ELEMENT_TYPE_CLASS
+ };
+ ULONG m_cbSize; // default value size (for blob)
+
+} MDDefaultValue;
+
+typedef struct
+{
+ RID m_ridFieldCur; // indexing to the field table
+ RID m_ridFieldEnd; // end index to field table
+} MD_CLASS_LAYOUT;
+
+typedef struct
+{
+ USHORT usMajorVersion; // Major Version.
+ USHORT usMinorVersion; // Minor Version.
+ USHORT usBuildNumber; // Build Number.
+ USHORT usRevisionNumber; // Revision Number.
+ LPCSTR szLocale; // Locale.
+ DWORD *rProcessor; // Processor array.
+ ULONG ulProcessor; // [IN/OUT] Size of the processor array/Actual # of entries filled in.
+ OSINFO *rOS; // OSINFO array.
+ ULONG ulOS; // [IN/OUT]Size of the OSINFO array/Actual # of entries filled in.
+} AssemblyMetaDataInternal;
+
+typedef struct
+{
+ mdMethodDef m_memberdef;
+ DWORD m_dwSemantics;
+} ASSOCIATE_RECORD;
+
+typedef BOOL (*PSIGCOMPARE)(PCCOR_SIGNATURE, DWORD, PCCOR_SIGNATURE, DWORD, void*);
+
+EXTERN_GUID(IID_IMDInternalImport, 0xce0f34ed, 0xbbc6, 0x11d2, 0x94, 0x1e, 0x0, 0x0, 0xf8, 0x8, 0x34, 0x60);
+#undef INTERFACE
+#define INTERFACE IMDInternalImport
+DECLARE_INTERFACE_(IMDInternalImport, IUnknown)
+{
+ //*****************************************************************************
+ // return the count of entries of a given kind in a scope
+ // For example, pass in mdtMethodDef will tell you how many MethodDef
+ // contained in a scope
+ //*****************************************************************************
+ STDMETHOD_(ULONG, GetCountWithTokenKind)(// return hresult
+ DWORD tkKind) PURE; // [IN] pass in the kind of token.
+
+ //*****************************************************************************
+ // enumerator for typedef
+ //*****************************************************************************
+ STDMETHOD(EnumTypeDefInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ STDMETHOD_(ULONG, EnumTypeDefGetCount)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ STDMETHOD_(void, EnumTypeDefReset)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ STDMETHOD_(bool, EnumTypeDefNext)( // return hresult
+ HENUMInternal *phEnum, // [IN] input enum
+ mdTypeDef *ptd) PURE; // [OUT] return token
+
+ STDMETHOD_(void, EnumTypeDefClose)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ //*****************************************************************************
+ // enumerator for MethodImpl
+ //*****************************************************************************
+ STDMETHOD(EnumMethodImplInit)( // return hresult
+ mdTypeDef td, // [IN] TypeDef over which to scope the enumeration.
+ HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens.
+ HENUMInternal *phEnumDecl) PURE; // [OUT] buffer to fill for enumerator data for MethodDecl tokens.
+
+ STDMETHOD_(ULONG, EnumMethodImplGetCount)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ STDMETHOD_(void, EnumMethodImplReset)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ STDMETHOD(EnumMethodImplNext)( // return hresult (S_OK = TRUE, S_FALSE = FALSE or error code)
+ HENUMInternal *phEnumBody, // [IN] input enum for MethodBody
+ HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl
+ mdToken *ptkBody, // [OUT] return token for MethodBody
+ mdToken *ptkDecl) PURE; // [OUT] return token for MethodDecl
+
+ STDMETHOD_(void, EnumMethodImplClose)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ //*****************************************
+ // Enumerator helpers for memberdef, memberref, interfaceimp,
+ // event, property, exception, param
+ //*****************************************
+
+ STDMETHOD(EnumGlobalFunctionsInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ STDMETHOD(EnumGlobalFieldsInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ STDMETHOD(EnumInit)( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ mdToken tkParent, // [IN] token to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ STDMETHOD(EnumAllInit)( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ STDMETHOD_(bool, EnumNext)(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk) PURE; // [OUT] token to scope the search
+
+ STDMETHOD_(ULONG, EnumGetCount)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ STDMETHOD_(void, EnumReset)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to be reset
+
+ STDMETHOD_(void, EnumClose)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to be closed
+
+ //*****************************************
+ // Enumerator helpers for declsecurity.
+ //*****************************************
+ STDMETHOD(EnumPermissionSetsInit)( // return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ CorDeclSecurity Action, // [IN] Action to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ //*****************************************
+ // Enumerator helpers for CustomAttribute
+ //*****************************************
+ STDMETHOD(EnumCustomAttributeByNameInit)(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ //*****************************************
+ // Nagivator helper to navigate back to the parent token given a token.
+ // For example, given a memberdef token, it will return the containing typedef.
+ //
+ // the mapping is as following:
+ // ---given child type---------parent type
+ // mdMethodDef mdTypeDef
+ // mdFieldDef mdTypeDef
+ // mdInterfaceImpl mdTypeDef
+ // mdParam mdMethodDef
+ // mdProperty mdTypeDef
+ // mdEvent mdTypeDef
+ //
+ //*****************************************
+ STDMETHOD(GetParentToken)(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent) PURE; // [OUT] returning parent
+
+ //*****************************************
+ // Custom value helpers
+ //*****************************************
+ STDMETHOD(GetCustomAttributeProps)( // S_OK or error.
+ mdCustomAttribute at, // [IN] The attribute.
+ mdToken *ptkType) PURE; // [OUT] Put attribute type here.
+
+ STDMETHOD(GetCustomAttributeAsBlob)(
+ mdCustomAttribute cv, // [IN] given custom value token
+ void const **ppBlob, // [OUT] return the pointer to internal blob
+ ULONG *pcbSize) PURE; // [OUT] return the size of the blob
+
+ // returned void in v1.0/v1.1
+ STDMETHOD (GetScopeProps)(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid) PURE; // [OUT] version id
+
+ // finding a particular method
+ STDMETHOD(FindMethodDef)(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmd) PURE; // [OUT] matching memberdef
+
+ // return a iSeq's param given a MethodDef
+ STDMETHOD(FindParamOfMethod)( // S_OK or error.
+ mdMethodDef md, // [IN] The owning method of the param.
+ ULONG iSeq, // [IN] The sequence # of the param.
+ mdParamDef *pparamdef) PURE; // [OUT] Put ParamDef token here.
+
+ //*****************************************
+ //
+ // GetName* functions
+ //
+ //*****************************************
+
+ // return the name and namespace of typedef
+ STDMETHOD(GetNameOfTypeDef)(
+ mdTypeDef classdef, // given classdef
+ LPCSTR *pszname, // return class name(unqualified)
+ LPCSTR *psznamespace) PURE; // return the name space name
+
+ STDMETHOD(GetIsDualOfTypeDef)(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pDual) PURE; // [OUT] return dual flag here.
+
+ STDMETHOD(GetIfaceTypeOfTypeDef)(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pIface) PURE; // [OUT] 0=dual, 1=vtable, 2=dispinterface
+
+ // get the name of either methoddef
+ STDMETHOD(GetNameOfMethodDef)( // return the name of the memberdef in UTF8
+ mdMethodDef md, // given memberdef
+ LPCSTR *pszName) PURE;
+
+ STDMETHOD(GetNameAndSigOfMethodDef)(
+ mdMethodDef methoddef, // [IN] given memberdef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName) PURE;
+
+ // return the name of a FieldDef
+ STDMETHOD(GetNameOfFieldDef)(
+ mdFieldDef fd, // given memberdef
+ LPCSTR *pszName) PURE;
+
+ // return the name of typeref
+ STDMETHOD(GetNameOfTypeRef)(
+ mdTypeRef classref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref name
+ LPCSTR *pszname) PURE; // [OUT] return typeref namespace
+
+ // return the resolutionscope of typeref
+ STDMETHOD(GetResolutionScopeOfTypeRef)(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope) PURE;
+
+ // Find the type token given the name.
+ STDMETHOD(FindTypeRefByName)(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeRef.
+ LPCSTR szName, // [IN] Name of the TypeRef.
+ mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef.
+ mdTypeRef *ptk) PURE; // [OUT] TypeRef token returned.
+
+ // return the TypeDef properties
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetTypeDefProps)(
+ mdTypeDef classdef, // given classdef
+ DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract
+ mdToken *ptkExtends) PURE; // [OUT] Put base class TypeDef/TypeRef here
+
+ // return the item's guid
+ STDMETHOD(GetItemGuid)(
+ mdToken tkObj, // [IN] given item.
+ CLSID *pGuid) PURE; // [out[ put guid here.
+
+ // Get enclosing class of the NestedClass.
+ STDMETHOD(GetNestedClassProps)( // S_OK or error
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass) PURE; // [OUT] EnclosingClass token.
+
+ // Get count of Nested classes given the enclosing class.
+ STDMETHOD(GetCountNestedClasses)( // return count of Nested classes.
+ mdTypeDef tkEnclosingClass, // Enclosing class.
+ ULONG *pcNestedClassesCount) PURE;
+
+ // Return array of Nested classes given the enclosing class.
+ STDMETHOD(GetNestedClasses)( // Return actual count.
+ mdTypeDef tkEnclosingClass, // [IN] Enclosing class.
+ mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens.
+ ULONG ulNestedClasses, // [IN] Size of array.
+ ULONG *pcNestedClasses) PURE;
+
+ // return the ModuleRef properties
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetModuleRefProps)(
+ mdModuleRef mur, // [IN] moduleref token
+ LPCSTR *pszName) PURE; // [OUT] buffer to fill with the moduleref name
+
+ //*****************************************
+ //
+ // GetSig* functions
+ //
+ //*****************************************
+ STDMETHOD(GetSigOfMethodDef)(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig) PURE;
+
+ STDMETHOD(GetSigOfFieldDef)(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig) PURE;
+
+ STDMETHOD(GetSigFromToken)(
+ mdToken tk,
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig) PURE;
+
+
+
+ //*****************************************
+ // get method property
+ //*****************************************
+ STDMETHOD(GetMethodDefProps)(
+ mdMethodDef md, // The method for which to get props.
+ DWORD *pdwFlags) PURE;
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetMethodImplProps)(
+ mdToken tk, // [IN] MethodDef
+ ULONG *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags) PURE; // [OUT] Impl. Flags
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ STDMETHOD(GetFieldRVA)(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulCodeRVA) PURE; // [OUT] CodeRVA
+
+ //*****************************************
+ // get field property
+ //*****************************************
+ STDMETHOD(GetFieldDefProps)(
+ mdFieldDef fd, // [IN] given fielddef
+ DWORD *pdwFlags) PURE; // [OUT] return fdPublic, fdPrive, etc flags
+
+ //*****************************************************************************
+ // return default value of a token(could be paramdef, fielddef, or property
+ //*****************************************************************************
+ STDMETHOD(GetDefaultValue)(
+ mdToken tk, // [IN] given FieldDef, ParamDef, or Property
+ MDDefaultValue *pDefaultValue) PURE;// [OUT] default value to fill
+
+
+ //*****************************************
+ // get dispid of a MethodDef or a FieldDef
+ //*****************************************
+ STDMETHOD(GetDispIdOfMemberDef)( // return hresult
+ mdToken tk, // [IN] given methoddef or fielddef
+ ULONG *pDispid) PURE; // [OUT] Put the dispid here.
+
+ //*****************************************
+ // return TypeRef/TypeDef given an InterfaceImpl token
+ //*****************************************
+ STDMETHOD(GetTypeOfInterfaceImpl)( // return the TypeRef/typedef token for the interfaceimpl
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType) PURE;
+
+ //*****************************************
+ // look up function for TypeDef
+ //*****************************************
+ STDMETHOD(FindTypeDef)(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeRef/TypeDef Token for the enclosing class.
+ mdTypeDef *ptypedef) PURE; // [IN] return typedef
+
+ //*****************************************
+ // return name and sig of a memberref
+ //*****************************************
+ STDMETHOD(GetNameAndSigOfMemberRef)( // return name here
+ mdMemberRef memberref, // given memberref
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName) PURE;
+
+ //*****************************************************************************
+ // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef
+ //*****************************************************************************
+ STDMETHOD(GetParentOfMemberRef)(
+ mdMemberRef memberref, // given memberref
+ mdToken *ptkParent) PURE; // return the parent token
+
+ STDMETHOD(GetParamDefProps)(
+ mdParamDef paramdef, // given a paramdef
+ USHORT *pusSequence, // [OUT] slot number for this parameter
+ DWORD *pdwAttr, // [OUT] flags
+ LPCSTR *pszName) PURE; // [OUT] return the name of the parameter
+
+ STDMETHOD(GetPropertyInfoForMethodDef)( // Result.
+ mdMethodDef md, // [IN] memberdef
+ mdProperty *ppd, // [OUT] put property token here
+ LPCSTR *pName, // [OUT] put pointer to name here
+ ULONG *pSemantic) PURE; // [OUT] put semantic here
+
+ //*****************************************
+ // class layout/sequence information
+ //*****************************************
+ STDMETHOD(GetClassPackSize)( // return error if class doesn't have packsize
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwPackSize) PURE; // [OUT] 1, 2, 4, 8, or 16
+
+ STDMETHOD(GetClassTotalSize)( // return error if class doesn't have total size info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwClassSize) PURE; // [OUT] return the total size of the class
+
+ STDMETHOD(GetClassLayoutInit)(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pLayout) PURE; // [OUT] set up the status of query here
+
+ STDMETHOD(GetClassLayoutNext)(
+ MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here
+ mdFieldDef *pfd, // [OUT] return the fielddef
+ ULONG *pulOffset) PURE; // [OUT] return the offset/ulSequence associate with it
+
+ //*****************************************
+ // marshal information of a field
+ //*****************************************
+ STDMETHOD(GetFieldMarshal)( // return error if no native type associate with the token
+ mdFieldDef fd, // [IN] given fielddef
+ PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature
+ ULONG *pcbNativeType) PURE; // [OUT] the count of bytes of *ppvNativeType
+
+
+ //*****************************************
+ // property APIs
+ //*****************************************
+ // find a property by name
+ STDMETHOD(FindProperty)(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp) PURE; // [OUT] return property token
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetPropertyProps)(
+ mdProperty prop, // [IN] property token
+ LPCSTR *szProperty, // [OUT] property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pcbSig) PURE; // [OUT] count of bytes in *ppvSig
+
+ //**********************************
+ // Event APIs
+ //**********************************
+ STDMETHOD(FindEvent)(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent) PURE; // [OUT] return event token
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetEventProps)(
+ mdEvent ev, // [IN] event token
+ LPCSTR *pszEvent, // [OUT] Event name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType) PURE; // [OUT] EventType class
+
+
+ //**********************************
+ // find a particular associate of a property or an event
+ //**********************************
+ STDMETHOD(FindAssociate)(
+ mdToken evprop, // [IN] given a property or event token
+ DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire)
+ mdMethodDef *pmd) PURE; // [OUT] return method def token
+
+ // Note, void function in v1.0/v1.1
+ STDMETHOD(EnumAssociateInit)(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum) PURE; // [OUT] cursor to hold the query result
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetAllAssociates)(
+ HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts
+ ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output
+ ULONG cAssociateRec) PURE; // [IN] size of the buffer
+
+
+ //**********************************
+ // Get info about a PermissionSet.
+ //**********************************
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetPermissionSetProps)(
+ mdPermission pm, // [IN] the permission token.
+ DWORD *pdwAction, // [OUT] CorDeclSecurity.
+ void const **ppvPermission, // [OUT] permission blob.
+ ULONG *pcbPermission) PURE; // [OUT] count of bytes of pvPermission.
+
+ //****************************************
+ // Get the String given the String token.
+ // Returns a pointer to the string, or NULL in case of error.
+ //****************************************
+ STDMETHOD(GetUserString)(
+ mdString stk, // [IN] the string token.
+ ULONG *pchString, // [OUT] count of characters in the string.
+ BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80.
+ LPCWSTR *pwszUserString) PURE;
+
+ //*****************************************************************************
+ // p-invoke APIs.
+ //*****************************************************************************
+ STDMETHOD(GetPinvokeMap)(
+ mdToken tk, // [IN] FieldDef, MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL) PURE; // [OUT] ModuleRef token for the target DLL.
+
+ //*****************************************************************************
+ // helpers to convert a text signature to a com format
+ //*****************************************************************************
+ STDMETHOD(ConvertTextSigToComSig)( // Return hresult.
+ BOOL fCreateTrIfNotFound, // [IN] create typeref if not found
+ LPCSTR pSignature, // [IN] class file format signature
+ CQuickBytes *pqbNewSig, // [OUT] place holder for CLR signature
+ ULONG *pcbCount) PURE; // [OUT] the result size of signature
+
+ //*****************************************************************************
+ // Assembly MetaData APIs.
+ //*****************************************************************************
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetAssemblyProps)(
+ mdAssembly mda, // [IN] The Assembly for which to get the properties.
+ const void **ppbPublicKey, // [OUT] Pointer to the public key.
+ ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key.
+ ULONG *pulHashAlgId, // [OUT] Hash Algorithm.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags) PURE;// [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetAssemblyRefProps)(
+ mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties.
+ const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token.
+ ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetFileProps)(
+ mdFile mdf, // [IN] The File for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob.
+ DWORD *pdwFileFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetExportedTypeProps)(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Namespace.
+ LPCSTR *pszName, // [OUT] Name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetManifestResourceProps)(
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file.
+ DWORD *pdwResourceFlags) PURE;// [OUT] Flags.
+
+ STDMETHOD(FindExportedTypeByName)( // S_OK or error
+ LPCSTR szNamespace, // [IN] Namespace of the ExportedType.
+ LPCSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] ExportedType for the enclosing class.
+ mdExportedType *pmct) PURE; // [OUT] Put ExportedType token here.
+
+ STDMETHOD(FindManifestResourceByName)( // S_OK or error
+ LPCSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *pmmr) PURE; // [OUT] Put ManifestResource token here.
+
+ STDMETHOD(GetAssemblyFromScope)( // S_OK or error
+ mdAssembly *ptkAssembly) PURE; // [OUT] Put token here.
+
+ STDMETHOD(GetCustomAttributeByName)( // S_OK or error
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) PURE; // [OUT] Put size of data here.
+
+ // Note: The return type of this method was void in v1
+ STDMETHOD(GetTypeSpecFromToken)( // S_OK or error.
+ mdTypeSpec typespec, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig) PURE; // [OUT] return size of signature.
+
+ STDMETHOD(SetUserContextData)( // S_OK or E_NOTIMPL
+ IUnknown *pIUnk) PURE; // The user context.
+
+ STDMETHOD_(BOOL, IsValidToken)( // True or False.
+ mdToken tk) PURE; // [IN] Given token.
+
+ STDMETHOD(TranslateSigWithScope)(
+ IMDInternalImport *pAssemImport, // [IN] import assembly scope.
+ const void *pbHashValue, // [IN] hash value for the import assembly.
+ ULONG cbHashValue, // [IN] count of bytes in the hash value.
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] assembly emit scope.
+ IMetaDataEmit *emit, // [IN] emit interface
+ CQuickBytes *pqkSigEmit, // [OUT] buffer to hold translated signature
+ ULONG *pcbSig) PURE; // [OUT] count of bytes in the translated signature
+
+ // since SOS does not need to call method below, change return value to IUnknown* (from IMetaModelCommon*)
+ STDMETHOD_(IUnknown*, GetMetaModelCommon)( // Return MetaModelCommon interface.
+ ) PURE;
+
+ STDMETHOD_(IUnknown *, GetCachedPublicInterface)(BOOL fWithLock) PURE; // return the cached public interface
+ STDMETHOD(SetCachedPublicInterface)(IUnknown *pUnk) PURE; // no return value
+ // since SOS does not use the next 2 methods replace UTSemReadWrite* with void* in the signature
+ STDMETHOD_(void*, GetReaderWriterLock)() PURE; // return the reader writer lock
+ STDMETHOD(SetReaderWriterLock)(void * pSem) PURE;
+
+ STDMETHOD_(mdModule, GetModuleFromScope)() PURE; // [OUT] Put mdModule token here.
+
+
+ //-----------------------------------------------------------------
+ // Additional custom methods
+
+ // finding a particular method
+ STDMETHOD(FindMethodDefUsingCompare)(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ PSIGCOMPARE pSignatureCompare, // [IN] Routine to compare signatures
+ void* pSignatureArgs, // [IN] Additional info to supply the compare function
+ mdMethodDef *pmd) PURE; // [OUT] matching memberdef
+
+ // Additional v2 methods.
+
+ //*****************************************
+ // return a field offset for a given field
+ //*****************************************
+ STDMETHOD(GetFieldOffset)(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset) PURE; // [OUT] FieldOffset
+
+ STDMETHOD(GetMethodSpecProps)(
+ mdMethodSpec ms, // [IN] The method instantiation
+ mdToken *tkParent, // [OUT] MethodDef or MemberRef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob) PURE; // [OUT] actual size of signature blob
+
+ STDMETHOD(GetTableInfoWithIndex)(
+ ULONG index, // [IN] pass in the table index
+ void **pTable, // [OUT] pointer to table at index
+ void **pTableSize) PURE; // [OUT] size of table at index
+
+ STDMETHOD(ApplyEditAndContinue)(
+ void *pDeltaMD, // [IN] the delta metadata
+ ULONG cbDeltaMD, // [IN] length of pData
+ IMDInternalImport **ppv) PURE; // [OUT] the resulting metadata interface
+
+ //**********************************
+ // Generics APIs
+ //**********************************
+ STDMETHOD(GetGenericParamProps)( // S_OK or error.
+ mdGenericParam rd, // [IN] The type parameter
+ ULONG* pulSequence, // [OUT] Parameter sequence number
+ DWORD* pdwAttr, // [OUT] Type parameter flags (for future use)
+ mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef)
+ DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use)
+ LPCSTR *szName) PURE; // [OUT] The name
+
+ STDMETHOD(GetGenericParamConstraintProps)( // S_OK or error.
+ mdGenericParamConstraint rd, // [IN] The constraint token
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType) PURE; // [OUT] TypeDef/Ref/Spec constraint
+
+ //*****************************************************************************
+ // This function gets the "built for" version of a metadata scope.
+ // NOTE: if the scope has never been saved, it will not have a built-for
+ // version, and an empty string will be returned.
+ //*****************************************************************************
+ STDMETHOD(GetVersionString)( // S_OK or error.
+ LPCSTR *pVer) PURE; // [OUT] Put version string here.
+
+
+ STDMETHOD(SafeAndSlowEnumCustomAttributeByNameInit)(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] The enumerator
+
+ STDMETHOD(SafeAndSlowEnumCustomAttributeByNameNext)(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum, // [IN] The enumerator
+ mdCustomAttribute *mdAttribute) PURE; // [OUT] The custom attribute that was found
+
+
+ STDMETHOD(GetTypeDefRefTokenInTypeSpec)(// return S_FALSE if enclosing type does not have a token
+ mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at
+ mdToken *tkEnclosedToken) PURE; // [OUT] The enclosed type token
+
+#define MD_STREAM_VER_1X 0x10000
+#define MD_STREAM_VER_2_B1 0x10001
+#define MD_STREAM_VER_2 0x20000
+ STDMETHOD_(DWORD, GetMetadataStreamVersion)() PURE; //returns DWORD with major version of
+ // MD stream in senior word and minor version--in junior word
+
+ STDMETHOD(GetNameOfCustomAttribute)(// S_OK or error
+ mdCustomAttribute mdAttribute, // [IN] The Custom Attribute
+ LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute.
+ LPCUTF8 *pszName) PURE; // [OUT] Name of Custom Attribute.
+
+ STDMETHOD(SetOptimizeAccessForSpeed)(// S_OK or error
+ BOOL fOptSpeed) PURE;
+
+ STDMETHOD(SetVerifiedByTrustedSource)(// S_OK or error
+ BOOL fVerified) PURE;
+
+}; // IMDInternalImport
+
+EXTERN_GUID(IID_IMetaDataHelper, 0xad93d71d, 0xe1f2, 0x11d1, 0x94, 0x9, 0x0, 0x0, 0xf8, 0x8, 0x34, 0x60);
+
+#undef INTERFACE
+#define INTERFACE IMetaDataHelper
+DECLARE_INTERFACE_(IMetaDataHelper, IUnknown)
+{
+ // helper functions
+ // This function is exposing the ability to translate signature from a given
+ // source scope to a given target scope.
+ //
+ STDMETHOD(TranslateSigWithScope)(
+ IMetaDataAssemblyImport *pAssemImport, // [IN] importing assembly interface
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *import, // [IN] importing interface
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] emit assembly interface
+ IMetaDataEmit *emit, // [IN] emit interface
+ PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature
+ ULONG cbTranslatedSigMax,
+ ULONG *pcbTranslatedSig) PURE;// [OUT] count of bytes in the translated signature
+
+ STDMETHOD(GetMetadata)(
+ ULONG ulSelect, // [IN] Selector.
+ void **ppData) PURE; // [OUT] Put pointer to data here.
+
+ STDMETHOD_(IUnknown *, GetCachedInternalInterface)(BOOL fWithLock) PURE; // S_OK or error
+ STDMETHOD(SetCachedInternalInterface)(IUnknown * pUnk) PURE; // S_OK or error
+ // since SOS does not use the next 2 methods replace UTSemReadWrite* with void* in the signature
+ STDMETHOD_(void*, GetReaderWriterLock)() PURE; // return the reader writer lock
+ STDMETHOD(SetReaderWriterLock)(void * pSem) PURE;
+};
+
+/********************************************************************************/
+
+// Fine grained formatting flags used by the PrettyPrint APIs below.
+// Upto FormatStubInfo they mirror the values used by TypeString, after that
+// they're used to enable specifying differences between the ILDASM-style
+// output and the C#-like output prefered by the rest of SOS.
+typedef enum
+{
+ FormatBasic = 0x00000000, // Not a bitmask, simply the tersest flag settings possible
+ FormatNamespace = 0x00000001, // Include namespace and/or enclosing class names in type names
+ FormatFullInst = 0x00000002, // Include namespace and assembly in generic types (regardless of other flag settings)
+ FormatAssembly = 0x00000004, // Include assembly display name in type names
+ FormatSignature = 0x00000008, // Include signature in method names
+ FormatNoVersion = 0x00000010, // Suppress version and culture information in all assembly names
+ FormatDebug = 0x00000020, // For debug printing of types only
+ FormatAngleBrackets = 0x00000040, // Whether generic types are C<T> or C[T]
+ FormatStubInfo = 0x00000080, // Include stub info like {unbox-stub}
+ // following flags are not present in TypeString::FormatFlags
+ FormatSlashSep = 0x00000100, // Whether nested types are NS.C1/C2 or NS.C1+C2
+ FormatKwInNames = 0x00000200, // Whether "class" and "valuetype" appear in type names in certain instances
+ FormatCSharp = 0x0000004b, // Used to generate a C#-like string representation of the token
+ FormatILDasm = 0x000003ff, // Used to generate an ILDASM-style string representation of the token
+}
+PPFormatFlags;
+
+char* asString(CQuickBytes *out);
+
+PCCOR_SIGNATURE PrettyPrintType(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI, // ptr to IMDInternal class with ComSig
+ DWORD formatFlags = FormatILDasm);
+
+const char* PrettyPrintClass(
+ CQuickBytes *out, // where to put the pretty printed string
+ mdToken tk, // The class token to look up
+ IMDInternalImport *pIMDI, // ptr to IMDInternalImport class with ComSig
+ DWORD formatFlags = FormatILDasm);
+
+// We have a proliferation of functions that translate a (module/token) pair to
+// a string, but none of them were as complete as PrettyPrintClass. Most were
+// not handling generic instantiations appropriately. PrettyPrintClassFromToken
+// provides this missing functionality. If passed "FormatCSharp" it will generate
+// a name fitting the format used throughout SOS, with the exception of !dumpil
+// (due to its ILDASM ancestry).
+// TODO: Refactor the code in PrettyPrintClassFromToken, NameForTypeDef_s,
+// TODO: NameForToken_s, MDInfo::TypeDef/RefName
+void PrettyPrintClassFromToken(
+ TADDR moduleAddr, // the module containing the token
+ mdToken tok, // the class token to look up
+ __out_ecount(cbName) WCHAR* mdName, // where to put the pretty printed string
+ size_t cbName, // the capacity of the buffer
+ DWORD formatFlags = FormatCSharp); // the format flags for the types
+
+inline HRESULT GetMDInternalFromImport(IMetaDataImport* pIMDImport, IMDInternalImport **ppIMDI)
+{
+ HRESULT hresult = E_FAIL;
+ IUnknown *pUnk = NULL;
+ IMetaDataHelper *pIMDH = NULL;
+
+ IfErrGoTo(pIMDImport->QueryInterface(IID_IMetaDataHelper, (void**)&pIMDH), Cleanup);
+ pUnk = pIMDH->GetCachedInternalInterface(FALSE);
+ if (pUnk == NULL)
+ goto Cleanup;
+ IfErrGoTo(pUnk->QueryInterface(IID_IMDInternalImport, (void**)ppIMDI), Cleanup);
+
+Cleanup:
+ if (pUnk)
+ pUnk->Release();
+ if (pIMDH != NULL)
+ pIMDH->Release();
+
+ return hresult;
+}
+
+#endif
+
diff --git a/src/ToolBox/SOS/Strike/sos_stacktrace.h b/src/ToolBox/SOS/Strike/sos_stacktrace.h
new file mode 100644
index 0000000000..4aba4ea52c
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos_stacktrace.h
@@ -0,0 +1,174 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+/* ---------------------------------------------------------------------------
+
+ SOS_Stacktrace.h
+
+ API exported from SOS.DLL for retrieving managed stack traces.
+ This extension function is called through the Windows Debugger extension
+ interfaces (dbgeng.h).
+
+ Additional functions exported from SOS are documented here as well.
+
+Notes:
+
+HRESULT CALLBACK _EFN_StackTrace(
+ PDEBUG_CLIENT client,
+ WCHAR wszTextOut[],
+ UINT *puiTextLength,
+ LPVOID pTransitionContexts,
+ UINT *puiTransitionContextCount,
+ UINT uiSizeOfContext);
+
+uiSizeOfContext must be either sizeof(SimpleContext) or sizeof(CONTEXT) for the
+architecture (x86, IA64, x64).
+
+if wszTextOut is NULL and *puiTextLength is non-NULL, the function will return
+the necessary string length in *puiTextLength.
+
+If wszTextOut is non-NULL, the function will fill wszTextOut up to the point
+given by *puiTextLength, returning success if there was enough room in the
+buffer or E_OUTOFMEMORY if the buffer wasn't long enough.
+
+The transition portion of the function will be completely ignored if
+pTransitionContexts and puiTransitionContextCount are both NULL. Some callers
+would just like text output of the function names.
+
+If pTransitionContexts is NULL and puiTransitionContextCount is non NULL, the
+function will return the necessary number of context entries in
+*puiTransitionContextCount.
+
+If pTransitionContexts is non NULL, the function will treat it as an array of
+structures of length *puiTransitionContextCount. The structure size is given
+by uiSizeOfContext, and must be the size of SimpleContext or CONTEXT for the
+architecture.
+
+wszTextOut will be written in the following format:
+
+"<ModuleName>!<Function Name>[+<offset in hex>]
+...
+(TRANSITION)
+..."
+
+if the offset in hex is 0, no offset will be written (this matches KB output).
+
+If there is no managed code on the thread currently in context,
+SOS_E_NOMANAGEDCODE will be returned.
+ ------------------------------------------------------------------------ */
+#ifndef __STACKTRACE_H
+#define __STACKTRACE_H
+#include <windows.h>
+#include <winerror.h>
+
+#ifndef FACILITY_SOS
+#define FACILITY_SOS 0xa0
+#endif
+
+#ifndef EMAKEHR
+#define EMAKEHR(val) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SOS, val)
+#endif
+
+// Custom Error returns
+#define SOS_E_NOMANAGEDCODE EMAKEHR(0x1000) // No managed code on the stack
+
+// Flags
+//
+// Turn on SOS_STACKTRACE_SHOWADDRESSES to see EBP ESP in front of each
+// module!functionname line. By default this is off.
+#define SOS_STACKTRACE_SHOWADDRESSES 0x00000001
+
+struct StackTrace_SimpleContext
+{
+ ULONG64 StackOffset; // esp on x86
+ ULONG64 FrameOffset; // ebp
+ ULONG64 InstructionOffset; // eip
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+HRESULT CALLBACK _EFN_StackTrace(
+ PDEBUG_CLIENT client,
+ __out_ecount(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags);
+
+/* ---------------------------------------------------------------------------
+
+ Additional functions are exported from SOS, and are useful
+ for debugging tasks with managed object pointers.
+
+ ------------------------------------------------------------------------ */
+
+// _EFN_GetManagedExcepStack - given a managed exception object address, returns a string
+// version of the stack trace contained inside.
+//
+// StackObjAddr - a managed object pointer, must be derived from System.Exception
+// szStackString - the string returned (out parameter)
+// cbString - number of characters available in the string buffer.
+//
+// The output will be truncated of cbString is not long enough for the full stack trace.
+HRESULT _EFN_GetManagedExcepStack(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount(cbString) PSTR szStackString,
+ ULONG cbString
+ );
+
+// _EFN_GetManagedExcepStackW - same as _EFN_GetManagedExcepStack, but returns
+// the stack as a wide string.
+HRESULT _EFN_GetManagedExcepStackW(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString
+ );
+
+// _EFN_GetManagedObjectName - given a managed object pointer, return the type name
+//
+// objAddr - a managed object pointer
+// szName - a buffer to be filled with the full type name
+// cbName - the number of characters available in the buffer
+//
+HRESULT _EFN_GetManagedObjectName(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount(cbName) PSTR szName,
+ ULONG cbName
+ );
+
+// _EFN_GetManagedObjectFieldInfo - given an object pointer and a field name, returns
+// the offset to the field from the start of the object,
+// and the field's value.
+//
+// objAddr - a managed object pointer
+// szFieldName - the field name you are interested in
+// pValue - the field value is written here. This parameter can be NULL.
+// pOffset - the offset from objAddr to the field. This parameter can be NULL.
+//
+// At least one of pValue and pOffset must be non-NULL.
+HRESULT _EFN_GetManagedObjectFieldInfo(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount (mdNameLen) PSTR szFieldName,
+ PULONG64 pValue,
+ PULONG pOffset
+ );
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus : extern "C"
+
+#endif // __STACKTRACE_H
+
diff --git a/src/ToolBox/SOS/Strike/sos_unixexports.src b/src/ToolBox/SOS/Strike/sos_unixexports.src
new file mode 100644
index 0000000000..ed811b6572
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos_unixexports.src
@@ -0,0 +1,54 @@
+; Licensed to the .NET Foundation under one or more agreements.
+; The .NET Foundation licenses this file to you under the MIT license.
+; See the LICENSE file in the project root for more information.
+
+bpmd
+ClrStack
+DumpArray
+DumpAssembly
+DumpClass
+DumpDomain
+DumpGCData
+DumpHeap
+DumpIL
+DumpLog
+DumpMD
+DumpModule
+DumpMT
+DumpObj
+DumpRuntimeTypes
+DumpSig
+DumpSigElem
+DumpStack
+DumpStackObjects
+DumpVC
+EEHeap
+GCWhere
+EEStack
+EHInfo
+FindAppDomain
+GCInfo
+GCRoot
+Help
+HistClear
+HistInit
+HistObj
+HistObjFind
+HistRoot
+HistStats
+IP2MD
+Name2EE
+PrintException
+StopOnCatch
+Threads
+ThreadState
+Token2EE
+u
+VerifyHeap
+VerifyStackTrace
+
+_EFN_GetManagedExcepStack
+_EFN_GetManagedExcepStackW
+_EFN_GetManagedObjectFieldInfo
+_EFN_GetManagedObjectName
+_EFN_StackTrace
diff --git a/src/ToolBox/SOS/Strike/sosdocs.txt b/src/ToolBox/SOS/Strike/sosdocs.txt
new file mode 100644
index 0000000000..594fca1bc2
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sosdocs.txt
@@ -0,0 +1,2572 @@
+-------------------------------------------------------------------------------
+NOTE: THIS FILE CONTAINS SOS DOCUMENTATION. THE FORMAT OF THE FILE IS:
+
+<optional comments>
+COMMAND: <cmd name, all lower case>
+<descriptive text of the command>
+\\ <these are two backslashes, immediately followed by a newline>
+
+<repeat the sequence above>
+
+The first command is "contents" which is the general help screen. The rest
+correspond to SOS command names. This file is embedded as a resource in the SOS
+binary. Be sure to list any new commands here.
+-------------------------------------------------------------------------------
+
+
+
+COMMAND: contents.
+SOS is a debugger extension DLL designed to aid in the debugging of managed
+programs. Functions are listed by category, then roughly in order of
+importance. Shortcut names for popular functions are listed in parenthesis.
+Type "!help <functionname>" for detailed info on that function.
+
+Object Inspection Examining code and stacks
+----------------------------- -----------------------------
+DumpObj (do) Threads
+DumpArray (da) ThreadState
+DumpStackObjects (dso) IP2MD
+DumpHeap U
+DumpVC DumpStack
+GCRoot EEStack
+ObjSize CLRStack
+FinalizeQueue GCInfo
+PrintException (pe) EHInfo
+TraverseHeap BPMD
+ COMState
+
+Examining CLR data structures Diagnostic Utilities
+----------------------------- -----------------------------
+DumpDomain VerifyHeap
+EEHeap VerifyObj
+Name2EE FindRoots
+SyncBlk HeapStat
+DumpMT GCWhere
+DumpClass ListNearObj (lno)
+DumpMD GCHandles
+Token2EE GCHandleLeaks
+EEVersion FinalizeQueue (fq)
+DumpModule FindAppDomain
+ThreadPool SaveModule
+DumpAssembly ProcInfo
+DumpSigElem StopOnException (soe)
+DumpRuntimeTypes DumpLog
+DumpSig VMMap
+RCWCleanupList VMStat
+DumpIL MinidumpMode
+DumpRCW AnalyzeOOM (ao)
+DumpCCW
+
+Examining the GC history Other
+----------------------------- -----------------------------
+HistInit FAQ
+HistRoot
+HistObj
+HistObjFind
+HistClear
+\\
+
+COMMAND: faq.
+>> Where can I get the right version of SOS for my build?
+
+If you are running version 1.1 or 2.0 of the CLR, SOS.DLL is installed in the
+same directory as the main CLR dll (CLR.DLL). Newer versions of the
+Windows Debugger provide a command to make it easy to load the right copy of
+SOS.DLL:
+
+ ".loadby sos clr"
+
+That will load the SOS extension DLL from the same place that CLR.DLL is
+loaded in the process. You shouldn't attempt to use a version of SOS.DLL that
+doesn't match the version of CLR.DLL. You can find the version of
+CLR.DLL by running
+
+ "lmvm clr"
+
+in the debugger. Note that if you are running CoreCLR (e.g. Silverlight)
+then you should replace "clr" with "coreclr".
+
+If you are using a dump file created on another machine, it is a little bit
+more complex. You need to make sure the mscordacwks.dll file that came with
+that install is on your symbol path, and you need to load the corresponding
+version of sos.dll (typing .load <full path to sos.dll> rather than using the
+.loadby shortcut). Within the Microsoft corpnet, we keep tagged versions
+of mscordacwks.dll, with names like mscordacwks_<architecture>_<version>.dll
+that the Windows Debugger can load. If you have the correct symbol path to the
+binaries for that version of the Runtime, the Windows Debugger will load the
+correct mscordacwks.dll file.
+
+>> I have a chicken and egg problem. I want to use SOS commands, but the CLR
+ isn't loaded yet. What can I do?
+
+In the debugger at startup you can type:
+
+ "sxe clrn"
+
+Let the program run, and it will stop with the notice
+
+ "CLR notification: module 'mscorlib' loaded"
+
+At this time you can use SOS commands. To turn off spurious notifications,
+type:
+
+ "sxd clrn"
+
+>> I got the following error message. Now what?
+
+ 0:000> .loadby sos clr
+ 0:000> !DumpStackObjects
+ Failed to find runtime DLL (clr.dll), 0x80004005
+ Extension commands need clr.dll in order to have something to do.
+ 0:000>
+
+This means that the CLR is not loaded yet, or has been unloaded. You need to
+wait until your managed program is running in order to use these commands. If
+you have just started the program a good way to do this is to type
+
+ bp clr!EEStartup "g @$ra"
+
+in the debugger, and let it run. After the function EEStartup is finished,
+there will be a minimal managed environment for executing SOS commands.
+
+>> I have a partial memory minidump, and !DumpObj doesn't work. Why?
+
+In order to run SOS commands, many CLR data structures need to be traversed.
+When creating a minidump without full memory, special functions are called at
+dump creation time to bring those structures into the minidump, and allow a
+minimum set of SOS debugging commands to work. At this time, those commands
+that can provide full or partial output are:
+
+CLRStack
+Threads
+Help
+PrintException
+EEVersion
+
+For a minidump created with this minimal set of functionality in mind, you
+will get an error message when running any other commands. A full memory dump
+(obtained with ".dump /ma <filename>" in the Windows Debugger) is often the
+best way to debug a managed program at this level.
+
+>> What other tools can I use to find my bug?
+
+Turn on Managed Debugging Assistants. These enable additional runtime diagnostics,
+particularly in the area of PInvoke/Interop. Adam Nathan has written some great
+information about that:
+
+http://blogs.msdn.com/adam_nathan/
+
+>> Does SOS support DML?
+
+Yes. SOS respects the .prefer_dml option in the debugger. If this setting is
+turned on, then SOS will output DML by default. Alternatively, you may leave
+it off and add /D to the beginning of a command to get DML based output for it.
+Not all SOS commands support DML output.
+
+\\
+
+COMMAND: stoponexception.
+!StopOnException [-derived]
+ [-create | -create2]
+ <Exception>
+ [<Pseudo-register number>]
+
+!StopOnException helps when you want the Windows Debugger to stop on a
+particular managed exception, say a System.OutOfMemoryException, but continue
+running if other exceptions are thrown. The command can be used in two ways:
+
+1) When you just want to stop on one particular CLR exception
+
+ At the debugger prompt, anytime after loading SOS, type:
+
+ !StopOnException -create System.OutOfMemoryException 1
+
+ The pseudo-register number (1) indicates that SOS can use register $t1 for
+ maintaining the breakpoint. The -create parameter allows SOS to go ahead
+ and set up the breakpoint as a first-chance exception. -create2 would set
+ it up as a 2nd-chance exception.
+
+2) When you need more complex logic for stopping on a CLR exception
+
+ !StopOnException can be used purely as a predicate in a larger expression.
+ If you type:
+
+ !StopOnException System.OutOfMemoryException 3
+
+ then register $t3 will be set to 1 if the last thrown exception on the
+ current thread is a System.OutOfMemoryException. Otherwise, $t3 will be set
+ to 0. Using the Windows Debugger scripting language, you could chain
+ such calls together to stop on various exception types. You'll have to
+ manually create such predicates, for example:
+
+ sxe -c "!soe System.OutOfMemoryException 3;
+ !soe -derived System.IOException 4;
+ .if(@$t3==1 || @$t4==1) { .echo 'stop' } .else {g}"
+
+The -derived option will cause StopOnException to set the pseudo-register to
+1 even if the thrown exception type doesn't exactly match the exception type
+given, but merely derives from it. So, "-derived System.Exception" would catch
+every exception in the System.Exception heirarchy.
+
+The pseudo-register number is optional. If you don't pass a number, SOS will
+use pseudo-register $t1.
+
+Note that !PrintException with no parameters will print out the last thrown
+exception on the current thread (if any). You can use !soe as a shortcut for
+!StopOnException.
+\\
+
+COMMAND: minidumpmode.
+!MinidumpMode <0 or 1>
+
+Minidumps created with ".dump /m" or ".dump" have a very small set of
+CLR-specific data, just enough to run a subset of SOS commands correctly. You
+are able to run other SOS commands, but they may fail with unexpected errors
+because required areas of memory are not mapped in or only partially mapped
+in. At this time, SOS cannot reliably detect if a dump file is of this type
+(for one thing, custom dump commands can map in additional memory, but there
+is no facility to read meta-information about this memory). You can turn this
+option on to protect against running unsafe commands against small minidumps.
+
+By default, MinidumpMode is 0, so there is no restriction on commands that will
+run against a minidump.
+\\
+
+COMMAND: dumpobj.
+!DumpObj [-nofields] <object address>
+
+This command allows you to examine the fields of an object, as well as learn
+important properties of the object such as the EEClass, the MethodTable, and
+the size.
+
+You might find an object pointer by running !DumpStackObjects and choosing
+from the resultant list. Here is a simple object:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 009038ec 4000008 4 Customer 0 instance 00a79ce4 name
+ 009038ec 4000009 8 Bank 0 instance 00a79d2c bank
+
+Note that fields of type Customer and Bank are themselves objects, and you can
+run !DumpObj on them too. You could look at the field directly in memory using
+the offset given. "dd a79d40+8 l1" would allow you to look at the bank field
+directly. Be careful about using this to set memory breakpoints, since objects
+can move around in the garbage collected heap.
+
+What else can you do with an object? You might run !GCRoot, to determine what
+roots are keeping it alive. Or you can find all objects of that type with
+"!DumpHeap -type Customer".
+
+The column VT contains the value 1 if the field is a valuetype structure, and
+0 if the field contains a pointer to another object. For valuetypes, you can
+take the MethodTable pointer in the MT column, and the Value and pass them to
+the command !DumpVC.
+
+The abbreviation !do can be used for brevity.
+
+The arguments in detail:
+-nofields: do not print fields of the object, useful for objects like
+ String
+\\
+
+COMMAND: dumparray.
+!DumpArray
+ [-start <startIndex>]
+ [-length <length>]
+ [-details]
+ [-nofields]
+ <array object address>
+
+This command allows you to examine elements of an array object.
+The arguments in detail:
+ -start <startIndex>: optional, only supported for single dimension array.
+ Specify from which index the command shows the elements.
+ -length <length>: optional, only supported for single dimension array.
+ Specify how many elements to show.
+ -details: optional. Ask the command to print out details
+ of the element using !DumpObj and !DumpVC format.
+ -nofields: optional, only takes effect when -details is used. Do
+ not print fields of the elements. Useful for arrays of
+ objects like String
+
+ Example output:
+
+ 0:000> !dumparray -start 2 -length 3 -details 00ad28d0
+ Name: Value[]
+ MethodTable: 03e41044
+ EEClass: 03e40fc0
+ Size: 132(0x84) bytes
+ Array: Rank 1, Number of elements 10, Type VALUETYPE
+ Element Type: Value
+ [2] 00ad28f0
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 2 x
+ 5b9a628c 4000002 4 System.Int32 instance 4 y
+ 5b9a628c 4000003 8 System.Int32 instance 6 z
+ [3] 00ad28fc
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 3 x
+ 5b9a628c 4000002 4 System.Int32 instance 6 y
+ 5b9a628c 4000003 8 System.Int32 instance 9 z
+ [4] 00ad2908
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 4 x
+ 5b9a628c 4000002 4 System.Int32 instance 8 y
+ 5b9a628c 4000003 8 System.Int32 instance 12 z
+
+
+\\
+
+COMMAND: dumpstackobjects.
+!DumpStackObjects [-verify] [top stack [bottom stack]]
+
+This command will display any managed objects it finds within the bounds of
+the current stack. Combined with the stack tracing commands like K and
+!CLRStack, it is a good aid to determining the values of locals and
+parameters.
+
+If you use the -verify option, each non-static CLASS field of an object
+candidate is validated. This helps to eliminate false positives. It is not
+on by default because very often in a debugging scenario, you are
+interested in objects with invalid fields.
+
+The abbreviation !dso can be used for brevity.
+\\
+
+COMMAND: dumpheap.
+!DumpHeap [-stat]
+ [-strings]
+ [-short]
+ [-min <size>]
+ [-max <size>]
+ [-live]
+ [-dead]
+ [-thinlock]
+ [-startAtLowerBound]
+ [-mt <MethodTable address>]
+ [-type <partial type name>]
+ [start [end]]
+
+!DumpHeap is a powerful command that traverses the garbage collected heap,
+collection statistics about objects. With it's various options, it can look for
+particular types, restrict to a range, or look for ThinLocks (see !SyncBlk
+documentation). Finally, it will provide a warning if it detects excessive
+fragmentation in the GC heap.
+
+When called without options, the output is first a list of objects in the heap,
+followed by a report listing all the types found, their size and number:
+
+ 0:000> !dumpheap
+ Address MT Size
+ 00a71000 0015cde8 12 Free
+ 00a7100c 0015cde8 12 Free
+ 00a71018 0015cde8 12 Free
+ 00a71024 5ba58328 68
+ 00a71068 5ba58380 68
+ 00a710ac 5ba58430 68
+ 00a710f0 5ba5dba4 68
+ ...
+ total 619 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource
+ 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag
+ 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer
+ ...
+ 0015cde8 6 10260 Free
+ 5ba57bf8 318 18136 System.String
+ ...
+
+"Free" objects are simply regions of space the garbage collector can use later.
+If 30% or more of the heap contains "Free" objects, the process may suffer from
+heap fragmentation. This is usually caused by pinning objects for a long time
+combined with a high rate of allocation. Here is example output where !DumpHeap
+provides a warning about fragmentation:
+
+ <After the Statistics section>
+ Fragmented blocks larger than 1MB:
+ Addr Size Followed by
+ 00a780c0 1.5MB 00bec800 System.Byte[]
+ 00da4e38 1.2MB 00ed2c00 System.Byte[]
+ 00f16df0 1.2MB 01044338 System.Byte[]
+
+The arguments in detail:
+
+-stat Restrict the output to the statistical type summary
+-strings Restrict the output to a statistical string value summary
+-short Limits output to just the address of each object. This allows you
+ to easily pipe output from the command to another debugger
+ command for automation.
+-min Ignore objects less than the size given in bytes
+-max Ignore objects larger than the size given in bytes
+-live Only print live objects
+-dead Only print dead objects (objects which will be collected in the
+ next full GC)
+-thinlock Report on any ThinLocks (an efficient locking scheme, see !SyncBlk
+ documentation for more info)
+-startAtLowerBound
+ Force heap walk to begin at lower bound of a supplied address range.
+ (During plan phase, the heap is often not walkable because objects
+ are being moved. In this case, DumpHeap may report spurious errors,
+ in particular bad objects. It may be possible to traverse more of
+ the heap after the reported bad object. Even if you specify an
+ address range, !DumpHeap will start its walk from the beginning of
+ the heap by default. If it finds a bad object before the specified
+ range, it will stop before displaying the part of the heap in which
+ you are interested. This switch will force !DumpHeap to begin its
+ walk at the specified lower bound. You must supply the address of a
+ good object as the lower bound for this to work. Display memory at
+ the address of the bad object to manually find the next method
+ table (use !dumpmt to verify). If the GC is currently in a call to
+ memcopy, You may also be able to find the next object's address by
+ adding the size to the start address given as parameters.)
+-mt List only those objects with the MethodTable given
+-type List only those objects whose type name is a substring match of the
+ string provided.
+start Begin listing from this address
+end Stop listing at this address
+
+A special note about -type: Often, you'd like to find not only Strings, but
+System.Object arrays that are constrained to contain Strings. ("new
+String[100]" actually creates a System.Object array, but it can only hold
+System.String object pointers). You can use -type in a special way to find
+these arrays. Just pass "-type System.String[]" and those Object arrays will
+be returned. More generally, "-type <Substring of interesting type>[]".
+
+The start/end parameters can be obtained from the output of !EEHeap -gc. For
+example, if you only want to list objects in the large heap segment:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00c32754
+ generation 1 starts at 0x00c32748
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 010443a8 005d33a8(6108072)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a75000 0x00004000(16384)
+ Total Size 0x5d73a8(6124456)
+ ------------------------------
+ GC Heap Size 0x5d73a8(6124456)
+
+ 0:000> !dumpheap 1a71000 1a75000
+ Address MT Size
+ 01a71000 5ba88bd8 2064
+ 01a71810 0019fe48 2032 Free
+ 01a72000 5ba88bd8 4096
+ 01a73000 0019fe48 4096 Free
+ 01a74000 5ba88bd8 4096
+ total 5 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 0019fe48 2 6128 Free
+ 5ba88bd8 3 10256 System.Object[]
+ Total 5 objects
+
+Finally, if GC heap corruption is present, you may see an error like this:
+
+ 0:000> !dumpheap -stat
+ object 00a73d24: does not have valid MT
+ curr_object : 00a73d24
+ Last good object: 00a73d14
+ ----------------
+
+That indicates a serious problem. See the help for !VerifyHeap for more
+information on diagnosing the cause.
+\\
+
+COMMAND: dumpvc.
+!DumpVC <MethodTable address> <Address>
+
+!DumpVC allows you to examine the fields of a value class. In C#, this is a
+struct, and lives on the stack or within an Object on the GC heap. You need
+to know the MethodTable address to tell SOS how to interpret the fields, as
+a value class is not a first-class object with it's own MethodTable as the
+first field. For example:
+
+ 0:000> !DumpObj a79d98
+ Name: Mainy
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype
+ 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep
+
+m_valuetype is a value type. The value in the MT column (0090320c) is the
+MethodTable for it, and the Value column provides the start address:
+
+ 0:000> !DumpVC 0090320c 00a79d9c
+ Name: Funny
+ MethodTable 0090320c
+ EEClass: 03ee14b8
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000001 0 CLASS instance 00a743d8 signature
+ 0090320c 4000002 8 System.Int32 instance 2345 m1
+ 0090320c 4000003 10 System.Boolean instance 1 b1
+ 0090320c 4000004 c System.Int32 instance 1234 m2
+ 0090320c 4000005 4 CLASS instance 00a79d98 backpointer
+
+!DumpVC is quite a specialized function. Some managed programs make heavy use
+of value classes, while others do not.
+\\
+
+COMMAND: gcroot.
+!GCRoot [-nostacks] <Object address>
+
+!GCRoot looks for references (or roots) to an object. These can exist in four
+places:
+
+ 1. On the stack
+ 2. Within a GC Handle
+ 3. In an object ready for finalization
+ 4. As a member of an object found in 1, 2 or 3 above.
+
+First, all stacks will be searched for roots, then handle tables, and finally
+the freachable queue of the finalizer. Some caution about the stack roots:
+!GCRoot doesn't attempt to determine if a stack root it encountered is valid
+or is old (discarded) data. You would have to use !CLRStack and !U to
+disassemble the frame that the local or argument value belongs to in order to
+determine if it is still in use.
+
+Because people often want to restrict the search to gc handles and freachable
+objects, there is a -nostacks option.
+\\
+
+COMMAND: objsize.
+!ObjSize [<Object address>]
+
+With no parameters, !ObjSize lists the size of all objects found on managed
+threads. It also enumerates all GCHandles in the process, and totals the size
+of any objects pointed to by those handles. In calculating object size,
+!ObjSize includes the size of all child objects in addition to the parent.
+
+For example, !DumpObj lists a size of 20 bytes for this Customer object:
+
+ 0:000> !do a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+but !ObjSize lists 152 bytes:
+
+ 0:000> !ObjSize a79d40
+ sizeof(00a79d40) = 152 ( 0x98) bytes (Customer)
+
+This is because a Customer points to a Bank, has a name, and the Bank points to
+an Address string. You can use !ObjSize to identify any particularly large
+objects, such as a managed cache in a web server.
+
+While running ObjSize with no arguments may point to specific roots that hold
+onto large amounts of memory it does not provide information regarding the
+amount of managed memory that is still alive. This is due to the fact that a
+number of roots can share a common subgraph, and that part will be reported in
+the size of all the roots that reference the subgraph.
+
+Please note the -aggregate parameter to !ObjSize has been removed. Please see
+'!DumpHeap -live' and '!DumpHeap -dead' for that functionality.
+
+\\
+
+COMMAND: finalizequeue.
+!FinalizeQueue [-detail] | [-allReady] [-short]
+
+This command lists the objects registered for finalization. Here is output from
+a simple program:
+
+ 0:000> !finalizequeue
+ SyncBlocks to be cleaned up: 0
+ MTA Interfaces to be released: 0
+ STA Interfaces to be released: 1
+ generation 0 has 4 finalizable objects (0015bc90->0015bca0)
+ generation 1 has 0 finalizable objects (0015bc90->0015bc90)
+ generation 2 has 0 finalizable objects (0015bc90->0015bc90)
+ Ready for finalization 0 objects (0015bca0->0015bca0)
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba6cf78 1 24 Microsoft.Win32.SafeHandles.SafeFileHandle
+ 5ba5db04 1 68 System.Threading.Thread
+ 5ba73e28 2 112 System.IO.StreamWriter
+ Total 4 objects
+
+The GC heap is divided into generations, and objects are listed accordingly. We
+see that only generation 0 (the youngest generation) has any objects registered
+for finalization. The notation "(0015bc90->0015bca0)" means that if you look at
+memory in that range, you'll see the object pointers that are registered:
+
+0:000> dd 15bc90 15bca0-4
+0015bc90 00a743f4 00a79f00 00a7b3d8 00a7b47c
+
+You could run !DumpObj on any of those pointers to learn more. In this example,
+there are no objects ready for finalization, presumably because they still have
+roots (You can use !GCRoot to find out). The statistics section provides a
+higher-level summary of the objects registered for finalization. Note that
+objects ready for finalization are also included in the statistics (if any).
+
+Specifying -short will inhibit any display related to SyncBlocks or RCWs.
+
+The arguments in detail:
+
+-allReady Specifying this argument will allow for the display of all objects
+ that are ready for finalization, whether they are already marked by
+ the GC as such, or whether the next GC will. The objects that are
+ not in the "Ready for finalization" list are finalizable objects that
+ are no longer rooted. This option can be very expensive, as it
+ verifies whether all the objects in the finalizable queues are still
+ rooted or not.
+-short Limits the output to just the address of each object. If used in
+ conjunction with -allReady it enumerates all objects that have a
+ finalizer that are no longer rooted. If used independently it lists
+ all objects in the finalizable and "ready for finalization" queues.
+-detail Will display extra information on any SyncBlocks that need to be
+ cleaned up, and on any RuntimeCallableWrappers (RCWs) that await
+ cleanup. Both of these data structures are cached and cleaned up by
+ the finalizer thread when it gets a chance to run.
+\\
+
+COMMAND: printexception.
+!PrintException [-nested] [-lines] [-ccw] [<Exception object address>] [<CCW pointer>]
+
+This will format fields of any object derived from System.Exception. One of the
+more useful aspects is that it will format the _stackTrace field, which is a
+binary array. If _stackTraceString field is not filled in, that can be helpful
+for debugging. You can of course use !DumpObj on the same exception object to
+explore more fields.
+
+If called with no parameters, PrintException will look for the last outstanding
+exception on the current thread and print it. This will be the same exception
+that shows up in a run of !Threads.
+
+!PrintException will notify you if there are any nested exceptions on the
+current managed thread. (A nested exception occurs when you throw another
+exception within a catch handler already being called for another exception).
+If there are nested exceptions, you can re-run !PrintException with the
+"-nested" option to get full details on the nested exception objects. The
+!Threads command will also tell you which threads have nested exceptions.
+
+!PrintException can display source information if available, by specifying the
+-lines command line argument.
+
+!PrintException prints the exception object corresponding to a given CCW pointer,
+which can be specified using the -ccw option.
+
+The abbreviation !pe can be used for brevity.
+\\
+
+COMMAND: traverseheap.
+!TraverseHeap [-xml] [-verify] <filename>
+
+!TraverseHeap writes out a file in a format understood by the CLR Profiler.
+You can download the CLR Profiler from this link:
+
+http://www.microsoft.com/downloads/details.aspx?FamilyId=86CE6052-D7F4-4AEB-
+9B7A-94635BEEBDDA&displaylang=en
+
+It creates a graphical display of the GC heap to help you analyze the state of
+your application.
+
+If you pass the -verify option it will do more sanity checking of the heap
+as it dumps it. Use this option if heap corruption is suspected.
+
+If you pass the "-xml" flag, the file is instead written out in an easy to
+understand xml format:
+
+ <gcheap>
+ <types>
+ <type id="1" name="System.String">
+ ...
+ </types>
+ <roots>
+ <root kind="handle" address="0x00a73ff0"/>
+ <root kind="stack" address="0x0069f0e0"/>
+ ...
+ </roots>
+ <objects>
+ <object address="0x00b73030" typeid="1" size="300"/>
+ <object address="0x00b75054" typeid="5" size="20">
+ <member address="0x00b75088" />
+ ...
+ </object>
+ ...
+ </objects>
+ </gcheap>
+
+You can break into your process, load SOS, take a snapshot of your heap with
+this function, then continue.
+\\
+COMMAND: threadstate.
+!ThreadState value
+
+The !Threads command outputs, among other things, the state of the thread.
+This is a bit field which corresponds to various states the thread is in.
+To check the state of the thread, simply pass that bit field from the
+output of !Threads into !ThreadState.
+
+Example:
+ 0:003> !Threads
+ ThreadCount: 2
+ UnstartedThread: 0
+ BackgroundThread: 1
+ PendingThread: 0
+ DeadThread: 0
+ Hosted Runtime: no
+ PreEmptive GC Alloc Lock
+ ID OSID ThreadOBJ State GC Context Domain Count APT Exception
+ 0 1 250 0019b068 a020 Disabled 02349668:02349fe8 0015def0 0 MTA
+ 2 2 944 001a6020 b220 Enabled 00000000:00000000 0015def0 0 MTA (Finalizer)
+ 0:003> !ThreadState b220
+ Legal to Join
+ Background
+ CLR Owns
+ CoInitialized
+ In Multi Threaded Apartment
+
+Possible thread states:
+ Thread Abort Requested
+ GC Suspend Pending
+ User Suspend Pending
+ Debug Suspend Pending
+ GC On Transitions
+ Legal to Join
+ Yield Requested
+ Hijacked by the GC
+ Blocking GC for Stack Overflow
+ Background
+ Unstarted
+ Dead
+ CLR Owns
+ CoInitialized
+ In Single Threaded Apartment
+ In Multi Threaded Apartment
+ Reported Dead
+ Fully initialized
+ Task Reset
+ Sync Suspended
+ Debug Will Sync
+ Stack Crawl Needed
+ Suspend Unstarted
+ Aborted
+ Thread Pool Worker Thread
+ Interruptible
+ Interrupted
+ Completion Port Thread
+ Abort Initiated
+ Finalized
+ Failed to Start
+ Detached
+\\
+COMMAND: threads.
+!Threads [-live] [-special]
+
+!Threads lists all the mananaged threads in the process.
+
+-live: optional. Only print threads associated with a live thread.
+-special: optional. With this switch, the command will display all the special
+ threads created by CLR. Those threads might not be managed threads
+ so they might not be shown in the first part of the command's
+ output. Example of special threads include: GC threads (in
+ concurrent GC and server GC), Debugger helper threads, Finalizer
+ threads, AppDomain Unload threads, and Threadpool timer threads.
+
+Each thread has many attributes, many of which can be ignored. The important
+ones are discussed below:
+
+There are three ID columns:
+
+1) The debugger shorthand ID (When the runtime is hosted this column might
+ display the special string "<<<<" when this internal thread object is not
+ associated with any physical thread - this may happen when the host reuses
+ the runtime internal thread object)
+2) The CLR Thread ID
+3) The OS thread ID.
+
+If PreEmptiveGC is enabled for a thread, then a garbage collection
+can occur while that thread is running. For example, if you break in while
+a managed thread is making a PInvoke call to a Win32 function, that thread
+will be in PreEmptive GC mode.
+
+The Domain column indicates what AppDomain the thread is currently executing
+in. You can pass this value to !DumpDomain to find out more.
+
+The APT column gives the COM apartment mode.
+
+Exception will list the last thrown exception (if any) for the thread. More
+details can be obtained by passing the pointer value to !PrintException. If
+you get the notation "(nested exceptions)", you can get details on those
+exceptions by switching to the thread in question, and running
+"!PrintException -nested".
+\\
+
+COMMAND: clrstack.
+!CLRStack [-a] [-l] [-p] [-n] [-f]
+!CLRStack [-a] [-l] [-p] [-i] [variable name] [frame]
+
+CLRStack attempts to provide a true stack trace for managed code only. It is
+handy for clean, simple traces when debugging straightforward managed
+programs. The -p parameter will show arguments to the managed function. The
+-l parameter can be used to show information on local variables in a frame.
+SOS can't retrieve local names at this time, so the output for locals is in
+the format <local address> = <value>. The -a (all) parameter is a short-cut
+for -l and -p combined.
+
+The -f option (full mode) displays the native frames intermixing them with
+the managed frames and the assembly name and function offset for the managed
+frames.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+When you see methods with the name "[Frame:...", that indicates a transition
+between managed and unmanaged code. You could run !IP2MD on the return
+addresses in the call stack to get more information on each managed method.
+
+On x64 platforms, Transition Frames are not displayed at this time. To avoid
+heavy optimization of parameters and locals one can request the JIT compiler
+to not optimize functions in the managed app by creating a file myapp.ini
+(if your program is myapp.exe) in the same directory. Put the following lines
+in myapp.ini and re-run:
+
+[.NET Framework Debugging Control]
+GenerateTrackingInfo=1
+AllowOptimize=0
+
+The -i option is a new EXPERIMENTAL addition to CLRStack and will use the ICorDebug
+interfaces to display the managed stack and variables. With this option you can also
+view and expand arrays and fields for managed variables. If a stack frame number is
+specified in the command line, CLRStack will show you the parameters and/or locals
+only for that frame (provided you specify -l or -p or -a of course). If a variable
+name and a stack frame number are specified in the command line, CLRStack will show
+you the parameters and/or locals for that frame, and will also show you the fields
+for that variable name you specified. Here are some examples:
+ !CLRStack -i -a : This will show you all parameters and locals for all frames
+ !CLRStack -i -a 3 : This will show you all parameters and locals, for frame 3
+ !CLRStack -i var1 0 : This will show you the fields of 'var1' for frame 0
+ !CLRStack -i var1.abc 2 : This will show you the fields of 'var1', and expand
+ 'var1.abc' to show you the fields of the 'abc' field,
+ for frame 2.
+ !CLRStack -i var1.[basetype] 0 : This will show you the fields of 'var1', and
+ expand the base type of 'var1' to show you its
+ fields.
+ !CLRStack -i var1.[6] 0 : If 'var1' is an array, this will show you the element
+ at index 6 in the array, along with its fields
+The -i options uses DML output for a better debugging experience, so typically you
+should only need to execute "!CLRStack -i", and from there, click on the DML
+hyperlinks to inspect the different managed stack frames and managed variables.
+\\
+
+COMMAND: ip2md.
+!IP2MD <Code address>
+
+Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc
+associated with it. For example, this output from K:
+
+ 0:000> K
+ ChildEBP RetAddr
+ 00a79c78 03ef02ab image00400000!Mainy.Top()+0xb
+ 00a79c78 03ef01a6 image00400000!Mainy.Level(Int32)+0xb
+ 00a79c78 5d3725a1 image00400000!Mainy.Main()+0xee
+ 0012ea04 5d512f59 clr!CallDescrWorkerInternal+0x30
+ 0012ee34 5d7946aa clr!CallDescrWorker+0x109
+
+ 0:000> !IP2MD 03ef01a6
+ MethodDesc: 00902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa38
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+ Transparency: Critical
+ Source file: c:\Code\prj.mini\exc.cs @ 39
+
+We have taken a return address into Mainy.Main, and discovered information
+about that method. You could run !U, !DumpMT, !DumpClass, !DumpMD, or
+!DumpModule on the fields listed to learn more.
+
+The "Source line" output will only be present if the debugger can find the
+symbols for the managed module containing the given <code address>, and if the
+debugger is configured to load line number information.
+\\
+
+COMMAND: u.
+!U [-gcinfo] [-ehinfo] [-n] [-o] <MethodDesc address> | <Code address>
+
+Presents an annotated disassembly of a managed method when given a MethodDesc
+pointer for the method, or a code address within the method body. Unlike the
+debugger "U" function, the entire method from start to finish is printed,
+with annotations that convert metadata tokens to names.
+
+ <example output>
+ ...
+ 03ef015d b901000000 mov ecx,0x1
+ 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713)
+ 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter)
+ 03ef016d 89442414 mov [esp+0x14],eax
+
+If you pass the -gcinfo flag, you'll get inline display of the GCInfo for
+the method. You can also obtain this information with the !GCInfo command.
+
+If you pass the -ehinfo flag, you'll get inline display of exception info
+for the method. (Beginning and end of try/finally/catch handlers, etc.).
+You can also obtain this information with the !EHInfo command.
+
+If you pass the -o flag, the byte offset of each instruction from the
+beginning of the method will be printed in addition to the absolute address of
+the instruction.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), and if symbols are available for the managed
+module containing the method being examined, the output of the command will
+include the source file name and line number corresponding to the
+disassembly. The -n (No line numbers) flag can be specified to disable this
+behavior.
+
+ <example output>
+ ...
+ c:\Code\prj.mini\exc.cs @ 38:
+ 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type <Enter> to continue: ")
+ 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b)
+ 001b00bb 90 nop
+
+ c:\Code\prj.mini\exc.cs @ 39:
+ 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6)
+ >>> 001b00c1 90 nop
+ ...
+\\
+
+COMMAND: dumpstack.
+!DumpStack [-EE] [-n] [top stack [bottom stack]]
+
+[x86 and x64 documentation]
+
+This command provides a verbose stack trace obtained by "scraping." Therefore
+the output is very noisy and potentially confusing. The command is good for
+viewing the complete call stack when "kb" gets confused. For best results,
+make sure you have valid symbols.
+
+-EE will only show managed functions.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+You can also pass a stack range to limit the output. Use the debugger
+extension !teb to get the top and bottom stack values.
+
+\\
+
+COMMAND: eestack.
+!EEStack [-short] [-EE]
+
+This command runs !DumpStack on all threads in the process. The -EE option is
+passed directly to !DumpStack. The -short option tries to narrow down the
+output to "interesting" threads only, which is defined by
+
+1) The thread has taken a lock.
+2) The thread has been "hijacked" in order to allow a garbage collection.
+3) The thread is currently in managed code.
+
+See the documentation for !DumpStack for more info.
+\\
+
+COMMAND: ehinfo.
+!EHInfo (<MethodDesc address> | <Code address>)
+
+!EHInfo shows the exception handling blocks in a jitted method. For each
+handler, it shows the type, including code addresses and offsets for the clause
+block and the handler block. For a TYPED handler, this would be the "try" and
+"catch" blocks respectively.
+
+Sample output:
+
+ 0:000> !ehinfo 33bbd3a
+ MethodDesc: 03310f68
+ Method Name: MainClass.Main()
+ Class: 03571358
+ MethodTable: 0331121c
+ mdToken: 0600000b
+ Module: 001e2fd8
+ IsJitted: yes
+ CodeAddr: 033bbca0
+ Transparency: Critical
+
+ EHHandler 0: TYPED catch(System.IO.FileNotFoundException)
+ Clause: [033bbd2b, 033bbd3c] [8b, 9c]
+ Handler: [033bbd3c, 033bbd50] [9c, b0]
+
+ EHHandler 1: FINALLY
+ Clause: [033bbd83, 033bbda3] [e3, 103]
+ Handler: [033bbda3, 033bbdc5] [103, 125]
+
+ EHHandler 2: TYPED catch(System.Exception)
+ Clause: [033bbd7a, 033bbdc5] [da, 125]
+ Handler: [033bbdc5, 033bbdd6] [125, 136]
+
+\\
+
+COMMAND: gcinfo.
+!GCInfo (<MethodDesc address> | <Code address>)
+
+!GCInfo is especially useful for CLR Devs who are trying to determine if there
+is a bug in the JIT Compiler. It parses the GCEncoding for a method, which is a
+compressed stream of data indicating when registers or stack locations contain
+managed objects. It is important to keep track of this information, because if
+a garbage collection occurs, the collector needs to know where roots are so it
+can update them with new object pointer values.
+
+Here is sample output where you can see the change in register state. Normally
+you would print this output out and read it alongside a disassembly of the
+method. For example, the notation "reg EDI becoming live" at offset 0x11 of the
+method might correspond to a "mov edi,ecx" statement.
+
+ 0:000> !gcinfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method)
+ entry point 5b68dbb8
+ preJIT generated code
+ GC info 5b9f2f09
+ Method info block:
+ method size = 0036
+ prolog size = 19
+ epilog size = 8
+ epilog count = 1
+ epilog end = yes
+ saved reg. mask = 000B
+ ebp frame = yes
+ fully interruptible=yes
+ double align = no
+ security check = no
+ exception handlers = no
+ local alloc = no
+ edit & continue = no
+ varargs = no
+ argument count = 4
+ stack frame size = 1
+ untracked count = 5
+ var ptr tab count = 0
+ epilog at 002E
+ 36 D4 8C C7 AA |
+ 93 F3 40 05 |
+
+ Pointer table:
+ 14 | [EBP+14H] an untracked local
+ 10 | [EBP+10H] an untracked local
+ 0C | [EBP+0CH] an untracked local
+ 08 | [EBP+08H] an untracked local
+ 44 | [EBP-04H] an untracked local
+ F1 79 | 0011 reg EDI becoming live
+ 72 | 0013 reg ESI becoming live
+ 83 | 0016 push ptr 0
+ 8B | 0019 push ptr 1
+ 93 | 001C push ptr 2
+ 9B | 001F push ptr 3
+ 56 | 0025 reg EDX becoming live
+ 4A | 0027 reg ECX becoming live
+ 0E | 002D reg ECX becoming dead
+ 10 | 002D reg EDX becoming dead
+ E0 | 002D pop 4 ptrs
+ F0 31 | 0036 reg ESI becoming dead
+ 38 | 0036 reg EDI becoming dead
+ FF |
+
+This function is important for CLR Devs, but very difficult for anyone else to
+make sense of it. You would usually come to use it if you suspect a gc heap
+corruption bug caused by invalid GCEncoding for a particular method.
+\\
+
+COMMAND: comstate.
+!COMState
+
+!COMState lists the com apartment model for each thread, as well as a Context
+pointer if provided.
+\\
+
+COMMAND: bpmd.
+!BPMD [-nofuturemodule] <module name> <method name> [<il offset>]
+!BPMD <source file name>:<line number>
+!BPMD -md <MethodDesc>
+!BPMD -list
+!BPMD -clear <pending breakpoint number>
+!BPMD -clearall
+
+!BPMD provides managed breakpoint support. If it can resolve the method name
+to a loaded, jitted or ngen'd function it will create a breakpoint with "bp".
+If not then either the module that contains the method hasn't been loaded yet
+or the module is loaded, but the function is not jitted yet. In these cases,
+!bpmd asks the Windows Debugger to receive CLR Notifications, and waits to
+receive news of module loads and JITs, at which time it will try to resolve
+the function to a breakpoint. -nofuturemodule can be used to suppress
+creating a breakpoint against a module that has not yet been loaded.
+
+Management of the list of pending breakpoints can be done via !BPMD -list,
+!BPMD -clear, and !BPMD -clearall commands. !BPMD -list generates a list of
+all of the pending breakpoints. If the pending breakpoint has a non-zero
+module id, then that pending breakpoint is specific to function in that
+particular loaded module. If the pending breakpoint has a zero module id, then
+the breakpoint applies to modules that have not yet been loaded. Use
+!BPMD -clear or !BPMD -clearall to remove pending breakpoints from the list.
+
+This brings up a good question: "I want to set a breakpoint on the main
+method of my application. How can I do this?"
+
+ 1) If you know the full path to SOS, use this command and skip to step 6
+ .load <the full path to sos.dll>
+
+ 2) If you don't know the full path to sos, its usually next to clr.dll
+ You can wait for clr to load and then find it.
+ Start the debugger and type:
+ sxe -c "" clrn
+ 3) g
+ 4) You'll get the following notification from the debugger:
+ "CLR notification: module 'mscorlib' loaded"
+ 5) Now you can load SOS. Type
+ .loadby sos clr
+
+ 6) Add the breakpoint with command such as:
+ !bpmd myapp.exe MyApp.Main
+ 7) g
+ 8) You will stop at the start of MyApp.Main. If you type "bl" you will
+ see the breakpoint listed.
+
+You can specify breakpoints by file and line number if:
+ a) You have some version of .Net Framework installed on your machine. Any OS from
+ Vista onwards should have .Net Framework installed by default.
+ b) You have PDBs for the managed modules that need breakpoints, and your symbol
+ path points to those PDBs.
+This is often easier than module and method name syntax. For example:
+ !bpmd Demo.cs:15
+
+
+To correctly specify explicitly implemented methods make sure to retrieve the
+method name from the metadata, or from the output of the "!dumpmt -md" command.
+For example:
+
+ public interface I1
+ {
+ void M1();
+ }
+ public class ExplicitItfImpl : I1
+ {
+ ...
+ void I1.M1() // this method's name is 'I1.M1'
+ { ... }
+ }
+
+ !bpmd myapp.exe ExplicitItfImpl.I1.M1
+
+
+!BPMD works equally well with generic types. Adding a breakpoint on a generic
+type sets breakpoints on all already JIT-ted generic methods and sets a pending
+breakpoint for any instantiation that will be JIT-ted in the future.
+
+Example for generics:
+ Given the following two classes:
+
+ class G3<T1, T2, T3>
+ {
+ ...
+ public void F(T1 p1, T2 p2, T3 p3)
+ { ... }
+ }
+
+ public class G1<T> {
+ // static method
+ static public void G<W>(W w)
+ { ... }
+ }
+
+ One would issue the following commands to set breapoints on G3.F() and
+ G1.G():
+
+ !bpmd myapp.exe G3`3.F
+ !bpmd myapp.exe G1`1.G
+
+And for explicitly implemented methods on generic interfaces:
+ public interface IT1<T>
+ {
+ void M1(T t);
+ }
+
+ public class ExplicitItfImpl<U> : IT1<U>
+ {
+ ...
+ void IT1<U>.M1(U u) // this method's name is 'IT1<U>.M1'
+ { ... }
+ }
+
+ !bpmd bpmd.exe ExplicitItfImpl`1.IT1<U>.M1
+
+Additional examples:
+ If IT1 and ExplicitItfImpl are types declared inside another class,
+ Outer, the bpmd command would become:
+
+ !bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1<U>.M1
+
+ (note that the fully qualified type name for ExplicitItfImpl became
+ Outer+ExplicitItfImpl, using the '+' separator, while the method name
+ is Outer.IT1<U>.M1, using a '.' as the separator)
+
+ Furthermore, if the Outer class resides in a namespace, NS, the bpmd
+ command to use becomes:
+
+ !bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1<U>.M1
+
+!BPMD does not accept offsets nor parameters in the method name. You can add
+an IL offset as an optional parameter seperate from the name. If there are overloaded
+methods, !bpmd will set a breakpoint for all of them.
+
+In the case of hosted environments such as SQL, the module name may be
+complex, like 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
+For this case, just be sure to surround the module name with single quotes,
+like:
+
+!bpmd 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' Price.M2
+
+\\
+
+COMMAND: dumpdomain.
+!DumpDomain [<Domain address>]
+
+When called with no parameters, !DumpDomain will list all the AppDomains in the
+process. It enumerates each Assembly loaded into those AppDomains as well.
+In addition to your application domain, and any domains it might create, there
+are two special domains: the Shared Domain and the System Domain.
+
+Any Assembly pointer in the output can be passed to !DumpAssembly. Any Module
+pointer in the output can be passed to !DumpModule. Any AppDomain pointer can
+be passed to !DumpDomain to limit output only to that AppDomain. Other
+functions provide an AppDomain pointer as well, such as !Threads where it lists
+the current AppDomain for each thread.
+\\
+
+COMMAND: eeheap.
+!EEHeap [-gc] [-loader]
+
+!EEHeap enumerates process memory consumed by internal CLR data structures. You
+can limit the output by passing "-gc" or "-loader". All information will be
+displayed otherwise.
+
+The information for the Garbage Collector lists the ranges of each Segment in
+the managed heap. This can be useful if you believe you have an object pointer.
+If the pointer falls within a segment range given by "!EEHeap -gc", then you do
+have an object pointer, and can attempt to run "!DumpObj" on it.
+
+Here is output for a simple program:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00a71018
+ generation 1 starts at 0x00a7100c
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 00a7e01c 0000d01c(53276)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a76000 0x00005000(20480)
+ Total Size 0x1201c(73756)
+ ------------------------------
+ GC Heap Size 0x1201c(73756)
+
+So the total size of the GC Heap is only 72K. On a large web server, with
+multiple processors, you can expect to see a GC Heap of 400MB or more. The
+Garbage Collector attempts to collect and reclaim memory only when required to
+by memory pressure for better performance. You can also see the notion of
+"generations," wherein the youngest objects live in generation 0, and
+long-lived objects eventually get "promoted" to generation 2.
+
+The loader output lists various private heaps associated with AppDomains. It
+also lists heaps associated with the JIT compiler, and heaps associated with
+Modules. For example:
+
+ 0:000> !EEHeap -loader
+ Loader Heap:
+ --------------------------------------
+ System Domain: 5e0662a0
+ LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes.
+ HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x3000(12288)bytes
+ --------------------------------------
+ Shared Domain: 5e066970
+ LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x6000(24576)bytes
+ --------------------------------------
+ Domain 1: 14f000
+ LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes.
+ StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x8000(32768)bytes
+ --------------------------------------
+ Jit code heap:
+ Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Module Thunk heaps:
+ Module 5ba22410: Size: 0x00000000 bytes.
+ Module 001c1320: Size: 0x00000000 bytes.
+ Module 001c03f0: Size: 0x00000000 bytes.
+ Module 001caa38: Size: 0x00000000 bytes.
+ Total size: 0x0(0)bytes
+ --------------------------------------
+ Module Lookup Table heaps:
+ Module 5ba22410:Size: 0x00000000 bytes.
+ Module 001c1320:Size: 0x00000000 bytes.
+ Module 001c03f0:Size: 0x00000000 bytes.
+ Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Total LoaderHeap size: 0x15000(86016)bytes
+ =======================================
+
+By using !EEHeap to keep track of the growth of these private heaps, we are
+able to rule out or include them as a source of a memory leak.
+\\
+
+COMMAND: name2ee.
+!Name2EE <module name> <type or method name>
+!Name2EE <module name>!<type or method name>
+
+This function allows you to turn a class name into a MethodTable and EEClass.
+It turns a method name into a MethodDesc. Here is an example for a method:
+
+ 0:000> !name2ee unittest.exe MainClass.Main
+ Module: 001caa38
+ Token: 0x0600000d
+ MethodDesc: 00902f40
+ Name: MainClass.Main()
+ JITTED Code Address: 03ef00b8
+
+and for a class:
+
+ 0:000> !name2ee unittest!MainClass
+ Module: 001caa38
+ Token: 0x02000005
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Name: MainClass
+
+The module you are "browsing" with Name2EE needs to be loaded in the process.
+To get a type name exactly right, first browse the module with ILDASM. You
+can also pass * as the <module name> to search all loaded managed modules.
+<module name> can also be the debugger's name for a module, such as
+mscorlib or image00400000.
+
+The Windows Debugger syntax of <module>!<type> is also supported. You can
+use an asterisk on the left of the !, but the type on the right side needs
+to be fully qualified.
+
+If you are looking for a way to display a static field of a class (and you
+don't have an instance of the class, so !dumpobj won't help you), note that
+once you have the EEClass, you can run !DumpClass, which will display the
+value of all static fields.
+
+There is yet one more way to specify a module name. In the case of modules
+loaded from an assembly store (such as a SQL db) rather than disk, the
+module name will look like this:
+
+price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
+
+For this kind of module, simply use price as the module name:
+
+ 0:044> !name2ee price Price
+ Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
+ Token: 0x02000002
+ MethodTable: 11a47ae0
+ EEClass: 11a538c8
+ Name: Price
+
+Where are we getting these module names from? Run !DumpDomain to see a list of
+all loaded modules in all domains. And remember that you can browse all the
+types in a module with !DumpModule -mt <module pointer>.
+\\
+
+COMMAND: syncblk.
+!SyncBlk [-all | <syncblk number>]
+
+A SyncBlock is a holder for extra information that doesn't need to be created
+for every object. It can hold COM Interop data, HashCodes, and locking
+information for thread-safe operations.
+
+When called without arguments, !SyncBlk will print the list of SyncBlocks
+corresponding to objects that are owned by a thread. For example, a
+
+ lock(MyObject)
+ {
+ ....
+ }
+
+statement will set MyObject to be owned by the current thread. A SyncBlock will
+be created for MyObject, and the thread ownership information stored there
+(this is an oversimplification, see NOTE below). If another thread tries to
+execute the same code, they won't be able to enter the block until the first
+thread exits.
+
+This makes !SyncBlk useful for detecting managed deadlocks. Consider that the
+following code is executed by Threads A & B:
+
+ Resource r1 = new Resource();
+ Resource r2 = new Resource();
+
+ ...
+
+ lock(r1) lock(r2)
+ { {
+ lock(r2) lock(r1)
+ { {
+ ... ...
+ } }
+ } }
+
+This is a deadlock situation, as Thread A could take r1, and Thread B r2,
+leaving both threads with no option but to wait forever in the second lock
+statement. !SyncBlk will detect this with the following output:
+
+ 0:003> !syncblk
+ Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
+ 238 001e40ec 3 1 001e4e60 e04 3 00a7a194 Resource
+ 239 001e4124 3 1 001e5980 ab8 4 00a7a1a4 Resource
+
+It means that Thread e04 owns object 00a7a194, and Thread ab8 owns object
+00a7a1a4. Combine that information with the call stacks of the deadlock:
+
+(threads 3 and 4 have similar output)
+ 0:003> k
+ ChildEBP RetAddr
+ 0404ea04 77f5c524 SharedUserData!SystemCallStub+0x4
+ 0404ea08 77e75ee0 ntdll!NtWaitForMultipleObjects+0xc
+ 0404eaa4 5d9de9d6 KERNEL32!WaitForMultipleObjectsEx+0x12c
+ 0404eb38 5d9def80 clr!Thread::DoAppropriateAptStateWait+0x156
+ 0404ecc4 5d9dd8bb clr!Thread::DoAppropriateWaitWorker+0x360
+ 0404ed20 5da628dd clr!Thread::DoAppropriateWait+0xbb
+ 0404ede4 5da4e2e2 clr!CLREvent::Wait+0x29d
+ 0404ee70 5da4dd41 clr!AwareLock::EnterEpilog+0x132
+ 0404ef34 5da4efa3 clr!AwareLock::Enter+0x2c1
+ 0404f09c 5d767880 clr!AwareLock::Contention+0x483
+ 0404f1c4 03f00229 clr!JITutil_MonContention+0x2c0
+ 0404f1f4 5b6ef077 image00400000!Worker.Work()+0x79
+ ...
+
+By looking at the code corresponding to Worker.Work()+0x79 (run "!u 03f00229"),
+you can see that thread 3 is attempting to acquire the Resource 00a7a1a4, which
+is owned by thread 4.
+
+NOTE:
+It is not always the case that a SyncBlock will be created for every object
+that is locked by a thread. In version 2.0 of the CLR and above, a mechanism
+called a ThinLock will be used if there is not already a SyncBlock for the
+object in question. ThinLocks will not be reported by the !SyncBlk command.
+You can use "!DumpHeap -thinlock" to list objects locked in this way.
+\\
+
+COMMAND: dumpmt.
+!DumpMT [-MD] <MethodTable address>
+
+Examine a MethodTable. Each managed object has a MethodTable pointer at the
+start. If you pass the "-MD" flag, you'll also see a list of all the methods
+defined on the object.
+\\
+
+COMMAND: dumpclass.
+!DumpClass <EEClass address>
+
+The EEClass is a data structure associated with an object type. !DumpClass
+will show attributes, as well as list the fields of the type. The output is
+similar to !DumpObj. Although static field values will be displayed,
+non-static values won't because you need an instance of an object for that.
+
+You can get an EEClass to look at from !DumpMT, !DumpObj, !Name2EE, and
+!Token2EE among others.
+\\
+
+COMMAND: dumpmd.
+!DumpMD <MethodDesc address>
+
+This command lists information about a MethodDesc. You can use !IP2MD to turn
+a code address in a managed function into a MethodDesc:
+
+ 0:000> !dumpmd 902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa78
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+
+If IsJitted is "yes," you can run !U on the CodeAddr pointer to see a
+disassembly of the JITTED code. You can also call !DumpClass, !DumpMT,
+!DumpModule on the Class, MethodTable and Module fields above.
+\\
+
+COMMAND: token2ee.
+!Token2EE <module name> <token>
+
+This function allows you to turn a metadata token into a MethodTable or
+MethodDesc. Here is an example showing class tokens being resolved:
+
+ 0:000> !token2ee unittest.exe 02000003
+ Module: 001caa38
+ Token: 0x02000003
+ MethodTable: 0090375c
+ EEClass: 03ee1ae0
+ Name: Bank
+ 0:000> !token2ee image00400000 02000004
+ Module: 001caa38
+ Token: 0x02000004
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Name: Customer
+
+The module you are "browsing" with Token2EE needs to be loaded in the process.
+This function doesn't see much use, especially since a tool like ILDASM can
+show the mapping between metadata tokens and types/methods in a friendlier way.
+But it could be handy sometimes.
+
+You can pass "*" for <module name> to find what that token maps to in every
+loaded managed module. <module name> can also be the debugger's name for a
+module, such as mscorlib or image00400000.
+\\
+
+COMMAND: eeversion.
+!EEVersion
+
+This prints the Common Language Runtime version. It also tells you if the code
+is running in "Workstation" or "Server" mode, a distinction which affects the
+garbage collector. The most apparent difference in the debugger is that in
+"Server" mode there is one dedicated garbage collector thread per CPU.
+
+A handy supplement to this function is to also run "lm v m clr". That
+will provide more details about the CLR, including where clr.dll is
+loaded from.
+\\
+
+COMMAND: dumpmodule.
+!DumpModule [-mt] <Module address>
+
+You can get a Module address from !DumpDomain, !DumpAssembly and other
+functions. Here is sample output:
+
+ 0:000> !DumpModule 1caa50
+ Name: C:\pub\unittest.exe
+ Attributes: PEFile
+ Assembly: 001ca248
+ LoaderHeap: 001cab3c
+ TypeDefToMethodTableMap: 03ec0010
+ TypeRefToMethodTableMap: 03ec0024
+ MethodDefToDescMap: 03ec0064
+ FieldDefToDescMap: 03ec00a4
+ MemberRefToDescMap: 03ec00e8
+ FileReferencesMap: 03ec0128
+ AssemblyReferencesMap: 03ec012c
+ MetaData start address: 00402230 (1888 bytes)
+
+The Maps listed map metadata tokens to CLR data structures. Without going into
+too much detail, you can examine memory at those addresses to find the
+appropriate structures. For example, the TypeDefToMethodTableMap above can be
+examined:
+
+ 0:000> dd 3ec0010
+ 03ec0010 00000000 00000000 0090320c 0090375c
+ 03ec0020 009038ec ...
+
+This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You
+can run !DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token
+and maps it to a MethodDesc, which can be passed to !DumpMD.
+
+There is a new option "-mt", which will display the types defined in a module,
+and the types referenced by the module. For example:
+
+ 0:000> !dumpmodule -mt 1aa580
+ Name: C:\pub\unittest.exe
+ ...<etc>...
+ MetaData start address: 0040220c (1696 bytes)
+
+ Types defined in this module
+
+ MT TypeDef Name
+ --------------------------------------------------------------------------
+ 030d115c 0x02000002 Funny
+ 030d1228 0x02000003 Mainy
+
+ Types referenced in this module
+
+ MT TypeRef Name
+ --------------------------------------------------------------------------
+ 030b6420 0x01000001 System.ValueType
+ 030b5cb0 0x01000002 System.Object
+ 030fceb4 0x01000003 System.Exception
+ 0334e374 0x0100000c System.Console
+ 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle
+ 0336a048 0x0100000f System.GC
+
+\\
+
+COMMAND: threadpool.
+!ThreadPool
+
+This command lists basic information about the ThreadPool, including the number
+of work requests in the queue, number of completion port threads, and number of
+timers.
+\\
+
+COMMAND: dumpassembly.
+!DumpAssembly <Assembly address>
+
+Example output:
+
+ 0:000> !dumpassembly 1ca248
+ Parent Domain: 0014f000
+ Name: C:\pub\unittest.exe
+ ClassLoader: 001ca060
+ Module Name
+ 001caa50 C:\pub\unittest.exe
+
+An assembly can consist of multiple modules, and those will be listed. You can
+get an Assembly address from the output of !DumpDomain.
+\\
+
+COMMAND: dumpruntimetypes.
+!DumpRuntimeTypes
+
+!DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and
+prints the type name and MethodTable they refer too. Sample output:
+
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ a515f4 14a740 5baf8d28 System.TypedReference
+ a51608 14a740 5bb05764 System.Globalization.BaseInfoTable
+ a51958 14a740 5bb05b24 System.Globalization.CultureInfo
+ a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly
+ a51de0 14a740 5bb069c8 System.Globalization.TextInfo
+ a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource
+ a56bbc 14a740 5baf7248 System.Int32
+ a56bd0 14a740 5baf3fdc System.String
+ a56cfc 14a740 5baf36a4 System.ValueType
+ ...
+
+This command will print a "?" in the domain column if the type is loaded into multiple
+AppDomains. For example:
+
+ 0:000> !DumpRuntimeTypes
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ 28435a0 ? 3f6a8c System.TypedReference
+ 28435b4 ? 214d6c System.ValueType
+ 28435c8 ? 216314 System.Enum
+ 28435dc ? 2147cc System.Object
+ 284365c ? 3cd57c System.IntPtr
+ 2843670 ? 3feaac System.Byte
+ 2843684 ? 23a544c System.IEquatable`1[[System.IntPtr, mscorlib]]
+ 2843784 ? 3c999c System.Int32
+ 2843798 ? 3caa04 System.IEquatable`1[[System.Int32, mscorlib]]
+
+\\
+
+COMMAND: dumpsig.
+!DumpSig <sigaddr> <moduleaddr>
+
+This command dumps the signature of a method or field given by <sigaddr>. This is
+useful when you are debugging parts of the runtime which returns a raw PCCOR_SIGNATURE
+structure and need to know what its contents are.
+
+Sample output for a method:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+The first section of the output is the calling convention. This includes, but is not
+limited to, "[DEFAULT]", "[C]", "[STDCALL]", "[THISCALL]", and so on. The second
+portion of the output is either "[hasThis]" or "[explicit]" for whether the method
+is an instance method or a static method respectively. The third portion of the
+output is the return value (in this case a "void"). Finally, the method's arguments
+are printed as the final portion of the output.
+
+Sample output for a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+!DumpSig will also work with generics. Here is the output for the following
+function:
+ public A Test(IEnumerable<B> n)
+
+ 0:000> !dumpsig 00000000`00bc2437 000007ff00043178
+ [DEFAULT] [hasThis] __Canon (Class System.Collections.Generic.IEnumerable`1<__Canon>)
+
+\\
+
+COMMAND: dumpsigelem.
+!DumpSigElem <sigaddr> <moduleaddr>
+
+This command dumps a single element of a signature object. For most circumstances,
+you should use !DumpSig to look at individual signature objects, but if you find a
+signature that has been corrupted in some manner you can use !DumpSigElem to read out
+the valid portions of it.
+
+If we look at a valid signature object for a method we see the following:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+We can look at the individual elements of this object by adding the offsets into the
+object which correspond to the return value and parameters:
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+2 0x000007fe`eabd1000
+ Void
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+3 0x000007fe`eabd1000
+ Boolean
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+4 0x000007fe`eabd1000
+ String
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+5 0x000007fe`eabd1000
+ String
+
+We can do something similar for fields. Here is the full signature of a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+Using !DumpSigElem we can find the type of the field by adding the offset of it (1) to
+the address of the signature:
+ 0:000> !dumpsigelem 0x000007fe`eb7fd8cd+1 0x000007fe`eabd1000
+ ValueClass System.RuntimeTypeHandle
+
+!DumpSigElem will also work with generics. Let a function be defined as follows:
+ public A Test(IEnumerable<B> n)
+
+The elements of this signature can be obtained by adding offsets into the signature
+when calling !DumpSigElem:
+
+ 0:000> !dumpsigelem 00000000`00bc2437+2 000007ff00043178
+ __Canon
+ 0:000> !dumpsigelem 00000000`00bc2437+4 000007ff00043178
+ Class System.Collections.Generic.IEnumerable`1<__Canon>
+
+The actual offsets that you should add are determined by the contents of the
+signature itself. By trial and error you should be able to find various elements
+of the signature.
+
+\\
+
+COMMAND: rcwcleanuplist.
+!RCWCleanupList [address]
+
+A RuntimeCallableWrapper is an internal CLR structure used to host COM objects
+which are exposed to managed code. This is exposed to managed code through the
+System.__ComObject class, and when objects of this type are collected, and a
+reference to the underlying COM object is no longer needed, the corresponding
+RCW is cleaned up. If you are trying to debug an issue related to one of these
+RCWs, then you can use the !RCWCleanupList function to display which COM objects
+will be released the next time a cleanup occurs.
+
+If given an address, this function will display the RCWCleanupList at that address.
+If no address is specified, it displays the default cleanup list, printing the
+wrapper, the context, and the thread of the object.
+
+Example:
+ 0:002> !rcwcleanuplist 001c04d0
+ RuntimeCallableWrappers (RCW) to be cleaned:
+ RCW CONTEXT THREAD Apartment
+ 1d54e0 192008 181180 STA
+ 1d4140 192178 0 MTA
+ 1dff50 192178 0 MTA
+ MTA Interfaces to be released: 2
+ STA Interfaces to be released: 1
+
+Note that CLR keeps track of which RCWs are bound to which managed objects through
+the SyncBlock of the object. As such, you can see more information about RCW
+objects through the !SyncBlk command. You can find more information about RCW
+cleanup through the !FinalizeQueue command.
+
+\\
+
+COMMAND: dumpil.
+!DumpIL <Managed DynamicMethod object> |
+ <DynamicMethodDesc pointer> |
+ <MethodDesc pointer> |
+ /i <IL pointer>
+
+!DumpIL prints the IL code associated with a managed method. We added this
+function specifically to debug DynamicMethod code which was constructed on
+the fly. Happily it works for non-dynamic code as well.
+
+You can use it in four ways:
+
+ 1) If you have a System.Reflection.Emit.DynamicMethod object, just pass
+ the pointer as the first argument.
+ 2) If you have a DynamicMethodDesc pointer you can use that to print the
+ IL associated with the dynamic method.
+ 3) If you have an ordinary MethodDesc, you can see the IL for that as well,
+ just pass it as the first argument.
+ 4) If you have a pointer directly to the IL, specify /i followed by the
+ the IL address. This is useful for writers of profilers that instrument
+ IL.
+
+
+Note that dynamic IL is constructed a bit differently. Rather than referring
+to metadata tokens, the IL points to objects in a managed object array. Here
+is a simple example of the output for a dynamic method:
+
+ 0:000> !dumpil b741dc
+ This is dynamic IL. Exception info is not reported at this time.
+ If a token is unresolved, run "!do <addr>" on the addr given
+ in parenthesis. You can also look at the token table yourself, by
+ running "!DumpArray 00b77388".
+
+ IL_0000: ldstr 70000002 "Inside invoked method "
+ IL_0005: call 6000003 System.Console.WriteLine(System.String)
+ IL_000a: ldc.i4.1
+ IL_000b: newarr 2000004 "System.Int32"
+ IL_0010: stloc.0
+ IL_0011: ldloc.0
+ IL_0012: ret
+
+\\
+
+COMMAND: verifyheap.
+!VerifyHeap
+
+!VerifyHeap is a diagnostic tool that checks the garbage collected heap for
+signs of corruption. It walks objects one by one in a pattern like this:
+
+ o = firstobject;
+ while(o != endobject)
+ {
+ o.ValidateAllFields();
+ o = (Object *) o + o.Size();
+ }
+
+If an error is found, !VerifyHeap will report it. I'll take a perfectly good
+object and corrupt it:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+ 0:000> ed a79d40+4 01 (change the name field to the bogus pointer value 1)
+ 0:000> !VerifyHeap
+ object 01ee60dc: bad member 00000003 at 01EE6168
+ Last good object: 01EE60C4.
+
+If this gc heap corruption exists, there is a serious bug in your own code or
+in the CLR. In user code, an error in constructing PInvoke calls can cause
+this problem, and running with Managed Debugging Assistants is advised. If that
+possibility is eliminated, consider contacting Microsoft Product Support for
+help.
+
+\\
+
+COMMAND: verifyobj.
+!VerifyObj <object address>
+
+!VerifyObj is a diagnostic tool that checks the object that is passed as an
+argument for signs of corruption.
+
+ 0:002> !verifyobj 028000ec
+ object 0x28000ec does not have valid method table
+
+ 0:002> !verifyobj 0680017c
+ object 0x680017c: bad member 00000001 at 06800184
+
+\\
+
+COMMAND: findroots.
+!FindRoots -gen <N> | -gen any | <object address>
+
+The "-gen" form causes the debugger to break in the debuggee on the next
+collection of the specified generation. The effect is reset as soon as the
+break occurs, in other words, if you need to break on the next collection you
+would need to reissue the command.
+
+The last form of this command is meant to be used after the break caused by the
+other forms has occurred. Now the debuggee is in the right state for
+!FindRoots to be able to identify roots for objects from the current condemned
+generations.
+
+!FindRoots is a diagnostic command that is meant to answer the following
+question:
+
+"I see that GCs are happening, however my objects have still not been
+collected. Why? Who is holding onto them?"
+
+The process of answering the question would go something like this:
+
+1. Find out the generation of the object of interest using the !GCWhere
+command, say it is gen 1:
+ !GCWhere <object address>
+
+2. Instruct the runtime to stop the next time it collects that generation using
+the !FindRoots command:
+ !FindRoots -gen 1
+ g
+
+3. When the next GC starts, and has proceeded past the mark phase a CLR
+notification will cause a break in the debugger:
+ (fd0.ec4): CLR notification exception - code e0444143 (first chance)
+ CLR notification: GC - end of mark phase.
+ Condemned generation: 1.
+
+4. Now we can use the !FindRoots <object address> to find out the cross
+generational references to the object of interest. In other words, even if the
+object is not referenced by any "proper" root it may still be referenced by an
+older object (from an older generation), from a generation that has not yet been
+scheduled for collection. At this point !FindRoots will search those older
+generations too, and report those roots.
+ 0:002> !findroots 06808094
+ older generations::Root: 068012f8(AAA.Test+a)->
+ 06808094(AAA.Test+b)
+
+
+\\
+
+COMMAND: heapstat.
+!HeapStat [-inclUnrooted | -iu]
+
+This command shows the generation sizes for each heap and the total, how much free
+space there is in each generation on each heap. If the -inclUnrooted option is
+specified the report will include information about the managed objects from the
+GC heap that are not rooted anymore.
+
+Sample output:
+
+ 0:002> !heapstat
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ 0:002> !heapstat -inclUnrooted
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ Unrooted objects: Percentage
+ Heap0 152212 0 306196 0 SOH: 94% LOH: 0%
+ Heap1 155704 0 0 0 SOH: 97% LOH: 0%
+ Total 307916 0 306196 0
+
+The percentage column contains a breakout of free or unrooted bytes to total bytes.
+
+\\
+
+COMMAND: analyzeoom.
+!AnalyzeOOM
+
+!AnalyzeOOM displays the info of the last OOM occurred on an allocation request to
+the GC heap (in Server GC it displays OOM, if any, on each GC heap).
+
+To see the managed exception(s) use the !Threads command which will show you
+managed exception(s), if any, on each managed thread. If you do see an
+OutOfMemoryException exception you can use the !PrintException command on it.
+To get the full callstack use the "kb" command in the debugger for that thread.
+For example, to display thread 3's stack use ~3kb.
+
+OOM exceptions could be because of the following reasons:
+
+1) allocation request to GC heap
+ in which case you will see JIT_New* on the call stack because managed code called new.
+2) other runtime allocation failure
+ for example, failure to expand the finalize queue when GC.ReRegisterForFinalize is
+ called.
+3) some other code you use throws a managed OOM exception
+ for example, some .NET framework code converts a native OOM exception to managed
+ and throws it.
+
+The !AnalyzeOOM command aims to help you with investigating 1) which is the most
+difficult because it requires some internal info from GC. The only exception is
+we don't support allocating objects larger than 2GB on CLR v2.0 or prior. And this
+command will not display any managed OOM because we will throw OOM right away
+instead of even trying to allocate it on the GC heap.
+
+There are 2 legitimate scenarios where GC would return OOM to allocation requests -
+one is if the process is running out of VM space to reserve a segment; the other
+is if the system is running out physical memory (+ page file if you have one) so
+GC can not commit memory it needs. You can look at these scenarios by using performance
+counters or debugger commands. For example for the former scenario the "!address
+-summary" debugger command will show you the largest free region in the VM. For
+the latter scenario you can look at the "Memory\% Committed Bytes In Use" see
+if you are running low on commit space. One important thing to keep in mind is
+when you do this kind of memory analysis it could an aftereffect and doesn't
+completely agree with what this command tells you, in which case the command should
+be respected because it truly reflects what happened during GC.
+
+The other cases should be fairly obvious from the callstack.
+
+Sample output:
+
+0:011> !ao
+---------Heap 2 ---------
+Managed OOM occurred after GC #28 (Requested to allocate 1234 bytes)
+Reason: Didn't have enough memory to commit
+Detail: SOH: Didn't have enough memory to grow the internal GC datastructures (800000 bytes) -
+ on GC entry available commit space was 500 MB
+---------Heap 4 ---------
+Managed OOM occurred after GC #12 (Requested to allocate 100000 bytes)
+Reason: Didn't have enough memory to allocate an LOH segment
+Detail: LOH: Failed to reserve memory (16777216 bytes)
+
+\\
+
+COMMAND: gcwhere.
+!GCWhere <object address>
+
+!GCWhere displays the location in the GC heap of the argument passed in.
+
+ 0:002> !GCWhere 02800038
+ Address Gen Heap segment begin allocated size
+ 02800038 2 0 02800000 02800038 0282b740 12
+
+When the argument lies in the managed heap, but is not a valid *object* address
+the "size" is displayed as 0:
+
+ 0:002> !GCWhere 0280003c
+ Address Gen Heap segment begin allocated size
+ 0280003c 2 0 02800000 02800038 0282b740 0
+
+\\
+
+COMMAND: listnearobj.
+!ListNearObj <object address>
+
+!ListNearObj is a diagnostic tool that displays the object preceeding and
+succeeding the address passed in:
+
+The command looks for the address in the GC heap that looks like a valid
+beginning of a managed object (based on a valid method table) and the object
+following the argument address.
+
+ 0:002> !ListNearObj 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+ 0:002> !ListNearObj 028000f0
+ Before: 0x28000ec 72 (0x48 ) System.ExecutionEngineException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+The command considers the heap as "locally consistent" if:
+ prev_obj_addr + prev_obj_size = arg_addr && arg_obj + arg_size = next_obj_addr
+OR
+ prev_obj_addr + prev_obj_size = next_obj_addr
+
+When the condition is not satisfied:
+
+ 0:002> !lno 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency not confirmed.
+
+\\
+
+COMMAND: dumplog.
+!DumpLog [-addr <addressOfStressLog>] [<Filename>]
+
+To aid in diagnosing hard-to-reproduce stress failures, the CLR team added an
+in-memory log capability. The idea was to avoid using locks or I/O which could
+disturb a fragile repro environment. The !DumpLog function allows you to write
+that log out to a file. If no Filename is specified, the file "Stresslog.txt"
+in the current directory is created.
+
+The optional argument addr allows one to specify a stress log other then the
+default one.
+
+ 0:000> !DumpLog
+ Attempting to dump Stress log to file 'StressLog.txt'
+ .................
+ SUCCESS: Stress log dumped
+
+To turn on the stress log, set the following registry keys under
+HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework:
+
+
+(DWORD) StressLog = 1
+(DWORD) LogFacility = 0xffffffbf (this is a bit mask, almost all logging is on.
+ This is also the default value if the key
+ isn't specified)
+(DWORD) StressLogSize = 65536 (this is the default value if the key isn't
+ specified)
+(DWORD) LogLevel = 6 (this is the default value if the key isn't
+ specified. The higher the number the more
+ detailed logs are generated. The maximum
+ value is decimal 10)
+
+StressLogSize is the size in bytes of the in-memory log allocated for each
+thread in the process. In the case above, each thread gets a 64K log. You
+could increase this to get more logging, but more memory will be required for
+this log in the process. For example, 20 threads with 524288 bytes per thread
+has a memory demand of 10 Megabytes. The stress log is circular so new entries
+will replace older ones on threads which have reached their buffer limit.
+
+The log facilities are defined as follows:
+ GC 0x00000001
+ GCINFO 0x00000002
+ STUBS 0x00000004
+ JIT 0x00000008
+ LOADER 0x00000010
+ METADATA 0x00000020
+ SYNC 0x00000040
+ EEMEM 0x00000080
+ GCALLOC 0x00000100
+ CORDB 0x00000200
+ CLASSLOADER 0x00000400
+ CORPROF 0x00000800
+ REMOTING 0x00001000
+ DBGALLOC 0x00002000
+ EH 0x00004000
+ ENC 0x00008000
+ ASSERT 0x00010000
+ VERIFIER 0x00020000
+ THREADPOOL 0x00040000
+ GCROOTS 0x00080000
+ INTEROP 0x00100000
+ MARSHALER 0x00200000
+ IJW 0x00400000
+ ZAP 0x00800000
+ STARTUP 0x01000000
+ APPDOMAIN 0x02000000
+ CODESHARING 0x04000000
+ STORE 0x08000000
+ SECURITY 0x10000000
+ LOCKS 0x20000000
+ BCL 0x40000000
+
+Here is some sample output:
+
+ 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering.
+ Thread state = a030
+
+ 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2,
+ collect_classes = 0) ==========={
+
+ 3560 9.981125826 : `GC` Segment mem 00C61000 alloc
+ = 00D071F0 used 00D09254 committed 00D17000
+
+ 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000
+ ] cur = 00000000
+
+ 3560 9.981124963 : `GC` GC Heap 00000000
+
+ 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0)
+
+The first column is the OS thread ID for the thread appending to the log,
+the second column is the timestamp, the third is the facility category for the
+log entry, and the fourth contains the log message. The facility field is
+expressed as `facility1`facility2`facility3`. This facilitates the creation of
+filters for displaying only specific message categories. To make sense of this
+log, you would probably want the Shared Source CLI to find out exactly where
+the log comes from.
+\\
+
+COMMAND: findappdomain.
+!FindAppDomain <Object address>
+
+!FindAppDomain will attempt to resolve the AppDomain of an object. For example,
+using an Object Pointer from the output of !DumpStackObjects:
+
+ 0:000> !findappdomain 00a79d98
+ AppDomain: 0014f000
+ Name: unittest.exe
+ ID: 1
+
+You can find out more about the AppDomain with the !DumpDomain command. Not
+every object has enough clues about it's origin to determine the AppDomain.
+Objects with Finalizers are the easiest case, as the CLR needs to be able to
+call those when an AppDomain shuts down.
+\\
+
+COMMAND: savemodule.
+!SaveModule <Base address> <Filename>
+
+This command allows you to take a image loaded in memory and write it to a
+file. This is especially useful if you are debugging a full memory dump, and
+don't have the original DLLs or EXEs. This is most often used to save a managed
+binary to a file, so you can disassemble the code and browse types with ILDASM.
+
+The base address of an image can be found with the "LM" debugger command:
+
+ 0:000> lm
+ start end module name
+ 00400000 00408000 image00400000 (deferred)
+ 10200000 102ac000 MSVCR80D (deferred)
+ 5a000000 5a0b1000 mscoree (deferred)
+ 5a140000 5a29e000 clrjit (deferred)
+ 5b660000 5c440000 mscorlib_dll (deferred)
+ 5d1d0000 5e13c000 clr (deferred)
+ ...
+
+If I wanted to save a copy of clr.dll, I could run:
+
+ 0:000> !SaveModule 5d1d0000 c:\pub\out.tmp
+ 4 sections in file
+ section 0 - VA=1000, VASize=e82da9, FileAddr=400, FileSize=e82e00
+ section 1 - VA=e84000, VASize=24d24, FileAddr=e83200, FileSize=ec00
+ section 2 - VA=ea9000, VASize=5a8, FileAddr=e91e00, FileSize=600
+ section 3 - VA=eaa000, VASize=c183c, FileAddr=e92400, FileSize=c1a00
+
+The diagnostic output indicates that the operation was successful. If
+c:\pub\out.tmp already exists, it will be overwritten.
+\\
+
+COMMAND: gchandles.
+!GCHandles [-type handletype] [-stat] [-perdomain]
+
+!GCHandles provides statistics about GCHandles in the process.
+
+Paremeters:
+ stat - Only display the statistics and not the list of handles and
+ what they point to.
+ perdomain - Break down the statistics by the app domain in which
+ the handles reside.
+ type - A type of handle to filter it by. The handle types are:
+ Pinned
+ RefCounted
+ WeakShort
+ WeakLong
+ Strong
+ Variable
+ AsyncPinned
+ SizedRef
+
+Sometimes the source of a memory leak is a GCHandle leak. For example, code
+might keep a 50 Megabyte array alive because a strong GCHandle points to it,
+and the handle was discarded without freeing it.
+
+The most common handles are "Strong Handles," which keep the object they point
+to alive until the handle is explicitly freed. "Pinned Handles" are used to
+prevent the garbage collector from moving an object during collection. These
+should be used sparingly, and for short periods of time. If you don't follow
+that precept, the gc heap can become very fragmented.
+
+Here is sample output from a very simple program. Note that the "RefCount"
+field only applies to RefCount Handles, and this field will contain the
+reference count:
+
+ 0:000> !GCHandles
+ Handle Type Object Size RefCount Type
+ 001611c0 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c4 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c8 Strong 01d1b48c 40 System.Diagnostics.LogSwitch
+ 001611d0 Strong 01cfd2c0 36 System.Security.PermissionSet
+ 001611d4 Strong 01cf7484 56 System.Object[]
+ 001611d8 Strong 01cf1238 32 System.SharedStatics
+ 001611dc Strong 01cf11c8 84 System.Threading.ThreadAbortException
+ 001611e0 Strong 01cf1174 84 System.Threading.ThreadAbortException
+ 001611e4 Strong 01cf1120 84 System.ExecutionEngineException
+ 001611e8 Strong 01cf10cc 84 System.StackOverflowException
+ 001611ec Strong 01cf1078 84 System.OutOfMemoryException
+ 001611f0 Strong 01cf1024 84 System.Exception
+ 001611f8 Strong 01cf2068 48 System.Threading.Thread
+ 001611fc Strong 01cf1328 112 System.AppDomain
+ 001613ec Pinned 02cf3268 8176 System.Object[]
+ 001613f0 Pinned 02cf2258 4096 System.Object[]
+ 001613f4 Pinned 02cf2038 528 System.Object[]
+ 001613f8 Pinned 01cf121c 12 System.Object
+ 001613fc Pinned 02cf1010 4116 System.Object[]
+
+ Statistics:
+ MT Count TotalSize Class Name
+ 563266dc 1 12 System.Object
+ 56329708 1 32 System.SharedStatics
+ 5632bc38 1 36 System.Security.PermissionSet
+ 5635f934 1 40 System.Diagnostics.LogSwitch
+ 5632759c 1 48 System.Threading.Thread
+ 5632735c 1 84 System.ExecutionEngineException
+ 56327304 1 84 System.StackOverflowException
+ 563272ac 1 84 System.OutOfMemoryException
+ 563270c4 1 84 System.Exception
+ 56328914 1 112 System.AppDomain
+ 56335f78 2 168 System.IndexOutOfRangeException
+ 563273b4 2 168 System.Threading.ThreadAbortException
+ 563208d0 5 16972 System.Object[]
+ Total 19 objects
+
+ Handles:
+ Strong Handles: 14
+ Pinned Handles: 5
+\\
+
+COMMAND: gchandleleaks.
+!GCHandleLeaks
+
+This command is an aid in tracking down GCHandle leaks. It searches all of
+memory for any references to the Strong and Pinned GCHandles in the process,
+and reports what it found. If a handle is found, you'll see the address of the
+reference. This might be a stack address or a field within an object, for
+example. If a handle is not found in memory, you'll get notification of that
+too.
+
+The command has diagnostic output which doesn't need to be repeated here. One
+thing to keep in mind is that anytime you search all of memory for a value, you
+can get false positives because even though the value was found, it might be
+garbage in that no code knows about the address. You can also get false
+negatives because a user is free to pass that GCHandle to unmanaged code that
+might store the handle in a strange way (shifting bits, for example).
+
+For example, a GCHandle valuetype is stored on the stack with the low bit set
+if it points to a Pinned handle. So !GCHandleLeaks ignores the low bit in it's
+searches.
+
+That said, if a serious leak is going on, you'll get a ever-growing set of
+handle addresses that couldn't be found.
+\\
+
+COMMAND: vmmap.
+!VMMap
+
+!VMMap traverses the virtual address space and lists the type of protection
+applied to each region. Sample output:
+
+ 0:000> !VMMap
+ Start Stop Length AllocProtect Protect State Type
+ 00000000-0000ffff 00010000 NA Free
+ 00010000-00011fff 00002000 RdWr RdWr Commit Private
+ 00012000-0001ffff 0000e000 NA Free
+ 00020000-00020fff 00001000 RdWr RdWr Commit Private
+ 00021000-0002ffff 0000f000 NA Free
+ 00030000-00030fff 00001000 RdWr Reserve Private
+ ...
+\\
+
+COMMAND: vmstat.
+!VMStat
+
+Provides a summary view of the virtual address space, ordered by each type of
+protection applied to that memory (free, reserved, committed, private, mapped,
+image). The TOTAL column is (AVERAGE * BLK COUNT). Sample output below:
+
+ 0:000> !VMStat
+ ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~ ~~~~~
+ TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL
+ Free:
+ Small 4,096 65,536 48,393 27 1,306,611
+ Medium 139,264 528,384 337,920 4 1,351,680
+ Large 6,303,744 974,778,368 169,089,706 12 2,029,076,472
+ Summary 4,096 974,778,368 47,249,646 43 2,031,734,778
+
+ Reserve:
+ Small 4,096 65,536 43,957 41 1,802,237
+ Medium 249,856 1,019,904 521,557 6 3,129,342
+ Large 2,461,696 16,703,488 11,956,224 3 35,868,672
+ Summary 4,096 16,703,488 816,005 50 40,800,250
+
+\\
+
+COMMAND: procinfo.
+!ProcInfo [-env] [-time] [-mem]
+
+!ProcInfo lists the environment variables for the process, kernel CPU time, as
+well as memory usage statistics.
+\\
+
+COMMAND: histinit.
+!HistInit
+
+Before running any of the Hist - family commands you need to initialize the SOS
+structures from the stress log saved in the debuggee. This is achieved by the
+HistInit command.
+
+Sample output:
+
+ 0:001> !HistInit
+ Attempting to read Stress log
+ STRESS LOG:
+ facilitiesToLog = 0xffffffff
+ levelToLog = 6
+ MaxLogSizePerThread = 0x10000 (65536)
+ MaxTotalLogSize = 0x1000000 (16777216)
+ CurrentTotalLogChunk = 9
+ ThreadsWithLogs = 3
+ Clock frequency = 3.392 GHz
+ Start time 15:26:31
+ Last message time 15:26:56
+ Total elapsed time 25.077 sec
+ .....................................
+ ---------------------------- 2407 total entries -----------------------------
+
+
+ SUCCESS: GCHist structures initialized
+
+\\
+
+COMMAND: histobjfind.
+!HistObjFind <obj_address>
+
+To examine log entries related to an object whose present address is known one
+would use this command. The output of this command contains all entries that
+reference the object:
+
+ 0:003> !HistObjFind 028970d4
+ GCCount Object Message
+ ---------------------------------------------------------
+ 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2296 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2296 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+ 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2295 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2295 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+
+\\
+
+COMMAND: histroot.
+!HistRoot <root>
+
+The root value obtained from !HistObjFind can be used to track the movement of
+an object through the GCs.
+
+HistRoot provides information related to both promotions and relocations of the
+root specified as the argument.
+
+ 0:003> !HistRoot 01e411b8
+ GCCount Value MT Promoted? Notes
+ ---------------------------------------------------------
+ 2296 028970d4 5b6c5cd8 yes
+ 2295 028970d4 5b6c5cd8 yes
+ 2294 028970d4 5b6c5cd8 yes
+ 2293 028970d4 5b6c5cd8 yes
+ 2292 028970d4 5b6c5cd8 yes
+ 2291 028970d4 5b6c5cd8 yes
+ 2290 028970d4 5b6c5cd8 yes
+ 2289 028970d4 5b6c5cd8 yes
+ 2288 028970d4 5b6c5cd8 yes
+ 2287 028970d4 5b6c5cd8 yes
+ 2286 028970d4 5b6c5cd8 yes
+ 2285 028970d4 5b6c5cd8 yes
+ 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs
+ ...
+
+\\
+
+COMMAND: histobj.
+!HistObj <obj_address>
+
+This command examines all stress log relocation records and displays the chain
+of GC relocations that may have led to the address passed in as an argument.
+Conceptually the output is:
+
+ GenN obj_address root1, root2, root3,
+ GenN-1 prev_obj_addr root1, root2,
+ GenN-2 prev_prev_oa root1, root4,
+ ...
+
+Sample output:
+ 0:003> !HistObj 028970d4
+ GCCount Object Roots
+ ---------------------------------------------------------
+ 2296 028970d4 00223fc4, 01e411b8,
+ 2295 028970d4 00223fc4, 01e411b8,
+ 2294 028970d4 00223fc4, 01e411b8,
+ 2293 028970d4 00223fc4, 01e411b8,
+ 2292 028970d4 00223fc4, 01e411b8,
+ 2291 028970d4 00223fc4, 01e411b8,
+ 2290 028970d4 00223fc4, 01e411b8,
+ 2289 028970d4 00223fc4, 01e411b8,
+ 2288 028970d4 00223fc4, 01e411b8,
+ 2287 028970d4 00223fc4, 01e411b8,
+ 2286 028970d4 00223fc4, 01e411b8,
+ 2285 028970d4 00223fc4, 01e411b8,
+ 322 028970d4 01e411b8,
+ 0 028970d4
+
+\\
+
+COMMAND: histclear.
+!HistClear
+
+This command releases any resources used by the Hist-family of commands.
+Generally there's no need to call this explicitly, as each HistInit will first
+cleanup the previous resources.
+
+ 0:003> !HistClear
+ Completed successfully.
+
+\\
+
+COMMAND: dumprcw.
+!DumpRCW <RCW address>
+
+This command lists information about a Runtime Callable Wrapper. You can use
+!DumpObj to obtain the RCW address corresponding to a managed object.
+
+The output contains all COM interface pointers that the RCW holds on to, which
+is useful for investigating lifetime issues of interop-heavy applications.
+\\
+
+COMMAND: dumpccw.
+!DumpCCW <CCW address or COM IP>
+
+This command lists information about a COM Callable Wrapper. You can use
+!DumpObj to obtain the CCW address corresponding to a managed object or pass
+a COM interface pointer to which the object has been marshaled.
+
+The output contains the COM reference count of the CCW, which is useful for
+investigating lifetime issues of interop-heavy applications.
+\\
+
+
diff --git a/src/ToolBox/SOS/Strike/sosdocsunix.txt b/src/ToolBox/SOS/Strike/sosdocsunix.txt
new file mode 100644
index 0000000000..52ec86dc4e
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sosdocsunix.txt
@@ -0,0 +1,1713 @@
+-------------------------------------------------------------------------------
+NOTE: THIS FILE CONTAINS SOS DOCUMENTATION. THE FORMAT OF THE FILE IS:
+
+<optional comments>
+COMMAND: <cmd name, all lower case>
+<descriptive text of the command>
+\\ <these are two backslashes, immediately followed by a newline>
+
+<repeat the sequence above>
+
+The first command is "contents" which is the general help screen. The rest
+correspond to SOS command names. This file is embedded as a resource in the SOS
+binary. Be sure to list any new commands here.
+-------------------------------------------------------------------------------
+
+
+
+COMMAND: contents.
+SOS is a debugger extension DLL designed to aid in the debugging of managed
+programs. Functions are listed by category, then roughly in order of
+importance. Shortcut names for popular functions are listed in parenthesis.
+Type "soshelp <functionname>" for detailed info on that function.
+
+Object Inspection Examining code and stacks
+----------------------------- -----------------------------
+DumpObj (dumpobj) Threads (clrthreads)
+DumpArray ThreadState
+DumpStackObjects (dso) IP2MD (ip2md)
+DumpHeap (dumpheap) u (clru)
+DumpVC DumpStack (dumpstack)
+GCRoot (gcroot) EEStack (eestack)
+PrintException (pe) ClrStack (clrstack)
+ GCInfo
+ EHInfo
+ bpmd (bpmd)
+
+Examining CLR data structures Diagnostic Utilities
+----------------------------- -----------------------------
+DumpDomain VerifyHeap
+EEHeap (eeheap) FindAppDomain
+Name2EE (name2ee) DumpLog (dumplog)
+DumpMT (dumpmt)
+DumpClass (dumpclass)
+DumpMD (dumpmd)
+Token2EE
+DumpModule (dumpmodule)
+DumpAssembly
+DumpRuntimeTypes
+DumpIL (dumpil)
+DumpSig
+DumpSigElem
+
+Examining the GC history Other
+----------------------------- -----------------------------
+HistInit (histinit) FAQ
+HistRoot (histroot) Help (soshelp)
+HistObj (histobj)
+HistObjFind (histobjfind)
+HistClear (histclear)
+\\
+
+COMMAND: faq.
+>> Where can I get the right version of SOS for my build?
+
+If you are running a xplat version of coreclr, the sos module (exact name
+is platform dependent) is installed in the same directory as the main coreclr
+module. There is also an lldb sos plugin command that allows the path where
+the sos, dac and dbi modules are loaded:
+
+ "setsospath /home/user/coreclr/bin/Product/Linux.x64.Debug""
+
+If you are using a dump file created on another machine, it is a little bit
+more complex. You need to make sure the dac module that came with that install
+is in the directory set with the above command.
+
+>> I have a chicken and egg problem. I want to use SOS commands, but the CLR
+ isn't loaded yet. What can I do?
+
+TBD
+
+>> I got the following error message. Now what?
+
+
+ (lldb) sos DumpStackObjects
+ The coreclr module is not loaded yet in the target process
+ (lldb)
+
+This means that the clr is not loaded yet, or has been unloaded. You need to
+wait until your managed program is running in order to use these commands. If
+you have just started the program a good way to do this is to type
+
+ breakpoint set coreclr`EEStartup
+
+in the debugger, and let it run. After the function EEStartup is finished,
+there will be a minimal managed environment for executing SOS commands.
+
+\\
+
+COMMAND: dumpobj.
+DumpObj [-nofields] <object address>
+
+This command allows you to examine the fields of an object, as well as learn
+important properties of the object such as the EEClass, the MethodTable, and
+the size.
+
+You might find an object pointer by running DumpStackObjects and choosing
+from the resultant list. Here is a simple object:
+
+ (lldb) dumpobj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 009038ec 4000008 4 Customer 0 instance 00a79ce4 name
+ 009038ec 4000009 8 Bank 0 instance 00a79d2c bank
+
+Note that fields of type Customer and Bank are themselves objects, and you can
+run DumpObj on them too. You could look at the field directly in memory using
+the offset given. "dd a79d40+8 l1" would allow you to look at the bank field
+directly. Be careful about using this to set memory breakpoints, since objects
+can move around in the garbage collected heap.
+
+What else can you do with an object? You might run GCRoot, to determine what
+roots are keeping it alive. Or you can find all objects of that type with
+"dumpheap -type Customer".
+
+The column VT contains the value 1 if the field is a valuetype structure, and
+0 if the field contains a pointer to another object. For valuetypes, you can
+take the MethodTable pointer in the MT column, and the Value and pass them to
+the command DumpVC.
+
+The arguments in detail:
+-nofields: do not print fields of the object, useful for objects like String
+\\
+
+COMMAND: dumparray.
+DumpArray
+ [-start <startIndex>]
+ [-length <length>]
+ [-details]
+ [-nofields]
+ <array object address>
+
+This command allows you to examine elements of an array object.
+The arguments in detail:
+ -start <startIndex>: optional, only supported for single dimension array.
+ Specify from which index the command shows the elements.
+ -length <length>: optional, only supported for single dimension array.
+ Specify how many elements to show.
+ -details: optional. Ask the command to print out details
+ of the element using DumpObj and DumpVC format.
+ -nofields: optional, only takes effect when -details is used. Do
+ not print fields of the elements. Useful for arrays of
+ objects like String
+
+ Example output:
+
+ (lldb) sos DumpArray -start 2 -length 3 -details 00ad28d0
+ Name: Value[]
+ MethodTable: 03e41044
+ EEClass: 03e40fc0
+ Size: 132(0x84) bytes
+ Array: Rank 1, Number of elements 10, Type VALUETYPE
+ Element Type: Value
+ [2] 00ad28f0
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (/home/user/bugs/225271/arraytest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 2 x
+ 5b9a628c 4000002 4 System.Int32 instance 4 y
+ 5b9a628c 4000003 8 System.Int32 instance 6 z
+ [3] 00ad28fc
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (/home/user/bugs/225271/arraytest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 3 x
+ 5b9a628c 4000002 4 System.Int32 instance 6 y
+ 5b9a628c 4000003 8 System.Int32 instance 9 z
+ [4] 00ad2908
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (/home/user/bugs/225271/arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 4 x
+ 5b9a628c 4000002 4 System.Int32 instance 8 y
+ 5b9a628c 4000003 8 System.Int32 instance 12 z
+
+
+\\
+
+COMMAND: dumpstackobjects.
+DumpStackObjects [-verify] [top stack [bottom stack]]
+
+This command will display any managed objects it finds within the bounds of
+the current stack. Combined with the stack tracing commands like K and
+CLRStack, it is a good aid to determining the values of locals and
+parameters.
+
+If you use the -verify option, each non-static CLASS field of an object
+candidate is validated. This helps to eliminate false positives. It is not
+on by default because very often in a debugging scenario, you are
+interested in objects with invalid fields.
+
+The abbreviation dso can be used for brevity.
+\\
+
+COMMAND: dumpheap.
+DumpHeap [-stat]
+ [-strings]
+ [-short]
+ [-min <size>]
+ [-max <size>]
+ [-live]
+ [-dead]
+ [-thinlock]
+ [-startAtLowerBound]
+ [-mt <MethodTable address>]
+ [-type <partial type name>]
+ [start [end]]
+
+DumpHeap is a powerful command that traverses the garbage collected heap,
+collection statistics about objects. With it's various options, it can look for
+particular types, restrict to a range, or look for ThinLocks (see SyncBlk
+documentation). Finally, it will provide a warning if it detects excessive
+fragmentation in the GC heap.
+
+When called without options, the output is first a list of objects in the heap,
+followed by a report listing all the types found, their size and number:
+
+ (lldb) dumpheap
+ Address MT Size
+ 00a71000 0015cde8 12 Free
+ 00a7100c 0015cde8 12 Free
+ 00a71018 0015cde8 12 Free
+ 00a71024 5ba58328 68
+ 00a71068 5ba58380 68
+ 00a710ac 5ba58430 68
+ 00a710f0 5ba5dba4 68
+ ...
+ total 619 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource
+ 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag
+ 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer
+ ...
+ 0015cde8 6 10260 Free
+ 5ba57bf8 318 18136 System.String
+ ...
+
+"Free" objects are simply regions of space the garbage collector can use later.
+If 30% or more of the heap contains "Free" objects, the process may suffer from
+heap fragmentation. This is usually caused by pinning objects for a long time
+combined with a high rate of allocation. Here is example output where DumpHeap
+provides a warning about fragmentation:
+
+ <After the Statistics section>
+ Fragmented blocks larger than 1MB:
+ Addr Size Followed by
+ 00a780c0 1.5MB 00bec800 System.Byte[]
+ 00da4e38 1.2MB 00ed2c00 System.Byte[]
+ 00f16df0 1.2MB 01044338 System.Byte[]
+
+The arguments in detail:
+
+-stat Restrict the output to the statistical type summary
+-strings Restrict the output to a statistical string value summary
+-short Limits output to just the address of each object. This allows you
+ to easily pipe output from the command to another debugger
+ command for automation.
+-min Ignore objects less than the size given in bytes
+-max Ignore objects larger than the size given in bytes
+-live Only print live objects
+-dead Only print dead objects (objects which will be collected in the
+ next full GC)
+-thinlock Report on any ThinLocks (an efficient locking scheme, see SyncBlk
+ documentation for more info)
+-startAtLowerBound
+ Force heap walk to begin at lower bound of a supplied address range.
+ (During plan phase, the heap is often not walkable because objects
+ are being moved. In this case, DumpHeap may report spurious errors,
+ in particular bad objects. It may be possible to traverse more of
+ the heap after the reported bad object. Even if you specify an
+ address range, DumpHeap will start its walk from the beginning of
+ the heap by default. If it finds a bad object before the specified
+ range, it will stop before displaying the part of the heap in which
+ you are interested. This switch will force DumpHeap to begin its
+ walk at the specified lower bound. You must supply the address of a
+ good object as the lower bound for this to work. Display memory at
+ the address of the bad object to manually find the next method
+ table (use DumpMT to verify). If the GC is currently in a call to
+ memcopy, You may also be able to find the next object's address by
+ adding the size to the start address given as parameters.)
+-mt List only those objects with the MethodTable given
+-type List only those objects whose type name is a substring match of the
+ string provided.
+start Begin listing from this address
+end Stop listing at this address
+
+A special note about -type: Often, you'd like to find not only Strings, but
+System.Object arrays that are constrained to contain Strings. ("new
+String[100]" actually creates a System.Object array, but it can only hold
+System.String object pointers). You can use -type in a special way to find
+these arrays. Just pass "-type System.String[]" and those Object arrays will
+be returned. More generally, "-type <Substring of interesting type>[]".
+
+The start/end parameters can be obtained from the output of eeheap -gc. For
+example, if you only want to list objects in the large heap segment:
+
+ (lldb) eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00c32754
+ generation 1 starts at 0x00c32748
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 010443a8 005d33a8(6108072)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a75000 0x00004000(16384)
+ Total Size 0x5d73a8(6124456)
+ ------------------------------
+ GC Heap Size 0x5d73a8(6124456)
+
+ (lldb) dumpheap 1a71000 1a75000
+ Address MT Size
+ 01a71000 5ba88bd8 2064
+ 01a71810 0019fe48 2032 Free
+ 01a72000 5ba88bd8 4096
+ 01a73000 0019fe48 4096 Free
+ 01a74000 5ba88bd8 4096
+ total 5 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 0019fe48 2 6128 Free
+ 5ba88bd8 3 10256 System.Object[]
+ Total 5 objects
+
+Finally, if GC heap corruption is present, you may see an error like this:
+
+ (lldb) dumpheap -stat
+ object 00a73d24: does not have valid MT
+ curr_object : 00a73d24
+ Last good object: 00a73d14
+ ----------------
+
+That indicates a serious problem. See the help for VerifyHeap for more
+information on diagnosing the cause.
+\\
+
+COMMAND: dumpvc.
+DumpVC <MethodTable address> <Address>
+
+DumpVC allows you to examine the fields of a value class. In C#, this is a
+struct, and lives on the stack or within an Object on the GC heap. You need
+to know the MethodTable address to tell SOS how to interpret the fields, as
+a value class is not a first-class object with it's own MethodTable as the
+first field. For example:
+
+ (lldb) sos DumpObj a79d98
+ Name: Mainy
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Size: 28(0x1c) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype
+ 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep
+
+m_valuetype is a value type. The value in the MT column (0090320c) is the
+MethodTable for it, and the Value column provides the start address:
+
+ (lldb) sos DumpVC 0090320c 00a79d9c
+ Name: Funny
+ MethodTable 0090320c
+ EEClass: 03ee14b8
+ Size: 28(0x1c) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000001 0 CLASS instance 00a743d8 signature
+ 0090320c 4000002 8 System.Int32 instance 2345 m1
+ 0090320c 4000003 10 System.Boolean instance 1 b1
+ 0090320c 4000004 c System.Int32 instance 1234 m2
+ 0090320c 4000005 4 CLASS instance 00a79d98 backpointer
+
+DumpVC is quite a specialized function. Some managed programs make heavy use
+of value classes, while others do not.
+\\
+
+COMMAND: gcroot.
+GCRoot [-nostacks] <Object address>
+
+GCRoot looks for references (or roots) to an object. These can exist in four
+places:
+
+ 1. On the stack
+ 2. Within a GC Handle
+ 3. In an object ready for finalization
+ 4. As a member of an object found in 1, 2 or 3 above.
+
+First, all stacks will be searched for roots, then handle tables, and finally
+the freachable queue of the finalizer. Some caution about the stack roots:
+GCRoot doesn't attempt to determine if a stack root it encountered is valid
+or is old (discarded) data. You would have to use CLRStack and U to
+disassemble the frame that the local or argument value belongs to in order to
+determine if it is still in use.
+
+Because people often want to restrict the search to gc handles and freachable
+objects, there is a -nostacks option.
+\\
+
+COMMAND: pe.
+COMMAND: printexception.
+PrintException [-nested] [-lines] [-ccw] [<Exception object address>] [<CCW pointer>]
+
+This will format fields of any object derived from System.Exception. One of the
+more useful aspects is that it will format the _stackTrace field, which is a
+binary array. If _stackTraceString field is not filled in, that can be helpful
+for debugging. You can of course use DumpObj on the same exception object to
+explore more fields.
+
+If called with no parameters, PrintException will look for the last outstanding
+exception on the current thread and print it. This will be the same exception
+that shows up in a run of clrthreads.
+
+PrintException will notify you if there are any nested exceptions on the
+current managed thread. (A nested exception occurs when you throw another
+exception within a catch handler already being called for another exception).
+If there are nested exceptions, you can re-run PrintException with the
+"-nested" option to get full details on the nested exception objects. The
+clrthreads command will also tell you which threads have nested exceptions.
+
+PrintException can display source information if available, by specifying the
+-lines command line argument.
+
+PrintException prints the exception object corresponding to a given CCW pointer,
+which can be specified using the -ccw option.
+
+The abbreviation 'pe' can be used for brevity.
+\\
+
+COMMAND: threadstate.
+ThreadState value
+
+The clrthreads command outputs, among other things, the state of the thread.
+This is a bit field which corresponds to various states the thread is in.
+To check the state of the thread, simply pass that bit field from the
+output of clrthreads into ThreadState.
+
+Example:
+ (lldb) clrthreads
+ ThreadCount: 2
+ UnstartedThread: 0
+ BackgroundThread: 1
+ PendingThread: 0
+ DeadThread: 0
+ Hosted Runtime: no
+ PreEmptive GC Alloc Lock
+ ID OSID ThreadOBJ State GC Context Domain Count APT Exception
+ 0 1 250 0019b068 a020 Disabled 02349668:02349fe8 0015def0 0 MTA
+ 2 2 944 001a6020 b220 Enabled 00000000:00000000 0015def0 0 MTA (Finalizer)
+ 0:003> sos ThreadState b220
+ Legal to Join
+ Background
+ CLR Owns
+ CoInitialized
+ In Multi Threaded Apartment
+
+Possible thread states:
+ Thread Abort Requested
+ GC Suspend Pending
+ User Suspend Pending
+ Debug Suspend Pending
+ GC On Transitions
+ Legal to Join
+ Yield Requested
+ Hijacked by the GC
+ Blocking GC for Stack Overflow
+ Background
+ Unstarted
+ Dead
+ CLR Owns
+ CoInitialized
+ In Single Threaded Apartment
+ In Multi Threaded Apartment
+ Reported Dead
+ Fully initialized
+ Task Reset
+ Sync Suspended
+ Debug Will Sync
+ Stack Crawl Needed
+ Suspend Unstarted
+ Aborted
+ Thread Pool Worker Thread
+ Interruptible
+ Interrupted
+ Completion Port Thread
+ Abort Initiated
+ Finalized
+ Failed to Start
+ Detached
+\\
+COMMAND: threads.
+COMMAND: clrthreads.
+Threads [-live] [-special]
+
+Threads (clrthreads) lists all the mananaged threads in the process.
+
+-live: optional. Only print threads associated with a live thread.
+-special: optional. With this switch, the command will display all the special
+ threads created by CLR. Those threads might not be managed threads
+ so they might not be shown in the first part of the command's
+ output. Example of special threads include: GC threads (in
+ concurrent GC and server GC), Debugger helper threads, Finalizer
+ threads, AppDomain Unload threads, and Threadpool timer threads.
+
+Each thread has many attributes, many of which can be ignored. The important
+ones are discussed below:
+
+There are three ID columns:
+
+1) The debugger shorthand ID (When the runtime is hosted this column might
+ display the special string "<<<<" when this internal thread object is not
+ associated with any physical thread - this may happen when the host reuses
+ the runtime internal thread object)
+2) The CLR Thread ID
+3) The OS thread ID.
+
+If PreEmptiveGC is enabled for a thread, then a garbage collection
+can occur while that thread is running. For example, if you break in while
+a managed thread is making a PInvoke call to a Win32 function, that thread
+will be in PreEmptive GC mode.
+
+The Domain column indicates what AppDomain the thread is currently executing
+in. You can pass this value to DumpDomain to find out more.
+
+The APT column gives the COM apartment mode.
+
+Exception will list the last thrown exception (if any) for the thread. More
+details can be obtained by passing the pointer value to PrintException. If
+you get the notation "(nested exceptions)", you can get details on those
+exceptions by switching to the thread in question, and running
+"PrintException -nested".
+\\
+
+COMMAND: clrstack.
+CLRStack [-a] [-l] [-p] [-n] [-f]
+CLRStack [-a] [-l] [-p] [-i] [variable name] [frame]
+
+CLRStack attempts to provide a true stack trace for managed code only. It is
+handy for clean, simple traces when debugging straightforward managed
+programs. The -p parameter will show arguments to the managed function. The
+-l parameter can be used to show information on local variables in a frame.
+SOS can't retrieve local names at this time, so the output for locals is in
+the format <local address> = <value>. The -a (all) parameter is a short-cut
+for -l and -p combined.
+
+The -f option (full mode) displays the native frames intermixing them with
+the managed frames and the assembly name and function offset for the managed
+frames.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+When you see methods with the name "[Frame:...", that indicates a transition
+between managed and unmanaged code. You could run IP2MD on the return
+addresses in the call stack to get more information on each managed method.
+
+On x64 platforms, Transition Frames are not displayed at this time. To avoid
+heavy optimization of parameters and locals one can request the JIT compiler
+to not optimize functions in the managed app by creating a file myapp.ini
+(if your program is myapp.exe) in the same directory. Put the following lines
+in myapp.ini and re-run:
+
+[.NET Framework Debugging Control]
+GenerateTrackingInfo=1
+AllowOptimize=0
+
+The -i option is a new EXPERIMENTAL addition to CLRStack and will use the ICorDebug
+interfaces to display the managed stack and variables. With this option you can also
+view and expand arrays and fields for managed variables. If a stack frame number is
+specified in the command line, CLRStack will show you the parameters and/or locals
+only for that frame (provided you specify -l or -p or -a of course). If a variable
+name and a stack frame number are specified in the command line, CLRStack will show
+you the parameters and/or locals for that frame, and will also show you the fields
+for that variable name you specified. Here are some examples:
+ clrstack -i -a : This will show you all parameters and locals for all frames
+ clrstack -i -a 3 : This will show you all parameters and locals, for frame 3
+ clrstack -i var1 0 : This will show you the fields of 'var1' for frame 0
+ clrstack -i var1.abc 2 : This will show you the fields of 'var1', and expand
+ 'var1.abc' to show you the fields of the 'abc' field,
+ for frame 2.
+ clrstack -i var1.[basetype] 0 : This will show you the fields of 'var1', and
+ expand the base type of 'var1' to show you its
+ fields.
+ clrstack -i var1.[6] 0 : If 'var1' is an array, this will show you the element
+ at index 6 in the array, along with its fields
+The -i options uses DML output for a better debugging experience, so typically you
+should only need to execute "clrstack -i", and from there, click on the DML
+hyperlinks to inspect the different managed stack frames and managed variables.
+\\
+
+COMMAND: ip2md.
+IP2MD <Code address>
+
+Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc
+associated with it. For example, this output from K:
+
+ (lldb) bt
+ ...
+ frame #9: 0x00007fffffffbf60 0x00007ffff61c6d89 libcoreclr.so`MethodDesc::DoPrestub(this=0x00007ffff041f870, pDispatchingMT=0x0000000000000000) + 3001 at prestub.cpp:1490
+ frame #10: 0x00007fffffffc140 0x00007ffff61c5f17 libcoreclr.so`::PreStubWorker(pTransitionBlock=0x00007fffffffc9a8, pMD=0x00007ffff041f870) + 1399 at prestub.cpp:1037
+ frame #11: 0x00007fffffffc920 0x00007ffff5f5238c libcoreclr.so`ThePreStub + 92 at theprestubamd64.S:800
+ frame #12: 0x00007fffffffca10 0x00007ffff04981cc
+ frame #13: 0x00007fffffffca30 0x00007ffff049773c
+ frame #14: 0x00007fffffffca80 0x00007ffff04975ad
+ ...
+ frame #22: 0x00007fffffffcc90 0x00007ffff5f51a0f libcoreclr.so`CallDescrWorkerInternal + 124 at calldescrworkeramd64.S:863
+ frame #23: 0x00007fffffffccb0 0x00007ffff5d6d6dc libcoreclr.so`CallDescrWorkerWithHandler(pCallDescrData=0x00007fffffffce80, fCriticalCall=0) + 476 at callhelpers.cpp:88
+ frame #24: 0x00007fffffffcd00 0x00007ffff5d6eb38 libcoreclr.so`MethodDescCallSite::CallTargetWorker(this=0x00007fffffffd0c8, pArguments=0x00007fffffffd048) + 2504 at callhelpers.cpp:633
+
+ (lldb) ip2md 0x00007ffff049773c
+ MethodDesc: 00007ffff7f71920
+ Method Name: Microsoft.Win32.SafeHandles.SafeFileHandle.Open(System.Func`1<Int32>)
+ Class: 00007ffff0494bf8
+ MethodTable: 00007ffff7f71a58
+ mdToken: 0000000006000008
+ Module: 00007ffff7f6b938
+ IsJitted: yes
+ CodeAddr: 00007ffff04976c0
+ Transparency: Critical
+
+We have taken a return address into Mainy.Main, and discovered information
+about that method. You could run U, DumpMT, DumpClass, DumpMD, or
+DumpModule on the fields listed to learn more.
+
+The "Source line" output will only be present if the debugger can find the
+symbols for the managed module containing the given <code address>, and if the
+debugger is configured to load line number information.
+\\
+
+COMMAND: clru.
+COMMAND: u.
+U [-gcinfo] [-ehinfo] [-n] [-o] <MethodDesc address> | <Code address>
+
+Presents an annotated disassembly of a managed method when given a MethodDesc
+pointer for the method, or a code address within the method body. Unlike the
+debugger "U" function, the entire method from start to finish is printed,
+with annotations that convert metadata tokens to names.
+
+ <example output>
+ ...
+ 03ef015d b901000000 mov ecx,0x1
+ 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713)
+ 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter)
+ 03ef016d 89442414 mov [esp+0x14],eax
+
+If you pass the -gcinfo flag, you'll get inline display of the GCInfo for
+the method. You can also obtain this information with the GCInfo command.
+
+If you pass the -ehinfo flag, you'll get inline display of exception info
+for the method. (Beginning and end of try/finally/catch handlers, etc.).
+You can also obtain this information with the EHInfo command.
+
+If you pass the -o flag, the byte offset of each instruction from the
+beginning of the method will be printed in addition to the absolute address of
+the instruction.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), and if symbols are available for the managed
+module containing the method being examined, the output of the command will
+include the source file name and line number corresponding to the
+disassembly. The -n (No line numbers) flag can be specified to disable this
+behavior.
+
+ <example output>
+ ...
+ c:\Code\prj.mini\exc.cs @ 38:
+ 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type <Enter> to continue: ")
+ 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b)
+ 001b00bb 90 nop
+
+ c:\Code\prj.mini\exc.cs @ 39:
+ 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6)
+ >>> 001b00c1 90 nop
+ ...
+\\
+
+COMMAND: dumpstack.
+DumpStack [-EE] [-n] [top stack [bottom stack]]
+
+[x86 and x64 documentation]
+
+This command provides a verbose stack trace obtained by "scraping." Therefore
+the output is very noisy and potentially confusing. The command is good for
+viewing the complete call stack when "kb" gets confused. For best results,
+make sure you have valid symbols.
+
+-EE will only show managed functions.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+You can also pass a stack range to limit the output.
+\\
+
+COMMAND: eestack.
+EEStack [-short] [-EE]
+
+This command runs DumpStack on all threads in the process. The -EE option is
+passed directly to DumpStack. The -short option tries to narrow down the
+output to "interesting" threads only, which is defined by
+
+1) The thread has taken a lock.
+2) The thread has been "hijacked" in order to allow a garbage collection.
+3) The thread is currently in managed code.
+
+See the documentation for DumpStack for more info.
+\\
+
+COMMAND: ehinfo.
+EHInfo (<MethodDesc address> | <Code address>)
+
+EHInfo shows the exception handling blocks in a jitted method. For each
+handler, it shows the type, including code addresses and offsets for the clause
+block and the handler block. For a TYPED handler, this would be the "try" and
+"catch" blocks respectively.
+
+Sample output:
+
+ (lldb) sos EHInfo 33bbd3a
+ MethodDesc: 03310f68
+ Method Name: MainClass.Main()
+ Class: 03571358
+ MethodTable: 0331121c
+ mdToken: 0600000b
+ Module: 001e2fd8
+ IsJitted: yes
+ CodeAddr: 033bbca0
+ Transparency: Critical
+
+ EHHandler 0: TYPED catch(System.IO.FileNotFoundException)
+ Clause: [033bbd2b, 033bbd3c] [8b, 9c]
+ Handler: [033bbd3c, 033bbd50] [9c, b0]
+
+ EHHandler 1: FINALLY
+ Clause: [033bbd83, 033bbda3] [e3, 103]
+ Handler: [033bbda3, 033bbdc5] [103, 125]
+
+ EHHandler 2: TYPED catch(System.Exception)
+ Clause: [033bbd7a, 033bbdc5] [da, 125]
+ Handler: [033bbdc5, 033bbdd6] [125, 136]
+
+\\
+
+COMMAND: gcinfo.
+GCInfo (<MethodDesc address> | <Code address>)
+
+GCInfo is especially useful for CLR Devs who are trying to determine if there
+is a bug in the JIT Compiler. It parses the GCEncoding for a method, which is a
+compressed stream of data indicating when registers or stack locations contain
+managed objects. It is important to keep track of this information, because if
+a garbage collection occurs, the collector needs to know where roots are so it
+can update them with new object pointer values.
+
+Here is sample output where you can see the change in register state. Normally
+you would print this output out and read it alongside a disassembly of the
+method. For example, the notation "reg EDI becoming live" at offset 0x11 of the
+method might correspond to a "mov edi,ecx" statement.
+
+ (lldb) sos GCInfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method)
+ entry point 5b68dbb8
+ preJIT generated code
+ GC info 5b9f2f09
+ Method info block:
+ method size = 0036
+ prolog size = 19
+ epilog size = 8
+ epilog count = 1
+ epilog end = yes
+ saved reg. mask = 000B
+ ebp frame = yes
+ fully interruptible=yes
+ double align = no
+ security check = no
+ exception handlers = no
+ local alloc = no
+ edit & continue = no
+ varargs = no
+ argument count = 4
+ stack frame size = 1
+ untracked count = 5
+ var ptr tab count = 0
+ epilog at 002E
+ 36 D4 8C C7 AA |
+ 93 F3 40 05 |
+
+ Pointer table:
+ 14 | [EBP+14H] an untracked local
+ 10 | [EBP+10H] an untracked local
+ 0C | [EBP+0CH] an untracked local
+ 08 | [EBP+08H] an untracked local
+ 44 | [EBP-04H] an untracked local
+ F1 79 | 0011 reg EDI becoming live
+ 72 | 0013 reg ESI becoming live
+ 83 | 0016 push ptr 0
+ 8B | 0019 push ptr 1
+ 93 | 001C push ptr 2
+ 9B | 001F push ptr 3
+ 56 | 0025 reg EDX becoming live
+ 4A | 0027 reg ECX becoming live
+ 0E | 002D reg ECX becoming dead
+ 10 | 002D reg EDX becoming dead
+ E0 | 002D pop 4 ptrs
+ F0 31 | 0036 reg ESI becoming dead
+ 38 | 0036 reg EDI becoming dead
+ FF |
+
+This function is important for CLR Devs, but very difficult for anyone else to
+make sense of it. You would usually come to use it if you suspect a gc heap
+corruption bug caused by invalid GCEncoding for a particular method.
+\\
+
+COMMAND: bpmd.
+bpmd [-nofuturemodule] <module name> <method name> [<il offset>]
+bpmd -md <MethodDesc>
+bpmd -list
+bpmd -clear <pending breakpoint number>
+bpmd -clearall
+
+bpmd provides managed breakpoint support. If it can resolve the method name
+to a loaded, jitted or ngen'd function it will create a breakpoint with "bp".
+If not then either the module that contains the method hasn't been loaded yet
+or the module is loaded, but the function is not jitted yet. In these cases,
+bpmd asks the Windows Debugger to receive CLR Notifications, and waits to
+receive news of module loads and JITs, at which time it will try to resolve
+the function to a breakpoint. -nofuturemodule can be used to suppress
+creating a breakpoint against a module that has not yet been loaded.
+
+Management of the list of pending breakpoints can be done via bpmd -list,
+bpmd -clear, and bpmd -clearall commands. bpmd -list generates a list of
+all of the pending breakpoints. If the pending breakpoint has a non-zero
+module id, then that pending breakpoint is specific to function in that
+particular loaded module. If the pending breakpoint has a zero module id, then
+the breakpoint applies to modules that have not yet been loaded. Use
+bpmd -clear or bpmd -clearall to remove pending breakpoints from the list.
+
+This brings up a good question: "I want to set a breakpoint on the main
+method of my application. How can I do this?"
+
+ 1) Stop after coreclr is loaded - TBD
+
+ 2) Add the breakpoint with command such as:
+ bpmd myapp.exe MyApp.Main
+ 3) g
+ 4) You will stop at the start of MyApp.Main. If you type "bl" you will
+ see the breakpoint listed.
+
+To correctly specify explicitly implemented methods make sure to retrieve the
+method name from the metadata, or from the output of the "dumpmt -md" command.
+For example:
+
+ public interface I1
+ {
+ void M1();
+ }
+ public class ExplicitItfImpl : I1
+ {
+ ...
+ void I1.M1() // this method's name is 'I1.M1'
+ { ... }
+ }
+
+ bpmd myapp.exe ExplicitItfImpl.I1.M1
+
+
+bpmd works equally well with generic types. Adding a breakpoint on a generic
+type sets breakpoints on all already JIT-ted generic methods and sets a pending
+breakpoint for any instantiation that will be JIT-ted in the future.
+
+Example for generics:
+ Given the following two classes:
+
+ class G3<T1, T2, T3>
+ {
+ ...
+ public void F(T1 p1, T2 p2, T3 p3)
+ { ... }
+ }
+
+ public class G1<T> {
+ // static method
+ static public void G<W>(W w)
+ { ... }
+ }
+
+ One would issue the following commands to set breapoints on G3.F() and
+ G1.G():
+
+ bpmd myapp.exe G3`3.F
+ bpmd myapp.exe G1`1.G
+
+And for explicitly implemented methods on generic interfaces:
+ public interface IT1<T>
+ {
+ void M1(T t);
+ }
+
+ public class ExplicitItfImpl<U> : IT1<U>
+ {
+ ...
+ void IT1<U>.M1(U u) // this method's name is 'IT1<U>.M1'
+ { ... }
+ }
+
+ bpmd bpmd.exe ExplicitItfImpl`1.IT1<U>.M1
+
+Additional examples:
+ If IT1 and ExplicitItfImpl are types declared inside another class,
+ Outer, the bpmd command would become:
+
+ bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1<U>.M1
+
+ (note that the fully qualified type name for ExplicitItfImpl became
+ Outer+ExplicitItfImpl, using the '+' separator, while the method name
+ is Outer.IT1<U>.M1, using a '.' as the separator)
+
+ Furthermore, if the Outer class resides in a namespace, NS, the bpmd
+ command to use becomes:
+
+ bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1<U>.M1
+
+bpmd does not accept offsets nor parameters in the method name. You can add
+an IL offset as an optional parameter seperate from the name. If there are overloaded
+methods, bpmd will set a breakpoint for all of them.
+
+In the case of hosted environments such as SQL, the module name may be
+complex, like 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
+For this case, just be sure to surround the module name with single quotes,
+like:
+
+bpmd 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' Price.M2
+
+\\
+
+COMMAND: dumpdomain.
+DumpDomain [<Domain address>]
+
+When called with no parameters, DumpDomain will list all the AppDomains in the
+process. It enumerates each Assembly loaded into those AppDomains as well.
+In addition to your application domain, and any domains it might create, there
+are two special domains: the Shared Domain and the System Domain.
+
+Any Assembly pointer in the output can be passed to DumpAssembly. Any Module
+pointer in the output can be passed to DumpModule. Any AppDomain pointer can
+be passed to DumpDomain to limit output only to that AppDomain. Other
+functions provide an AppDomain pointer as well, such as clrthreads where it lists
+the current AppDomain for each thread.
+\\
+
+COMMAND: eeheap.
+EEHeap [-gc] [-loader]
+
+EEHeap enumerates process memory consumed by internal CLR data structures. You
+can limit the output by passing "-gc" or "-loader". All information will be
+displayed otherwise.
+
+The information for the Garbage Collector lists the ranges of each Segment in
+the managed heap. This can be useful if you believe you have an object pointer.
+If the pointer falls within a segment range given by "eeheap -gc", then you do
+have an object pointer, and can attempt to run "dumpobj" on it.
+
+Here is output for a simple program:
+
+ (lldb) eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00a71018
+ generation 1 starts at 0x00a7100c
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 00a7e01c 0000d01c(53276)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a76000 0x00005000(20480)
+ Total Size 0x1201c(73756)
+ ------------------------------
+ GC Heap Size 0x1201c(73756)
+
+So the total size of the GC Heap is only 72K. On a large web server, with
+multiple processors, you can expect to see a GC Heap of 400MB or more. The
+Garbage Collector attempts to collect and reclaim memory only when required to
+by memory pressure for better performance. You can also see the notion of
+"generations," wherein the youngest objects live in generation 0, and
+long-lived objects eventually get "promoted" to generation 2.
+
+The loader output lists various private heaps associated with AppDomains. It
+also lists heaps associated with the JIT compiler, and heaps associated with
+Modules. For example:
+
+ (lldb) eeheap -loader
+ Loader Heap:
+ --------------------------------------
+ System Domain: 5e0662a0
+ LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes.
+ HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x3000(12288)bytes
+ --------------------------------------
+ Shared Domain: 5e066970
+ LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x6000(24576)bytes
+ --------------------------------------
+ Domain 1: 14f000
+ LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes.
+ StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x8000(32768)bytes
+ --------------------------------------
+ Jit code heap:
+ Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Module Thunk heaps:
+ Module 5ba22410: Size: 0x00000000 bytes.
+ Module 001c1320: Size: 0x00000000 bytes.
+ Module 001c03f0: Size: 0x00000000 bytes.
+ Module 001caa38: Size: 0x00000000 bytes.
+ Total size: 0x0(0)bytes
+ --------------------------------------
+ Module Lookup Table heaps:
+ Module 5ba22410:Size: 0x00000000 bytes.
+ Module 001c1320:Size: 0x00000000 bytes.
+ Module 001c03f0:Size: 0x00000000 bytes.
+ Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Total LoaderHeap size: 0x15000(86016)bytes
+ =======================================
+
+By using eeheap to keep track of the growth of these private heaps, we are
+able to rule out or include them as a source of a memory leak.
+\\
+
+COMMAND: name2ee.
+Name2EE <module name> <type or method name>
+Name2EE <module name>!<type or method name>
+
+This function allows you to turn a class name into a MethodTable and EEClass.
+It turns a method name into a MethodDesc. Here is an example for a method:
+
+ (lldb) name2ee unittest.exe MainClass.Main
+ Module: 001caa38
+ Token: 0x0600000d
+ MethodDesc: 00902f40
+ Name: MainClass.Main()
+ JITTED Code Address: 03ef00b8
+
+and for a class:
+
+ (lldb) name2ee unittest!MainClass
+ Module: 001caa38
+ Token: 0x02000005
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Name: MainClass
+
+The module you are "browsing" with Name2EE needs to be loaded in the process.
+To get a type name exactly right, first browse the module with ILDASM. You
+can also pass * as the <module name> to search all loaded managed modules.
+<module name> can also be the debugger's name for a module, such as
+mscorlib or image00400000.
+
+The <module>!<type> syntax is also supported. You can use an asterisk on the
+left of the !, but the type on the right side needs to be fully qualified.
+
+If you are looking for a way to display a static field of a class (and you
+don't have an instance of the class, so dumpobj won't help you), note that
+once you have the EEClass, you can run DumpClass, which will display the
+value of all static fields.
+
+There is yet one more way to specify a module name. In the case of modules
+loaded from an assembly store (such as a SQL db) rather than disk, the
+module name will look like this:
+
+price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
+
+For this kind of module, simply use price as the module name:
+
+ 0:044> name2ee price Price
+ Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
+ Token: 0x02000002
+ MethodTable: 11a47ae0
+ EEClass: 11a538c8
+ Name: Price
+
+Where are we getting these module names from? Run DumpDomain to see a list of
+all loaded modules in all domains. And remember that you can browse all the
+types in a module with DumpModule -mt <module pointer>.
+\\
+
+COMMAND: dumpmt.
+DumpMT [-MD] <MethodTable address>
+
+Examine a MethodTable. Each managed object has a MethodTable pointer at the
+start. If you pass the "-MD" flag, you'll also see a list of all the methods
+defined on the object.
+\\
+
+COMMAND: dumpclass.
+DumpClass <EEClass address>
+
+The EEClass is a data structure associated with an object type. DumpClass
+will show attributes, as well as list the fields of the type. The output is
+similar to DumpObj. Although static field values will be displayed,
+non-static values won't because you need an instance of an object for that.
+
+You can get an EEClass to look at from DumpMT, DumpObj, Name2EE, and
+Token2EE among others.
+\\
+
+COMMAND: dumpmd.
+DumpMD <MethodDesc address>
+
+This command lists information about a MethodDesc. You can use ip2md to turn
+a code address in a managed function into a MethodDesc:
+
+ (lldb) dumpmd 902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa78
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+
+If IsJitted is "yes," you can run U on the CodeAddr pointer to see a
+disassembly of the JITTED code. You can call also DumpClass, DumpMT,
+DumpModule on the Class, MethodTable and Module fields above.
+\\
+
+COMMAND: token2ee.
+Token2EE <module name> <token>
+
+This function allows you to turn a metadata token into a MethodTable or
+MethodDesc. Here is an example showing class tokens being resolved:
+
+ (lldb) sos Token2EE unittest.exe 02000003
+ Module: 001caa38
+ Token: 0x02000003
+ MethodTable: 0090375c
+ EEClass: 03ee1ae0
+ Name: Bank
+ (lldb) sos Token2EE image00400000 02000004
+ Module: 001caa38
+ Token: 0x02000004
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Name: Customer
+
+The module you are "browsing" with Token2EE needs to be loaded in the process.
+This function doesn't see much use, especially since a tool like ILDASM can
+show the mapping between metadata tokens and types/methods in a friendlier way.
+But it could be handy sometimes.
+
+You can pass "*" for <module name> to find what that token maps to in every
+loaded managed module. <module name> can also be the debugger's name for a
+module, such as mscorlib or image00400000.
+\\
+
+COMMAND: dumpmodule.
+DumpModule [-mt] <Module address>
+
+You can get a Module address from DumpDomain, DumpAssembly and other
+functions. Here is sample output:
+
+ (lldb) sos DumpModule 1caa50
+ Name: /home/user/pub/unittest
+ Attributes: PEFile
+ Assembly: 001ca248
+ LoaderHeap: 001cab3c
+ TypeDefToMethodTableMap: 03ec0010
+ TypeRefToMethodTableMap: 03ec0024
+ MethodDefToDescMap: 03ec0064
+ FieldDefToDescMap: 03ec00a4
+ MemberRefToDescMap: 03ec00e8
+ FileReferencesMap: 03ec0128
+ AssemblyReferencesMap: 03ec012c
+ MetaData start address: 00402230 (1888 bytes)
+
+The Maps listed map metadata tokens to CLR data structures. Without going into
+too much detail, you can examine memory at those addresses to find the
+appropriate structures. For example, the TypeDefToMethodTableMap above can be
+examined:
+
+ (lldb) dd 3ec0010
+ 03ec0010 00000000 00000000 0090320c 0090375c
+ 03ec0020 009038ec ...
+
+This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You
+can run DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token
+and maps it to a MethodDesc, which can be passed to dumpmd.
+
+There is a new option "-mt", which will display the types defined in a module,
+and the types referenced by the module. For example:
+
+ (lldb) sos DumpModule -mt 1aa580
+ Name: /home/user/pub/unittest
+ ...<etc>...
+ MetaData start address: 0040220c (1696 bytes)
+
+ Types defined in this module
+
+ MT TypeDef Name
+ --------------------------------------------------------------------------
+ 030d115c 0x02000002 Funny
+ 030d1228 0x02000003 Mainy
+
+ Types referenced in this module
+
+ MT TypeRef Name
+ --------------------------------------------------------------------------
+ 030b6420 0x01000001 System.ValueType
+ 030b5cb0 0x01000002 System.Object
+ 030fceb4 0x01000003 System.Exception
+ 0334e374 0x0100000c System.Console
+ 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle
+ 0336a048 0x0100000f System.GC
+
+\\
+
+COMMAND: dumpassembly.
+DumpAssembly <Assembly address>
+
+Example output:
+
+ (lldb) sos DumpAssembly 1ca248
+ Parent Domain: 0014f000
+ Name: /home/user/pub/unittest
+ ClassLoader: 001ca060
+ Module Name
+ 001caa50 /home/user/pub/unittest
+
+An assembly can consist of multiple modules, and those will be listed. You can
+get an Assembly address from the output of DumpDomain.
+\\
+
+COMMAND: dumpruntimetypes.
+DumpRuntimeTypes
+
+DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and
+prints the type name and MethodTable they refer too. Sample output:
+
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ a515f4 14a740 5baf8d28 System.TypedReference
+ a51608 14a740 5bb05764 System.Globalization.BaseInfoTable
+ a51958 14a740 5bb05b24 System.Globalization.CultureInfo
+ a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly
+ a51de0 14a740 5bb069c8 System.Globalization.TextInfo
+ a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource
+ a56bbc 14a740 5baf7248 System.Int32
+ a56bd0 14a740 5baf3fdc System.String
+ a56cfc 14a740 5baf36a4 System.ValueType
+ ...
+
+This command will print a "?" in the domain column if the type is loaded into multiple
+AppDomains. For example:
+
+ (lldb) sos DumpRuntimeTypes
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ 28435a0 ? 3f6a8c System.TypedReference
+ 28435b4 ? 214d6c System.ValueType
+ 28435c8 ? 216314 System.Enum
+ 28435dc ? 2147cc System.Object
+ 284365c ? 3cd57c System.IntPtr
+ 2843670 ? 3feaac System.Byte
+ 2843684 ? 23a544c System.IEquatable`1[[System.IntPtr, mscorlib]]
+ 2843784 ? 3c999c System.Int32
+ 2843798 ? 3caa04 System.IEquatable`1[[System.Int32, mscorlib]]
+\\
+
+COMMAND: dumpsig.
+DumpSig <sigaddr> <moduleaddr>
+
+This command dumps the signature of a method or field given by <sigaddr>. This is
+useful when you are debugging parts of the runtime which returns a raw PCCOR_SIGNATURE
+structure and need to know what its contents are.
+
+Sample output for a method:
+ 0:000> sos DumpSig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+The first section of the output is the calling convention. This includes, but is not
+limited to, "[DEFAULT]", "[C]", "[STDCALL]", "[THISCALL]", and so on. The second
+portion of the output is either "[hasThis]" or "[explicit]" for whether the method
+is an instance method or a static method respectively. The third portion of the
+output is the return value (in this case a "void"). Finally, the method's arguments
+are printed as the final portion of the output.
+
+Sample output for a field:
+ 0:000> sos DumpSig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+DumpSig will also work with generics. Here is the output for the following
+function:
+ public A Test(IEnumerable<B> n)
+
+ 0:000> sos DumpSig 00000000`00bc2437 000007ff00043178
+ [DEFAULT] [hasThis] __Canon (Class System.Collections.Generic.IEnumerable`1<__Canon>)
+\\
+
+COMMAND: dumpsigelem.
+DumpSigElem <sigaddr> <moduleaddr>
+
+This command dumps a single element of a signature object. For most circumstances,
+you should use DumpSig to look at individual signature objects, but if you find a
+signature that has been corrupted in some manner you can use DumpSigElem to read out
+the valid portions of it.
+
+If we look at a valid signature object for a method we see the following:
+ 0:000> dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+We can look at the individual elements of this object by adding the offsets into the
+object which correspond to the return value and parameters:
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+2 0x000007fe`eabd1000
+ Void
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+3 0x000007fe`eabd1000
+ Boolean
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+4 0x000007fe`eabd1000
+ String
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+5 0x000007fe`eabd1000
+ String
+
+We can do something similar for fields. Here is the full signature of a field:
+ 0:000> dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+Using DumpSigElem we can find the type of the field by adding the offset of it (1) to
+the address of the signature:
+ 0:000> sos DumpSigElem 0x000007fe`eb7fd8cd+1 0x000007fe`eabd1000
+ ValueClass System.RuntimeTypeHandle
+
+DumpSigElem will also work with generics. Let a function be defined as follows:
+ public A Test(IEnumerable<B> n)
+
+The elements of this signature can be obtained by adding offsets into the signature
+when calling DumpSigElem:
+
+ 0:000> sos DumpSigElem 00000000`00bc2437+2 000007ff00043178
+ __Canon
+ 0:000> sos DumpSigElem 00000000`00bc2437+4 000007ff00043178
+ Class System.Collections.Generic.IEnumerable`1<__Canon>
+
+The actual offsets that you should add are determined by the contents of the
+signature itself. By trial and error you should be able to find various elements
+of the signature.
+\\
+
+COMMAND: dumpil.
+DumpIL <Managed DynamicMethod object> |
+ <DynamicMethodDesc pointer> |
+ <MethodDesc pointer> |
+ /i <IL pointer>
+
+DumpIL prints the IL code associated with a managed method. We added this
+function specifically to debug DynamicMethod code which was constructed on
+the fly. Happily it works for non-dynamic code as well.
+
+You can use it in four ways:
+
+ 1) If you have a System.Reflection.Emit.DynamicMethod object, just pass
+ the pointer as the first argument.
+ 2) If you have a DynamicMethodDesc pointer you can use that to print the
+ IL associated with the dynamic method.
+ 3) If you have an ordinary MethodDesc, you can see the IL for that as well,
+ just pass it as the first argument.
+ 4) If you have a pointer directly to the IL, specify /i followed by the
+ the IL address. This is useful for writers of profilers that instrument
+ IL.
+
+
+Note that dynamic IL is constructed a bit differently. Rather than referring
+to metadata tokens, the IL points to objects in a managed object array. Here
+is a simple example of the output for a dynamic method:
+
+ 0:000> sos DumpIL b741dc
+ This is dynamic IL. Exception info is not reported at this time.
+ If a token is unresolved, run "sos DumpObj <addr>" on the addr given
+ in parenthesis. You can also look at the token table yourself, by
+ running "DumpArray 00b77388".
+
+ IL_0000: ldstr 70000002 "Inside invoked method "
+ IL_0005: call 6000003 System.Console.WriteLine(System.String)
+ IL_000a: ldc.i4.1
+ IL_000b: newarr 2000004 "System.Int32"
+ IL_0010: stloc.0
+ IL_0011: ldloc.0
+ IL_0012: ret
+\\
+
+COMMAND: verifyheap.
+VerifyHeap
+
+VerifyHeap is a diagnostic tool that checks the garbage collected heap for
+signs of corruption. It walks objects one by one in a pattern like this:
+
+ o = firstobject;
+ while(o != endobject)
+ {
+ o.ValidateAllFields();
+ o = (Object *) o + o.Size();
+ }
+
+If an error is found, VerifyHeap will report it. I'll take a perfectly good
+object and corrupt it:
+
+ (lldb) dumpobj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+ (lldb) ed a79d40+4 01 (change the name field to the bogus pointer value 1)
+ (lldb) sos VerifyHeap
+ object 01ee60dc: bad member 00000003 at 01EE6168
+ Last good object: 01EE60C4.
+
+If this gc heap corruption exists, there is a serious bug in your own code or
+in the CLR. In user code, an error in constructing PInvoke calls can cause
+this problem, and running with Managed Debugging Assistants is advised. If that
+possibility is eliminated, consider contacting Microsoft Product Support for
+help.
+\\
+
+COMMAND: dumplog.
+DumpLog [-addr <addressOfStressLog>] [<Filename>]
+
+To aid in diagnosing hard-to-reproduce stress failures, the CLR team added an
+in-memory log capability. The idea was to avoid using locks or I/O which could
+disturb a fragile repro environment. The DumpLog function allows you to write
+that log out to a file. If no Filename is specified, the file "Stresslog.txt"
+in the current directory is created.
+
+The optional argument addr allows one to specify a stress log other then the
+default one.
+
+ (lldb) dumplog
+ Attempting to dump Stress log to file 'StressLog.txt'
+ .................
+ SUCCESS: Stress log dumped
+
+To turn on the stress log, set the following registry keys under
+HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework:
+
+
+(DWORD) StressLog = 1
+(DWORD) LogFacility = 0xffffffbf (this is a bit mask, almost all logging is on.
+ This is also the default value if the key
+ isn't specified)
+(DWORD) StressLogSize = 65536 (this is the default value if the key isn't
+ specified)
+(DWORD) LogLevel = 6 (this is the default value if the key isn't
+ specified. The higher the number the more
+ detailed logs are generated. The maximum
+ value is decimal 10)
+
+StressLogSize is the size in bytes of the in-memory log allocated for each
+thread in the process. In the case above, each thread gets a 64K log. You
+could increase this to get more logging, but more memory will be required for
+this log in the process. For example, 20 threads with 524288 bytes per thread
+has a memory demand of 10 Megabytes. The stress log is circular so new entries
+will replace older ones on threads which have reached their buffer limit.
+
+The log facilities are defined as follows:
+ GC 0x00000001
+ GCINFO 0x00000002
+ STUBS 0x00000004
+ JIT 0x00000008
+ LOADER 0x00000010
+ METADATA 0x00000020
+ SYNC 0x00000040
+ EEMEM 0x00000080
+ GCALLOC 0x00000100
+ CORDB 0x00000200
+ CLASSLOADER 0x00000400
+ CORPROF 0x00000800
+ REMOTING 0x00001000
+ DBGALLOC 0x00002000
+ EH 0x00004000
+ ENC 0x00008000
+ ASSERT 0x00010000
+ VERIFIER 0x00020000
+ THREADPOOL 0x00040000
+ GCROOTS 0x00080000
+ INTEROP 0x00100000
+ MARSHALER 0x00200000
+ IJW 0x00400000
+ ZAP 0x00800000
+ STARTUP 0x01000000
+ APPDOMAIN 0x02000000
+ CODESHARING 0x04000000
+ STORE 0x08000000
+ SECURITY 0x10000000
+ LOCKS 0x20000000
+ BCL 0x40000000
+
+Here is some sample output:
+
+ 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering.
+ Thread state = a030
+
+ 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2,
+ collect_classes = 0) ==========={
+
+ 3560 9.981125826 : `GC` Segment mem 00C61000 alloc
+ = 00D071F0 used 00D09254 committed 00D17000
+
+ 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000
+ ] cur = 00000000
+
+ 3560 9.981124963 : `GC` GC Heap 00000000
+
+ 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0)
+
+The first column is the OS thread ID for the thread appending to the log,
+the second column is the timestamp, the third is the facility category for the
+log entry, and the fourth contains the log message. The facility field is
+expressed as `facility1`facility2`facility3`. This facilitates the creation of
+filters for displaying only specific message categories. To make sense of this
+log, you would probably want the Shared Source CLI to find out exactly where
+the log comes from.
+\\
+
+COMMAND: findappdomain.
+FindAppDomain <Object address>
+
+FindAppDomain will attempt to resolve the AppDomain of an object. For example,
+using an Object Pointer from the output of DumpStackObjects:
+
+ (lldb) sos FindAppDomain 00a79d98
+ AppDomain: 0014f000
+ Name: unittest.exe
+ ID: 1
+
+You can find out more about the AppDomain with the DumpDomain command. Not
+every object has enough clues about it's origin to determine the AppDomain.
+Objects with Finalizers are the easiest case, as the CLR needs to be able to
+call those when an AppDomain shuts down.
+\\
+
+COMMAND: histinit.
+HistInit
+
+Before running any of the Hist - family commands you need to initialize the SOS
+structures from the stress log saved in the debuggee. This is achieved by the
+HistInit command.
+
+Sample output:
+
+ (lldb) histinit
+ Attempting to read Stress log
+ STRESS LOG:
+ facilitiesToLog = 0xffffffff
+ levelToLog = 6
+ MaxLogSizePerThread = 0x10000 (65536)
+ MaxTotalLogSize = 0x1000000 (16777216)
+ CurrentTotalLogChunk = 9
+ ThreadsWithLogs = 3
+ Clock frequency = 3.392 GHz
+ Start time 15:26:31
+ Last message time 15:26:56
+ Total elapsed time 25.077 sec
+ .....................................
+ ---------------------------- 2407 total entries -----------------------------
+
+
+ SUCCESS: GCHist structures initialized
+
+\\
+
+COMMAND: histobjfind.
+HistObjFind <obj_address>
+
+To examine log entries related to an object whose present address is known one
+would use this command. The output of this command contains all entries that
+reference the object:
+
+ (lldb) histobjfind 028970d4
+ GCCount Object Message
+ ---------------------------------------------------------
+ 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2296 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2296 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+ 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2295 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2295 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+
+\\
+
+COMMAND: histroot.
+HistRoot <root>
+
+The root value obtained from !HistObjFind can be used to track the movement of
+an object through the GCs.
+
+HistRoot provides information related to both promotions and relocations of the
+root specified as the argument.
+
+ (lldb) histroot 01e411b8
+ GCCount Value MT Promoted? Notes
+ ---------------------------------------------------------
+ 2296 028970d4 5b6c5cd8 yes
+ 2295 028970d4 5b6c5cd8 yes
+ 2294 028970d4 5b6c5cd8 yes
+ 2293 028970d4 5b6c5cd8 yes
+ 2292 028970d4 5b6c5cd8 yes
+ 2291 028970d4 5b6c5cd8 yes
+ 2290 028970d4 5b6c5cd8 yes
+ 2289 028970d4 5b6c5cd8 yes
+ 2288 028970d4 5b6c5cd8 yes
+ 2287 028970d4 5b6c5cd8 yes
+ 2286 028970d4 5b6c5cd8 yes
+ 2285 028970d4 5b6c5cd8 yes
+ 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs
+ ...
+
+\\
+
+COMMAND: histobj.
+HistObj <obj_address>
+
+This command examines all stress log relocation records and displays the chain
+of GC relocations that may have led to the address passed in as an argument.
+Conceptually the output is:
+
+ GenN obj_address root1, root2, root3,
+ GenN-1 prev_obj_addr root1, root2,
+ GenN-2 prev_prev_oa root1, root4,
+ ...
+
+Sample output:
+ (lldb) histobj 028970d4
+ GCCount Object Roots
+ ---------------------------------------------------------
+ 2296 028970d4 00223fc4, 01e411b8,
+ 2295 028970d4 00223fc4, 01e411b8,
+ 2294 028970d4 00223fc4, 01e411b8,
+ 2293 028970d4 00223fc4, 01e411b8,
+ 2292 028970d4 00223fc4, 01e411b8,
+ 2291 028970d4 00223fc4, 01e411b8,
+ 2290 028970d4 00223fc4, 01e411b8,
+ 2289 028970d4 00223fc4, 01e411b8,
+ 2288 028970d4 00223fc4, 01e411b8,
+ 2287 028970d4 00223fc4, 01e411b8,
+ 2286 028970d4 00223fc4, 01e411b8,
+ 2285 028970d4 00223fc4, 01e411b8,
+ 322 028970d4 01e411b8,
+ 0 028970d4
+
+\\
+
+COMMAND: histclear.
+HistClear
+
+This command releases any resources used by the Hist-family of commands.
+Generally there's no need to call this explicitly, as each HistInit will first
+cleanup the previous resources.
+
+ (lldb) histclear
+ Completed successfully.
+
+\\
diff --git a/src/ToolBox/SOS/Strike/stressLogDump.cpp b/src/ToolBox/SOS/Strike/stressLogDump.cpp
new file mode 100644
index 0000000000..f277f92434
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/stressLogDump.cpp
@@ -0,0 +1,549 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#include "strike.h"
+#include "util.h"
+#include <stdio.h>
+#include <ctype.h>
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+
+
+void GcHistClear();
+void GcHistAddLog(LPCSTR msg, StressMsg* stressMsg);
+
+
+/*********************************************************************************/
+static const WCHAR* getTime(const FILETIME* time, __out_ecount (buffLen) WCHAR* buff, int buffLen)
+{
+ SYSTEMTIME systemTime;
+ static const WCHAR badTime[] = W("BAD TIME");
+
+ if (!FileTimeToSystemTime(time, &systemTime))
+ return badTime;
+
+#ifdef FEATURE_PAL
+ int length = _snwprintf(buff, buffLen, W("%02d:%02d:%02d"), systemTime.wHour, systemTime.wMinute, systemTime.wSecond);
+ if (length <= 0)
+ return badTime;
+#else // FEATURE_PAL
+ static const WCHAR format[] = W("HH:mm:ss");
+
+ SYSTEMTIME localTime;
+ SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime);
+
+ // we want a non null buff for the following
+ int ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &localTime, format, buff, buffLen);
+ if (ret == 0)
+ return badTime;
+#endif // FEATURE_PAL else
+
+ return buff;
+}
+
+/*********************************************************************************/
+static inline __int64& toInt64(FILETIME& t)
+{
+ return *((__int64 *) &t);
+}
+
+/*********************************************************************************/
+ThreadStressLog* ThreadStressLog::FindLatestThreadLog() const
+{
+ const ThreadStressLog* latestLog = 0;
+ for (const ThreadStressLog* ptr = this; ptr != NULL; ptr = ptr->next)
+ {
+ if (ptr->readPtr != NULL)
+ if (latestLog == 0 || ptr->readPtr->timeStamp > latestLog->readPtr->timeStamp)
+ latestLog = ptr;
+ }
+ return const_cast<ThreadStressLog*>(latestLog);
+}
+
+const char *getFacilityName(DWORD_PTR lf)
+{
+ struct FacilityName_t { size_t lf; const char* lfName; };
+ #define DEFINE_LOG_FACILITY(logname, value) {logname, #logname},
+ static FacilityName_t facilities[] =
+ {
+ #include <loglf.h>
+ { LF_ALWAYS, "LF_ALWAYS" }
+ };
+ static char buff[1024] = "`";
+ if ( lf == LF_ALL )
+ {
+ return "`ALL`";
+ }
+ else
+ {
+ buff[1] = '\0';
+ for ( int i = 0; i < 32; ++i )
+ {
+ if ( lf & 0x1 )
+ {
+ strcat_s ( buff, _countof(buff), &(facilities[i].lfName[3]) );
+ strcat_s ( buff, _countof(buff), "`" );
+ }
+ lf >>= 1;
+ }
+ return buff;
+ }
+}
+
+/***********************************************************************************/
+/* recognize special pretty printing instructions in the format string */
+/* Note that this function might have side effect such that args array value might */
+/* be altered if format string contains %s */
+// TODO: This function assumes the pointer size of the target equals the pointer size of the host
+// TODO: replace uses of void* with appropriate TADDR or CLRDATA_ADDRESS
+void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inout __inout_z char* format, unsigned threadId, double timeStamp, DWORD_PTR facility, ___in void** args)
+{
+ fprintf(file, "%4x %13.9f : ", threadId, timeStamp);
+ fprintf(file, "%-20s ", getFacilityName ( facility ));
+
+ CQuickBytes fullname;
+ char* ptr = format;
+ void** argsPtr = args;
+ const SIZE_T capacity_buff = 2048;
+ LPWSTR buff = (LPWSTR)alloca(capacity_buff * sizeof(WCHAR));
+ static char formatCopy[256];
+
+ int iArgCount = 0;
+
+ strcpy_s(formatCopy, _countof(formatCopy), format);
+ for(;;)
+ {
+ char c = *ptr++;
+ if (c == 0)
+ break;
+ if (c == '{') // Reverse the '{' 's because the log is displayed backwards
+ ptr[-1] = '}';
+ else if (c == '}')
+ ptr[-1] = '{';
+ else if (c == '%')
+ {
+ argsPtr++; // This format will consume one of the args
+ if (*ptr == '%')
+ {
+ ptr++; // skip the whole %%
+ --argsPtr; // except for a %%
+ }
+ else if (*ptr == 'p')
+ { // It is a %p
+ ptr++;
+ if (isalpha(*ptr))
+ { // It is a special %p formatter
+ // Print the string up to that point
+ c = *ptr;
+ *ptr = 0; // Terminate the string temporarily
+ fprintf(file, format, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ *ptr = c; // Put it back
+
+ // move the argument pointers past the part the was printed
+ format = ptr + 1;
+ args = argsPtr;
+ iArgCount = -1;
+ DWORD_PTR arg = DWORD_PTR(argsPtr[-1]);
+
+ switch (c)
+ {
+ case 'M': // format as a method Desc
+ if (g_bDacBroken)
+ {
+ fprintf(file," (MethodDesc: %p)",arg);
+ }
+ else
+ {
+ if (!IsMethodDesc(arg))
+ {
+ if (arg != 0)
+ fprintf(file, " (BAD Method)");
+ }
+ else
+ {
+ DacpMethodDescData MethodDescData;
+ MethodDescData.Request(g_sos,(CLRDATA_ADDRESS)arg);
+
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+ if (g_sos->GetMethodDescName(arg, 1024, wszNameBuffer, NULL) != S_OK)
+ {
+ wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN METHODDESC"));
+ }
+
+ wcscpy_s(buff, capacity_buff, wszNameBuffer);
+ fprintf(file, " (%S)", wszNameBuffer);
+ }
+ }
+ break;
+
+ // fall through
+ case 'T': // format as a MethodTable
+ if (g_bDacBroken)
+ {
+ fprintf(file, "(MethodTable: %p)",arg);
+ }
+ else
+ {
+ if (arg & 3)
+ {
+ arg &= ~3; // GC steals the lower bits for its own use during GC.
+ fprintf(file, " Low Bit(s) Set");
+ }
+ if (!IsMethodTable(arg))
+ {
+ fprintf(file, " (BAD MethodTable)");
+ }
+ else
+ {
+ NameForMT_s (arg, g_mdName, mdNameLen);
+ fprintf(file, " (%S)", g_mdName);
+ }
+ }
+ break;
+
+ case 'V':
+ { // format as a C vtable pointer
+ char Symbol[1024];
+ ULONG64 Displacement;
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(arg), Symbol, 1024, NULL, &Displacement);
+ if (SUCCEEDED(hr) && Symbol[0] != '\0' && Displacement == 0)
+ fprintf(file, " (%s)", Symbol);
+ else
+ fprintf(file, " (Unknown VTable)");
+ }
+ break;
+ case 'K':
+ { // format a frame in stack trace
+ char Symbol[1024];
+ ULONG64 Displacement;
+ HRESULT hr = g_ExtSymbols->GetNameByOffset (TO_CDADDR(arg), Symbol, 1024, NULL, &Displacement);
+ if (SUCCEEDED (hr) && Symbol[0] != '\0')
+ {
+ fprintf (file, " (%s", Symbol);
+ if (Displacement)
+ {
+ fprintf (file, "+%#x", Displacement);
+ }
+ fprintf (file, ")");
+ }
+ else
+ fprintf (file, " (Unknown function)");
+ }
+ break;
+ default:
+ format = ptr; // Just print the character.
+ }
+ }
+ }
+ else if (*ptr == 's' || (*ptr == 'h' && *(ptr+1) == 's' && ++ptr))
+ {
+ HRESULT hr;
+
+ // need to _alloca, instead of declaring a local buffer
+ // since we may have more than one %s in the format
+ ULONG cbStrBuf = 256;
+ char* strBuf = (char *)_alloca(cbStrBuf);
+
+ hr = memCallBack->ReadVirtual(TO_CDADDR((char* )args[iArgCount]), strBuf, cbStrBuf, 0);
+ if (hr != S_OK)
+ {
+ strcpy_s(strBuf, cbStrBuf, "(#Could not read address of string#)");
+ }
+
+ args[iArgCount] = strBuf;
+ }
+ else if (*ptr == 'S' || (*ptr == 'l' && *(ptr+1) == 's' && ++ptr))
+ {
+ HRESULT hr;
+
+ // need to _alloca, instead of declaring a local buffer
+ // since we may have more than one %s in the format
+ ULONG cbWstrBuf = 256 * sizeof(WCHAR);
+ WCHAR* wstrBuf = (WCHAR *)_alloca(cbWstrBuf);
+
+ hr = memCallBack->ReadVirtual(TO_CDADDR((char* )args[iArgCount]), wstrBuf, cbWstrBuf, 0);
+ if (hr != S_OK)
+ {
+ wcscpy_s(wstrBuf, cbWstrBuf/sizeof(WCHAR), W("(#Could not read address of string#)"));
+ }
+
+ args[iArgCount] = wstrBuf;
+ }
+ iArgCount++;
+ }
+ }
+ // Print anything after the last special format instruction.
+ fprintf(file, format, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ fprintf(file, "\n");
+}
+
+void __cdecl
+vDoOut(BOOL bToConsole, FILE* file, PCSTR Format, ...)
+{
+ va_list Args;
+
+ va_start(Args, Format);
+
+ if (bToConsole)
+ {
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
+ }
+ else
+ {
+ vfprintf(file, Format, Args);
+ }
+
+ va_end(Args);
+}
+
+
+/*********************************************************************************/
+HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugDataSpaces* memCallBack)
+{
+ ULONG64 g_hThisInst;
+ BOOL bDoGcHist = (fileName == NULL);
+ FILE* file = NULL;
+
+ // Fetch the circular buffer bookeeping data
+ StressLog inProcLog;
+ HRESULT hr = memCallBack->ReadVirtual(UL64_TO_CDA(outProcLog), &inProcLog, sizeof(StressLog), 0);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+ if (inProcLog.logs.Load() == NULL || inProcLog.moduleOffset == 0)
+ {
+ ExtOut ( "----- No thread logs in the image: The stress log was probably not initialized correctly. -----\n");
+ return S_FALSE;
+ }
+
+ g_hThisInst = (ULONG64) inProcLog.moduleOffset;
+
+ if (bDoGcHist)
+ {
+ GcHistClear();
+ }
+ else
+ {
+ ExtOut("Writing to file: %s\n", fileName);
+ ExtOut("Stress log in module 0x%p\n", SOS_PTR(g_hThisInst));
+ ExtOut("Stress log address = 0x%p\n", SOS_PTR(outProcLog));
+ }
+ // Fetch the circular buffers for each thread into the 'logs' list
+ ThreadStressLog* logs = 0;
+
+ CLRDATA_ADDRESS outProcPtr = TO_CDADDR(inProcLog.logs.Load());
+ ThreadStressLog* inProcPtr;
+ ThreadStressLog** logsPtr = &logs;
+ int threadCtr = 0;
+ unsigned __int64 lastTimeStamp = 0;// timestamp of last log entry
+
+ while(outProcPtr != 0) {
+ inProcPtr = new ThreadStressLog;
+ hr = memCallBack->ReadVirtual(outProcPtr, inProcPtr, sizeof (*inProcPtr), 0);
+ if (hr != S_OK || inProcPtr->chunkListHead == NULL)
+ {
+ delete inProcPtr;
+ goto FREE_MEM;
+ }
+
+ CLRDATA_ADDRESS outProcListHead = TO_CDADDR(inProcPtr->chunkListHead);
+ CLRDATA_ADDRESS outProcChunkPtr = outProcListHead;
+ StressLogChunk ** chunksPtr = &inProcPtr->chunkListHead;
+ StressLogChunk * inProcPrevChunkPtr = NULL;
+ BOOL curPtrInitialized = FALSE;
+ do
+ {
+ StressLogChunk * inProcChunkPtr = new StressLogChunk;
+ hr = memCallBack->ReadVirtual (outProcChunkPtr, inProcChunkPtr, sizeof (*inProcChunkPtr), NULL);
+ if (hr != S_OK || !inProcChunkPtr->IsValid ())
+ {
+ if (hr != S_OK)
+ ExtOut ("ReadVirtual failed with code hr = %x.\n", hr );
+ else
+ ExtOut ("Invalid stress log chunk: %p", SOS_PTR(outProcChunkPtr));
+
+ // Now cleanup
+ delete inProcChunkPtr;
+ // if this is the first time through, inProcPtr->chunkListHead may still contain
+ // the out-of-process value for the chunk pointer. NULL it to avoid AVs
+ if (TO_CDADDR(inProcPtr->chunkListHead) == outProcListHead)
+ inProcPtr->chunkListHead = NULL;
+ delete inProcPtr;
+ goto FREE_MEM;
+ }
+
+ if (!curPtrInitialized && outProcChunkPtr == TO_CDADDR(inProcPtr->curWriteChunk))
+ {
+ inProcPtr->curPtr = (StressMsg *)((BYTE *)inProcChunkPtr + ((BYTE *)inProcPtr->curPtr - (BYTE *)inProcPtr->curWriteChunk));
+ inProcPtr->curWriteChunk = inProcChunkPtr;
+ curPtrInitialized = TRUE;
+ }
+
+ outProcChunkPtr = TO_CDADDR(inProcChunkPtr->next);
+ *chunksPtr = inProcChunkPtr;
+ chunksPtr = &inProcChunkPtr->next;
+ inProcChunkPtr->prev = inProcPrevChunkPtr;
+ inProcPrevChunkPtr = inProcChunkPtr;
+
+ if (outProcChunkPtr == outProcListHead)
+ {
+ inProcChunkPtr->next = inProcPtr->chunkListHead;
+ inProcPtr->chunkListHead->prev = inProcChunkPtr;
+ inProcPtr->chunkListTail = inProcChunkPtr;
+ }
+ } while (outProcChunkPtr != outProcListHead);
+
+ if (!curPtrInitialized)
+ {
+ delete inProcPtr;
+ goto FREE_MEM;
+ }
+
+ // TODO: fix on 64 bit
+ inProcPtr->Activate ();
+ if (inProcPtr->readPtr->timeStamp > lastTimeStamp)
+ {
+ lastTimeStamp = inProcPtr->readPtr->timeStamp;
+ }
+
+ outProcPtr = TO_CDADDR(inProcPtr->next);
+ *logsPtr = inProcPtr;
+ logsPtr = &inProcPtr->next;
+ threadCtr++;
+ }
+
+ if (!bDoGcHist && ((file = fopen(fileName, "w")) == NULL))
+ {
+ hr = GetLastError();
+ goto FREE_MEM;
+ }
+ hr = S_FALSE; // return false if there are no message to print to the log
+
+ vDoOut(bDoGcHist, file, "STRESS LOG:\n"
+ " facilitiesToLog = 0x%x\n"
+ " levelToLog = %d\n"
+ " MaxLogSizePerThread = 0x%x (%d)\n"
+ " MaxTotalLogSize = 0x%x (%d)\n"
+ " CurrentTotalLogChunk = %d\n"
+ " ThreadsWithLogs = %d\n",
+ inProcLog.facilitiesToLog, inProcLog.levelToLog, inProcLog.MaxSizePerThread, inProcLog.MaxSizePerThread,
+ inProcLog.MaxSizeTotal, inProcLog.MaxSizeTotal, inProcLog.totalChunk.Load(), threadCtr);
+
+ FILETIME endTime;
+ double totalSecs;
+ totalSecs = ((double) (lastTimeStamp - inProcLog.startTimeStamp)) / inProcLog.tickFrequency;
+ toInt64(endTime) = toInt64(inProcLog.startTime) + ((__int64) (totalSecs * 1.0E7));
+
+ WCHAR timeBuff[64];
+ vDoOut(bDoGcHist, file, " Clock frequency = %5.3f GHz\n", inProcLog.tickFrequency / 1.0E9);
+ vDoOut(bDoGcHist, file, " Start time %S\n", getTime(&inProcLog.startTime, timeBuff, 64));
+ vDoOut(bDoGcHist, file, " Last message time %S\n", getTime(&endTime, timeBuff, 64));
+ vDoOut(bDoGcHist, file, " Total elapsed time %5.3f sec\n", totalSecs);
+
+ if (!bDoGcHist)
+ {
+ fprintf(file, "\nTHREAD TIMESTAMP FACILITY MESSAGE\n");
+ fprintf(file, " ID (sec from start)\n");
+ fprintf(file, "--------------------------------------------------------------------------------------\n");
+ }
+ char format[257];
+ format[256] = format[0] = 0;
+ void** args;
+ unsigned msgCtr;
+ msgCtr = 0;
+ for (;;)
+ {
+ ThreadStressLog* latestLog = logs->FindLatestThreadLog();
+
+ if (IsInterrupt())
+ {
+ vDoOut(bDoGcHist, file, "----- Interrupted by user -----\n");
+ break;
+ }
+
+ if (latestLog == 0)
+ {
+ break;
+ }
+
+ StressMsg* latestMsg = latestLog->readPtr;
+ if (latestMsg->formatOffset != 0 && !latestLog->CompletedDump())
+ {
+ TADDR taFmt = (latestMsg->formatOffset) + TO_TADDR(g_hThisInst);
+ hr = memCallBack->ReadVirtual(TO_CDADDR(taFmt), format, 256, 0);
+ if (hr != S_OK)
+ strcpy_s(format, _countof(format), "Could not read address of format string");
+
+ double deltaTime = ((double) (latestMsg->timeStamp - inProcLog.startTimeStamp)) / inProcLog.tickFrequency;
+ if (bDoGcHist)
+ {
+ if (strcmp(format, ThreadStressLog::TaskSwitchMsg()) == 0)
+ {
+ latestLog->threadId = (unsigned)(size_t)latestMsg->args[0];
+ }
+ GcHistAddLog(format, latestMsg);
+ }
+ else
+ {
+ if (strcmp(format, ThreadStressLog::TaskSwitchMsg()) == 0)
+ {
+ fprintf (file, "Task was switched from %x\n", (unsigned)(size_t)latestMsg->args[0]);
+ latestLog->threadId = (unsigned)(size_t)latestMsg->args[0];
+ }
+ else
+ {
+ args = latestMsg->args;
+ formatOutput(memCallBack, file, format, latestLog->threadId, deltaTime, latestMsg->facility, args);
+ }
+ }
+ msgCtr++;
+ }
+
+ latestLog->readPtr = latestLog->AdvanceRead();
+ if (latestLog->CompletedDump())
+ {
+ latestLog->readPtr = NULL;
+ if (!bDoGcHist)
+ {
+ fprintf(file, "------------ Last message from thread %x -----------\n", latestLog->threadId);
+ }
+ }
+
+ if (msgCtr % 64 == 0)
+ {
+ ExtOut("."); // to indicate progress
+ if (msgCtr % (64*64) == 0)
+ ExtOut("\n");
+ }
+ }
+ ExtOut("\n");
+
+ vDoOut(bDoGcHist, file, "---------------------------- %d total entries ------------------------------------\n", msgCtr);
+ if (!bDoGcHist)
+ {
+ fclose(file);
+ }
+
+FREE_MEM:
+ // clean up the 'logs' list
+ while (logs) {
+ ThreadStressLog* temp = logs;
+ logs = logs->next;
+ delete temp;
+ }
+
+ return hr;
+}
+
diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp
new file mode 100644
index 0000000000..731e2f505d
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/strike.cpp
@@ -0,0 +1,14462 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+
+// ===========================================================================
+// STRIKE.CPP
+// ===========================================================================
+//
+// History:
+// 09/07/99 Microsoft Created
+//
+//************************************************************************************************
+// SOS is the native debugging extension designed to support investigations into CLR (mis-)
+// behavior by both users of the runtime as well as the code owners. It allows inspection of
+// internal structures, of user visible entities, as well as execution control.
+//
+// This is the main SOS file hosting the implementation of all the exposed commands. A good
+// starting point for understanding the semantics of these commands is the sosdocs.txt file.
+//
+// #CrossPlatformSOS
+// SOS currently supports cross platform debugging from x86 to ARM. It takes a different approach
+// from the DAC: whereas for the DAC we produce one binary for each supported host-target
+// architecture pair, for SOS we produce only one binary for each host architecture; this one
+// binary contains code for all supported target architectures. In doing this SOS depends on two
+// assumptions:
+// . that the debugger will load the appropriate DAC, and
+// . that the host and target word size is identical.
+// The second assumption is identical to the DAC assumption, and there will be considerable effort
+// required (in the EE, the DAC, and SOS) if we ever need to remove it.
+//
+// In an ideal world SOS would be able to retrieve all platform specific information it needs
+// either from the debugger or from DAC. However, SOS has taken some subtle and not so subtle
+// dependencies on the CLR and the target platform.
+// To resolve this problem, SOS now abstracts the target behind the IMachine interface, and uses
+// calls on IMachine to take target-specific actions. It implements X86Machine, ARMMachine, and
+// AMD64Machine. An instance of these exists in each appropriate host (e.g. the X86 version of SOS
+// contains instaces of X86Machine and ARMMachine, the ARM version contains an instance of
+// ARMMachine, and the AMD64 version contains an instance of AMD64Machine). The code included in
+// each version if determined by the SosTarget*** MSBuild symbols, and SOS_TARGET_*** conditional
+// compilation symbols (as specified in sos.targets).
+//
+// Most of the target specific code is hosted in disasm.h/.cpp, and disasmX86.cpp, disasmARM.cpp.
+// Some code currently under _TARGET_*** ifdefs may need to be reviewed/revisited.
+//
+// Issues:
+// The one-binary-per-host decision does have some drawbacks:
+// . Currently including system headers or even CLR headers will only account for the host
+// target, IOW, when building the X86 version of SOS, CONTEXT will refer to the X86 CONTEXT
+// structure, so we need to be careful when debugging ARM targets. The CONTEXT issue is
+// partially resolved by CROSS_PLATFORM_CONTEXT (there is still a need to be very careful
+// when handling arrays of CONTEXTs - see _EFN_StackTrace for details on this).
+// . For larger includes (e.g. GC info), we will need to include files in specific namespaces,
+// with specific _TARGET_*** macros defined in order to avoid name clashes and ensure correct
+// system types are used.
+// -----------------------------------------------------------------------------------------------
+
+#define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand()
+
+#include <windows.h>
+#include <winver.h>
+#include <winternl.h>
+#include <psapi.h>
+#ifndef FEATURE_PAL
+#include <list>
+#endif // !FEATURE_PAL
+#include <wchar.h>
+
+#include "platformspecific.h"
+
+#define NOEXTAPI
+#define KDEXT_64BIT
+#include <wdbgexts.h>
+#undef DECLARE_API
+#undef StackTrace
+
+#include <dbghelp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "strike.h"
+#include "sos.h"
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+
+#include "util.h"
+
+#include "corhdr.h"
+#include "cor.h"
+#include "cordebug.h"
+#include "dacprivate.h"
+#include "corexcep.h"
+
+#define CORHANDLE_MASK 0x1
+#define SWITCHED_OUT_FIBER_OSID 0xbaadf00d;
+
+#define DEFINE_EXT_GLOBALS
+
+#include "data.h"
+#include "disasm.h"
+
+#include "predeftlsslot.h"
+
+#include "hillclimbing.h"
+
+#include "sos_md.h"
+
+#ifndef FEATURE_PAL
+
+#include "ExpressionNode.h"
+#include "WatchCmd.h"
+
+#include <set>
+#include <algorithm>
+#include <vector>
+
+#include "tls.h"
+
+typedef struct _VM_COUNTERS {
+ SIZE_T PeakVirtualSize;
+ SIZE_T VirtualSize;
+ ULONG PageFaultCount;
+ SIZE_T PeakWorkingSetSize;
+ SIZE_T WorkingSetSize;
+ SIZE_T QuotaPeakPagedPoolUsage;
+ SIZE_T QuotaPagedPoolUsage;
+ SIZE_T QuotaPeakNonPagedPoolUsage;
+ SIZE_T QuotaNonPagedPoolUsage;
+ SIZE_T PagefileUsage;
+ SIZE_T PeakPagefileUsage;
+} VM_COUNTERS;
+typedef VM_COUNTERS *PVM_COUNTERS;
+
+const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
+
+#endif // !FEATURE_PAL
+
+BOOL CallStatus;
+BOOL ControlC = FALSE;
+
+IMetaDataDispenserEx *pDisp = NULL;
+WCHAR g_mdName[mdNameLen];
+
+#ifndef FEATURE_PAL
+HMODULE g_hInstance = NULL;
+#include <vector>
+#include <algorithm>
+#endif // !FEATURE_PAL
+
+#ifdef _MSC_VER
+#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
+#pragma warning(disable:4189) // local variable is initialized but not referenced
+#endif
+
+#ifdef FEATURE_PAL
+#define SOSPrefix ""
+#else
+#define SOSPrefix "!"
+#endif
+
+#if defined _X86_ && !defined FEATURE_PAL
+// disable FPO for X86 builds
+#pragma optimize("y", off)
+#endif
+
+#undef assert
+
+#ifdef _MSC_VER
+#pragma warning(default:4244)
+#pragma warning(default:4189)
+#endif
+
+#ifndef FEATURE_PAL
+#include "ntinfo.h"
+#endif // FEATURE_PAL
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+#ifdef FEATURE_PAL
+
+#define NOTHROW
+#define MINIDUMP_NOT_SUPPORTED()
+
+#else // !FEATURE_PAL
+
+#define MINIDUMP_NOT_SUPPORTED() \
+ if (IsMiniDumpFile()) \
+ { \
+ ExtOut("This command is not supported in a minidump without full memory\n"); \
+ ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \
+ return Status; \
+ }
+
+#define NOTHROW (std::nothrow)
+
+#include "safemath.h"
+
+DECLARE_API (MinidumpMode)
+{
+ INIT_API ();
+ DWORD_PTR Value=0;
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&Value, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg == 0)
+ {
+ // Print status of current mode
+ ExtOut("Current mode: %s - unsafe minidump commands are %s.\n",
+ g_InMinidumpSafeMode ? "1" : "0",
+ g_InMinidumpSafeMode ? "disabled" : "enabled");
+ }
+ else
+ {
+ if (Value != 0 && Value != 1)
+ {
+ ExtOut("Mode must be 0 or 1\n");
+ return Status;
+ }
+
+ g_InMinidumpSafeMode = (BOOL) Value;
+ ExtOut("Unsafe minidump commands are %s.\n",
+ g_InMinidumpSafeMode ? "disabled" : "enabled");
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the MethodDesc for a given eip *
+* *
+\**********************************************************************/
+DECLARE_API(IP2MD)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ TADDR IP = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&IP, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ EnableDMLHolder dmlHolder(dml);
+
+ if (IP == 0)
+ {
+ ExtOut("%s is not IP\n", args);
+ return Status;
+ }
+
+ CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP);
+ CLRDATA_ADDRESS pMD;
+
+
+ if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK)
+ {
+ ExtOut("Failed to request MethodData, not in JIT code range\n");
+ return Status;
+ }
+
+ DMLOut("MethodDesc: %s\n", DMLMethodDesc(pMD));
+ DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
+
+ WCHAR filename[MAX_LONGPATH];
+ ULONG linenum;
+ // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ ULONG symlines = 0;
+ if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+
+ if (symlines != 0 &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename))))
+ {
+ ExtOut("Source file: %S @ %d\n", filename, linenum);
+ }
+
+ return Status;
+}
+
+// (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
+#define MAX_STACK_FRAMES 1000
+
+#ifdef _TARGET_WIN64_
+
+// I use a global set of frames for stack walking on win64 because the debugger's
+// GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
+// and I'd like to have a reasonably big maximum without overflowing the stack by declaring
+// the buffer locally and I also want to get a managed trace in a low memory environment
+// (so no dynamic allocation if possible).
+DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
+AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
+
+static HRESULT
+GetContextStackTrace(PULONG pnumFrames)
+{
+ PDEBUG_CONTROL4 debugControl4;
+ HRESULT hr;
+
+ // Do we have advanced capability?
+ if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK)
+ {
+ // GetContextStackTrace fills g_X64FrameContexts as an array of
+ // contexts packed as target architecture contexts. We cannot
+ // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since
+ // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
+ hr = debugControl4->GetContextStackTrace(
+ NULL,
+ 0,
+ g_Frames,
+ MAX_STACK_FRAMES,
+ g_X64FrameContexts,
+ MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
+ g_targetMachine->GetContextSize(),
+ pnumFrames);
+
+ debugControl4->Release();
+ }
+ return hr;
+}
+
+#endif // _TARGET_WIN64_
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function displays the stack trace. It looks at each DWORD *
+* on stack. If the DWORD is a return address, the symbol name or
+* managed function name is displayed. *
+* *
+\**********************************************************************/
+void DumpStackInternal(DumpStackFlag *pDSFlag)
+{
+ ReloadSymbolWithLineInfo();
+
+ ULONG64 StackOffset;
+ g_ExtRegisters->GetStackOffset (&StackOffset);
+ if (pDSFlag->top == 0) {
+ pDSFlag->top = TO_TADDR(StackOffset);
+ }
+ size_t value;
+ while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) {
+ if (IsInterrupt())
+ return;
+ pDSFlag->top = NextOSPageAddress(pDSFlag->top);
+ }
+
+#ifndef FEATURE_PAL
+ if (pDSFlag->end == 0) {
+ // Find the current stack range
+ NT_TIB teb;
+ ULONG64 dwTebAddr=0;
+
+ g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
+ if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
+ {
+ if (pDSFlag->top > TO_TADDR(teb.StackLimit)
+ && pDSFlag->top <= TO_TADDR(teb.StackBase))
+ {
+ if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
+ pDSFlag->end = TO_TADDR(teb.StackBase);
+ }
+ }
+ }
+#endif // FEATURE_PAL
+
+ if (pDSFlag->end == 0)
+ {
+ ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n");
+ pDSFlag->end = pDSFlag->top + 0xFFFF;
+ }
+
+ if (pDSFlag->end < pDSFlag->top)
+ {
+ ExtOut("Wrong option: stack selection wrong\n");
+ return;
+ }
+
+ DumpStackWorker(*pDSFlag);
+}
+
+
+DECLARE_API(DumpStack)
+{
+ INIT_API_NO_RET_ON_FAILURE();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ DumpStackFlag DSFlag;
+ DSFlag.fEEonly = FALSE;
+ DSFlag.fSuppressSrcInfo = FALSE;
+ DSFlag.top = 0;
+ DSFlag.end = 0;
+
+ BOOL dml = FALSE;
+ CMDOption option[] = {
+ // name, vptr, type, hasValue
+ {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
+ {"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] = {
+ // vptr, type
+ {&DSFlag.top, COHEX},
+ {&DSFlag.end, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ return Status;
+
+ // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ ULONG symlines = 0;
+ if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+ DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0);
+
+ EnableDMLHolder enabledml(dml);
+
+ ULONG id = 0;
+ g_ExtSystem->GetCurrentThreadSystemId(&id);
+ ExtOut("OS Thread Id: 0x%x ", id);
+ g_ExtSystem->GetCurrentThreadId(&id);
+ ExtOut("(%d)\n", id);
+
+ DumpStackInternal(&DSFlag);
+ return Status;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function displays the stack trace for threads that EE knows *
+* from ThreadStore. *
+* *
+\**********************************************************************/
+DECLARE_API (EEStack)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ DumpStackFlag DSFlag;
+ DSFlag.fEEonly = FALSE;
+ DSFlag.fSuppressSrcInfo = FALSE;
+ DSFlag.top = 0;
+ DSFlag.end = 0;
+
+ BOOL bShortList = FALSE;
+ BOOL dml = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
+ {"-short", &bShortList, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder enableDML(dml);
+
+ ULONG Tid;
+ g_ExtSystem->GetCurrentThreadId(&Tid);
+
+ DacpThreadStoreData ThreadStore;
+ if ((Status = ThreadStore.Request(g_sos)) != S_OK)
+ {
+ ExtOut("Failed to request ThreadStore\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ if (IsInterrupt())
+ break;
+
+ DacpThreadData Thread;
+ if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
+ {
+ ExtOut("Failed to request Thread at %p\n", CurThread);
+ return Status;
+ }
+
+ ULONG id=0;
+ if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK)
+ {
+ CurThread = Thread.nextThread;
+ continue;
+ }
+
+ ExtOut("---------------------------------------------\n");
+ ExtOut("Thread %3d\n", id);
+ BOOL doIt = FALSE;
+
+
+#define TS_Hijacked 0x00000080
+
+ if (!bShortList)
+ {
+ doIt = TRUE;
+ }
+ else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked))
+ {
+ // TODO: bring back || (int)vThread.m_pFrame != -1 {
+ doIt = TRUE;
+ }
+ else
+ {
+ ULONG64 IP;
+ g_ExtRegisters->GetInstructionOffset (&IP);
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR gcinfoAddr;
+ IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr);
+ if (methodDesc)
+ {
+ doIt = TRUE;
+ }
+ }
+
+ if (doIt)
+ {
+ g_ExtSystem->SetCurrentThreadId(id);
+ DSFlag.top = 0;
+ DSFlag.end = 0;
+ DumpStackInternal(&DSFlag);
+ }
+
+ CurThread = Thread.nextThread;
+ }
+
+ g_ExtSystem->SetCurrentThreadId(Tid);
+ return Status;
+}
+
+HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
+{
+ size_t StackTop = 0;
+ size_t StackBottom = 0;
+ if (nArg==0)
+ {
+ ULONG64 StackOffset;
+ g_ExtRegisters->GetStackOffset(&StackOffset);
+
+ StackTop = TO_TADDR(StackOffset);
+ }
+ else
+ {
+ StackTop = GetExpression(exprTop);
+ if (StackTop == 0)
+ {
+ ExtOut("wrong option: %s\n", exprTop);
+ return E_FAIL;
+ }
+
+ if (nArg==2)
+ {
+ StackBottom = GetExpression(exprBottom);
+ if (StackBottom == 0)
+ {
+ ExtOut("wrong option: %s\n", exprBottom);
+ return E_FAIL;
+ }
+ }
+ }
+
+#ifndef FEATURE_PAL
+ NT_TIB teb;
+ ULONG64 dwTebAddr=0;
+ HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
+ if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL))
+ {
+ if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
+ {
+ if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
+ StackBottom = TO_TADDR(teb.StackBase);
+ }
+ }
+#endif
+
+ if (StackBottom == 0)
+ StackBottom = StackTop + 0xFFFF;
+
+ if (StackBottom < StackTop)
+ {
+ ExtOut("Wrong option: stack selection wrong\n");
+ return E_FAIL;
+ }
+
+ // We can use the gc snapshot to eliminate object addresses that are
+ // not on the gc heap.
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to determine bounds of gc heap\n");
+ return E_FAIL;
+ }
+
+ // Print thread ID.
+ ULONG id = 0;
+ g_ExtSystem->GetCurrentThreadSystemId (&id);
+ ExtOut("OS Thread Id: 0x%x ", id);
+ g_ExtSystem->GetCurrentThreadId (&id);
+ ExtOut("(%d)\n", id);
+
+ DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the address and name of all *
+* Managed Objects on the stack. *
+* *
+\**********************************************************************/
+DECLARE_API(DumpStackObjects)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+ StringHolder exprTop, exprBottom;
+
+ BOOL bVerify = FALSE;
+ BOOL dml = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-verify", &bVerify, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&exprTop.data, COSTRING},
+ {&exprBottom.data, COSTRING}
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder enableDML(dml);
+
+ return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a MethodDesc *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpMD)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR dwStartAddr = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ DumpMDInfo(dwStartAddr);
+
+ return Status;
+}
+
+BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray,
+ DacpObjectData *tokenArray, TADDR *ptokenArrayAddr)
+{
+ BOOL bRet = FALSE;
+ int iOffset;
+ DacpObjectData objData; // temp object
+
+ if (codeArray == NULL || tokenArray == NULL)
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset(DynamicMethodObj, objData.MethodTable, W("m_resolver"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR resolverPtr;
+ if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset)))
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset(resolverPtr, objData.MethodTable, W("m_code"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR codePtr;
+ if (FAILED(MOVE(codePtr, resolverPtr + iOffset)))
+ return bRet;
+
+ if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK)
+ return bRet;
+
+ if (codeArray->dwComponentSize != 1)
+ return bRet;
+
+ // We also need the resolution table
+ iOffset = GetObjFieldOffset (resolverPtr, objData.MethodTable, W("m_scope"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR scopePtr;
+ if (FAILED(MOVE(scopePtr, resolverPtr + iOffset)))
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset (scopePtr, objData.MethodTable, W("m_tokens"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR tokensPtr;
+ if (FAILED(MOVE(tokensPtr, scopePtr + iOffset)))
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset(tokensPtr, objData.MethodTable, W("_items"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR itemsPtr;
+ MOVE (itemsPtr, tokensPtr + iOffset);
+
+ *ptokenArrayAddr = itemsPtr;
+
+ if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK)
+ return bRet;
+
+ bRet = TRUE; // whew.
+ return bRet;
+}
+
+DECLARE_API(DumpIL)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+ DWORD_PTR dwStartAddr = NULL;
+ DWORD_PTR dwDynamicMethodObj = NULL;
+ BOOL dml = FALSE;
+ BOOL fILPointerDirectlySpecified = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (dwStartAddr == NULL)
+ {
+ ExtOut("Must pass a valid expression\n");
+ return Status;
+ }
+
+ if (fILPointerDirectlySpecified)
+ {
+ return DecodeILFromAddress(NULL, dwStartAddr);
+ }
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ if (g_snapshot.GetHeap(dwStartAddr) != NULL)
+ {
+ dwDynamicMethodObj = dwStartAddr;
+ }
+
+ if (dwDynamicMethodObj == NULL)
+ {
+ // We have been given a MethodDesc
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwStartAddr));
+ return Status;
+ }
+
+ if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject)
+ {
+ dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject);
+ if (dwDynamicMethodObj == NULL)
+ {
+ ExtOut("Unable to print IL for DynamicMethodDesc %p\n", SOS_PTR(dwDynamicMethodObj));
+ return Status;
+ }
+ }
+ else
+ {
+ // This is not a dynamic method, print the IL for it.
+ // Get the module
+ DacpModuleData dmd;
+ if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK)
+ {
+ ExtOut("Unable to get module\n");
+ return Status;
+ }
+
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd);
+ if (pImport == NULL)
+ {
+ ExtOut("bad import\n");
+ return Status;
+ }
+
+ ULONG pRva;
+ DWORD dwFlags;
+ if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK)
+ {
+ ExtOut("error in import\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS ilAddrClr;
+ if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK)
+ {
+ ExtOut("FindIL failed\n");
+ return Status;
+ }
+
+ TADDR ilAddr = TO_TADDR(ilAddrClr);
+ IfFailRet(DecodeILFromAddress(pImport, ilAddr));
+ }
+ }
+
+ if (dwDynamicMethodObj != NULL)
+ {
+ // We have a DynamicMethod managed object, let us visit the town and paint.
+ DacpObjectData codeArray;
+ DacpObjectData tokenArray;
+ DWORD_PTR tokenArrayAddr;
+ if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr))
+ {
+ DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj));
+ return Status;
+ }
+
+ // Read the memory into a local buffer
+ BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents];
+ if (pArray == NULL)
+ {
+ ExtOut("Not enough memory to read IL\n");
+ return Status;
+ }
+
+ Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL);
+ if (Status != S_OK)
+ {
+ ExtOut("Failed to read memory\n");
+ delete [] pArray;
+ return Status;
+ }
+
+ // Now we have a local copy of the IL, and a managed array for token resolution.
+ // Visit our IL parser with this info.
+ ExtOut("This is dynamic IL. Exception info is not reported at this time.\n");
+ ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n");
+ ExtOut("in parenthesis. You can also look at the token table yourself, by\n");
+ ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr));
+ DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray);
+
+ delete [] pArray;
+ }
+ return Status;
+}
+
+void DumpSigWorker (
+ DWORD_PTR dwSigAddr,
+ DWORD_PTR dwModuleAddr,
+ BOOL fMethod)
+{
+ //
+ // Find the length of the signature and copy it into the debugger process.
+ //
+
+ ULONG cbSig = 0;
+ const ULONG cbSigInc = 256;
+ ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc];
+ if (pSig == NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ CQuickBytes sigString;
+ for (;;)
+ {
+ if (IsInterrupt())
+ return;
+
+ ULONG cbCopied;
+ if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied))
+ return;
+ cbSig += cbCopied;
+
+ sigString.ReSize(0);
+ GetSignatureStringResults result;
+ if (fMethod)
+ result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
+ else
+ result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
+
+ if (GSS_ERROR == result)
+ return;
+
+ if (GSS_SUCCESS == result)
+ break;
+
+ // If we didn't get the full amount back, and we failed to parse the
+ // signature, it's not valid because of insufficient data
+ if (cbCopied < 256)
+ {
+ ExtOut("Invalid signature\n");
+ return;
+ }
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free"
+#endif
+
+ PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc);
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+ if (pSigNew == NULL)
+ {
+ ExtOut("Out of memory\n");
+ return;
+ }
+
+ pSig = pSigNew;
+ }
+
+ ExtOut("%S\n", (PCWSTR)sigString.Ptr());
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump a signature object. *
+* *
+\**********************************************************************/
+DECLARE_API(DumpSig)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ //
+ // Fetch arguments
+ //
+
+ StringHolder sigExpr;
+ StringHolder moduleExpr;
+ CMDValue arg[] =
+ {
+ {&sigExpr.data, COSTRING},
+ {&moduleExpr.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg != 2)
+ {
+ ExtOut("!DumpSig <sigaddr> <moduleaddr>\n");
+ return Status;
+ }
+
+ DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
+ DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
+
+ if (dwSigAddr == 0 || dwModuleAddr == 0)
+ {
+ ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
+ return Status;
+ }
+
+ DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE);
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump a portion of a signature object. *
+* *
+\**********************************************************************/
+DECLARE_API(DumpSigElem)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ //
+ // Fetch arguments
+ //
+
+ StringHolder sigExpr;
+ StringHolder moduleExpr;
+ CMDValue arg[] =
+ {
+ {&sigExpr.data, COSTRING},
+ {&moduleExpr.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (nArg != 2)
+ {
+ ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n");
+ return Status;
+ }
+
+ DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
+ DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
+
+ if (dwSigAddr == 0 || dwModuleAddr == 0)
+ {
+ ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
+ return Status;
+ }
+
+ DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE);
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an EEClass from *
+* a given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpClass)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR dwStartAddr = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (nArg == 0)
+ {
+ ExtOut("Missing EEClass address\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ CLRDATA_ADDRESS methodTable;
+ if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
+ {
+ ExtOut("Invalid EEClass address\n");
+ return Status;
+ }
+
+ DacpMethodTableData mtdata;
+ if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK)
+ {
+ ExtOut("EEClass has an invalid MethodTable address\n");
+ return Status;
+ }
+
+ sos::MethodTable mt = TO_TADDR(methodTable);
+ ExtOut("Class Name: %S\n", mt.GetName());
+
+ WCHAR fileName[MAX_LONGPATH];
+ FileNameForModule(TO_TADDR(mtdata.Module), fileName);
+ ExtOut("mdToken: %p\n", mtdata.cl);
+ ExtOut("File: %S\n", fileName);
+
+ CLRDATA_ADDRESS ParentEEClass = NULL;
+ if (mtdata.ParentMethodTable)
+ {
+ DacpMethodTableData mtdataparent;
+ if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK)
+ {
+ ExtOut("EEClass has an invalid MethodTable address\n");
+ return Status;
+ }
+ ParentEEClass = mtdataparent.Class;
+ }
+
+ DMLOut("Parent Class: %s\n", DMLClass(ParentEEClass));
+ DMLOut("Module: %s\n", DMLModule(mtdata.Module));
+ DMLOut("Method Table: %s\n", DMLMethodTable(methodTable));
+ ExtOut("Vtable Slots: %x\n", mtdata.wNumVirtuals);
+ ExtOut("Total Method Slots: %x\n", mtdata.wNumVtableSlots);
+ ExtOut("Class Attributes: %x ", mtdata.dwAttrClass);
+
+ if (IsTdInterface(mtdata.dwAttrClass))
+ ExtOut("Interface, ");
+ if (IsTdAbstract(mtdata.dwAttrClass))
+ ExtOut("Abstract, ");
+ if (IsTdImport(mtdata.dwAttrClass))
+ ExtOut("ComImport, ");
+
+ ExtOut("\n");
+
+ DacpMethodTableTransparencyData transparency;
+ if (SUCCEEDED(transparency.Request(g_sos, methodTable)))
+ {
+ ExtOut("Transparency: %s\n", GetTransparency(transparency));
+ }
+
+ DacpMethodTableFieldData vMethodTableFields;
+ if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
+ {
+ ExtOut("NumInstanceFields: %x\n", vMethodTableFields.wNumInstanceFields);
+ ExtOut("NumStaticFields: %x\n", vMethodTableFields.wNumStaticFields);
+
+ if (vMethodTableFields.wNumThreadStaticFields != 0)
+ {
+ ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
+ }
+
+
+ if (vMethodTableFields.wContextStaticsSize)
+ {
+ ExtOut("ContextStaticOffset: %x\n", vMethodTableFields.wContextStaticOffset);
+ ExtOut("ContextStaticsSize: %x\n", vMethodTableFields.wContextStaticsSize);
+ }
+
+
+ if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+ {
+ DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE);
+ }
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a MethodTable *
+* from a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpMT)
+{
+ DWORD_PTR dwStartAddr=0;
+ DWORD_PTR dwOriginalAddr;
+
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bDumpMDTable = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-MD", &bDumpMDTable, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ TableOutput table(2, 16, AlignLeft, false);
+
+ if (nArg == 0)
+ {
+ Print("Missing MethodTable address\n");
+ return Status;
+ }
+
+ dwOriginalAddr = dwStartAddr;
+ dwStartAddr = dwStartAddr&~3;
+
+ if (!IsMethodTable(dwStartAddr))
+ {
+ Print(dwOriginalAddr, " is not a MethodTable\n");
+ return Status;
+ }
+
+ DacpMethodTableData vMethTable;
+ vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr));
+
+ if (vMethTable.bIsFree)
+ {
+ Print("Free MethodTable\n");
+ return Status;
+ }
+
+ table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
+
+ table.WriteRow("Module:", ModulePtr(vMethTable.Module));
+
+ sos::MethodTable mt = (TADDR)dwStartAddr;
+ table.WriteRow("Name:", mt.GetName());
+
+ WCHAR fileName[MAX_LONGPATH];
+ FileNameForModule(TO_TADDR(vMethTable.Module), fileName);
+ table.WriteRow("mdToken:", Pointer(vMethTable.cl));
+ table.WriteRow("File:", fileName[0] ? fileName : W("Unknown Module"));
+
+ table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
+ table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
+ table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods));
+
+ table.SetColWidth(0, 29);
+ table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces));
+
+ if (bDumpMDTable)
+ {
+ table.ReInit(4, POINTERSIZE_HEX, AlignRight);
+ table.SetColAlignment(3, AlignLeft);
+ table.SetColWidth(2, 6);
+
+ Print("--------------------------------------\n");
+ Print("MethodDesc Table\n");
+
+ table.WriteRow("Entry", "MethodDesc", "JIT", "Name");
+
+ for (DWORD n = 0; n < vMethTable.wNumMethods; n++)
+ {
+ JITTypes jitType;
+ DWORD_PTR methodDesc=0;
+ DWORD_PTR gcinfoAddr;
+
+ CLRDATA_ADDRESS entry;
+ if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK)
+ {
+ PrintLn("<error getting slot ", Decimal(n), ">");
+ continue;
+ }
+
+ IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr);
+ table.WriteColumn(0, entry);
+ table.WriteColumn(1, MethodDescPtr(methodDesc));
+
+ if (jitType == TYPE_UNKNOWN && methodDesc != NULL)
+ {
+ // We can get a more accurate jitType from NativeCodeAddr of the methoddesc,
+ // because the methodtable entry hasn't always been patched.
+ DacpMethodDescData tmpMethodDescData;
+ if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK)
+ {
+ jitType = (JITTypes) codeHeaderData.JITType;
+ }
+ }
+ }
+
+ const char *pszJitType = "NONE";
+ if (jitType == TYPE_JIT)
+ pszJitType = "JIT";
+ else if (jitType == TYPE_PJIT)
+ pszJitType = "PreJIT";
+ else
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ // Is it an fcall?
+ if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) &&
+ ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size))))
+ {
+ pszJitType = "FCALL";
+ }
+ }
+ }
+
+ table.WriteColumn(2, pszJitType);
+
+ NameForMD_s(methodDesc,g_mdName,mdNameLen);
+ table.WriteColumn(3, g_mdName);
+ }
+ }
+ return Status;
+}
+
+extern size_t Align (size_t nbytes);
+
+HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
+{
+ HRESULT Status;
+ DacpMethodTableData mtabledata;
+ if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK)
+ return Status;
+
+ size_t size = mtabledata.BaseSize;
+ if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK)
+ return Status;
+
+ ExtOut("Name: %S\n", g_mdName);
+ DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
+ DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
+ ExtOut("Size: %d(0x%x) bytes\n", size, size);
+
+ FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
+ ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
+
+ if (bPrintFields)
+ {
+ DacpMethodTableFieldData vMethodTableFields;
+ if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK)
+ return Status;
+
+ ExtOut("Fields:\n");
+
+ if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+ DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE);
+ }
+
+ return S_OK;
+}
+
+void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData)
+{
+ // Get the method table
+ int iOffset = GetObjFieldOffset(p_rtObject, rtObjectData.MethodTable, W("m_handle"));
+ if (iOffset > 0)
+ {
+ TADDR mtPtr;
+ if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr)))
+ {
+ sos::MethodTable mt = mtPtr;
+ ExtOut("Type Name: %S\n", mt.GetName());
+ DMLOut("Type MT: %s\n", DMLMethodTable(mtPtr));
+ }
+ }
+}
+
+HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
+{
+ if (!sos::IsObject(taObj, true))
+ {
+ ExtOut("<Note: this object has an invalid CLASS field>\n");
+ }
+
+ DacpObjectData objData;
+ HRESULT Status;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ if (objData.ObjectType==OBJ_FREE)
+ {
+ ExtOut("Free Object\n");
+ DWORD_PTR size = (DWORD_PTR)objData.Size;
+ ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
+ return S_OK;
+ }
+
+ sos::Object obj = taObj;
+ ExtOut("Name: %S\n", obj.GetTypeName());
+ DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
+
+
+ DacpMethodTableData mtabledata;
+ if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
+ {
+ DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
+ }
+ else
+ {
+ ExtOut("Invalid EEClass address\n");
+ return Status;
+ }
+
+ if (objData.RCW != NULL)
+ {
+ DMLOut("RCW: %s\n", DMLRCWrapper(objData.RCW));
+ }
+ if (objData.CCW != NULL)
+ {
+ DMLOut("CCW: %s\n", DMLCCWrapper(objData.CCW));
+ }
+
+ DWORD_PTR size = (DWORD_PTR)objData.Size;
+ ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
+
+ if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType")) == 0)
+ {
+ PrintRuntimeTypeInfo(taObj, objData);
+ }
+
+ if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType+RuntimeTypeCache")) == 0)
+ {
+ // Get the method table
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_runtimeType"));
+ if (iOffset > 0)
+ {
+ TADDR rtPtr;
+ if (MOVE(rtPtr, taObj + iOffset) == S_OK)
+ {
+ DacpObjectData rtObjectData;
+ if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK)
+ {
+ ExtOut("Error when reading RuntimeType field\n");
+ return Status;
+ }
+
+ PrintRuntimeTypeInfo(rtPtr, rtObjectData);
+ }
+ }
+ }
+
+ if (objData.ObjectType==OBJ_ARRAY)
+ {
+ ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s",
+ objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
+
+ IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj));
+ ExtOut("\n");
+
+ if (objData.ElementType == ELEMENT_TYPE_I1 ||
+ objData.ElementType == ELEMENT_TYPE_U1 ||
+ objData.ElementType == ELEMENT_TYPE_CHAR)
+ {
+ bool wide = objData.ElementType == ELEMENT_TYPE_CHAR;
+
+ // Get the size of the character array, but clamp it to a reasonable length.
+ TADDR pos = taObj + (2 * sizeof(DWORD_PTR));
+ DWORD_PTR num;
+ moveN(num, taObj + sizeof(DWORD_PTR));
+
+ if (IsDMLEnabled())
+ DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>: ", (wide) ? "dw" : "db", pos, num);
+ else
+ ExtOut("Content: ");
+ CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide);
+ ExtOut("\n");
+ }
+ }
+ else
+ {
+ FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
+ ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
+ }
+
+ if (objData.ObjectType == OBJ_STRING)
+ {
+ ExtOut("String: ");
+ StringObjectContent(taObj);
+ ExtOut("\n");
+ }
+ else if (objData.ObjectType == OBJ_OBJECT)
+ {
+ ExtOut("Object\n");
+ }
+
+ if (bPrintFields)
+ {
+ DacpMethodTableFieldData vMethodTableFields;
+ if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK)
+ return Status;
+
+ ExtOut("Fields:\n");
+ if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+ {
+ DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE);
+ }
+ else
+ {
+ ExtOut("None\n");
+ }
+ }
+
+ sos::ThinLockInfo lockInfo;
+ if (obj.GetThinLock(lockInfo))
+ {
+ ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId,
+ SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
+ }
+
+ return S_OK;
+}
+
+BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
+{
+ int i = 0;
+ if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
+ {
+ ExtOut("<integer underflow>\n");
+ return FALSE;
+ }
+
+ for (; i >= 0; i--)
+ {
+ if (indices[i] >= bounds[i] + lowerBounds[i])
+ {
+ if (i == 0)
+ {
+ return FALSE;
+ }
+
+ indices[i] = lowerBounds[i];
+ indices[i - 1]++;
+ }
+ }
+
+ return TRUE;
+}
+
+void ExtOutIndices (DWORD * indices, DWORD rank)
+{
+ for (DWORD i = 0; i < rank; i++)
+ {
+ ExtOut("[%d]", indices[i]);
+ }
+}
+
+size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
+{
+ _ASSERTE(rank >= 0);
+ size_t multiplier = 1;
+ size_t offset = 0;
+ int i = 0;
+ if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
+ {
+ ExtOut("<integer underflow>\n");
+ return 0;
+ }
+
+ for (; i >= 0; i--)
+ {
+ DWORD curIndex = indices[i] - lowerBounds[i];
+ offset += curIndex * multiplier;
+ multiplier *= bounds[i];
+ }
+
+ return offset;
+}
+HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint);
+#ifdef _DEBUG
+HRESULT PrintPermissionSet (TADDR p_PermSet)
+{
+ HRESULT Status = S_OK;
+
+ DacpObjectData PermSetData;
+ if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+
+ sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable);
+ if (_wcscmp (W("System.Security.PermissionSet"), mt.GetName()) != 0 && _wcscmp(W("System.Security.NamedPermissionSet"), mt.GetName()) != 0)
+ {
+ ExtOut("Invalid PermissionSet object\n");
+ return S_FALSE;
+ }
+
+ ExtOut("PermissionSet object: %p\n", SOS_PTR(p_PermSet));
+
+ // Print basic info
+
+ // Walk the fields, printing some fields in a special way.
+
+ int iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_Unrestricted"));
+
+ if (iOffset > 0)
+ {
+ BYTE unrestricted;
+ MOVE(unrestricted, p_PermSet + iOffset);
+ if (unrestricted)
+ ExtOut("Unrestricted: TRUE\n");
+ else
+ ExtOut("Unrestricted: FALSE\n");
+ }
+
+ iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_permSet"));
+ if (iOffset > 0)
+ {
+ TADDR tbSetPtr;
+ MOVE(tbSetPtr, p_PermSet + iOffset);
+ if (tbSetPtr != NULL)
+ {
+ DacpObjectData tbSetData;
+ if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Set"));
+ if (iOffset > 0)
+ {
+ DWORD_PTR PermsArrayPtr;
+ MOVE(PermsArrayPtr, tbSetPtr + iOffset);
+ if (PermsArrayPtr != NULL)
+ {
+ // Print all the permissions in the array
+ DacpObjectData objData;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+ DumpArrayFlags flags;
+ flags.bDetail = TRUE;
+ return PrintArray(objData, flags, TRUE);
+ }
+ }
+
+ iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Obj"));
+ if (iOffset > 0)
+ {
+ DWORD_PTR PermObjPtr;
+ MOVE(PermObjPtr, tbSetPtr + iOffset);
+ if (PermObjPtr != NULL)
+ {
+ // Print the permission object
+ return PrintObj(PermObjPtr);
+ }
+ }
+
+
+ }
+ }
+ return Status;
+}
+
+#endif // _DEBUG
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an object from a *
+* given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpArray)
+{
+ INIT_API();
+
+ DumpArrayFlags flags;
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-start", &flags.startIndex, COSIZE_T, TRUE},
+ {"-length", &flags.Length, COSIZE_T, TRUE},
+ {"-details", &flags.bDetail, COBOOL, FALSE},
+ {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&flags.strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DWORD_PTR p_Object = GetExpression (flags.strObject);
+ if (p_Object == 0)
+ {
+ ExtOut("Invalid parameter %s\n", flags.strObject);
+ return Status;
+ }
+
+ if (!sos::IsObject(p_Object, true))
+ {
+ ExtOut("<Note: this object has an invalid CLASS field>\n");
+ }
+
+ DacpObjectData objData;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ if (objData.ObjectType != OBJ_ARRAY)
+ {
+ ExtOut("Not an array, please use !DumpObj instead\n");
+ return S_OK;
+ }
+ return PrintArray(objData, flags, FALSE);
+}
+
+
+HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint)
+{
+ HRESULT Status = S_OK;
+
+ if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0))
+ {
+ ExtOut("For multi-dimension array, length and start index are supported\n");
+ return S_OK;
+ }
+
+ if (flags.startIndex > objData.dwNumComponents)
+ {
+ ExtOut("Start index out of range\n");
+ return S_OK;
+ }
+
+ if (!flags.bDetail && flags.bNoFieldsForElement)
+ {
+ ExtOut("-nofields has no effect unless -details is specified\n");
+ }
+
+ DWORD i;
+ if (!isPermSetPrint)
+ {
+ // TODO: don't depend on this being a MethodTable
+ NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen);
+
+ ExtOut("Name: %S[", g_mdName);
+ for (i = 1; i < objData.dwRank; i++)
+ ExtOut(",");
+ ExtOut("]\n");
+
+ DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
+
+ {
+ DacpMethodTableData mtdata;
+ if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable)))
+ {
+ DMLOut("EEClass: %s\n", DMLClass(mtdata.Class));
+ }
+ }
+
+ DWORD_PTR size = (DWORD_PTR)objData.Size;
+ ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
+
+ ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n",
+ objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
+ DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle));
+ }
+
+ BOOL isElementValueType = IsElementValueType(objData.ElementType);
+
+ DWORD dwRankAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize))
+ {
+ ExtOut("Integer overflow on array rank\n");
+ return Status;
+ }
+
+ DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize);
+ if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL))
+ {
+ ExtOut("Failed to read lower bounds info from the array\n");
+ return S_OK;
+ }
+
+ DWORD *bounds = (DWORD *)alloca(dwRankAllocSize);
+ if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL))
+ {
+ ExtOut("Failed to read bounds info from the array\n");
+ return S_OK;
+ }
+
+ //length is only supported for single-dimension array
+ if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1)
+ {
+ bounds[0] = _min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]);
+ }
+
+ DWORD *indices = (DWORD *)alloca(dwRankAllocSize);
+ for (i = 0; i < objData.dwRank; i++)
+ {
+ indices[i] = lowerBounds[i];
+ }
+
+ //start index is only supported for single-dimension array
+ if (objData.dwRank == 1)
+ {
+ indices[0] = (DWORD)flags.startIndex;
+ }
+
+ //Offset should be calculated by OffsetFromIndices. However because of the way
+ //how we grow indices, incrementing offset by one happens to match indices in every iteration
+ for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank);
+ IndicesInRange (indices, lowerBounds, bounds, objData.dwRank);
+ indices[objData.dwRank - 1]++, offset++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("interrupted by user\n");
+ break;
+ }
+
+ TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize);
+ TADDR p_Element = NULL;
+ if (isElementValueType)
+ {
+ p_Element = elementAddress;
+ }
+ else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL))
+ {
+ ExtOut("Failed to read element at ");
+ ExtOutIndices(indices, objData.dwRank);
+ ExtOut("\n");
+ continue;
+ }
+
+ if (p_Element)
+ {
+ ExtOutIndices(indices, objData.dwRank);
+
+ if (isElementValueType)
+ {
+ DMLOut( " %s\n", DMLValueClass(objData.MethodTable, p_Element));
+ }
+ else
+ {
+ DMLOut(" %s\n", DMLObject(p_Element));
+ }
+ }
+ else if (!isPermSetPrint)
+ {
+ ExtOutIndices(indices, objData.dwRank);
+ ExtOut(" null\n");
+ }
+
+ if (flags.bDetail)
+ {
+ IncrementIndent();
+ if (isElementValueType)
+ {
+ PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement);
+ }
+ else if (p_Element != NULL)
+ {
+ PrintObj(p_Element, !flags.bNoFieldsForElement);
+ }
+ DecrementIndent();
+ }
+ }
+
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an object from a *
+* given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpObj)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ BOOL bNoFields = FALSE;
+ BOOL bRefs = FALSE;
+ StringHolder str_Object;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-nofields", &bNoFields, COBOOL, FALSE},
+ {"-refs", &bRefs, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&str_Object.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ DWORD_PTR p_Object = GetExpression(str_Object.data);
+ EnableDMLHolder dmlHolder(dml);
+ if (p_Object == 0)
+ {
+ ExtOut("Invalid parameter %s\n", args);
+ return Status;
+ }
+
+ Status = PrintObj(p_Object, !bNoFields);
+
+ if (SUCCEEDED(Status) && bRefs)
+ {
+ ExtOut("GC Refs:\n");
+ TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
+ out.WriteRow("offset", "object");
+ for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
+ out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
+ }
+
+ return Status;
+}
+
+CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
+{
+ // We want to follow back until we get the mt for System.Exception
+ DacpMethodTableData dmtd;
+ CLRDATA_ADDRESS walkMT = mtObj;
+ while(walkMT != NULL)
+ {
+ if (dmtd.Request(g_sos, walkMT) != S_OK)
+ {
+ break;
+ }
+ if (walkMT == g_special_usefulGlobals.ExceptionMethodTable)
+ {
+ return walkMT;
+ }
+ walkMT = dmtd.ParentMethodTable;
+ }
+ return NULL;
+}
+
+CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj)
+{
+ // We want to follow back until we get the mt for System.Exception
+ DacpMethodTableData dmtd;
+ CLRDATA_ADDRESS walkMT = mtObj;
+ while(walkMT != NULL)
+ {
+ if (dmtd.Request(g_sos, walkMT) != S_OK)
+ {
+ break;
+ }
+ NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
+ if (_wcscmp(W("System.Security.SecurityException"), g_mdName) == 0)
+ {
+ return walkMT;
+ }
+ walkMT = dmtd.ParentMethodTable;
+ }
+ return NULL;
+}
+
+// Fill the passed in buffer with a text header for generated exception information.
+// Returns the number of characters in the wszBuffer array on exit.
+// If NULL is passed for wszBuffer, just returns the number of characters needed.
+size_t AddExceptionHeader (__out_ecount_opt(bufferLength) WCHAR *wszBuffer, size_t bufferLength)
+{
+#ifdef _TARGET_WIN64_
+ const WCHAR *wszHeader = W(" SP IP Function\n");
+#else
+ const WCHAR *wszHeader = W(" SP IP Function\n");
+#endif // _TARGET_WIN64_
+ if (wszBuffer)
+ {
+ swprintf_s(wszBuffer, bufferLength, wszHeader);
+ }
+ return _wcslen(wszHeader);
+}
+
+struct StackTraceElement
+{
+ UINT_PTR ip;
+ UINT_PTR sp;
+ DWORD_PTR pFunc; // MethodDesc
+#if defined(FEATURE_EXCEPTIONDISPATCHINFO)
+ // TRUE if this element represents the last frame of the foreign
+ // exception stack trace.
+ BOOL fIsLastFrameFromForeignStackTrace;
+#endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
+
+};
+
+#include "sos_stacktrace.h"
+
+class StringOutput
+{
+public:
+ CQuickString cs;
+ StringOutput()
+ {
+ cs.Alloc(1024);
+ cs.String()[0] = L'\0';
+ }
+
+ BOOL Append(__in_z LPCWSTR pszStr)
+ {
+ size_t iInputLen = _wcslen (pszStr);
+ size_t iCurLen = _wcslen (cs.String());
+ if ((iCurLen + iInputLen + 1) > cs.Size())
+ {
+ if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK)
+ {
+ return FALSE;
+ }
+ }
+
+ wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE);
+ return TRUE;
+ }
+
+ size_t Length()
+ {
+ return _wcslen(cs.String());
+ }
+
+ WCHAR *String()
+ {
+ return cs.String();
+ }
+};
+
+static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so);
+
+// Using heuristics to determine if an exception object represented an async (hardware) or a
+// managed exception
+// We need to use these heuristics when the System.Exception object is not the active exception
+// on some thread, but it's something found somewhere on the managed heap.
+
+// uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions
+// to managed exceptions and their HRESULTs
+static const HRESULT AsyncHResultValues[] =
+{
+ COR_E_ARITHMETIC, // kArithmeticException
+ COR_E_OVERFLOW, // kOverflowException
+ COR_E_DIVIDEBYZERO, // kDivideByZeroException
+ COR_E_FORMAT, // kFormatException
+ COR_E_NULLREFERENCE, // kNullReferenceException
+ E_POINTER, // kAccessViolationException
+ // the EE is raising the next exceptions more often than the OS will raise an async
+ // exception for these conditions, so in general treat these as Synchronous
+ // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException
+ // COR_E_OUTOFMEMORY, // kOutOfMemoryException
+ // COR_E_STACKOVERFLOW, // kStackOverflowException
+ COR_E_DATAMISALIGNED, // kDataMisalignedException
+
+};
+BOOL IsAsyncException(TADDR taObj, TADDR mtObj)
+{
+ // by default we'll treat exceptions as synchronous
+ UINT32 xcode = EXCEPTION_COMPLUS;
+ int iOffset = GetObjFieldOffset (taObj, mtObj, W("_xcode"));
+ if (iOffset > 0)
+ {
+ HRESULT hr = MOVE(xcode, taObj + iOffset);
+ if (hr != S_OK)
+ {
+ xcode = EXCEPTION_COMPLUS;
+ goto Done;
+ }
+ }
+
+ if (xcode == EXCEPTION_COMPLUS)
+ {
+ HRESULT ehr = 0;
+ iOffset = GetObjFieldOffset (taObj, mtObj, W("_HResult"));
+ if (iOffset > 0)
+ {
+ HRESULT hr = MOVE(ehr, taObj + iOffset);
+ if (hr != S_OK)
+ {
+ xcode = EXCEPTION_COMPLUS;
+ goto Done;
+ }
+ for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
+ {
+ if (ehr == AsyncHResultValues[idx])
+ {
+ xcode = ehr;
+ break;
+ }
+ }
+ }
+ }
+Done:
+ return xcode != EXCEPTION_COMPLUS;
+}
+
+// Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS
+BOOL IsAsyncException(const DacpExceptionObjectData & excData)
+{
+ if (excData.XCode != EXCEPTION_COMPLUS)
+ return TRUE;
+
+ HRESULT ehr = excData.HResult;
+ for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
+ {
+ if (ehr == AsyncHResultValues[idx])
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+#define SOS_STACKTRACE_SHOWEXPLICITFRAMES 0x00000002
+size_t FormatGeneratedException (DWORD_PTR dataPtr,
+ UINT bytes,
+ __out_ecount_opt(bufferLength) WCHAR *wszBuffer,
+ size_t bufferLength,
+ BOOL bAsync,
+ BOOL bNestedCase = FALSE,
+ BOOL bLineNumbers = FALSE)
+{
+ UINT count = bytes / sizeof(StackTraceElement);
+ size_t Length = 0;
+
+ if (wszBuffer && bufferLength > 0)
+ {
+ wszBuffer[0] = L'\0';
+ }
+
+ // Buffer is calculated for sprintf below (" %p %p %S\n");
+ WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_LONGPATH + 8];
+
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (bNestedCase)
+ {
+ // If we are computing the call stack for a nested exception, we
+ // don't want to print the last frame, because the outer exception
+ // will have that frame.
+ count--;
+ }
+
+ for (UINT i = 0; i < count; i++)
+ {
+ StackTraceElement ste;
+ MOVE (ste, dataPtr + i*sizeof(StackTraceElement));
+
+ // ste.ip must be adjusted because of an ancient workaround in the exception
+ // infrastructure. The workaround is that the exception needs to have
+ // an ip address that will map to the line number where the exception was thrown.
+ // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp)
+ //
+ // This "counterhack" is not 100% accurate
+ // The biggest issue is that !PrintException must work with exception objects
+ // that may not be currently active; as a consequence we cannot rely on the
+ // state of some "current thread" to infer whether the IP values stored in
+ // the exception object have been adjusted or not. If we could, we may examine
+ // the topmost "Frame" and make the decision based on whether it's a
+ // FaultingExceptionFrame or not.
+ // 1. On IA64 the IP values are never adjusted by the EE so there's nothing
+ // to adjust back.
+ // 2. On AMD64:
+ // (a) if the exception was an async (hardware) exception add 1 to all
+ // IP values in the exception object
+ // (b) if the exception was a managed exception (either raised by the
+ // EE or thrown by managed code) do not adjust any IP values
+ // 3. On X86:
+ // (a) if the exception was an async (hardware) exception add 1 to
+ // all but the topmost IP value in the exception object
+ // (b) if the exception was a managed exception (either raised by
+ // the EE or thrown by managed code) add 1 to all IP values in
+ // the exception object
+#if defined(_TARGET_AMD64_)
+ if (bAsync)
+ {
+ ste.ip += 1;
+ }
+#elif defined(_TARGET_X86_)
+ if (IsDbgTargetX86() && (!bAsync || i != 0))
+ {
+ ste.ip += 1;
+ }
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_)
+
+ StringOutput so;
+ HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so);
+
+ // If DumpMDInfoBuffer failed (due to out of memory or missing metadata),
+ // or did not update so (when ste is an explicit frames), do not update wszBuffer
+ if (Status == S_OK)
+ {
+ WCHAR filename[MAX_LONGPATH] = W("");
+ ULONG linenum = 0;
+ if (bLineNumbers &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
+ {
+ swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
+ }
+ else
+ {
+ swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s\n"), so.String());
+ }
+
+ Length += _wcslen(wszLineBuffer);
+
+ if (wszBuffer)
+ {
+ wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE);
+ }
+ }
+ }
+
+ return Length;
+}
+
+// ExtOut has an internal limit for the string size
+void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len)
+{
+ const size_t chunkLen = 2048;
+
+ WCHAR *pwsz = pwszLargeString; // beginning of a chunk
+ size_t count = len/chunkLen;
+ // write full chunks
+ for (size_t idx = 0; idx < count; ++idx)
+ {
+ WCHAR *pch = pwsz + chunkLen; // after the chunk
+ // zero terminate the chunk
+ WCHAR ch = *pch;
+ *pch = L'\0';
+
+ ExtOut("%S", pwsz);
+
+ // restore whacked char
+ *pch = ch;
+
+ // advance to next chunk
+ pwsz += chunkLen;
+ }
+
+ // last chunk
+ ExtOut("%S", pwsz);
+}
+
+HRESULT FormatException(TADDR taObj, BOOL bLineNumbers = FALSE)
+{
+ HRESULT Status = S_OK;
+
+ DacpObjectData objData;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ // Make sure it is an exception object, and get the MT of Exception
+ CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
+ if (exceptionMT == NULL)
+ {
+ ExtOut("Not a valid exception object\n");
+ return Status;
+ }
+
+ DMLOut("Exception object: %s\n", DMLObject(taObj));
+
+ if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen))
+ {
+ ExtOut("Exception type: %S\n", g_mdName);
+ }
+ else
+ {
+ ExtOut("Exception type: <Unknown>\n");
+ }
+
+ // Print basic info
+
+ // First try to get exception object data using ISOSDacInterface2
+ DacpExceptionObjectData excData;
+ BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, TO_CDADDR(taObj)));
+
+ // Walk the fields, printing some fields in a special way.
+ // HR, InnerException, Message, StackTrace, StackTraceString
+
+ {
+ TADDR taMsg = 0;
+ if (bGotExcData)
+ {
+ taMsg = TO_TADDR(excData.Message);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_message"));
+ if (iOffset > 0)
+ {
+ MOVE (taMsg, taObj + iOffset);
+ }
+ }
+
+ ExtOut("Message: ");
+
+ if (taMsg)
+ StringObjectContent(taMsg);
+ else
+ ExtOut("<none>");
+
+ ExtOut("\n");
+ }
+
+ {
+ TADDR taInnerExc = 0;
+ if (bGotExcData)
+ {
+ taInnerExc = TO_TADDR(excData.InnerException);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_innerException"));
+ if (iOffset > 0)
+ {
+ MOVE (taInnerExc, taObj + iOffset);
+ }
+ }
+
+ ExtOut("InnerException: ");
+ if (taInnerExc)
+ {
+ TADDR taMT;
+ if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT)))
+ {
+ NameForMT_s(taMT, g_mdName, mdNameLen);
+ ExtOut("%S, ", g_mdName);
+ if (IsDMLEnabled())
+ DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc);
+ else
+ ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc));
+ }
+ else
+ {
+ ExtOut("<invalid MethodTable of inner exception>");
+ }
+ }
+ else
+ {
+ ExtOut("<none>\n");
+ }
+ }
+
+ BOOL bAsync = bGotExcData ? IsAsyncException(excData)
+ : IsAsyncException(taObj, TO_TADDR(objData.MethodTable));
+
+ {
+ TADDR taStackTrace = 0;
+ if (bGotExcData)
+ {
+ taStackTrace = TO_TADDR(excData.StackTrace);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTrace"));
+ if (iOffset > 0)
+ {
+ MOVE(taStackTrace, taObj + iOffset);
+ }
+ }
+
+ ExtOut("StackTrace (generated):\n");
+ if (taStackTrace)
+ {
+ DWORD arrayLen;
+ HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR));
+
+ if (arrayLen != 0 && hr == S_OK)
+ {
+#ifdef _TARGET_WIN64_
+ DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
+#else
+ DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD);
+#endif // _TARGET_WIN64_
+ size_t stackTraceSize = 0;
+ MOVE (stackTraceSize, dataPtr);
+
+ DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
+ dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
+
+ if (stackTraceSize == 0)
+ {
+ ExtOut("Unable to decipher generated stack trace\n");
+ }
+ else
+ {
+ size_t iHeaderLength = AddExceptionHeader (NULL, 0);
+ size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers);
+ WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1];
+ if (pwszBuffer)
+ {
+ AddExceptionHeader(pwszBuffer, iHeaderLength + 1);
+ FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers);
+ SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1);
+ delete[] pwszBuffer;
+ }
+ ExtOut("\n");
+ }
+ }
+ else
+ {
+ ExtOut("<Not Available>\n");
+ }
+ }
+ else
+ {
+ ExtOut("<none>\n");
+ }
+ }
+
+ {
+ TADDR taStackString;
+ if (bGotExcData)
+ {
+ taStackString = TO_TADDR(excData.StackTraceString);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTraceString"));
+ MOVE (taStackString, taObj + iOffset);
+ }
+
+ ExtOut("StackTraceString: ");
+ if (taStackString)
+ {
+ StringObjectContent(taStackString);
+ ExtOut("\n\n"); // extra newline looks better
+ }
+ else
+ {
+ ExtOut("<none>\n");
+ }
+ }
+
+ {
+ DWORD hResult;
+ if (bGotExcData)
+ {
+ hResult = excData.HResult;
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_HResult"));
+ MOVE (hResult, taObj + iOffset);
+ }
+
+ ExtOut("HResult: %lx\n", hResult);
+ }
+
+ if (isSecurityExceptionObj(objData.MethodTable) != NULL)
+ {
+ // We have a SecurityException Object: print out the debugString if present
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_debugString"));
+ if (iOffset > 0)
+ {
+ TADDR taDebugString;
+ MOVE (taDebugString, taObj + iOffset);
+
+ if (taDebugString)
+ {
+ ExtOut("SecurityException Message: ");
+ StringObjectContent(taDebugString);
+ ExtOut("\n\n"); // extra newline looks better
+ }
+ }
+ }
+
+ return Status;
+}
+
+DECLARE_API(PrintException)
+{
+ INIT_API();
+
+ BOOL dml = FALSE;
+ BOOL bShowNested = FALSE;
+ BOOL bLineNumbers = FALSE;
+ BOOL bCCW = FALSE;
+ StringHolder strObject;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-nested", &bShowNested, COBOOL, FALSE},
+ {"-lines", &bLineNumbers, COBOOL, FALSE},
+ {"-l", &bLineNumbers, COBOOL, FALSE},
+ {"-ccw", &bCCW, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (bLineNumbers)
+ {
+ ULONG symlines = 0;
+ if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+ if (symlines == 0)
+ {
+ ExtOut("In order for the option -lines to enable display of source information\n"
+ "the debugger must be configured to load the line number information from\n"
+ "the symbol files. Use the \".lines; .reload\" command to achieve this.\n");
+ // don't even try
+ bLineNumbers = FALSE;
+ }
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DWORD_PTR p_Object = NULL;
+ if (nArg == 0)
+ {
+ if (bCCW)
+ {
+ ExtOut("No CCW pointer specified\n");
+ return Status;
+ }
+
+ // Look at the last exception object on this thread
+
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ DWORD_PTR dwAddr = NULL;
+ if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &dwAddr,
+ sizeof(dwAddr), NULL)) || (dwAddr==NULL))
+ {
+ ExtOut("There is no current managed exception on this thread\n");
+ }
+ else
+ {
+ p_Object = dwAddr;
+ }
+ }
+ else
+ {
+ p_Object = GetExpression(strObject.data);
+ if (p_Object == 0)
+ {
+ if (bCCW)
+ {
+ ExtOut("Invalid CCW pointer %s\n", args);
+ }
+ else
+ {
+ ExtOut("Invalid exception object %s\n", args);
+ }
+ return Status;
+ }
+
+ if (bCCW)
+ {
+ // check if the address is a CCW pointer and then
+ // get the exception object from it
+ DacpCCWData ccwData;
+ if (ccwData.Request(g_sos, p_Object) == S_OK)
+ {
+ p_Object = TO_TADDR(ccwData.managedObject);
+ }
+ }
+ }
+
+ if (p_Object)
+ {
+ FormatException(p_Object, bLineNumbers);
+ }
+
+ // Are there nested exceptions?
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ if (Thread.firstNestedException)
+ {
+ if (!bShowNested)
+ {
+ ExtOut("There are nested exceptions on this thread. Run with -nested for details\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
+ do
+ {
+ CLRDATA_ADDRESS obj = 0, next = 0;
+ Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error retrieving nested exception info %p\n", SOS_PTR(currentNested));
+ return Status;
+ }
+
+ if (IsInterrupt())
+ {
+ ExtOut("<aborted>\n");
+ return Status;
+ }
+
+ ExtOut("\nNested exception -------------------------------------------------------------\n");
+ Status = FormatException((DWORD_PTR) obj, bLineNumbers);
+ if (Status != S_OK)
+ {
+ return Status;
+ }
+
+ currentNested = next;
+ }
+ while(currentNested != NULL);
+ }
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an object from a *
+* given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpVC)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_MT = NULL;
+ DWORD_PTR p_Object = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_MT, COHEX},
+ {&p_Object, COHEX},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (nArg!=2)
+ {
+ ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n");
+ return Status;
+ }
+
+ if (!IsMethodTable(p_MT))
+ {
+ ExtOut("Not a managed object\n");
+ return S_OK;
+ }
+
+ return PrintVC(p_MT, p_Object);
+}
+
+#ifndef FEATURE_PAL
+
+#ifdef FEATURE_COMINTEROP
+
+DECLARE_API(DumpRCW)
+{
+ INIT_API();
+
+ BOOL dml = FALSE;
+ StringHolder strObject;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (nArg == 0)
+ {
+ ExtOut("Missing RCW address\n");
+ return Status;
+ }
+ else
+ {
+ DWORD_PTR p_RCW = GetExpression(strObject.data);
+ if (p_RCW == 0)
+ {
+ ExtOut("Invalid RCW %s\n", args);
+ }
+ else
+ {
+ DacpRCWData rcwData;
+ if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK)
+ {
+ ExtOut("Error requesting RCW data\n");
+ return Status;
+ }
+ BOOL isDCOMProxy;
+ if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy)))
+ {
+ isDCOMProxy = FALSE;
+ }
+
+ DMLOut("Managed object: %s\n", DMLObject(rcwData.managedObject));
+ DMLOut("Creating thread: %p\n", SOS_PTR(rcwData.creatorThread));
+ ExtOut("IUnknown pointer: %p\n", SOS_PTR(rcwData.unknownPointer));
+ ExtOut("COM Context: %p\n", SOS_PTR(rcwData.ctxCookie));
+ ExtOut("Managed ref count: %d\n", rcwData.refCount);
+ ExtOut("IUnknown V-table pointer : %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr));
+
+ ExtOut("Flags: %s%s%s%s%s%s%s%s\n",
+ (rcwData.isDisconnected? "IsDisconnected " : ""),
+ (rcwData.supportsIInspectable? "SupportsIInspectable " : ""),
+ (rcwData.isAggregated? "IsAggregated " : ""),
+ (rcwData.isContained? "IsContained " : ""),
+ (rcwData.isJupiterObject? "IsJupiterObject " : ""),
+ (rcwData.isFreeThreaded? "IsFreeThreaded " : ""),
+ (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""),
+ (isDCOMProxy ? "IsDCOMProxy " : "")
+ );
+
+ // Jupiter data hidden by default
+ if (rcwData.isJupiterObject)
+ {
+ ExtOut("IJupiterObject: %p\n", SOS_PTR(rcwData.jupiterObject));
+ }
+
+ ExtOut("COM interface pointers:\n");
+
+ ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount];
+ if (pArray == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK)
+ {
+ ExtOut("Error requesting COM interface pointers\n");
+ return Status;
+ }
+
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT");
+ for (int i = 0; i < rcwData.interfaceCount; i++)
+ {
+ // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject
+ // is saved as NULL MethodTable at first slot, and we've already printed outs its
+ // value earlier.
+ if (pArray[i].methodTable == NULL)
+ continue;
+
+ NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
+
+ DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName);
+ }
+ }
+ }
+
+ return Status;
+}
+
+DECLARE_API(DumpCCW)
+{
+ INIT_API();
+
+ BOOL dml = FALSE;
+ StringHolder strObject;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (nArg == 0)
+ {
+ ExtOut("Missing CCW address\n");
+ return Status;
+ }
+ else
+ {
+ DWORD_PTR p_CCW = GetExpression(strObject.data);
+ if (p_CCW == 0)
+ {
+ ExtOut("Invalid CCW %s\n", args);
+ }
+ else
+ {
+ DacpCCWData ccwData;
+ if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK)
+ {
+ ExtOut("Error requesting CCW data\n");
+ return Status;
+ }
+
+ if (ccwData.ccwAddress != p_CCW)
+ ExtOut("CCW: %p\n", SOS_PTR(ccwData.ccwAddress));
+
+ DMLOut("Managed object: %s\n", DMLObject(ccwData.managedObject));
+ ExtOut("Outer IUnknown: %p\n", SOS_PTR(ccwData.outerIUnknown));
+ ExtOut("Ref count: %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : "");
+ ExtOut("Flags: %s%s\n",
+ (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""),
+ (ccwData.isAggregated? "IsAggregated " : "")
+ );
+
+ // Jupiter information hidden by default
+ if (ccwData.jupiterRefCount > 0)
+ {
+ ExtOut("Jupiter ref count: %d%s%s%s%s\n",
+ ccwData.jupiterRefCount,
+ (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "",
+ ccwData.isPegged ? " Jupiter " : "",
+ (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "",
+ ccwData.isGlobalPegged ? " CLR " : ""
+ );
+ }
+
+ ExtOut("RefCounted Handle: %p%s\n",
+ SOS_PTR(ccwData.handle),
+ (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)"));
+
+ ExtOut("COM interface pointers:\n");
+
+ ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount];
+ if (pArray == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK)
+ {
+ ExtOut("Error requesting COM interface pointers\n");
+ return Status;
+ }
+
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type");
+ for (int i = 0; i < ccwData.interfaceCount; i++)
+ {
+ if (pArray[i].methodTable == NULL)
+ {
+ wcscpy_s(g_mdName, mdNameLen, W("IDispatch/IUnknown"));
+ }
+ else
+ {
+ NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
+ }
+
+ DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName);
+ }
+ }
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_COMINTEROP
+
+#ifdef _DEBUG
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a PermissionSet *
+* from a given address. *
+* *
+\**********************************************************************/
+/*
+ COMMAND: dumppermissionset.
+ !DumpPermissionSet <PermissionSet object address>
+
+ This command allows you to examine a PermissionSet object. Note that you can
+ also use DumpObj such an object in greater detail. DumpPermissionSet attempts
+ to extract all the relevant information from a PermissionSet that you might be
+ interested in when performing Code Access Security (CAS) related debugging.
+
+ Here is a simple PermissionSet object:
+
+ 0:000> !DumpPermissionSet 014615f4
+ PermissionSet object: 014615f4
+ Unrestricted: TRUE
+
+ Note that this is an unrestricted PermissionSet object that does not contain
+ any individual permissions.
+
+ Here is another example of a PermissionSet object, one that is not unrestricted
+ and contains a single permission:
+
+ 0:003> !DumpPermissionSet 01469fa8
+ PermissionSet object: 01469fa8
+ Unrestricted: FALSE
+ Name: System.Security.Permissions.ReflectionPermission
+ MethodTable: 5b731308
+ EEClass: 5b7e0d78
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.
+ 0.0__b77a5c561934e089\mscorlib.dll)
+
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b73125c 4001d66 4 System.Int32 0 instance 2 m_flags
+
+ Here is another example of an unrestricted PermissionSet, one that contains
+ several permissions. The numbers in parentheses before each Permission object
+ represents the index of that Permission in the PermissionSet.
+
+ 0:003> !DumpPermissionSet 01467bd8
+ PermissionSet object: 01467bd8
+ Unrestricted: FALSE
+ [1] 01467e90
+ Name: System.Security.Permissions.FileDialogPermission
+ MethodTable: 5b73023c
+ EEClass: 5b7dfb18
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b730190 4001cc2 4 System.Int32 0 instance 1 access
+ [4] 014682a8
+ Name: System.Security.Permissions.ReflectionPermission
+ MethodTable: 5b731308
+ EEClass: 5b7e0d78
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b73125c 4001d66 4 System.Int32 0 instance 0 m_flags
+ [17] 0146c060
+ Name: System.Diagnostics.EventLogPermission
+ MethodTable: 569841c4
+ EEClass: 56a03e5c
+ Size: 28(0x1c) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b6d65d4 4003078 4 System.Object[] 0 instance 0146c190 tagNames
+ 5b6c9ed8 4003079 8 System.Type 0 instance 0146c17c permissionAccessType
+ 5b6cd928 400307a 10 System.Boolean 0 instance 0 isUnrestricted
+ 5b6c45f8 400307b c ...ections.Hashtable 0 instance 0146c1a4 rootTable
+ 5b6c090c 4003077 bfc System.String 0 static 00000000 computerName
+ 56984434 40030e7 14 ...onEntryCollection 0 instance 00000000 innerCollection
+ [18] 0146ceb4
+ Name: System.Net.WebPermission
+ MethodTable: 5696dfc4
+ EEClass: 569e256c
+ Size: 20(0x14) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b6cd928 400238e c System.Boolean 0 instance 0 m_Unrestricted
+ 5b6cd928 400238f d System.Boolean 0 instance 0 m_UnrestrictedConnect
+ 5b6cd928 4002390 e System.Boolean 0 instance 0 m_UnrestrictedAccept
+ 5b6c639c 4002391 4 ...ections.ArrayList 0 instance 0146cf3c m_connectList
+ 5b6c639c 4002392 8 ...ections.ArrayList 0 instance 0146cf54 m_acceptList
+ 569476f8 4002393 8a4 ...Expressions.Regex 0 static 00000000 s_MatchAllRegex
+ [19] 0146a5fc
+ Name: System.Net.DnsPermission
+ MethodTable: 56966408
+ EEClass: 569d3c08
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b6cd928 4001d2c 4 System.Boolean 0 instance 1 m_noRestriction
+ [20] 0146d8ec
+ Name: System.Web.AspNetHostingPermission
+ MethodTable: 569831bc
+ EEClass: 56a02ccc
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 56983090 4003074 4 System.Int32 0 instance 600 _level
+ [21] 0146e394
+ Name: System.Net.NetworkInformation.NetworkInformationPermission
+ MethodTable: 5697ac70
+ EEClass: 569f7104
+ Size: 16(0x10) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5697ab38 4002c34 4 System.Int32 0 instance 0 access
+ 5b6cd928 4002c35 8 System.Boolean 0 instance 0 unrestricted
+
+
+ The abbreviation !dps can be used for brevity.
+
+ \\
+*/
+DECLARE_API(DumpPermissionSet)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_Object = NULL;
+
+ CMDValue arg[] =
+ {
+ {&p_Object, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg!=1)
+ {
+ ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n");
+ return Status;
+ }
+
+
+ return PrintPermissionSet(p_Object);
+}
+
+#endif // _DEBUG
+
+void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
+void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
+
+#endif // FEATURE_PAL
+
+void DisplayInvalidStructuresMessage()
+{
+ ExtOut("The garbage collector data structures are not in a valid state for traversal.\n");
+ ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n");
+ ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n");
+ ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n");
+ ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n");
+ ExtOut("consistency errors.\n");
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps GC heap size. *
+* *
+\**********************************************************************/
+DECLARE_API(EEHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ BOOL showgc = FALSE;
+ BOOL showloader = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-gc", &showgc, COBOOL, FALSE},
+ {"-loader", &showloader, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (showloader || !showgc)
+ {
+ // Loader heap.
+ DWORD_PTR allHeapSize = 0;
+ DWORD_PTR wasted = 0;
+ DacpAppDomainStoreData adsData;
+ if ((Status=adsData.Request(g_sos))!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain information\n");
+ return Status;
+ }
+
+ // The first one is the system domain.
+ ExtOut("Loader Heap:\n");
+ IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
+ IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
+
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
+ {
+ ExtOut("Unable to get the array of all AppDomains.\n");
+ return Status;
+ }
+
+ for (int n=0;n<adsData.DomainCount;n++)
+ {
+ if (IsInterrupt())
+ break;
+
+ char domain[16];
+ sprintf_s(domain, _countof(domain), "Domain %d", n+1);
+
+ IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
+
+ }
+
+ // Jit code heap
+ ExtOut("--------------------------------------\n");
+ ExtOut("Jit code heap:\n");
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut("<no information>\n");
+ }
+ else
+ {
+ allHeapSize += JitHeapInfo();
+ }
+
+
+ // Module Data
+ {
+ int numModule;
+ ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n");
+ }
+ else
+ {
+ // Module Thunk Heaps
+ ExtOut("--------------------------------------\n");
+ ExtOut("Module Thunk heaps:\n");
+ allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
+
+ // Module Lookup Table Heaps
+ ExtOut("--------------------------------------\n");
+ ExtOut("Module Lookup Table heaps:\n");
+ allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
+ }
+ }
+
+ ExtOut("--------------------------------------\n");
+ ExtOut("Total LoaderHeap size: ");
+ PrintHeapSize(allHeapSize, wasted);
+ ExtOut("=======================================\n");
+ }
+
+ if (showgc || !showloader)
+ {
+ // GC Heap
+ DWORD dwNHeaps = 1;
+
+ if (!GetGcStructuresValid())
+ {
+ DisplayInvalidStructuresMessage();
+ }
+
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return Status;
+ }
+
+ if (gcheap.bServerMode)
+ {
+ dwNHeaps = gcheap.HeapCount;
+ }
+
+ ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
+ DWORD_PTR totalSize = 0;
+ if (!gcheap.bServerMode)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ GCHeapInfo (heapDetails, totalSize);
+ ExtOut("Total Size: ");
+ PrintHeapSize(totalSize, 0);
+ }
+ else
+ {
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ DWORD n;
+ for (n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+ ExtOut("------------------------------\n");
+ ExtOut("Heap %d (%p)\n", n, SOS_PTR(heapAddrs[n]));
+ DWORD_PTR heapSize = 0;
+ GCHeapInfo (heapDetails, heapSize);
+ totalSize += heapSize;
+ ExtOut("Heap Size: " WIN86_8SPACES);
+ PrintHeapSize(heapSize, 0);
+ }
+ }
+ ExtOut("------------------------------\n");
+ ExtOut("GC Heap Size: " WIN86_8SPACES);
+ PrintHeapSize(totalSize, 0);
+ }
+ return Status;
+}
+
+void PrintGCStat(HeapStat *inStat, const char* label=NULL)
+{
+ if (inStat)
+ {
+ bool sorted = false;
+ try
+ {
+ inStat->Sort();
+ sorted = true;
+ inStat->Print(label);
+ }
+ catch(...)
+ {
+ ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort");
+ }
+
+ inStat->Delete();
+ }
+}
+
+#ifndef FEATURE_PAL
+
+DECLARE_API(TraverseHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bXmlFormat = FALSE;
+ BOOL bVerify = FALSE;
+ StringHolder Filename;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-xml", &bXmlFormat, COBOOL, FALSE},
+ {"-verify", &bVerify, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&Filename.data, COSTRING},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (nArg != 1)
+ {
+ ExtOut("usage: HeapTraverse [-xml] filename\n");
+ return Status;
+ }
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ FILE* file = NULL;
+ if (fopen_s(&file, Filename.data, "w") != 0) {
+ ExtOut("Unable to open file\n");
+ return Status;
+ }
+
+ if (!bVerify)
+ ExtOut("Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option\n");
+
+ HeapTraverser traverser(bVerify != FALSE);
+
+ ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data);
+ ExtOut("Gathering types...\n");
+
+ // TODO: there may be a canonical list of methodtables in the runtime that we can
+ // traverse instead of exploring the gc heap for that list. We could then simplify the
+ // tree structure to a sorted list of methodtables, and the index is the ID.
+
+ // TODO: "Traversing object members" code should be generalized and shared between
+ // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse.
+
+ if (!traverser.Initialize())
+ {
+ ExtOut("Error initializing heap traversal\n");
+ fclose(file);
+ return Status;
+ }
+
+ if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER))
+ {
+ ExtOut("Unable to write heap report\n");
+ fclose(file);
+ return Status;
+ }
+
+ fclose(file);
+ ExtOut("\nfile %s saved\n", Filename.data);
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+struct PrintRuntimeTypeArgs
+{
+ DWORD_PTR mtOfRuntimeType;
+ int handleFieldOffset;
+ DacpAppDomainStoreData adstore;
+};
+
+void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
+{
+ PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
+
+ if (pArgs->mtOfRuntimeType == NULL)
+ {
+ NameForMT_s(methodTable, g_mdName, mdNameLen);
+
+ if (_wcscmp(g_mdName, W("System.RuntimeType")) == 0)
+ {
+ pArgs->mtOfRuntimeType = methodTable;
+ pArgs->handleFieldOffset = GetObjFieldOffset(objAddr, methodTable, W("m_handle"));
+ if (pArgs->handleFieldOffset <= 0)
+ ExtOut("Error getting System.RuntimeType.m_handle offset\n");
+
+ pArgs->adstore.Request(g_sos);
+ }
+ }
+
+ if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
+ {
+ // Get the method table and display the information.
+ DWORD_PTR mtPtr;
+ if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
+ {
+ DMLOut(DMLObject(objAddr));
+
+ CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
+ if (appDomain != NULL)
+ {
+ if (appDomain == pArgs->adstore.sharedDomain)
+ ExtOut(" %" POINTERSIZE "s", "Shared");
+
+ else if (appDomain == pArgs->adstore.systemDomain)
+ ExtOut(" %" POINTERSIZE "s", "System");
+ else
+ DMLOut(" %s", DMLDomain(appDomain));
+ }
+ else
+ {
+ ExtOut(" %" POINTERSIZE "s", "?");
+ }
+
+ NameForMT_s(mtPtr, g_mdName, mdNameLen);
+ DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
+ }
+ }
+}
+
+
+DECLARE_API(DumpRuntimeTypes)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ return Status;
+
+ EnableDMLHolder dmlHolder(dml);
+
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name \n",
+ "Address", "Domain", "MT");
+ ExtOut("------------------------------------------------------------------------------\n");
+
+ PrintRuntimeTypeArgs pargs;
+ ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
+
+ GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
+ return Status;
+}
+
+#define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512)
+namespace sos
+{
+ class FragmentationBlock
+ {
+ public:
+ FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt)
+ : mAddress(addr), mSize(size), mNext(next), mNextMT(mt)
+ {
+ }
+
+ inline TADDR GetAddress() const
+ {
+ return mAddress;
+ }
+ inline size_t GetSize() const
+ {
+ return mSize;
+ }
+
+ inline TADDR GetNextObject() const
+ {
+ return mNext;
+ }
+
+ inline TADDR GetNextMT() const
+ {
+ return mNextMT;
+ }
+
+ private:
+ TADDR mAddress;
+ size_t mSize;
+ TADDR mNext;
+ TADDR mNextMT;
+ };
+}
+
+class DumpHeapImpl
+{
+public:
+ DumpHeapImpl(PCSTR args)
+ : mStart(0), mStop(0), mMT(0), mMinSize(0), mMaxSize(~0),
+ mStat(FALSE), mStrings(FALSE), mVerify(FALSE),
+ mThinlock(FALSE), mShort(FALSE), mDML(FALSE),
+ mLive(FALSE), mDead(FALSE), mType(NULL)
+ {
+ ArrayHolder<char> type = NULL;
+
+ TADDR minTemp = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-mt", &mMT, COHEX, TRUE}, // dump objects with a given MethodTable
+ {"-type", &type, COSTRING, TRUE}, // list objects of specified type
+ {"-stat", &mStat, COBOOL, FALSE}, // dump a summary of types and the number of instances of each
+ {"-strings", &mStrings, COBOOL, FALSE}, // dump a summary of string objects
+ {"-verify", &mVerify, COBOOL, FALSE}, // verify heap objects (!heapverify)
+ {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks
+ {"-short", &mShort, COBOOL, FALSE}, // list only addresses
+ {"-min", &mMinSize, COHEX, TRUE}, // min size of objects to display
+ {"-max", &mMaxSize, COHEX, TRUE}, // max size of objects to display
+ {"-live", &mLive, COHEX, FALSE}, // only print live objects
+ {"-dead", &mDead, COHEX, FALSE}, // only print dead objects
+#ifndef FEATURE_PAL
+ {"/d", &mDML, COBOOL, FALSE}, // Debugger Markup Language
+#endif
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&mStart, COHEX},
+ {&mStop, COHEX}
+ };
+
+ size_t nArgs = 0;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
+ sos::Throw<sos::Exception>("Failed to parse command line arguments.");
+
+ if (mStart == 0)
+ mStart = minTemp;
+
+ if (mStop == 0)
+ mStop = sos::GCHeap::HeapEnd;
+
+ if (type && mMT)
+ {
+ sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
+ }
+
+ if (mLive && mDead)
+ {
+ sos::Throw<sos::Exception>("Cannot specify both -live and -dead.");
+ }
+
+ if (mMinSize > mMaxSize)
+ {
+ sos::Throw<sos::Exception>("wrong argument");
+ }
+
+ // If the user gave us a type, convert it to unicode and clean up "type".
+ if (type && !mStrings)
+ {
+ size_t iLen = strlen(type) + 1;
+ mType = new WCHAR[iLen];
+ MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen);
+ }
+ }
+
+ ~DumpHeapImpl()
+ {
+ if (mType)
+ delete [] mType;
+ }
+
+ void Run()
+ {
+ // enable Debugger Markup Language
+ EnableDMLHolder dmlholder(mDML);
+ sos::GCHeap gcheap;
+
+ if (!gcheap.AreGCStructuresValid())
+ DisplayInvalidStructuresMessage();
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n");
+ ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n");
+ }
+
+#ifndef FEATURE_PAL
+ if (mLive || mDead)
+ {
+ GCRootImpl gcroot;
+ mLiveness = gcroot.GetLiveObjects();
+ }
+#endif
+
+ // Some of the "specialty" versions of DumpHeap have slightly
+ // different implementations than the standard version of DumpHeap.
+ // We seperate them out to not clutter the standard DumpHeap function.
+ if (mShort)
+ DumpHeapShort(gcheap);
+ else if (mThinlock)
+ DumpHeapThinlock(gcheap);
+ else if (mStrings)
+ DumpHeapStrings(gcheap);
+ else
+ DumpHeap(gcheap);
+
+ if (mVerify)
+ ValidateSyncTable(gcheap);
+ }
+
+ static bool ValidateSyncTable(sos::GCHeap &gcheap)
+ {
+ bool succeeded = true;
+ for (sos::SyncBlkIterator itr; itr; ++itr)
+ {
+ sos::CheckInterrupt();
+
+ if (!itr->IsFree())
+ {
+ if (!sos::IsObject(itr->GetObject(), true))
+ {
+ ExtOut("SyncBlock %d corrupted, points to invalid object %p\n",
+ itr->GetIndex(), SOS_PTR(itr->GetObject()));
+ succeeded = false;
+ }
+ else
+ {
+ // Does the object header point to this syncblock index?
+ sos::Object obj = itr->GetObject();
+ ULONG header = 0;
+
+ if (!obj.TryGetHeader(header))
+ {
+ ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n",
+ SOS_PTR(itr->GetObject()), itr->GetIndex());
+ succeeded = false;
+ }
+ else
+ {
+ bool valid = false;
+ if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0)
+ {
+ ULONG index = header & MASK_SYNCBLOCKINDEX;
+ valid = (ULONG)itr->GetIndex() == index;
+ }
+
+ if (!valid)
+ {
+ ExtOut("Object header for %p should have a SyncBlock index of %d.\n",
+ SOS_PTR(itr->GetObject()), itr->GetIndex());
+ succeeded = false;
+ }
+ }
+ }
+ }
+ }
+
+ return succeeded;
+ }
+private:
+ DumpHeapImpl(const DumpHeapImpl &);
+
+ bool Verify(const sos::ObjectIterator &itr)
+ {
+ if (mVerify)
+ {
+ char buffer[1024];
+ if (!itr.Verify(buffer, _countof(buffer)))
+ {
+ ExtOut(buffer);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool IsCorrectType(const sos::Object &obj)
+ {
+ if (mMT != NULL)
+ return mMT == obj.GetMT();
+
+ if (mType != NULL)
+ {
+ WString name = obj.GetTypeName();
+ return _wcsstr(name.c_str(), mType) != NULL;
+ }
+
+ return true;
+ }
+
+ bool IsCorrectSize(const sos::Object &obj)
+ {
+ size_t size = obj.GetSize();
+ return size >= mMinSize && size <= mMaxSize;
+ }
+
+ bool IsCorrectLiveness(const sos::Object &obj)
+ {
+#ifndef FEATURE_PAL
+ if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end())
+ return false;
+
+ if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree()))
+ return false;
+#endif
+ return true;
+ }
+
+
+
+ inline void PrintHeader()
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size");
+ }
+
+ void DumpHeap(sos::GCHeap &gcheap)
+ {
+ HeapStat stats;
+
+ // For heap fragmentation tracking.
+ TADDR lastFreeObj = NULL;
+ size_t lastFreeSize = 0;
+
+ if (!mStat)
+ PrintHeader();
+
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (!Verify(itr))
+ return;
+
+ bool onLOH = itr.IsCurrObjectOnLOH();
+
+ // Check for free objects to report fragmentation
+ if (lastFreeObj != NULL)
+ ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT());
+
+ if (!onLOH && itr->IsFree())
+ {
+ lastFreeObj = *itr;
+ lastFreeSize = itr->GetSize();
+ }
+ else
+ {
+ lastFreeObj = NULL;
+ }
+
+ if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
+ {
+ stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize());
+ if (!mStat)
+ DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(),
+ itr->IsFree() ? " Free":" ");
+ }
+ }
+
+ if (!mStat)
+ ExtOut("\n");
+
+ stats.Sort();
+ stats.Print();
+
+ PrintFragmentationReport();
+ }
+
+ struct StringSetEntry
+ {
+ StringSetEntry() : count(0), size(0)
+ {
+ str[0] = 0;
+ }
+
+ StringSetEntry(__in_ecount(64) WCHAR tmp[64], size_t _size)
+ : count(1), size(_size)
+ {
+ memcpy(str, tmp, sizeof(str));
+ }
+
+ void Add(size_t _size) const
+ {
+ count++;
+ size += _size;
+ }
+
+ mutable size_t count;
+ mutable size_t size;
+ WCHAR str[64];
+
+ bool operator<(const StringSetEntry &rhs) const
+ {
+ return _wcscmp(str, rhs.str) < 0;
+ }
+ };
+
+
+ static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2)
+ {
+ return a1.size < a2.size;
+ }
+
+ void DumpHeapStrings(sos::GCHeap &gcheap)
+ {
+#ifdef FEATURE_PAL
+ ExtOut("Not implemented.\n");
+#else
+ const int offset = sos::Object::GetStringDataOffset();
+ typedef std::set<StringSetEntry> Set;
+ Set set; // A set keyed off of the string's text
+
+ StringSetEntry tmp; // Temp string used to keep track of the set
+ ULONG fetched = 0;
+
+ TableOutput out(3, POINTERSIZE_HEX, AlignRight);
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
+ {
+ CLRDATA_ADDRESS addr = itr->GetAddress();
+ size_t size = itr->GetSize();
+
+ if (!mStat)
+ out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size));
+
+ // Don't bother calculating the size of the string, just read the full 64 characters of the buffer. The null
+ // terminator we read will terminate the string.
+ HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(_countof(tmp.str)-1), &fetched);
+ if (SUCCEEDED(hr))
+ {
+ // Ensure we null terminate the string. Note that this will not overrun the buffer as we only
+ // wrote a max of 63 characters into the 64 character buffer.
+ tmp.str[fetched/sizeof(WCHAR)] = 0;
+ Set::iterator sitr = set.find(tmp);
+ if (sitr == set.end())
+ {
+ tmp.size = size;
+ tmp.count = 1;
+ set.insert(tmp);
+ }
+ else
+ {
+ sitr->Add(size);
+ }
+ }
+ }
+ }
+
+ ExtOut("\n");
+
+ // Now flatten the set into a vector. This is much faster than keeping two sets, or using a multimap.
+ typedef std::vector<StringSetEntry> Vect;
+ Vect v(set.begin(), set.end());
+ std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare);
+
+ // Now print out the data. The call to Flatten ensures that we don't print newlines to break up the
+ // output in strange ways.
+ for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr)
+ {
+ if (IsInterrupt())
+ break;
+
+ Flatten(vitr->str, (unsigned int)_wcslen(vitr->str));
+ out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str);
+ }
+#endif // FEATURE_PAL
+ }
+
+ void DumpHeapShort(sos::GCHeap &gcheap)
+ {
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (!Verify(itr))
+ return;
+
+ if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
+ DMLOut("%s\n", DMLObject(itr->GetAddress()));
+ }
+ }
+
+ void DumpHeapThinlock(sos::GCHeap &gcheap)
+ {
+ int count = 0;
+
+ PrintHeader();
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (!Verify(itr))
+ return;
+
+ sos::ThinLockInfo lockInfo;
+ if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo))
+ {
+ DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
+ ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId,
+ SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
+
+ count++;
+ }
+ }
+
+ ExtOut("Found %d objects.\n", count);
+ }
+
+private:
+ TADDR mStart,
+ mStop,
+ mMT,
+ mMinSize,
+ mMaxSize;
+
+ BOOL mStat,
+ mStrings,
+ mVerify,
+ mThinlock,
+ mShort,
+ mDML,
+ mLive,
+ mDead;
+
+
+ WCHAR *mType;
+
+private:
+#if !defined(FEATURE_PAL)
+ // Windows only
+ std::unordered_set<TADDR> mLiveness;
+ typedef std::list<sos::FragmentationBlock> FragmentationList;
+ FragmentationList mFrag;
+
+ void InitFragmentationList()
+ {
+ mFrag.clear();
+ }
+
+ void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt)
+ {
+ if (size >= MIN_FRAGMENTATIONBLOCK_BYTES)
+ mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt));
+ }
+
+ void PrintFragmentationReport()
+ {
+ if (mFrag.size() > 0)
+ {
+ ExtOut("Fragmented blocks larger than 0.5 MB:\n");
+ ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by");
+
+ for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr)
+ {
+ sos::MethodTable mt = itr->GetNextMT();
+ ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n",
+ SOS_PTR(itr->GetAddress()),
+ ((double)itr->GetSize()) / 1024.0 / 1024.0,
+ SOS_PTR(itr->GetNextObject()),
+ mt.GetName());
+ }
+ }
+ }
+#else
+ void InitFragmentationList() {}
+ void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {}
+ void PrintFragmentationReport() {}
+#endif
+};
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps all objects on GC heap. It also displays *
+* statistics of objects. If GC heap is corrupted, it will stop at
+* the bad place. (May not work if GC is in progress.) *
+* *
+\**********************************************************************/
+DECLARE_API(DumpHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return E_FAIL;
+ }
+
+ try
+ {
+ DumpHeapImpl dumpHeap(args);
+ dumpHeap.Run();
+
+ return S_OK;
+ }
+ catch(const sos::Exception &e)
+ {
+ ExtOut("%s\n", e.what());
+ return E_FAIL;
+ }
+}
+
+DECLARE_API(VerifyHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return E_FAIL;
+ }
+
+ try
+ {
+ bool succeeded = true;
+ char buffer[1024];
+ sos::GCHeap gcheap;
+ sos::ObjectIterator itr = gcheap.WalkHeap();
+
+ while (itr)
+ {
+ if (itr.Verify(buffer, _countof(buffer)))
+ {
+ ++itr;
+ }
+ else
+ {
+ succeeded = false;
+ ExtOut(buffer);
+ itr.MoveToNextObjectCarefully();
+ }
+ }
+
+ if (!DumpHeapImpl::ValidateSyncTable(gcheap))
+ succeeded = false;
+
+ if (succeeded)
+ ExtOut("No heap corruption detected.\n");
+
+ return S_OK;
+ }
+ catch(const sos::Exception &e)
+ {
+ ExtOut("%s\n", e.what());
+ return E_FAIL;
+ }
+}
+
+#ifndef FEATURE_PAL
+
+enum failure_get_memory
+{
+ fgm_no_failure = 0,
+ fgm_reserve_segment = 1,
+ fgm_commit_segment_beg = 2,
+ fgm_commit_eph_segment = 3,
+ fgm_grow_table = 4,
+ fgm_commit_table = 5
+};
+
+enum oom_reason
+{
+ oom_no_failure = 0,
+ oom_budget = 1,
+ oom_cant_commit = 2,
+ oom_cant_reserve = 3,
+ oom_loh = 4,
+ oom_low_mem = 5,
+ oom_unproductive_full_gc = 6
+};
+
+static const char *const str_oom[] =
+{
+ "There was no managed OOM due to allocations on the GC heap", // oom_no_failure
+ "This is likely to be a bug in GC", // oom_budget
+ "Didn't have enough memory to commit", // oom_cant_commit
+ "This is likely to be a bug in GC", // oom_cant_reserve
+ "Didn't have enough memory to allocate an LOH segment", // oom_loh
+ "Low on memory during GC", // oom_low_mem
+ "Could not do a full GC" // oom_unproductive_full_gc
+};
+
+static const char *const str_fgm[] =
+{
+ "There was no failure to allocate memory", // fgm_no_failure
+ "Failed to reserve memory", // fgm_reserve_segment
+ "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg
+ "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment
+ "Didn't have enough memory to grow the internal GC datastructures", // fgm_grow_table
+ "Didn't have enough memory to commit the internal GC datastructures", // fgm_commit_table
+};
+
+void PrintOOMInfo(DacpOomData* oomData)
+{
+ ExtOut("Managed OOM occurred after GC #%d (Requested to allocate %d bytes)\n",
+ oomData->gc_index, oomData->alloc_size);
+
+ if ((oomData->reason == oom_budget) ||
+ (oomData->reason == oom_cant_reserve))
+ {
+ // TODO: This message needs to be updated with more precious info.
+ ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]);
+ }
+ else
+ {
+ ExtOut("Reason: %s\n", str_oom[oomData->reason]);
+ }
+
+ // Now print out the more detailed memory info if any.
+ if (oomData->fgm != fgm_no_failure)
+ {
+ ExtOut("Detail: %s: %s (%d bytes)",
+ (oomData->loh_p ? "LOH" : "SOH"),
+ str_fgm[oomData->fgm],
+ oomData->size);
+
+ if ((oomData->fgm == fgm_commit_segment_beg) ||
+ (oomData->fgm == fgm_commit_eph_segment) ||
+ (oomData->fgm == fgm_grow_table) ||
+ (oomData->fgm == fgm_commit_table))
+ {
+ // If it's a commit error (fgm_grow_table can indicate a reserve
+ // or a commit error since we make one VirtualAlloc call to
+ // reserve and commit), we indicate the available commit
+ // space if we recorded it.
+ if (oomData->available_pagefile_mb)
+ {
+ ExtOut(" - on GC entry available commit space was %d MB",
+ oomData->available_pagefile_mb);
+ }
+ }
+
+ ExtOut("\n");
+ }
+}
+
+DECLARE_API(AnalyzeOOM)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+#ifndef FEATURE_PAL
+
+ if (!InitializeHeapData ())
+ {
+ ExtOut("GC Heap not initialized yet.\n");
+ return S_OK;
+ }
+
+ BOOL bHasManagedOOM = FALSE;
+ DacpOomData oomData;
+ memset (&oomData, 0, sizeof(oomData));
+ if (!IsServerBuild())
+ {
+ if (oomData.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting OOM data\n");
+ return E_FAIL;
+ }
+ if (oomData.reason != oom_no_failure)
+ {
+ bHasManagedOOM = TRUE;
+ PrintOOMInfo(&oomData);
+ }
+ }
+ else
+ {
+ DWORD dwNHeaps = GetGcHeapCount();
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ if (oomData.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Heap %d: Error requesting OOM data\n", n);
+ return E_FAIL;
+ }
+ if (oomData.reason != oom_no_failure)
+ {
+ if (!bHasManagedOOM)
+ {
+ bHasManagedOOM = TRUE;
+ }
+ ExtOut("---------Heap %#-2d---------\n", n);
+ PrintOOMInfo(&oomData);
+ }
+ }
+ }
+
+ if (!bHasManagedOOM)
+ {
+ ExtOut("%s\n", str_oom[oomData.reason]);
+ }
+
+ return S_OK;
+#else
+ _ASSERTE(false);
+ return E_FAIL;
+#endif // FEATURE_PAL
+}
+
+DECLARE_API(VerifyObj)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ TADDR taddrObj = 0;
+ TADDR taddrMT;
+ size_t objSize;
+
+ BOOL bValid = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taddrObj, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ BOOL bContainsPointers;
+
+ if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
+ !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
+ {
+ ExtOut("object %#p does not have valid method table\n", SOS_PTR(taddrObj));
+ goto Exit;
+ }
+
+ // we need to build g_snapshot as it is later used in GetGeneration
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ goto Exit;
+ }
+ DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
+ bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
+
+Exit:
+ if (bValid)
+ {
+ ExtOut("object %#p is a valid object\n", SOS_PTR(taddrObj));
+ }
+
+ return Status;
+}
+
+void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size)
+{
+ sos::Object obj(currentObj, pMT);
+ DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName());
+}
+
+DECLARE_API(ListNearObj)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+#if !defined(FEATURE_PAL)
+
+ TADDR taddrArg = 0;
+ TADDR taddrObj = 0;
+ // we may want to provide a more exact version of searching for the
+ // previous object in the heap, using the brick table, instead of
+ // looking for what may be valid method tables...
+ //BOOL bExact;
+ //CMDOption option[] =
+ //{
+ // // name, vptr, type, hasValue
+ // {"-exact", &bExact, COBOOL, FALSE}
+ //};
+
+ BOOL dml = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&taddrArg, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
+ {
+ ExtOut("Usage: !ListNearObj <obj_address>\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ taddrObj = Align(taddrArg);
+
+ DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg);
+ if (heap == NULL)
+ {
+ ExtOut("Address %p does not lie in the managed heap\n", SOS_PTR(taddrObj));
+ return Status;
+ }
+
+ TADDR_SEGINFO trngSeg = {0, 0, 0};
+ TADDR_RANGE allocCtx = {0, 0};
+ BOOL bLarge;
+ int gen;
+ if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge))
+ {
+ ExtOut("Failed to find the segment of the managed heap where the object %p resides\n",
+ SOS_PTR(taddrObj));
+ return Status;
+ }
+
+ TADDR objMT = NULL;
+ size_t objSize = 0;
+ BOOL bObj = FALSE;
+ TADDR taddrCur;
+ TADDR curMT = 0;
+ size_t curSize = 0;
+ BOOL bCur = FALSE;
+ TADDR taddrNxt;
+ TADDR nxtMT = 0;
+ size_t nxtSize = 0;
+ BOOL bNxt = FALSE;
+ BOOL bContainsPointers;
+
+ std::vector<TADDR> candidate;
+ candidate.reserve(10);
+
+ // since we'll be reading back I'll prime the read cache to a buffer before the current address
+ MOVE(taddrCur, _max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE));
+
+ // ===== Look for a good candidate preceeding taddrObj
+
+ for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR))
+ {
+ // currently we don't pay attention to allocation contexts. if this
+ // proves to be an issue we need to reconsider the code below
+ if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) &&
+ GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers))
+ {
+ // remember this as one of the possible "good" objects preceeding taddrObj
+ candidate.push_back(taddrCur);
+
+ std::vector<TADDR>::iterator it =
+ std::find(candidate.begin(), candidate.end(), taddrCur+curSize);
+ if (it != candidate.end())
+ {
+ // We found a chain of two objects preceeding taddrObj. We'll
+ // trust this is a good indication that the two objects are valid.
+ // What is not valid is possibly the object following the second
+ // one...
+ taddrCur = *it;
+ GetMTOfObject(taddrCur, &curMT);
+ GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
+ bCur = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!bCur && !candidate.empty())
+ {
+ // pick the closest object to taddrObj
+ taddrCur = *(candidate.begin());
+ GetMTOfObject(taddrCur, &curMT);
+ GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
+ // we have a candidate, even if not confirmed
+ bCur = TRUE;
+ }
+
+ taddrNxt = taddrObj;
+ if (taddrArg == taddrObj)
+ {
+ taddrNxt += sizeof(TADDR);
+ }
+
+ // ===== Now look at taddrObj
+ if (taddrObj == taddrArg)
+ {
+ // only look at taddrObj if it's the same as what user passed in, meaning it's aligned.
+ if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) &&
+ GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
+ {
+ bObj = TRUE;
+ taddrNxt = taddrObj+objSize;
+ }
+ }
+
+ if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end)
+ {
+ if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) &&
+ GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
+ {
+ taddrNxt = taddrCur+curSize;
+ }
+ }
+
+ // ===== And finally move on to elements following taddrObj
+
+ for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR))
+ {
+ if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) &&
+ GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers))
+ {
+ bNxt = TRUE;
+ break;
+ }
+ }
+
+ if (bCur)
+ LNODisplayOutput(W("Before: "), curMT, taddrCur, curSize);
+ else
+ ExtOut("Before: couldn't find any object between %#p and %#p\n",
+ SOS_PTR(trngSeg.start), SOS_PTR(taddrArg));
+
+ if (bObj)
+ LNODisplayOutput(W("Current:"), objMT, taddrObj, objSize);
+
+ if (bNxt)
+ LNODisplayOutput(W("After: "), nxtMT, taddrNxt, nxtSize);
+ else
+ ExtOut("After: couldn't find any object between %#p and %#p\n",
+ SOS_PTR(taddrArg), SOS_PTR(trngSeg.end));
+
+ if (bCur && bNxt &&
+ (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt)))
+ {
+ ExtOut("Heap local consistency confirmed.\n");
+ }
+ else
+ {
+ ExtOut("Heap local consistency not confirmed.\n");
+ }
+
+ return Status;
+
+#else
+
+ _ASSERTE(false);
+ return E_FAIL;
+
+#endif // FEATURE_PAL
+}
+
+
+DECLARE_API(GCHeapStat)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+#ifndef FEATURE_PAL
+
+ BOOL bIncUnreachable = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] = {
+ // name, vptr, type, hasValue
+ {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE},
+ {"-iu", &bIncUnreachable, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE}
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH");
+
+ if (!IsServerBuild())
+ {
+ float tempf;
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting gc heap details\n");
+ return Status;
+ }
+
+ HeapUsageStat hpUsage;
+ if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage))
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0,
+ hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd,
+ hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd);
+ ExtOut("\nFree space: Percentage\n");
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
+ hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed,
+ hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed);
+ tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) /
+ (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd)));
+ if (bIncUnreachable)
+ {
+ ExtOut("\nUnrooted objects: Percentage\n");
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
+ hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted,
+ hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted);
+ tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) /
+ (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd)));
+ }
+ }
+ }
+ else
+ {
+ float tempf;
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting GC Heap data\n");
+ return Status;
+ }
+
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = gcheap.HeapCount;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtErr("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtErr("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps];
+ if (hpUsage == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ // aggregate stats accross heaps / generation
+ GenUsageStat genUsageStat[4] = {0, 0, 0, 0};
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtErr("Error requesting gc heap details\n");
+ return Status;
+ }
+
+ if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n]))
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ genUsageStat[i].allocd += hpUsage[n].genUsage[i].allocd;
+ genUsageStat[i].freed += hpUsage[n].genUsage[i].freed;
+ if (bIncUnreachable)
+ {
+ genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted;
+ }
+ }
+ }
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n,
+ hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd,
+ hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd);
+ }
+ ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
+ genUsageStat[0].allocd, genUsageStat[1].allocd,
+ genUsageStat[2].allocd, genUsageStat[3].allocd);
+
+ ExtOut("\nFree space: Percentage\n");
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
+ hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed,
+ hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed);
+
+ tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) /
+ (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd))
+ );
+ }
+ ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
+ genUsageStat[0].freed, genUsageStat[1].freed,
+ genUsageStat[2].freed, genUsageStat[3].freed);
+
+ if (bIncUnreachable)
+ {
+ ExtOut("\nUnrooted objects: Percentage\n");
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
+ hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted,
+ hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted);
+
+ tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) /
+ (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd)));
+ }
+ ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
+ genUsageStat[0].unrooted, genUsageStat[1].unrooted,
+ genUsageStat[2].unrooted, genUsageStat[3].unrooted);
+ }
+
+ }
+
+ return Status;
+
+#else
+
+ _ASSERTE(false);
+ return E_FAIL;
+
+#endif // FEATURE_PAL
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps what is in the syncblock cache. By default *
+* it dumps all active syncblocks. Using -all to dump all syncblocks
+* *
+\**********************************************************************/
+DECLARE_API(SyncBlk)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bDumpAll = FALSE;
+ size_t nbAsked = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-all", &bDumpAll, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&nbAsked, COSIZE_T}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DacpSyncBlockData syncBlockData;
+ if (syncBlockData.Request(g_sos,1) != S_OK)
+ {
+ ExtOut("Error requesting SyncBlk data\n");
+ return Status;
+ }
+
+ DWORD dwCount = syncBlockData.SyncBlockCount;
+
+ ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES " SyncBlock Owner\n");
+ ULONG freeCount = 0;
+ ULONG CCWCount = 0;
+ ULONG RCWCount = 0;
+ ULONG CFCount = 0;
+ for (DWORD nb = 1; nb <= dwCount; nb++)
+ {
+ if (IsInterrupt())
+ return Status;
+
+ if (nbAsked && nb != nbAsked)
+ {
+ continue;
+ }
+
+ if (syncBlockData.Request(g_sos,nb) != S_OK)
+ {
+ ExtOut("SyncBlock %d is invalid%s\n", nb,
+ (nb != nbAsked) ? ", continuing..." : "");
+ continue;
+ }
+
+ BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree));
+
+ if (bPrint)
+ {
+ ExtOut("%5d ", nb);
+ if (!syncBlockData.bFree || nb != nbAsked)
+ {
+ ExtOut("%p ", syncBlockData.SyncBlockPointer);
+ ExtOut("%11d ", syncBlockData.MonitorHeld);
+ ExtOut("%9d ", syncBlockData.Recursion);
+ ExtOut("%p ", syncBlockData.HoldingThread);
+
+ if (syncBlockData.HoldingThread == ~0ul)
+ {
+ ExtOut(" orphaned ");
+ }
+ else if (syncBlockData.HoldingThread != NULL)
+ {
+ DacpThreadData Thread;
+ if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK)
+ {
+ ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread);
+ return Status;
+ }
+
+ DMLOut(DMLThreadID(Thread.osThreadId));
+ ULONG id;
+ if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
+ {
+ ExtOut("%4d ", id);
+ }
+ else
+ {
+ ExtOut(" XXX ");
+ }
+ }
+ else
+ {
+ ExtOut(" none ");
+ }
+
+ if (syncBlockData.bFree)
+ {
+ ExtOut(" %8d", 0); // TODO: do we need to print the free synctable list?
+ }
+ else
+ {
+ sos::Object obj = TO_TADDR(syncBlockData.Object);
+ DMLOut(" %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName());
+ }
+ }
+ }
+
+ if (syncBlockData.bFree)
+ {
+ freeCount ++;
+ if (bPrint) {
+ ExtOut(" Free");
+ }
+ }
+ else
+ {
+#ifdef FEATURE_COMINTEROP
+ if (syncBlockData.COMFlags) {
+ switch (syncBlockData.COMFlags) {
+ case SYNCBLOCKDATA_COMFLAGS_CCW:
+ CCWCount ++;
+ break;
+ case SYNCBLOCKDATA_COMFLAGS_RCW:
+ RCWCount ++;
+ break;
+ case SYNCBLOCKDATA_COMFLAGS_CF:
+ CFCount ++;
+ break;
+ }
+ }
+#endif // FEATURE_COMINTEROP
+ }
+
+ if (syncBlockData.MonitorHeld > 1)
+ {
+ // TODO: implement this
+ /*
+ ExtOut(" ");
+ DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext;
+ DWORD_PTR pNext = pHead;
+ Thread vThread;
+
+ while (1)
+ {
+ if (IsInterrupt())
+ return Status;
+ DWORD_PTR pWaitEventLink = pNext - offsetLinkSB;
+ WaitEventLink vWaitEventLink;
+ vWaitEventLink.Fill(pWaitEventLink);
+ if (!CallStatus) {
+ break;
+ }
+ DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread;
+ ExtOut("%x ", dwAddr);
+ vThread.Fill (dwAddr);
+ if (!CallStatus) {
+ break;
+ }
+ if (bPrint)
+ DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId));
+ pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext;
+ if (pNext == 0)
+ break;
+ }
+ */
+ }
+
+ if (bPrint)
+ ExtOut("\n");
+ }
+
+ ExtOut("-----------------------------\n");
+ ExtOut("Total %d\n", dwCount);
+ ExtOut("CCW %d\n", CCWCount);
+ ExtOut("RCW %d\n", RCWCount);
+ ExtOut("ComClassFactory %d\n", CFCount);
+ ExtOut("Free %d\n", freeCount);
+
+ return Status;
+}
+
+#ifdef FEATURE_COMINTEROP
+struct VisitRcwArgs
+{
+ BOOL bDetail;
+ UINT MTACount;
+ UINT STACount;
+ ULONG FTMCount;
+};
+
+void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token)
+{
+ VisitRcwArgs *pArgs = (VisitRcwArgs *) token;
+
+ if (pArgs->bDetail)
+ {
+ if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0)
+ {
+ // First time, print a header
+ ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n",
+ "RCW", "CONTEXT", "THREAD");
+ }
+ LPCSTR szThreadApartment;
+ if (bIsFreeThreaded)
+ {
+ szThreadApartment = "(FreeThreaded)";
+ pArgs->FTMCount++;
+ }
+ else if (Thread == NULL)
+ {
+ szThreadApartment = "(MTA)";
+ pArgs->MTACount++;
+ }
+ else
+ {
+ szThreadApartment = "(STA)";
+ pArgs->STACount++;
+ }
+
+ ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n",
+ SOS_PTR(RCW),
+ SOS_PTR(Context),
+ SOS_PTR(Thread),
+ szThreadApartment);
+ }
+}
+
+DECLARE_API(RCWCleanupList)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_CleanupList = GetExpression(args);
+
+ VisitRcwArgs travArgs;
+ ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
+ travArgs.bDetail = TRUE;
+
+ // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0
+ // (to print out an Invalid parameter message), but at the same time we need to allow an
+ // empty argument list which would result in p_CleanupList equaling 0.
+ if (p_CleanupList || strlen(args) == 0)
+ {
+ HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs);
+
+ if (SUCCEEDED(hr))
+ {
+ ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
+ ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
+ ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
+ }
+ else
+ {
+ ExtOut("An error occurred while traversing the cleanup list.\n");
+ }
+ }
+ else
+ {
+ ExtOut("Invalid parameter %s\n", args);
+ }
+
+ return Status;
+}
+#endif // FEATURE_COMINTEROP
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of the finalizer *
+* queue. *
+* *
+\**********************************************************************/
+DECLARE_API(FinalizeQueue)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bDetail = FALSE;
+ BOOL bAllReady = FALSE;
+ BOOL bShort = FALSE;
+ BOOL dml = FALSE;
+ TADDR taddrMT = 0;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-detail", &bDetail, COBOOL, FALSE},
+ {"-allReady", &bAllReady, COBOOL, FALSE},
+ {"-short", &bShort, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE},
+ {"-mt", &taddrMT, COHEX, TRUE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (!bShort)
+ {
+ DacpSyncBlockCleanupData dsbcd;
+ CLRDATA_ADDRESS sbCurrent = NULL;
+ ULONG cleanCount = 0;
+ while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
+ {
+ if (bDetail)
+ {
+ if (cleanCount == 0) // print first time only
+ {
+ ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "SyncBlock", "RCW", "CCW", "ComClassFactory");
+ }
+
+ ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n",
+ (ULONG64) dsbcd.SyncBlockPointer,
+ (ULONG64) dsbcd.blockRCW,
+ (ULONG64) dsbcd.blockCCW,
+ (ULONG64) dsbcd.blockClassFactory);
+ }
+
+ cleanCount++;
+ sbCurrent = dsbcd.nextSyncBlock;
+ if (sbCurrent == NULL)
+ {
+ break;
+ }
+ }
+
+ ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
+
+#ifdef FEATURE_COMINTEROP
+ VisitRcwArgs travArgs;
+ ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
+ travArgs.bDetail = bDetail;
+ g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
+ ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
+ ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
+ ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
+#endif // FEATURE_COMINTEROP
+
+// noRCW:
+ ExtOut("----------------------------------\n");
+ }
+
+ // GC Heap
+ DWORD dwNHeaps = GetGcHeapCount();
+
+ HeapStat hpStat;
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
+ }
+ else
+ {
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ ExtOut("------------------------------\n");
+ ExtOut("Heap %d\n", n);
+ GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
+ }
+ }
+
+ if (!bShort)
+ {
+ if (bAllReady)
+ {
+ PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
+ }
+ else
+ {
+ PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
+ }
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+enum {
+ // These are the values set in m_dwTransientFlags.
+ // Note that none of these flags survive a prejit save/restore.
+
+ M_CRST_NOTINITIALIZED = 0x00000001, // Used to prevent destruction of garbage m_crst
+ M_LOOKUPCRST_NOTINITIALIZED = 0x00000002,
+
+ SUPPORTS_UPDATEABLE_METHODS = 0x00000020,
+ CLASSES_FREED = 0x00000040,
+ HAS_PHONY_IL_RVAS = 0x00000080,
+ IS_EDIT_AND_CONTINUE = 0x00000200,
+};
+
+void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token)
+{
+ ULONG32 rid = (ULONG32)(size_t)token;
+ NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen);
+
+ DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName);
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a Module *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpModule)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ DWORD_PTR p_ModuleAddr = NULL;
+ BOOL bMethodTables = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-mt", &bMethodTables, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_ModuleAddr, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg != 1)
+ {
+ ExtOut("Usage: DumpModule [-mt] <Module Address>\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DacpModuleData module;
+ if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK)
+ {
+ ExtOut("Fail to fill Module %p\n", SOS_PTR(p_ModuleAddr));
+ return Status;
+ }
+
+ WCHAR FileName[MAX_LONGPATH];
+ FileNameForModule (&module, FileName);
+ ExtOut("Name: %S\n", FileName[0] ? FileName : W("Unknown Module"));
+
+ ExtOut("Attributes: ");
+ if (module.bIsPEFile)
+ ExtOut("PEFile ");
+ if (module.bIsReflection)
+ ExtOut("Reflection ");
+ if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS)
+ ExtOut("SupportsUpdateableMethods");
+ ExtOut("\n");
+
+ DMLOut("Assembly: %s\n", DMLAssembly(module.Assembly));
+
+ ExtOut("LoaderHeap: %p\n", SOS_PTR(module.pLookupTableHeap));
+ ExtOut("TypeDefToMethodTableMap: %p\n", SOS_PTR(module.TypeDefToMethodTableMap));
+ ExtOut("TypeRefToMethodTableMap: %p\n", SOS_PTR(module.TypeRefToMethodTableMap));
+ ExtOut("MethodDefToDescMap: %p\n", SOS_PTR(module.MethodDefToDescMap));
+ ExtOut("FieldDefToDescMap: %p\n", SOS_PTR(module.FieldDefToDescMap));
+ ExtOut("MemberRefToDescMap: %p\n", SOS_PTR(module.MemberRefToDescMap));
+ ExtOut("FileReferencesMap: %p\n", SOS_PTR(module.FileReferencesMap));
+ ExtOut("AssemblyReferencesMap: %p\n", SOS_PTR(module.ManifestModuleReferencesMap));
+
+ if (module.ilBase && module.metadataStart)
+ ExtOut("MetaData start address: %p (%d bytes)\n", SOS_PTR(module.metadataStart), module.metadataSize);
+
+ if (bMethodTables)
+ {
+ ExtOut("\nTypes defined in this module\n\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name");
+
+ ExtOut("------------------------------------------------------------------------------\n");
+ g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
+
+ ExtOut("\nTypes referenced in this module\n\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name");
+
+ ExtOut("------------------------------------------------------------------------------\n");
+ g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a Domain *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpDomain)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_DomainAddr = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_DomainAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ DacpAppDomainStoreData adsData;
+ if ((Status=adsData.Request(g_sos))!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain information\n");
+ return Status;
+ }
+
+ if (p_DomainAddr)
+ {
+ DacpAppDomainData appDomain1;
+ if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK)
+ {
+ ExtOut("Fail to fill AppDomain\n");
+ return Status;
+ }
+
+ ExtOut("--------------------------------------\n");
+
+ if (p_DomainAddr == adsData.sharedDomain)
+ {
+ DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
+ }
+ else if (p_DomainAddr == adsData.systemDomain)
+ {
+ DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
+ }
+ else
+ {
+ DMLOut("Domain %d:%s %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr));
+ }
+
+ DomainInfo(&appDomain1);
+ return Status;
+ }
+
+ ExtOut("--------------------------------------\n");
+ DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
+ DacpAppDomainData appDomain;
+ if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK)
+ {
+ ExtOut("Unable to get system domain info.\n");
+ return Status;
+ }
+ DomainInfo(&appDomain);
+
+ ExtOut("--------------------------------------\n");
+ DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
+ if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK)
+ {
+ ExtOut("Unable to get shared domain info\n");
+ return Status;
+ }
+ DomainInfo(&appDomain);
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
+ {
+ ExtOut("Unable to get array of AppDomains\n");
+ return Status;
+ }
+
+ for (int n=0;n<adsData.DomainCount;n++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK)
+ {
+ ExtOut("Failed to get appdomain %p, error %lx\n", SOS_PTR(pArray[n]), Status);
+ return Status;
+ }
+
+ ExtOut("--------------------------------------\n");
+ DMLOut("Domain %d:%s %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n]));
+ DomainInfo(&appDomain);
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a Assembly *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpAssembly)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_AssemblyAddr = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_AssemblyAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if (p_AssemblyAddr == 0)
+ {
+ ExtOut("Invalid Assembly %s\n", args);
+ return Status;
+ }
+
+ DacpAssemblyData Assembly;
+ if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK)
+ {
+ ExtOut("Fail to fill Assembly\n");
+ return Status;
+ }
+ DMLOut("Parent Domain: %s\n", DMLDomain(Assembly.ParentDomain));
+ if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK)
+ ExtOut("Name: %S\n", g_mdName);
+ else
+ ExtOut("Name: Unknown\n");
+
+ AssemblyInfo(&Assembly);
+ return Status;
+}
+
+
+String GetHostingCapabilities(DWORD hostConfig)
+{
+ String result;
+
+ bool bAnythingPrinted = false;
+
+#define CHK_AND_PRINT(hType,hStr) \
+ if (hostConfig & (hType)) { \
+ if (bAnythingPrinted) result += ", "; \
+ result += hStr; \
+ bAnythingPrinted = true; \
+ }
+
+ CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory");
+ CHK_AND_PRINT(CLRTASKHOSTED, "Task");
+ CHK_AND_PRINT(CLRSYNCHOSTED, "Sync");
+ CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool");
+ CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion");
+ CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly");
+ CHK_AND_PRINT(CLRGCHOSTED, "GC");
+ CHK_AND_PRINT(CLRSECURITYHOSTED, "Security");
+
+#undef CHK_AND_PRINT
+
+ return result;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the managed threads *
+* *
+\**********************************************************************/
+HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly)
+{
+ HRESULT Status;
+
+ DacpThreadStoreData ThreadStore;
+ if ((Status = ThreadStore.Request(g_sos)) != S_OK)
+ {
+ Print("Failed to request ThreadStore\n");
+ return Status;
+ }
+
+ TableOutput table(2, 17);
+
+ table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount));
+ table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount));
+ table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount));
+ table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount));
+ table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount));
+
+ if (ThreadStore.fHostConfig & ~CLRHOSTED)
+ {
+ String hosting = "yes";
+
+ hosting += " (";
+ hosting += GetHostingCapabilities(ThreadStore.fHostConfig);
+ hosting += ")";
+
+ table.WriteRow("Hosted Runtime:", hosting);
+ }
+ else
+ {
+ table.WriteRow("Hosted Runtime:", "no");
+ }
+
+ const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0;
+ table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX);
+ table.SetWidths(10, 4, 4, 4, _max(9, POINTERSIZE_HEX),
+ 8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX,
+ 5, 3, POINTERSIZE_HEX);
+
+ table.SetColAlignment(0, AlignRight);
+ table.SetColAlignment(1, AlignRight);
+ table.SetColAlignment(2, AlignRight);
+ table.SetColAlignment(4, AlignRight);
+
+ table.WriteColumn(8, "Lock");
+ table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt");
+
+ if (hosted)
+ table.WriteColumn("Fiber");
+
+ table.WriteColumn("Exception");
+
+ DacpThreadData Thread;
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ if (IsInterrupt())
+ break;
+
+ if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
+ {
+ PrintLn("Failed to request Thread at ", Pointer(CurThread));
+ return Status;
+ }
+
+ BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID;
+ if (!IsKernelDebugger())
+ {
+ ULONG id = 0;
+
+ if (bSwitchedOutFiber)
+ {
+ table.WriteColumn(0, "<<<< ");
+ }
+ else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
+ {
+ table.WriteColumn(0, Decimal(id));
+ }
+ else if (bPrintLiveThreadsOnly)
+ {
+ CurThread = Thread.nextThread;
+ continue;
+ }
+ else
+ {
+ table.WriteColumn(0, "XXXX ");
+ }
+ }
+
+ table.WriteColumn(1, Decimal(Thread.corThreadId));
+ table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId));
+ table.WriteColumn(3, Pointer(CurThread));
+ table.WriteColumn(4, ThreadState(Thread.state));
+ table.WriteColumn(5, Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive");
+ table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit);
+
+ if (Thread.domain)
+ {
+ table.WriteColumn(7, AppDomainPtr(Thread.domain));
+ }
+ else
+ {
+ CLRDATA_ADDRESS domain = 0;
+ if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain)))
+ table.WriteColumn(7, "<error>");
+ else
+ table.WriteColumn(7, AppDomainPtr(domain));
+ }
+
+ table.WriteColumn(8, Decimal(Thread.lockCount));
+
+ // Apartment state
+#ifndef FEATURE_PAL
+ DWORD_PTR OleTlsDataAddr;
+ if (!bSwitchedOutFiber
+ && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle),
+ &OleTlsDataAddr,
+ sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0)
+ {
+ DWORD AptState;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
+ &AptState,
+ sizeof(AptState), NULL))
+ {
+ if (AptState & OLETLS_APARTMENTTHREADED)
+ table.WriteColumn(9, "STA");
+ else if (AptState & OLETLS_MULTITHREADED)
+ table.WriteColumn(9, "MTA");
+ else if (AptState & OLETLS_INNEUTRALAPT)
+ table.WriteColumn(9, "NTA");
+ else
+ table.WriteColumn(9, "Ukn");
+ }
+ else
+ {
+ table.WriteColumn(9, "Ukn");
+ }
+ }
+ else
+#endif // FEATURE_PAL
+ table.WriteColumn(9, "Ukn");
+
+ if (hosted)
+ table.WriteColumn(10, Thread.fiberData);
+
+ WString lastCol;
+ if (CurThread == ThreadStore.finalizerThread)
+ lastCol += W("(Finalizer) ");
+ if (CurThread == ThreadStore.gcThread)
+ lastCol += W("(GC) ");
+
+ const int TS_TPWorkerThread = 0x01000000; // is this a threadpool worker thread?
+ const int TS_CompletionPortThread = 0x08000000; // is this is a completion port thread?
+
+ if (Thread.state & TS_TPWorkerThread)
+ lastCol += W("(Threadpool Worker) ");
+ else if (Thread.state & TS_CompletionPortThread)
+ lastCol += W("(Threadpool Completion Port) ");
+
+
+ TADDR taLTOH;
+ if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &taLTOH, sizeof(taLTOH), NULL) && taLTOH)
+ {
+ TADDR taMT;
+ if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
+ {
+ if (NameForMT_s(taMT, g_mdName, mdNameLen))
+ lastCol += WString(g_mdName) + W(" ") + ExceptionPtr(taLTOH);
+ else
+ lastCol += WString(W("<Invalid Object> (")) + Pointer(taLTOH) + W(")");
+
+ // Print something if there are nested exceptions on the thread
+ if (Thread.firstNestedException)
+ lastCol += W(" (nested exceptions)");
+ }
+ }
+
+ table.WriteColumn(lastCol);
+ CurThread = Thread.nextThread;
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+HRESULT PrintSpecialThreads()
+{
+ Print("\n");
+
+ DWORD dwCLRTLSDataIndex = 0;
+ HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex);
+
+ if (!SUCCEEDED (Status))
+ {
+ Print("Failed to retrieve Tls Data index\n");
+ return Status;
+ }
+
+
+ ULONG ulOriginalThreadID = 0;
+ Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID);
+ if (!SUCCEEDED (Status))
+ {
+ Print("Failed to require current Thread ID\n");
+ return Status;
+ }
+
+ ULONG ulTotalThreads = 0;
+ Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads);
+ if (!SUCCEEDED (Status))
+ {
+ Print("Failed to require total thread number\n");
+ return Status;
+ }
+
+ TableOutput table(3, 4, AlignRight, 5);
+ table.WriteRow("", "OSID", "Special thread type");
+
+ for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++)
+ {
+ ULONG Id = 0;
+ ULONG SysId = 0;
+ HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId);
+ if (!SUCCEEDED (threadStatus))
+ {
+ PrintLn("Failed to get thread ID for thread ", Decimal(ulThread));
+ continue;
+ }
+
+ threadStatus = g_ExtSystem->SetCurrentThreadId(Id);
+ if (!SUCCEEDED (threadStatus))
+ {
+ PrintLn("Failed to switch to thread ", ThreadID(SysId));
+ continue;
+ }
+
+ CLRDATA_ADDRESS cdaTeb = 0;
+ threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
+ if (!SUCCEEDED (threadStatus))
+ {
+ PrintLn("Failed to get Teb for Thread ", ThreadID(SysId));
+ continue;
+ }
+
+ TADDR CLRTLSDataAddr = 0;
+
+#ifdef FEATURE_IMPLICIT_TLS
+ TADDR tlsArrayAddr = NULL;
+ if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL))
+ {
+ PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ TADDR moduleTlsDataAddr = 0;
+
+ if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * dwCLRTLSDataIndex, &moduleTlsDataAddr, sizeof (void**), NULL))
+ {
+ PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ CLRTLSDataAddr = moduleTlsDataAddr + OFFSETOF__TLS__tls_EETlsData;
+#else
+ if (dwCLRTLSDataIndex < TLS_MINIMUM_AVAILABLE)
+ {
+ CLRTLSDataAddr = TO_TADDR(cdaTeb) + offsetof(TEB, TlsSlots) + sizeof (void*) * dwCLRTLSDataIndex;
+ }
+ else
+ {
+ //if TLS index is bigger than TLS_MINIMUM_AVAILABLE, the TLS slot lives in ExpansionSlots
+ TADDR TebExpsionAddr = NULL;
+ if (!SafeReadMemory (TO_TADDR(cdaTeb) + offsetof(TEB, TlsExpansionSlots) , &TebExpsionAddr, sizeof (void**), NULL))
+ {
+ PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ if (TebExpsionAddr == NULL)
+ {
+ continue;
+ }
+
+ CLRTLSDataAddr = TebExpsionAddr + sizeof (void*) * (dwCLRTLSDataIndex - TLS_MINIMUM_AVAILABLE);
+ }
+#endif // FEATURE_IMPLICIT_TLS
+
+ TADDR CLRTLSData = NULL;
+ if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL))
+ {
+ PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ if (CLRTLSData == NULL)
+ {
+ continue;
+ }
+
+ size_t ThreadType = 0;
+ if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL))
+ {
+ PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ if (ThreadType == 0)
+ {
+ continue;
+ }
+
+ table.WriteColumn(0, Decimal(Id));
+ table.WriteColumn(1, ThreadID(SysId));
+
+ String type;
+ if (ThreadType & ThreadType_GC)
+ {
+ type += "GC ";
+ }
+ if (ThreadType & ThreadType_Timer)
+ {
+ type += "Timer ";
+ }
+ if (ThreadType & ThreadType_Gate)
+ {
+ type += "Gate ";
+ }
+ if (ThreadType & ThreadType_DbgHelper)
+ {
+ type += "DbgHelper ";
+ }
+ if (ThreadType & ThreadType_Shutdown)
+ {
+ type += "Shutdown ";
+ }
+ if (ThreadType & ThreadType_DynamicSuspendEE)
+ {
+ type += "SuspendEE ";
+ }
+ if (ThreadType & ThreadType_Finalizer)
+ {
+ type += "Finalizer ";
+ }
+ if (ThreadType & ThreadType_ADUnloadHelper)
+ {
+ type += "ADUnloadHelper ";
+ }
+ if (ThreadType & ThreadType_ShutdownHelper)
+ {
+ type += "ShutdownHelper ";
+ }
+ if (ThreadType & ThreadType_Threadpool_IOCompletion)
+ {
+ type += "IOCompletion ";
+ }
+ if (ThreadType & ThreadType_Threadpool_Worker)
+ {
+ type += "ThreadpoolWorker ";
+ }
+ if (ThreadType & ThreadType_Wait)
+ {
+ type += "Wait ";
+ }
+ if (ThreadType & ThreadType_ProfAPI_Attach)
+ {
+ type += "ProfilingAPIAttach ";
+ }
+ if (ThreadType & ThreadType_ProfAPI_Detach)
+ {
+ type += "ProfilingAPIDetach ";
+ }
+
+ table.WriteColumn(2, type);
+ }
+
+ Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID);
+ if (!SUCCEEDED (Status))
+ {
+ ExtOut("Failed to switch to original thread\n");
+ return Status;
+ }
+
+ return Status;
+}
+#endif //FEATURE_PAL
+
+struct ThreadStateTable
+{
+ unsigned int State;
+ const char * Name;
+};
+static const struct ThreadStateTable ThreadStates[] =
+{
+ {0x1, "Thread Abort Requested"},
+ {0x2, "GC Suspend Pending"},
+ {0x4, "User Suspend Pending"},
+ {0x8, "Debug Suspend Pending"},
+ {0x10, "GC On Transitions"},
+ {0x20, "Legal to Join"},
+ {0x40, "Yield Requested"},
+ {0x80, "Hijacked by the GC"},
+ {0x100, "Blocking GC for Stack Overflow"},
+ {0x200, "Background"},
+ {0x400, "Unstarted"},
+ {0x800, "Dead"},
+ {0x1000, "CLR Owns"},
+ {0x2000, "CoInitialized"},
+ {0x4000, "In Single Threaded Apartment"},
+ {0x8000, "In Multi Threaded Apartment"},
+ {0x10000, "Reported Dead"},
+ {0x20000, "Fully initialized"},
+ {0x40000, "Task Reset"},
+ {0x80000, "Sync Suspended"},
+ {0x100000, "Debug Will Sync"},
+ {0x200000, "Stack Crawl Needed"},
+ {0x400000, "Suspend Unstarted"},
+ {0x800000, "Aborted"},
+ {0x1000000, "Thread Pool Worker Thread"},
+ {0x2000000, "Interruptible"},
+ {0x4000000, "Interrupted"},
+ {0x8000000, "Completion Port Thread"},
+ {0x10000000, "Abort Initiated"},
+ {0x20000000, "Finalized"},
+ {0x40000000, "Failed to Start"},
+ {0x80000000, "Detached"},
+};
+
+DECLARE_API(ThreadState)
+{
+ INIT_API_NODAC();
+
+ size_t state = GetExpression(args);
+ int count = 0;
+
+ if (state)
+ {
+
+ for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
+ if (state & ThreadStates[i].State)
+ {
+ ExtOut(" %s\n", ThreadStates[i].Name);
+ count++;
+ }
+ }
+
+ // If we did not find any thread states, print out a message to let the user
+ // know that the function is working correctly.
+ if (count == 0)
+ ExtOut(" No thread states for '%s'\n", args);
+
+ return Status;
+}
+
+DECLARE_API(Threads)
+{
+ INIT_API();
+
+ BOOL bPrintSpecialThreads = FALSE;
+ BOOL bPrintLiveThreadsOnly = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-special", &bPrintSpecialThreads, COBOOL, FALSE},
+ {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ // We need to support minidumps for this command.
+ BOOL bMiniDump = IsMiniDumpFile();
+
+ if (bMiniDump && bPrintSpecialThreads)
+ {
+ Print("Special thread information is not available in mini dumps.\n");
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ try
+ {
+ Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly);
+ if (!bMiniDump && bPrintSpecialThreads)
+ {
+#ifdef FEATURE_PAL
+ Print("\n-special not supported.\n");
+#else //FEATURE_PAL
+ HRESULT Status2 = PrintSpecialThreads();
+ if (!SUCCEEDED(Status2))
+ Status = Status2;
+#endif //FEATURE_PAL
+ }
+ }
+ catch (sos::Exception &e)
+ {
+ ExtOut("%s\n", e.what());
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the Watson Buckets. *
+* *
+\**********************************************************************/
+DECLARE_API(WatsonBuckets)
+{
+ INIT_API();
+
+ // We don't need to support minidumps for this command.
+ if (IsMiniDumpFile())
+ {
+ ExtOut("Not supported on mini dumps.\n");
+ }
+
+ // Get the current managed thread.
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ // Get the definition of GenericModeBlock.
+#include <msodw.h>
+ GenericModeBlock gmb;
+
+ if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK)
+ {
+ ExtOut("Can't get Watson Buckets\n");
+ return Status;
+ }
+
+ ExtOut("Watson Bucket parameters:\n");
+ ExtOut("b1: %S\n", gmb.wzP1);
+ ExtOut("b2: %S\n", gmb.wzP2);
+ ExtOut("b3: %S\n", gmb.wzP3);
+ ExtOut("b4: %S\n", gmb.wzP4);
+ ExtOut("b5: %S\n", gmb.wzP5);
+ ExtOut("b6: %S\n", gmb.wzP6);
+ ExtOut("b7: %S\n", gmb.wzP7);
+ ExtOut("b8: %S\n", gmb.wzP8);
+ ExtOut("b9: %S\n", gmb.wzP9);
+
+ return Status;
+} // WatsonBuckets()
+#endif // FEATURE_PAL
+
+struct PendingBreakpoint
+{
+ WCHAR szModuleName[MAX_LONGPATH];
+ WCHAR szFunctionName[mdNameLen];
+ WCHAR szFilename[MAX_LONGPATH];
+ DWORD lineNumber;
+ TADDR pModule;
+ DWORD ilOffset;
+ mdMethodDef methodToken;
+ void SetModule(TADDR module)
+ {
+ pModule = module;
+ }
+
+ bool ModuleMatches(TADDR compare)
+ {
+ return (compare == pModule);
+ }
+
+ PendingBreakpoint *pNext;
+ PendingBreakpoint() : lineNumber(0), ilOffset(0), methodToken(0), pNext(NULL)
+ {
+ szModuleName[0] = L'\0';
+ szFunctionName[0] = L'\0';
+ szFilename[0] = L'\0';
+ }
+};
+
+void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
+{
+ const int MaxBPsCached = 1024;
+ static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached];
+ static int curLimit = 0;
+
+ // on ARM the debugger requires breakpoint addresses to be sanitized
+ if (IsDbgTargetArm())
+#ifndef FEATURE_PAL
+ addr &= ~THUMB_CODE;
+#else
+ addr |= THUMB_CODE; // lldb expects thumb code bit set
+#endif
+
+ // if we overflowed our cache consider all new BPs unique...
+ BOOL bUnique = curLimit >= MaxBPsCached;
+ if (!bUnique)
+ {
+ bUnique = TRUE;
+ for (int i = 0; i < curLimit; ++i)
+ {
+ if (alreadyPlacedBPs[i] == addr)
+ {
+ bUnique = FALSE;
+ break;
+ }
+ }
+ }
+ if (bUnique)
+ {
+ char buffer[64]; // sufficient for "bp <pointersize>"
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+
+ // get the MethodDesc name
+ CLRDATA_ADDRESS pMD;
+ if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
+ || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
+ {
+ wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
+ }
+
+#ifndef FEATURE_PAL
+ sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr);
+#else
+ sprintf_s(buffer, _countof(buffer), "breakpoint set --address 0x%p", (void*) (size_t) addr);
+#endif
+ ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+
+ if (curLimit < MaxBPsCached)
+ {
+ alreadyPlacedBPs[curLimit++] = addr;
+ }
+ }
+}
+
+class Breakpoints
+{
+ PendingBreakpoint* m_breakpoints;
+public:
+ Breakpoints()
+ {
+ m_breakpoints = NULL;
+ }
+ ~Breakpoints()
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ delete pCur;
+ pCur = pNext;
+ }
+ m_breakpoints = NULL;
+ }
+
+ void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset)
+ {
+ if (!IsIn(szModule, szName, mod))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
+ wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
+ pNew->SetModule(mod);
+ pNew->ilOffset = ilOffset;
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
+ {
+ if (!IsIn(methodToken, mod, ilOffset))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
+ wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
+ pNew->methodToken = methodToken;
+ pNew->SetModule(mod);
+ pNew->ilOffset = ilOffset;
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
+ {
+ if (!IsIn(szFilename, lineNumber, mod))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
+ pNew->lineNumber = lineNumber;
+ pNew->SetModule(mod);
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
+ {
+ if (!IsIn(methodToken, mod, ilOffset))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
+ pNew->lineNumber = lineNumber;
+ pNew->methodToken = methodToken;
+ pNew->SetModule(mod);
+ pNew->ilOffset = ilOffset;
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ //returns true if updates are still needed for this module, FALSE if all BPs are now bound
+ BOOL Update(TADDR mod, BOOL isNewModule)
+ {
+ BOOL bNeedUpdates = FALSE;
+ PendingBreakpoint *pCur = NULL;
+
+ if(isNewModule)
+ {
+ SymbolReader symbolReader;
+ SymbolReader* pSymReader = &symbolReader;
+ if(LoadSymbolsForModule(mod, &symbolReader) != S_OK)
+ pSymReader = NULL;
+
+ // Get tokens for any modules that match. If there was a change,
+ // update notifications.
+ pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader);
+ pCur = pNext;
+ }
+ }
+
+ pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ if (ResolvePendingBreakpoint(mod, pCur))
+ {
+ bNeedUpdates = TRUE;
+ }
+ pCur = pNext;
+ }
+ return bNeedUpdates;
+ }
+
+ void RemovePendingForModule(TADDR mod)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ if (pCur->ModuleMatches(mod))
+ {
+ // Delete the current node, and keep going
+ Delete(pCur);
+ }
+
+ pCur = pNext;
+ }
+ }
+
+ void ListBreakpoints()
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ size_t iBreakpointIndex = 1;
+ ExtOut(SOSPrefix "bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
+ while(pCur)
+ {
+ //windbg likes to format %p as always being 64 bits
+ ULONG64 modulePtr = (ULONG64)pCur->pModule;
+
+ if(pCur->szModuleName[0] != L'\0')
+ ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken);
+ else
+ ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken);
+ iBreakpointIndex++;
+ pCur = pCur->pNext;
+ }
+ }
+
+#ifndef FEATURE_PAL
+ void SaveBreakpoints(FILE* pFile)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if(pCur->szModuleName[0] != L'\0')
+ fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset);
+ else
+ fprintf_s(pFile, "!bpmd %ws:%d\n", pCur->szFilename, pCur->lineNumber);
+ pCur = pCur->pNext;
+ }
+ }
+#endif
+
+ void CleanupNotifications()
+ {
+#ifdef FEATURE_PAL
+ if (m_breakpoints == NULL)
+ {
+ g_ExtServices->ClearExceptionCallback();
+ }
+#endif
+ }
+
+ void ClearBreakpoint(size_t breakPointToClear)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ size_t iBreakpointIndex = 1;
+ while(pCur)
+ {
+ if (breakPointToClear == iBreakpointIndex)
+ {
+ ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule);
+ ExtOut("Cleared\n");
+ Delete(pCur);
+ break;
+ }
+ iBreakpointIndex++;
+ pCur = pCur->pNext;
+ }
+
+ if (pCur == NULL)
+ {
+ ExtOut("Invalid pending breakpoint index.\n");
+ }
+ CleanupNotifications();
+ }
+
+ void ClearAllBreakpoints()
+ {
+ size_t iBreakpointIndex = 1;
+ for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; )
+ {
+ PendingBreakpoint* pNext = pCur->pNext;
+ Delete(pCur);
+ iBreakpointIndex++;
+ pCur = pNext;
+ }
+ CleanupNotifications();
+
+ ExtOut("All pending breakpoints cleared.\n");
+ }
+
+ HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
+ {
+ HRESULT Status = S_OK;
+ ToRelease<IXCLRDataModule> pModule;
+ IfFailRet(g_sos->GetModule(mod, &pModule));
+
+ ToRelease<IMetaDataImport> pMDImport = NULL;
+ IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
+
+ IfFailRet(pSymbolReader->LoadSymbols(pMDImport, pModule));
+
+ return S_OK;
+ }
+
+ HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader)
+ {
+ HRESULT Status = S_OK;
+ if(pSymbolReader == NULL)
+ return S_FALSE; // no symbols, can't bind here
+
+ mdMethodDef methodDef;
+ ULONG32 ilOffset;
+ if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, mod, &methodDef, &ilOffset)))
+ {
+ return S_FALSE; // not binding in a module is typical
+ }
+
+ Add(pFilename, lineNumber, methodDef, mod, ilOffset);
+ return Status;
+ }
+
+ HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
+ {
+ HRESULT Status = S_OK;
+ char szName[mdNameLen];
+ int numModule;
+
+ ToRelease<IXCLRDataModule> module;
+ IfFailRet(g_sos->GetModule(mod, &module));
+
+ WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szName, mdNameLen, NULL, NULL);
+
+ ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n");
+ return E_FAIL;
+ }
+
+ for (int i = 0; i < numModule; i++)
+ {
+ // If any one entry in moduleList matches, then the current PendingBreakpoint
+ // is the right one.
+ if(moduleList[i] != TO_TADDR(mod))
+ continue;
+
+ CLRDATA_ENUM h;
+ if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ mdMethodDef methodToken;
+ ToRelease<IXCLRDataModule> pUnusedModule;
+ IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule));
+
+ Add(pModuleName, pMethodName, methodToken, mod, ilOffset);
+ pMeth->Release();
+ }
+ module->EndEnumMethodDefinitionsByName(h);
+ }
+ }
+ return S_OK;
+ }
+
+ // Return TRUE if there might be more instances that will be JITTED later
+ static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset)
+ {
+ BOOL bFoundCode = FALSE;
+ BOOL bNeedDefer = FALSE;
+ CLRDATA_ENUM h1;
+
+ if (pMeth->StartEnumInstances (NULL, &h1) == S_OK)
+ {
+ IXCLRDataMethodInstance *inst = NULL;
+ while (pMeth->EnumInstance (&h1, &inst) == S_OK)
+ {
+ BOOL foundByIlOffset = FALSE;
+ ULONG32 rangesNeeded = 0;
+ if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK)
+ {
+ ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded];
+ if (ranges != NULL)
+ {
+ if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK)
+ {
+ for (DWORD i = 0; i < rangesNeeded; i++)
+ {
+ IssueDebuggerBPCommand(ranges[i].startAddress);
+ bFoundCode = TRUE;
+ foundByIlOffset = TRUE;
+ }
+ }
+ }
+ }
+
+ if (!foundByIlOffset && ilOffset == 0)
+ {
+ CLRDATA_ADDRESS addr = 0;
+ if (inst->GetRepresentativeEntryAddress(&addr) == S_OK)
+ {
+ IssueDebuggerBPCommand(addr);
+ bFoundCode = TRUE;
+ }
+ }
+ }
+ pMeth->EndEnumInstances (h1);
+ }
+
+ // if this is a generic method we need to add a defered bp
+ BOOL bGeneric = FALSE;
+ pMeth->HasClassOrMethodInstantiation(&bGeneric);
+
+ bNeedDefer = !bFoundCode || bGeneric;
+ // This is down here because we only need to call SetCodeNofiication once.
+ if (bNeedDefer)
+ {
+ if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK)
+ {
+ bNeedDefer = FALSE;
+ ExtOut("Failed to set code notification\n");
+ }
+ }
+ return bNeedDefer;
+ }
+
+private:
+ BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if (pCur->ModuleMatches(mod) &&
+ _wcsicmp(pCur->szModuleName, szModule) == 0 &&
+ _wcscmp(pCur->szFunctionName, szName) == 0)
+ {
+ return TRUE;
+ }
+ pCur = pCur->pNext;
+ }
+ return FALSE;
+ }
+
+ BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if (pCur->ModuleMatches(mod) &&
+ _wcsicmp(pCur->szFilename, szFilename) == 0 &&
+ pCur->lineNumber == lineNumber)
+ {
+ return TRUE;
+ }
+ pCur = pCur->pNext;
+ }
+ return FALSE;
+ }
+
+ BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if (pCur->ModuleMatches(mod) &&
+ pCur->methodToken == token &&
+ pCur->ilOffset == ilOffset)
+ {
+ return TRUE;
+ }
+ pCur = pCur->pNext;
+ }
+ return FALSE;
+ }
+
+ void Delete(PendingBreakpoint *pDelete)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ PendingBreakpoint *pPrev = NULL;
+ while(pCur)
+ {
+ if (pCur == pDelete)
+ {
+ if (pPrev == NULL)
+ {
+ m_breakpoints = pCur->pNext;
+ }
+ else
+ {
+ pPrev->pNext = pCur->pNext;
+ }
+ delete pCur;
+ return;
+ }
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+
+
+
+ HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader)
+ {
+ // This function only works with pending breakpoints that are not module bound.
+ if (pCur->pModule == NULL)
+ {
+ if(pCur->szModuleName[0] != L'\0')
+ {
+ return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset);
+ }
+ else
+ {
+ return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader);
+ }
+ }
+ else
+ {
+ return S_OK;
+ }
+ }
+
+ // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved
+ BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur)
+ {
+ // Only go forward if the module matches the current PendingBreakpoint
+ if (!pCur->ModuleMatches(addr))
+ {
+ return FALSE;
+ }
+
+ ToRelease<IXCLRDataModule> mod;
+ if (FAILED(g_sos->GetModule(addr, &mod)))
+ {
+ return FALSE;
+ }
+
+ if(pCur->methodToken == 0)
+ {
+ return FALSE;
+ }
+
+ ToRelease<IXCLRDataMethodDefinition> pMeth = NULL;
+ mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth);
+
+ // We may not need the code notification. Maybe it was ngen'd and we
+ // already have the method?
+ // We can delete the current entry if ResolveMethodInstances() set all BPs
+ return ResolveMethodInstances(pMeth, pCur->ilOffset);
+ }
+};
+
+Breakpoints g_bpoints;
+
+// Controls whether optimizations are disabled on module load and whether NGEN can be used
+BOOL g_fAllowJitOptimization = TRUE;
+
+// Controls whether a one-shot breakpoint should be inserted the next time
+// execution is about to enter a catch clause
+BOOL g_stopOnNextCatch = FALSE;
+
+// According to the latest debuggers these callbacks will not get called
+// unless the user (or an extension, like SOS :-)) had previously enabled
+// clrn with "sxe clrn".
+class CNotification : public IXCLRDataExceptionNotification4
+{
+ static int s_condemnedGen;
+
+ int m_count;
+ int m_dbgStatus;
+public:
+ CNotification()
+ : m_count(0)
+ , m_dbgStatus(DEBUG_STATUS_NO_CHANGE)
+ {}
+
+ int GetDebugStatus()
+ {
+ return m_dbgStatus;
+ }
+
+ STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject)
+ {
+ if (ppvObject == NULL)
+ return E_INVALIDARG;
+
+ if (IsEqualIID(iid, IID_IUnknown)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4))
+ {
+ *ppvObject = static_cast<IXCLRDataExceptionNotification4*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ return E_NOINTERFACE;
+
+ }
+
+ STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; }
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ m_count--;
+ if (m_count < 0)
+ {
+ m_count = 0;
+ }
+ return m_count;
+ }
+
+
+ /*
+ * New code was generated or discarded for a method.:
+ */
+ STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method)
+ {
+ // Some method has been generated, make a breakpoint and remove it.
+ ULONG32 len = mdNameLen;
+ LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK)
+ {
+ ToRelease<IXCLRDataModule> pMod;
+ HRESULT hr = method->GetTokenAndScope(NULL, &pMod);
+ if (SUCCEEDED(hr))
+ {
+ len = mdNameLen;
+ if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK)
+ {
+ ExtOut("JITTED %S!%S\n", szModuleName, g_mdName);
+
+ // Add breakpoint, perhaps delete pending breakpoint
+ DacpGetModuleAddress dgma;
+ if (SUCCEEDED(dgma.Request(pMod)))
+ {
+ g_bpoints.Update(TO_TADDR(dgma.ModulePtr), FALSE);
+ }
+ else
+ {
+ ExtOut("Failed to request module address.\n");
+ }
+ }
+ }
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method)
+ {
+ return E_NOTIMPL;
+ }
+
+ /*
+ * The process or task reached the desired execution state.
+ */
+ STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; }
+ STDMETHODIMP OnTaskExecution(IXCLRDataTask* task,
+ ULONG32 state) { return E_NOTIMPL; }
+
+ /*
+ * The given module was loaded or unloaded.
+ */
+ STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod)
+ {
+ DacpGetModuleAddress dgma;
+ if (SUCCEEDED(dgma.Request(mod)))
+ {
+ g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE);
+ }
+
+ if(!g_fAllowJitOptimization)
+ {
+ HRESULT hr;
+ ToRelease<IXCLRDataModule2> mod2;
+ if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2)))
+ {
+ ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n");
+ }
+ else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
+ {
+ if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
+ ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n");
+ else
+ ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr);
+ }
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod)
+ {
+ DacpGetModuleAddress dgma;
+ if (SUCCEEDED(dgma.Request(mod)))
+ {
+ g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr));
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ /*
+ * The given type was loaded or unloaded.
+ */
+ STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst)
+ { return E_NOTIMPL; }
+ STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst)
+ { return E_NOTIMPL; }
+
+ STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain)
+ { return E_NOTIMPL; }
+ STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain)
+ { return E_NOTIMPL; }
+ STDMETHODIMP OnException(IXCLRDataExceptionState* exception)
+ { return E_NOTIMPL; }
+
+ STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs)
+{
+ // by default don't stop on these notifications...
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+
+ IXCLRDataProcess2* idp2 = NULL;
+ if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
+ {
+ if (gcEvtArgs.typ == GC_MARK_END)
+ {
+ // erase notification request
+ GcEvtArgs gea = { GC_MARK_END, { 0 } };
+ idp2->SetGcNotification(gea);
+
+ s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration);
+
+ ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen);
+
+ // GC_MARK_END notification means: give the user a chance to examine the debuggee
+ m_dbgStatus = DEBUG_STATUS_BREAK;
+ }
+ }
+
+ return S_OK;
+ }
+
+ /*
+ * Catch is about to be entered
+ */
+ STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset)
+ {
+ if(g_stopOnNextCatch)
+ {
+ CLRDATA_ADDRESS startAddr;
+ if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK)
+ {
+ CHAR buffer[100];
+#ifndef FEATURE_PAL
+ sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset));
+#else
+ sprintf_s(buffer, _countof(buffer), "breakpoint set --one-shot --address 0x%p", (void*) (size_t) (startAddr+catcherNativeOffset));
+#endif
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ }
+ g_stopOnNextCatch = FALSE;
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ static int GetCondemnedGen()
+ {
+ return s_condemnedGen;
+ }
+
+};
+
+int CNotification::s_condemnedGen = -1;
+
+BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
+{
+ ISOSDacInterface4 *psos4 = NULL;
+ CLRDATA_ADDRESS arguments[3];
+ HRESULT Status;
+
+ if (SUCCEEDED(Status = g_sos->QueryInterface(__uuidof(ISOSDacInterface4), (void**) &psos4)))
+ {
+ int count = _countof(arguments);
+ int countNeeded = 0;
+
+ Status = psos4->GetClrNotification(arguments, count, &countNeeded);
+ psos4->Release();
+
+ if (SUCCEEDED(Status))
+ {
+ memset(&pdle->ExceptionRecord, 0, sizeof(pdle->ExceptionRecord));
+ pdle->FirstChance = TRUE;
+ pdle->ExceptionRecord.ExceptionCode = CLRDATA_NOTIFY_EXCEPTION;
+
+ _ASSERTE(count <= EXCEPTION_MAXIMUM_PARAMETERS);
+ for (int i = 0; i < count; i++)
+ {
+ pdle->ExceptionRecord.ExceptionInformation[i] = arguments[i];
+ }
+ // The rest of the ExceptionRecord isn't used by TranslateExceptionRecordToNotification
+ return TRUE;
+ }
+ // No pending exception notification
+ return FALSE;
+ }
+
+ // The new DAC based interface doesn't exists so ask the debugger for the last exception
+ // information. NOTE: this function doesn't work on xplat version when the coreclr symbols
+ // have been stripped.
+
+ ULONG Type, ProcessId, ThreadId;
+ ULONG ExtraInformationUsed;
+ Status = g_ExtControl->GetLastEventInformation(
+ &Type,
+ &ProcessId,
+ &ThreadId,
+ pdle,
+ sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION),
+ &ExtraInformationUsed,
+ NULL,
+ 0,
+ NULL);
+
+ if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION)
+ {
+ return FALSE;
+ }
+
+ if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+HRESULT HandleCLRNotificationEvent()
+{
+ /*
+ * Did we get module load notification? If so, check if any in our pending list
+ * need to be registered for jit notification.
+ *
+ * Did we get a jit notification? If so, check if any can be removed and
+ * real breakpoints be set.
+ */
+ DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
+ CNotification Notification;
+
+ if (!CheckCLRNotificationEvent(&dle))
+ {
+#ifndef FEATURE_PAL
+ ExtOut("Expecting first chance CLRN exception\n");
+ return E_FAIL;
+#else
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
+ return S_OK;
+#endif
+ }
+
+ // Notification only needs to live for the lifetime of the call below, so it's a non-static
+ // local.
+ HRESULT Status = g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification);
+ if (Status != S_OK)
+ {
+ ExtErr("Error processing exception notification\n");
+ return Status;
+ }
+ else
+ {
+ switch (Notification.GetDebugStatus())
+ {
+ case DEBUG_STATUS_GO:
+ case DEBUG_STATUS_GO_HANDLED:
+ case DEBUG_STATUS_GO_NOT_HANDLED:
+#ifndef FEATURE_PAL
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
+#else
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
+#endif
+ break;
+ default:
+ break;
+ }
+ }
+
+ return S_OK;
+}
+
+#ifndef FEATURE_PAL
+
+DECLARE_API(HandleCLRN)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ return HandleCLRNotificationEvent();
+}
+
+#else // FEATURE_PAL
+
+HRESULT HandleExceptionNotification(ILLDBServices *client)
+{
+ INIT_API();
+ return HandleCLRNotificationEvent();
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(bpmd)
+{
+ INIT_API_NOEE();
+ MINIDUMP_NOT_SUPPORTED();
+ int i;
+ char buffer[1024];
+
+ if (IsDumpFile())
+ {
+ ExtOut(SOSPrefix "bpmd is not supported on a dump file.\n");
+ return Status;
+ }
+
+
+ // We keep a list of managed breakpoints the user wants to set, and display pending bps
+ // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp.
+ // bpmd acts as a feeder of breakpoints to bp when the time is right.
+ //
+
+ StringHolder DllName,TypeName;
+ int lineNumber = 0;
+ size_t Offset = 0;
+
+ DWORD_PTR pMD = NULL;
+ BOOL fNoFutureModule = FALSE;
+ BOOL fList = FALSE;
+ size_t clearItem = 0;
+ BOOL fClearAll = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-md", &pMD, COHEX, TRUE},
+ {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE},
+ {"-list", &fList, COBOOL, FALSE},
+ {"-clear", &clearItem, COSIZE_T, TRUE},
+ {"-clearall", &fClearAll, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&DllName.data, COSTRING},
+ {&TypeName.data, COSTRING},
+ {&Offset, COSIZE_T},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ bool fBadParam = false;
+ bool fIsFilename = false;
+ int commandsParsed = 0;
+
+ if (pMD != NULL)
+ {
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ commandsParsed++;
+ }
+ if (fList)
+ {
+ commandsParsed++;
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ }
+ if (fClearAll)
+ {
+ commandsParsed++;
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ }
+ if (clearItem != 0)
+ {
+ commandsParsed++;
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ }
+ if (1 <= nArg && nArg <= 3)
+ {
+ commandsParsed++;
+ // did we get dll and type name or file:line#? Search for a colon in the first arg
+ // to see if it is in fact a file:line#
+ CHAR* pColon = strchr(DllName.data, ':');
+#ifndef FEATURE_PAL
+ if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, NULL, NULL))) {
+#else
+ if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, NULL))) {
+#endif
+ ExtOut("%s not loaded yet\n", MAIN_CLR_DLL_NAME_A);
+ return Status;
+ }
+
+ if(NULL != pColon)
+ {
+ fIsFilename = true;
+ *pColon = '\0';
+ pColon++;
+ if(1 != sscanf_s(pColon, "%d", &lineNumber))
+ {
+ ExtOut("Unable to parse line number\n");
+ fBadParam = true;
+ }
+ else if(lineNumber < 0)
+ {
+ ExtOut("Line number must be positive\n");
+ fBadParam = true;
+ }
+ if(nArg != 1) fBadParam = 1;
+ }
+ }
+
+ if (fBadParam || (commandsParsed != 1))
+ {
+ ExtOut("Usage: " SOSPrefix "bpmd -md <MethodDesc pointer>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
+ ExtOut("Usage: " SOSPrefix "bpmd <filename>:<line number>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -list\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -clear <pending breakpoint number>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -clearall\n");
+#ifdef FEATURE_PAL
+ ExtOut("See \"soshelp bpmd\" for more details.\n");
+#else
+ ExtOut("See \"!help bpmd\" for more details.\n");
+#endif
+ return Status;
+ }
+
+ if (fList)
+ {
+ g_bpoints.ListBreakpoints();
+ return Status;
+ }
+ if (clearItem != 0)
+ {
+ g_bpoints.ClearBreakpoint(clearItem);
+ return Status;
+ }
+ if (fClearAll)
+ {
+ g_bpoints.ClearAllBreakpoints();
+ return Status;
+ }
+ // Add a breakpoint
+ // Do we already have this breakpoint?
+ // Or, before setting it, is the module perhaps already loaded and code
+ // is available? If so, don't add to our pending list, just go ahead and
+ // set the real breakpoint.
+
+ LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ LPWSTR Filename = (LPWSTR)alloca(MAX_LONGPATH * sizeof(WCHAR));
+
+ BOOL bNeedNotificationExceptions = FALSE;
+
+ if (pMD == NULL)
+ {
+ int numModule = 0;
+ int numMethods = 0;
+
+ ArrayHolder<DWORD_PTR> moduleList = NULL;
+
+ if(!fIsFilename)
+ {
+ MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen);
+ MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen);
+ }
+ else
+ {
+ MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_LONGPATH);
+ }
+
+ // Get modules that may need a breakpoint bound
+ if ((Status = CheckEEDll()) == S_OK)
+ {
+ if ((Status = LoadClrDebugDll()) != S_OK)
+ {
+ // if the EE is loaded but DAC isn't we should stop.
+ DACMessage(Status);
+ return Status;
+ }
+ g_bDacBroken = FALSE; \
+
+ // Get the module list
+ moduleList = ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule);
+
+ // Its OK if moduleList is NULL
+ // There is a very normal case when checking for modules after clr is loaded
+ // but before any AppDomains or assemblies are created
+ // for example:
+ // >sxe ld:clr
+ // >g
+ // ...
+ // ModLoad: clr.dll
+ // >!bpmd Foo.dll Foo.Bar
+ }
+ // If LoadClrDebugDll() succeeded make sure we release g_clrData
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData);
+ ToRelease<ISOSDacInterface> spISD(g_sos);
+ ResetGlobals();
+
+ // we can get here with EE not loaded => 0 modules
+ // EE is loaded => 0 or more modules
+ ArrayHolder<DWORD_PTR> pMDs = NULL;
+ for (int iModule = 0; iModule < numModule; iModule++)
+ {
+ ToRelease<IXCLRDataModule> ModDef;
+ if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK)
+ {
+ continue;
+ }
+
+ HRESULT symbolsLoaded = S_FALSE;
+ if(!fIsFilename)
+ {
+ g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset);
+ }
+ else
+ {
+ SymbolReader symbolReader;
+ symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader);
+ if(symbolsLoaded == S_OK &&
+ g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK)
+ {
+ // if we have symbols then get the function name so we can lookup the MethodDescs
+ mdMethodDef methodDefToken;
+ ULONG32 ilOffset;
+ if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, moduleList[iModule], &methodDefToken, &ilOffset)))
+ {
+ ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL;
+ if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef)))
+ {
+ ULONG32 nameLen = 0;
+ pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName);
+
+ // get the size of the required buffer
+ int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL);
+
+ TypeName.data = new NOTHROW char[buffSize];
+ if (TypeName.data != NULL)
+ {
+ int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
+ _ASSERTE(bytesWritten == buffSize);
+ }
+ }
+ }
+ }
+ }
+
+ HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods);
+ if (FAILED(gotMethodDescs) && (!fIsFilename))
+ {
+ // BPs via file name will enumerate through modules so there will be legitimate failures.
+ // for module/type name we already found a match so this shouldn't fail (this is the original behavior).
+ ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]);
+ return Status;
+ }
+
+ // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
+ if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
+ {
+ for (int i = 0; i < numMethods; i++)
+ {
+ if (pMDs[i] == MD_NOT_YET_LOADED)
+ {
+ continue;
+ }
+ ExtOut("MethodDesc = %p\n", SOS_PTR(pMDs[i]));
+ }
+ }
+
+ if (g_bpoints.Update(moduleList[iModule], FALSE))
+ {
+ bNeedNotificationExceptions = TRUE;
+ }
+ }
+
+ if (!fNoFutureModule)
+ {
+ // add a pending breakpoint that will find future loaded modules, and
+ // wait for the module load notification.
+ if (!fIsFilename)
+ {
+ g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset);
+ }
+ else
+ {
+ g_bpoints.Add(Filename, lineNumber, NULL);
+ }
+ bNeedNotificationExceptions = TRUE;
+
+ ULONG32 flags = 0;
+ g_clrData->GetOtherNotificationFlags(&flags);
+ flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD);
+ g_clrData->SetOtherNotificationFlags(flags);
+ }
+ }
+ else /* We were given a MethodDesc already */
+ {
+ // if we've got an explicit MD, then we better have CLR and mscordacwks loaded
+ INIT_API_EE()
+ INIT_API_DAC();
+
+ DacpMethodDescData MethodDescData;
+ ExtOut("MethodDesc = %p\n", SOS_PTR(pMD));
+ if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK)
+ {
+ ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(pMD));
+ return Status;
+ }
+
+ if (MethodDescData.bHasNativeCode)
+ {
+ IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr);
+ }
+ else if (MethodDescData.bIsDynamic)
+ {
+#ifndef FEATURE_PAL
+ // Dynamic methods don't have JIT notifications. This is something we must
+ // fix in the next release. Until then, you have a cumbersome user experience.
+ ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
+ MethodDescData.AddressOfNativeCodeSlot);
+
+ sprintf_s(buffer, _countof(buffer),
+#ifdef _TARGET_WIN64_
+ "ba w8"
+#else
+ "ba w4"
+#endif // _TARGET_WIN64_
+
+ " /1 %p \"bp poi(%p); g\"",
+ (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot,
+ (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot);
+
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
+ ExtOut("Attempted to run: %s\n", buffer);
+ }
+#else
+ ExtErr("This DynamicMethodDesc is not yet JITTED %p\n", MethodDescData.AddressOfNativeCodeSlot);
+#endif // FEATURE_PAL
+ }
+ else
+ {
+ // Must issue a pending breakpoint.
+ if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK)
+ {
+ ExtOut("Unable to get method name for MethodDesc %p\n", SOS_PTR(pMD));
+ return Status;
+ }
+
+ FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName);
+
+ // We didn't find code, add a breakpoint.
+ g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0);
+ g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE);
+ bNeedNotificationExceptions = TRUE;
+ }
+ }
+
+ if (bNeedNotificationExceptions)
+ {
+ ExtOut("Adding pending breakpoints...\n");
+#ifndef FEATURE_PAL
+ sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+#else
+ Status = g_ExtServices->SetExceptionCallback(HandleExceptionNotification);
+#endif // FEATURE_PAL
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the managed threadpool *
+* *
+\**********************************************************************/
+DECLARE_API(ThreadPool)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DacpThreadpoolData threadpool;
+
+ if ((Status = threadpool.Request(g_sos)) == S_OK)
+ {
+ BOOL doHCDump = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-ti", &doHCDump, COBOOL, FALSE}
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);
+ ExtOut ("Worker Thread:");
+ ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
+ ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads);
+ ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads);
+ ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads);
+ ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads);
+ ExtOut ("\n");
+
+ int numWorkRequests = 0;
+ CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
+ DacpWorkRequestData workRequestData;
+ while (workRequestPtr)
+ {
+ if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a WorkRequest\n");
+ return Status;
+ }
+ numWorkRequests++;
+ workRequestPtr = workRequestData.NextWorkRequest;
+ }
+
+ ExtOut ("Work Request in Queue: %d\n", numWorkRequests);
+ workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
+ while (workRequestPtr)
+ {
+ if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a WorkRequest\n");
+ return Status;
+ }
+
+ if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr)
+ ExtOut (" AsyncTimerCallbackCompletion TimerInfo@%p\n", SOS_PTR(workRequestData.Context));
+ else
+ ExtOut (" Unknown Function: %p Context: %p\n", SOS_PTR(workRequestData.Function),
+ SOS_PTR(workRequestData.Context));
+
+ workRequestPtr = workRequestData.NextWorkRequest;
+ }
+
+ if (doHCDump)
+ {
+ ExtOut ("--------------------------------------\n");
+ ExtOut ("\nThread Injection History\n");
+ if (threadpool.HillClimbingLogSize > 0)
+ {
+ static char const * const TransitionNames[] =
+ {
+ "Warmup",
+ "Initializing",
+ "RandomMove",
+ "ClimbingMove",
+ "ChangePoint",
+ "Stabilizing",
+ "Starvation",
+ "ThreadTimedOut",
+ "Undefined"
+ };
+
+ ExtOut("\n Time Transition New #Threads #Samples Throughput\n");
+ DacpHillClimbingLogEntry entry;
+
+ // get the most recent entry first, so we can calculate time offsets
+
+ int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity;
+ CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
+ if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a HillClimbing log entry\n");
+ return Status;
+ }
+ DWORD endTime = entry.TickCount;
+
+ for (int i = 0; i < threadpool.HillClimbingLogSize; i++)
+ {
+ index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity;
+ entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
+
+ if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a HillClimbing log entry\n");
+ return Status;
+ }
+
+ ExtOut("%8.2lf %-14s %12d %12d %11.2lf\n",
+ (double)(int)(entry.TickCount - endTime) / 1000.0,
+ TransitionNames[entry.Transition],
+ entry.NewControlSetting,
+ entry.LastHistoryCount,
+ entry.LastHistoryMean);
+ }
+ }
+ }
+
+ ExtOut ("--------------------------------------\n");
+ ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
+ ExtOut ("--------------------------------------\n");
+
+ ExtOut ("Completion Port Thread:");
+ ExtOut ("Total: %d", threadpool.NumCPThreads);
+ ExtOut (" Free: %d", threadpool.NumFreeCPThreads);
+ ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);
+ ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
+ ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
+ ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);
+ ExtOut ("\n");
+ }
+ else
+ {
+ ExtOut("Failed to request ThreadpoolMgr information\n");
+ }
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(FindAppDomain)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_Object = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_Object, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if ((p_Object == 0) || !sos::IsObject(p_Object))
+ {
+ ExtOut("%p is not a valid object\n", SOS_PTR(p_Object));
+ return Status;
+ }
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error getting AppDomain information\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object));
+
+ if (appDomain != NULL)
+ {
+ DMLOut("AppDomain: %s\n", DMLDomain(appDomain));
+ if (appDomain == adstore.sharedDomain)
+ {
+ ExtOut("Name: Shared Domain\n");
+ ExtOut("ID: (shared domain)\n");
+ }
+ else if (appDomain == adstore.systemDomain)
+ {
+ ExtOut("Name: System Domain\n");
+ ExtOut("ID: (system domain)\n");
+ }
+ else
+ {
+ DacpAppDomainData domain;
+ if ((domain.Request(g_sos, appDomain) != S_OK) ||
+ (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK))
+ {
+ ExtOut("Error getting AppDomain %p.\n", SOS_PTR(appDomain));
+ return Status;
+ }
+
+ ExtOut("Name: %S\n", (g_mdName[0]!=L'\0') ? g_mdName : W("None"));
+ ExtOut("ID: %d\n", domain.dwId);
+ }
+ }
+ else
+ {
+ ExtOut("The type is declared in the shared domain and other\n");
+ ExtOut("methods of finding the AppDomain failed. Try running\n");
+ if (IsDMLEnabled())
+ DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object);
+ else
+ ExtOut("!gcroot %p, and if you find a root on a\n", p_Object);
+ ExtOut("stack, check the AppDomain of that stack with !threads.\n");
+ ExtOut("Note that the Thread could have transitioned between\n");
+ ExtOut("multiple AppDomains.\n");
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the COM state (e.g. APT,contexe *
+* activity. *
+* *
+\**********************************************************************/
+#ifdef FEATURE_COMINTEROP
+DECLARE_API(COMState)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ ULONG numThread;
+ ULONG maxId;
+ g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId);
+
+ ULONG curId;
+ g_ExtSystem->GetCurrentThreadId(&curId);
+
+ SIZE_T AllocSize;
+ if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize))
+ {
+ ExtOut(" Error! integer overflow on numThread 0x%08x\n", numThread);
+ return Status;
+ }
+ ULONG *ids = (ULONG*)alloca(AllocSize);
+ ULONG *sysIds = (ULONG*)alloca(AllocSize);
+ g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds);
+#if defined(_TARGET_WIN64_)
+ ExtOut(" ID TEB APT APTId CallerTID Context\n");
+#else
+ ExtOut(" ID TEB APT APTId CallerTID Context\n");
+#endif
+ for (ULONG i = 0; i < numThread; i ++) {
+ g_ExtSystem->SetCurrentThreadId(ids[i]);
+ CLRDATA_ADDRESS cdaTeb;
+ g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
+ ExtOut("%3d %4x %p", ids[i], sysIds[i], SOS_PTR(CDA_TO_UL64(cdaTeb)));
+ // Apartment state
+ TADDR OleTlsDataAddr;
+ if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle),
+ &OleTlsDataAddr,
+ sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) {
+ DWORD AptState;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
+ &AptState,
+ sizeof(AptState), NULL)) {
+ if (AptState & OLETLS_APARTMENTTHREADED) {
+ ExtOut(" STA");
+ }
+ else if (AptState & OLETLS_MULTITHREADED) {
+ ExtOut(" MTA");
+ }
+ else if (AptState & OLETLS_INNEUTRALAPT) {
+ ExtOut(" NTA");
+ }
+ else {
+ ExtOut(" Ukn");
+ }
+
+ // Read these fields only if we were able to read anything of the SOleTlsData structure
+ DWORD dwApartmentID;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID),
+ &dwApartmentID,
+ sizeof(dwApartmentID), NULL)) {
+ ExtOut(" %8x", dwApartmentID);
+ }
+ else
+ ExtOut(" %8x", 0);
+
+ DWORD dwTIDCaller;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller),
+ &dwTIDCaller,
+ sizeof(dwTIDCaller), NULL)) {
+ ExtOut(" %8x", dwTIDCaller);
+ }
+ else
+ ExtOut(" %8x", 0);
+
+ size_t Context;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx),
+ &Context,
+ sizeof(Context), NULL)) {
+ ExtOut(" %p", SOS_PTR(Context));
+ }
+ else
+ ExtOut(" %p", SOS_PTR(0));
+
+ }
+ else
+ ExtOut(" Ukn");
+ }
+ else
+ ExtOut(" Ukn");
+ ExtOut("\n");
+ }
+
+ g_ExtSystem->SetCurrentThreadId(curId);
+ return Status;
+}
+#endif // FEATURE_COMINTEROP
+
+#endif // FEATURE_PAL
+
+BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
+{
+ size_t methodStart = (size_t) token;
+
+ if (IsInterrupt())
+ {
+ return FALSE;
+ }
+
+ ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType));
+
+ LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo);
+ if (typeName != NULL)
+ {
+ ExtOut("catch(%S) ", typeName);
+ }
+
+ if (IsClonedFinally(pEHInfo))
+ ExtOut("(cloned finally)");
+ else if (pEHInfo->isDuplicateClause)
+ ExtOut("(duplicate)");
+
+ ExtOut("\n");
+ ExtOut("Clause: ");
+
+ ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart;
+ ULONG64 addrEnd = pEHInfo->tryEndOffset + methodStart;
+
+#ifdef _WIN64
+ ExtOut("[%08x`%08x, %08x`%08x]",
+ (ULONG)(addrStart >> 32), (ULONG)addrStart,
+ (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
+#else
+ ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
+#endif
+
+ ExtOut(" [%x, %x]\n",
+ (UINT32) pEHInfo->tryStartOffset,
+ (UINT32) pEHInfo->tryEndOffset);
+
+ ExtOut("Handler: ");
+
+ addrStart = pEHInfo->handlerStartOffset + methodStart;
+ addrEnd = pEHInfo->handlerEndOffset + methodStart;
+
+#ifdef _WIN64
+ ExtOut("[%08x`%08x, %08x`%08x]",
+ (ULONG)(addrStart >> 32), (ULONG)addrStart,
+ (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
+#else
+ ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
+#endif
+
+ ExtOut(" [%x, %x]\n",
+ (UINT32) pEHInfo->handlerStartOffset,
+ (UINT32) pEHInfo->handlerEndOffset);
+
+ if (pEHInfo->clauseType == EHFilter)
+ {
+ ExtOut("Filter: ");
+
+ addrStart = pEHInfo->filterOffset + methodStart;
+
+#ifdef _WIN64
+ ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart);
+#else
+ ExtOut("[%08x]", (ULONG)addrStart);
+#endif
+
+ ExtOut(" [%x]\n",
+ (UINT32) pEHInfo->filterOffset);
+ }
+
+ ExtOut("\n");
+ return TRUE;
+}
+
+DECLARE_API(EHInfo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR dwStartAddr = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DWORD_PTR tmpAddr = dwStartAddr;
+
+ if (!IsMethodDesc(dwStartAddr))
+ {
+ JITTypes jitType;
+ DWORD_PTR methodDesc;
+ DWORD_PTR gcinfoAddr;
+ IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr);
+ tmpAddr = methodDesc;
+ }
+
+ DacpMethodDescData MD;
+ if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(tmpAddr));
+ return Status;
+ }
+
+ if (1 == nArg && !MD.bHasNativeCode)
+ {
+ ExtOut("No EH info available\n");
+ return Status;
+ }
+
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
+ {
+ ExtOut("Unable to get codeHeader information\n");
+ return Status;
+ }
+
+ DMLOut("MethodDesc: %s\n", DMLMethodDesc(MD.MethodDescPtr));
+ DumpMDInfo(TO_TADDR(MD.MethodDescPtr));
+
+ ExtOut("\n");
+ Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr);
+
+ if (Status == E_ABORT)
+ {
+ ExtOut("<user aborted>\n");
+ }
+ else if (Status != S_OK)
+ {
+ ExtOut("Failed to perform EHInfo traverse\n");
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the GC encoding of a managed *
+* function. *
+* *
+\**********************************************************************/
+DECLARE_API(GCInfo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ TADDR taStartAddr = NULL;
+ TADDR taGCInfoAddr;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taStartAddr, COHEX},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ TADDR tmpAddr = taStartAddr;
+
+ if (!IsMethodDesc(taStartAddr))
+ {
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR gcinfoAddr;
+ IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr);
+ tmpAddr = methodDesc;
+ }
+
+ DacpMethodDescData MD;
+ if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
+ {
+ ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(taStartAddr));
+ return Status;
+ }
+
+ if (1 == nArg && !MD.bHasNativeCode)
+ {
+ ExtOut("No GC info available\n");
+ return Status;
+ }
+
+ DacpCodeHeaderData codeHeaderData;
+
+ if (
+ // Try to get code header data from taStartAddr. This will get the code
+ // header corresponding to the IP address, even if the function was rejitted
+ (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) &&
+
+ // If that didn't work, just try to use the code address that the MD
+ // points to. If the function was rejitted, this will only give you the
+ // original JITted code, but that's better than nothing
+ (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
+ )
+ {
+ // We always used to emit this (before rejit support), even if we couldn't get
+ // the code header, so keep on doing so.
+ ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr));
+
+ // And now the error....
+ ExtOut("Unable to get codeHeader information\n");
+ return Status;
+ }
+
+ // We have the code header, so use it to determine the method start
+
+ ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart));
+
+ if (codeHeaderData.JITType == TYPE_UNKNOWN)
+ {
+ ExtOut("unknown Jit\n");
+ return Status;
+ }
+ else if (codeHeaderData.JITType == TYPE_JIT)
+ {
+ ExtOut("Normal JIT generated code\n");
+ }
+ else if (codeHeaderData.JITType == TYPE_PJIT)
+ {
+ ExtOut("preJIT generated code\n");
+ }
+
+ taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo);
+
+ ExtOut("GC info %p\n", SOS_PTR(taGCInfoAddr));
+
+ // assume that GC encoding table is never more than
+ // 40 + methodSize * 2
+ int tableSize = 0;
+ if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
+ !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
+ {
+ ExtOut("<integer overflow>\n");
+ return E_FAIL;
+ }
+ ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize];
+ if (table == NULL)
+ {
+ ExtOut("Could not allocate memory to read the gc info.\n");
+ return E_OUTOFMEMORY;
+ }
+
+ memset(table, 0, tableSize);
+ // We avoid using move here, because we do not want to return
+ if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL))
+ {
+ ExtOut("Could not read memory %p\n", SOS_PTR(taGCInfoAddr));
+ return Status;
+ }
+
+ // Mutable table pointer since we need to pass the appropriate
+ // offset into the table to DumpGCTable.
+ GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
+ unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
+
+ g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
+
+ return Status;
+}
+
+#if !defined(FEATURE_PAL)
+
+void DecodeGCTableEntry (const char *fmt, ...)
+{
+ GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
+ va_list va;
+
+ //
+ // Append the new data to the buffer
+ //
+
+ va_start(va, fmt);
+
+ int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
+ if (cch >= 0)
+ pInfo->cch += cch;
+
+ va_end(va);
+
+ pInfo->buf[pInfo->cch] = '\0';
+
+ //
+ // If there are complete lines in the buffer, decode them.
+ //
+
+ for (;;)
+ {
+ char *pNewLine = strchr(pInfo->buf, '\n');
+
+ if (!pNewLine)
+ break;
+
+ //
+ // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
+ // offset. strtoul returns ULONG_MAX or 0 on failure. 0 is a valid
+ // offset for the first encoding, or while the last offset was 0.
+ //
+
+ if (isxdigit(pInfo->buf[0]))
+ {
+ char *pEnd;
+ ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
+
+ if ( isspace(*pEnd)
+ && -1 != ofs
+ && ( -1 == pInfo->ofs
+ || 0 == pInfo->ofs
+ || ofs > 0))
+ {
+ pInfo->ofs = ofs;
+ *pNewLine = '\0';
+
+ SwitchToFiber(pInfo->pvMainFiber);
+ }
+ }
+ else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
+ {
+ pInfo->ofs = 0;
+ *pNewLine = '\0';
+
+ SwitchToFiber(pInfo->pvMainFiber);
+ }
+
+ //
+ // Shift the remaining data to the start of the buffer
+ //
+
+ strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
+ pInfo->cch = (int)strlen(pInfo->buf);
+ }
+}
+
+
+VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
+{
+ GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
+ GCInfoToken gcInfoToken = { pInfo->table, GCINFO_VERSION };
+ g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
+
+ pInfo->fDoneDecoding = true;
+ SwitchToFiber(pInfo->pvMainFiber);
+}
+#endif // !FEATURE_PAL
+
+BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
+{
+ SOSEHInfo *pInfo = (SOSEHInfo *) token;
+
+ if (pInfo == NULL)
+ {
+ return FALSE;
+ }
+
+ if (pInfo->m_pInfos == NULL)
+ {
+ // First time, initialize structure
+ pInfo->EHCount = totalClauses;
+ pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses];
+ if (pInfo->m_pInfos == NULL)
+ {
+ ReportOOM();
+ return FALSE;
+ }
+ }
+
+ pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo);
+ return TRUE;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to unassembly a managed function. *
+* It tries to print symbolic info for function call, contants... *
+* *
+\**********************************************************************/
+DECLARE_API(u)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ DWORD_PTR dwStartAddr = NULL;
+ BOOL fWithGCInfo = FALSE;
+ BOOL fWithEHInfo = FALSE;
+ BOOL bSuppressLines = FALSE;
+ BOOL bDisplayOffsets = FALSE;
+ BOOL dml = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
+#endif
+ {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
+ {"-n", &bSuppressLines, COBOOL, FALSE},
+ {"-o", &bDisplayOffsets, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
+ {
+ return Status;
+ }
+ // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ ULONG symlines = 0;
+ if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+ bSuppressLines = bSuppressLines || (symlines == 0);
+
+ EnableDMLHolder dmlHolder(dml);
+ // dwStartAddr is either some IP address or a MethodDesc. Start off assuming it's a
+ // MethodDesc.
+ DWORD_PTR methodDesc = dwStartAddr;
+ if (!IsMethodDesc(methodDesc))
+ {
+ // Not a methodDesc, so gotta find it ourselves
+ DWORD_PTR tmpAddr = dwStartAddr;
+ JITTypes jt;
+ DWORD_PTR gcinfoAddr;
+ IP2MethodDesc (tmpAddr, methodDesc, jt,
+ gcinfoAddr);
+ if (!methodDesc || jt == TYPE_UNKNOWN)
+ {
+ // It is not managed code.
+ ExtOut("Unmanaged code\n");
+ UnassemblyUnmanaged(dwStartAddr, bSuppressLines);
+ return Status;
+ }
+ }
+
+ DacpMethodDescData MethodDescData;
+ if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK)
+ {
+ ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr));
+ return Status;
+ }
+
+ if (!MethodDescData.bHasNativeCode)
+ {
+ ExtOut("Not jitted yet\n");
+ return Status;
+ }
+
+ // Get the appropriate code header. If we were passed an MD, then use
+ // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use
+ // that IP to find the code header. This ensures that, for rejitted functions, we
+ // disassemble the rejit version that the user explicitly specified with their IP.
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(
+ g_sos,
+ TO_CDADDR(
+ (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr)
+ ) != S_OK)
+
+ {
+ ExtOut("Unable to get codeHeader information\n");
+ return Status;
+ }
+
+ if (codeHeaderData.MethodStart == 0)
+ {
+ ExtOut("not a valid MethodDesc\n");
+ return Status;
+ }
+
+ if (codeHeaderData.JITType == TYPE_UNKNOWN)
+ {
+ ExtOut("unknown Jit\n");
+ return Status;
+ }
+ else if (codeHeaderData.JITType == TYPE_JIT)
+ {
+ ExtOut("Normal JIT generated code\n");
+ }
+ else if (codeHeaderData.JITType == TYPE_PJIT)
+ {
+ ExtOut("preJIT generated code\n");
+ }
+
+ NameForMD_s(methodDesc, g_mdName, mdNameLen);
+ ExtOut("%S\n", g_mdName);
+ if (codeHeaderData.ColdRegionStart != NULL)
+ {
+ ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n",
+ SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.HotRegionSize,
+ SOS_PTR(codeHeaderData.ColdRegionStart), codeHeaderData.ColdRegionSize);
+ }
+ else
+ {
+ ExtOut("Begin %p, size %x\n", SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.MethodSize);
+ }
+
+#if !defined(FEATURE_PAL)
+ //
+ // Set up to mix gc info with the code if requested
+ //
+
+ GCEncodingInfo gcEncodingInfo = {0};
+
+ // The actual GC Encoding Table, this is updated during the course of the function.
+ gcEncodingInfo.table = NULL;
+
+ // The holder to make sure we clean up the memory for the table
+ ArrayHolder<BYTE> table = NULL;
+
+ if (fWithGCInfo)
+ {
+ // assume that GC encoding table is never more than 40 + methodSize * 2
+ int tableSize = 0;
+ if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
+ !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
+ {
+ ExtOut("<integer overflow>\n");
+ return E_FAIL;
+ }
+
+
+ // Assign the new array to the mutable gcEncodingInfo table and to the
+ // table ArrayHolder to clean this up when the function exits.
+ table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
+
+ if (gcEncodingInfo.table == NULL)
+ {
+ ExtOut("Could not allocate memory to read the gc info.\n");
+ return E_OUTOFMEMORY;
+ }
+
+ memset (gcEncodingInfo.table, 0, tableSize);
+ // We avoid using move here, because we do not want to return
+ if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
+ {
+ ExtOut("Could not read memory %p\n", SOS_PTR(codeHeaderData.GCInfo));
+ return Status;
+ }
+
+ //
+ // Skip the info header
+ //
+ gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
+
+ //
+ // DumpGCTable will call gcPrintf for each encoding. We'd like a "give
+ // me the next encoding" interface, but we're stuck with the callback.
+ // To reconcile this without messing up too much code, we'll create a
+ // fiber to dump the gc table. When we need the next gc encoding,
+ // we'll switch to this fiber. The callback will note the next offset,
+ // and switch back to the main fiber.
+ //
+
+ gcEncodingInfo.ofs = -1;
+ gcEncodingInfo.hotSizeToAdd = 0;
+
+ gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
+ if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
+ gcEncodingInfo.pvMainFiber = GetCurrentFiber();
+
+ if (!gcEncodingInfo.pvMainFiber)
+ return Status;
+
+ gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
+ if (!gcEncodingInfo.pvGCTableFiber)
+ return Status;
+
+ SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
+ }
+#endif
+
+ SOSEHInfo *pInfo = NULL;
+ if (fWithEHInfo)
+ {
+ pInfo = new NOTHROW SOSEHInfo;
+ if (pInfo == NULL)
+ {
+ ReportOOM();
+ }
+ else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK)
+ {
+ ExtOut("Failed to gather EHInfo data\n");
+ delete pInfo;
+ pInfo = NULL;
+ }
+ }
+
+ if (codeHeaderData.ColdRegionStart == NULL)
+ {
+ g_targetMachine->Unassembly (
+ (DWORD_PTR) codeHeaderData.MethodStart,
+ ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
+ dwStartAddr,
+ (DWORD_PTR) MethodDescData.GCStressCodeCopy,
+#if !defined(FEATURE_PAL)
+ fWithGCInfo ? &gcEncodingInfo :
+#endif
+ NULL,
+ pInfo,
+ bSuppressLines,
+ bDisplayOffsets
+ );
+ }
+ else
+ {
+ ExtOut("Hot region:\n");
+ g_targetMachine->Unassembly (
+ (DWORD_PTR) codeHeaderData.MethodStart,
+ ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
+ dwStartAddr,
+ (DWORD_PTR) MethodDescData.GCStressCodeCopy,
+#if !defined(FEATURE_PAL)
+ fWithGCInfo ? &gcEncodingInfo :
+#endif
+ NULL,
+ pInfo,
+ bSuppressLines,
+ bDisplayOffsets
+ );
+
+ ExtOut("Cold region:\n");
+
+#if !defined(FEATURE_PAL)
+ // Displaying gcinfo for a cold region requires knowing the size of
+ // the hot region preceeding.
+ gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
+#endif
+ g_targetMachine->Unassembly (
+ (DWORD_PTR) codeHeaderData.ColdRegionStart,
+ ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
+ dwStartAddr,
+ ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,
+#if !defined(FEATURE_PAL)
+ fWithGCInfo ? &gcEncodingInfo :
+#endif
+ NULL,
+ pInfo,
+ bSuppressLines,
+ bDisplayOffsets
+ );
+
+ }
+
+ if (pInfo)
+ {
+ delete pInfo;
+ pInfo = NULL;
+ }
+
+#if !defined(FEATURE_PAL)
+ if (fWithGCInfo)
+ DeleteFiber(gcEncodingInfo.pvGCTableFiber);
+#endif
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the in-memory stress log *
+* !DumpLog [filename] *
+* will dump the stress log corresponding to the clr.dll *
+* loaded in the debuggee's VAS *
+* !DumpLog -addr <addr_of_StressLog::theLog> [filename] *
+* will dump the stress log associated with any DLL linked *
+* against utilcode.lib, most commonly mscordbi.dll *
+* (e.g. !DumpLog -addr mscordbi!StressLog::theLog) *
+* *
+\**********************************************************************/
+DECLARE_API(DumpLog)
+{
+ INIT_API_NO_RET_ON_FAILURE();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ const char* fileName = "StressLog.txt";
+
+ CLRDATA_ADDRESS StressLogAddress = NULL;
+
+ StringHolder sFileName, sLogAddr;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-addr", &sLogAddr.data, COSTRING, TRUE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&sFileName.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg > 0 && sFileName.data != NULL)
+ {
+ fileName = sFileName.data;
+ }
+
+ // allow users to specify -addr mscordbdi!StressLog::theLog, for example.
+ if (sLogAddr.data != NULL)
+ {
+ StressLogAddress = GetExpression(sLogAddr.data);
+ }
+
+ if (StressLogAddress == NULL)
+ {
+ if (g_bDacBroken)
+ {
+#ifdef FEATURE_PAL
+ ExtOut("No stress log address. DAC is broken; can't get it\n");
+ return E_FAIL;
+#else
+ // Try to find stress log symbols
+ DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
+ StressLogAddress = dwAddr;
+#endif
+ }
+ else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
+ {
+ ExtOut("Unable to find stress log via DAC\n");
+ return E_FAIL;
+ }
+ }
+
+ if (StressLogAddress == NULL)
+ {
+ ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n");
+ return E_FAIL;
+ }
+
+ ExtOut("Attempting to dump Stress log to file '%s'\n", fileName);
+
+
+
+ Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData);
+
+ if (Status == S_OK)
+ ExtOut("SUCCESS: Stress log dumped\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, no file written\n");
+ else
+ ExtOut("FAILURE: Stress log not dumped\n");
+
+ return Status;
+}
+
+#ifdef TRACE_GC
+
+DECLARE_API (DumpGCLog)
+{
+ INIT_API_NODAC();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (GetEEFlavor() == UNKNOWNEE)
+ {
+ ExtOut("CLR not loaded\n");
+ return Status;
+ }
+
+ const char* fileName = "GCLog.txt";
+
+ while (isspace (*args))
+ args ++;
+
+ if (*args != 0)
+ fileName = args;
+
+ DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer");
+ moveN (dwAddr, dwAddr);
+
+ if (dwAddr == 0)
+ {
+ dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer");
+ moveN (dwAddr, dwAddr);
+ if (dwAddr == 0)
+ {
+ ExtOut("Can't get either WKS or SVR GC's log file");
+ return E_FAIL;
+ }
+ }
+
+ ExtOut("Dumping GC log at %08x\n", dwAddr);
+
+ g_bDacBroken = FALSE;
+
+ ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
+
+ Status = E_FAIL;
+
+ HANDLE hGCLog = CreateFileA(
+ fileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hGCLog == INVALID_HANDLE_VALUE)
+ {
+ ExtOut("failed to create file: %d\n", GetLastError());
+ goto exit;
+ }
+
+ int iLogSize = 1024*1024;
+ BYTE* bGCLog = new NOTHROW BYTE[iLogSize];
+ if (bGCLog == NULL)
+ {
+ ReportOOM();
+ goto exit;
+ }
+
+ memset (bGCLog, 0, iLogSize);
+ if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
+ {
+ ExtOut("failed to read memory from %08x\n", dwAddr);
+ }
+
+ int iRealLogSize = iLogSize - 1;
+ while (iRealLogSize >= 0)
+ {
+ if (bGCLog[iRealLogSize] != '*')
+ {
+ break;
+ }
+
+ iRealLogSize--;
+ }
+
+ DWORD dwWritten = 0;
+ WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL);
+
+ Status = S_OK;
+
+exit:
+
+ if (hGCLog != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (hGCLog);
+ }
+
+ if (Status == S_OK)
+ ExtOut("SUCCESS: Stress log dumped\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, no file written\n");
+ else
+ ExtOut("FAILURE: Stress log not dumped\n");
+
+ return Status;
+}
+#endif //TRACE_GC
+
+#ifndef FEATURE_PAL
+DECLARE_API (DumpGCConfigLog)
+{
+ INIT_API();
+#ifdef GC_CONFIG_DRIVEN
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (GetEEFlavor() == UNKNOWNEE)
+ {
+ ExtOut("CLR not loaded\n");
+ return Status;
+ }
+
+ const char* fileName = "GCConfigLog.txt";
+
+ while (isspace (*args))
+ args ++;
+
+ if (*args != 0)
+ fileName = args;
+
+ if (!InitializeHeapData ())
+ {
+ ExtOut("GC Heap not initialized yet.\n");
+ return S_OK;
+ }
+
+ BOOL fIsServerGC = IsServerBuild();
+
+ DWORD_PTR dwAddr = 0;
+ DWORD_PTR dwAddrOffset = 0;
+
+ if (fIsServerGC)
+ {
+ dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer");
+ dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer_offset");
+ }
+ else
+ {
+ dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer");
+ dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer_offset");
+ }
+
+ moveN (dwAddr, dwAddr);
+ moveN (dwAddrOffset, dwAddrOffset);
+
+ if (dwAddr == 0)
+ {
+ ExtOut("Can't get either WKS or SVR GC's config log buffer");
+ return E_FAIL;
+ }
+
+ ExtOut("Dumping GC log at %08x\n", dwAddr);
+
+ g_bDacBroken = FALSE;
+
+ ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
+
+ Status = E_FAIL;
+
+ HANDLE hGCLog = CreateFileA(
+ fileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hGCLog == INVALID_HANDLE_VALUE)
+ {
+ ExtOut("failed to create file: %d\n", GetLastError());
+ goto exit;
+ }
+
+ {
+ int iLogSize = (int)dwAddrOffset;
+
+ ArrayHolder<BYTE> bGCLog = new NOTHROW BYTE[iLogSize];
+ if (bGCLog == NULL)
+ {
+ ReportOOM();
+ goto exit;
+ }
+
+ memset (bGCLog, 0, iLogSize);
+ if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
+ {
+ ExtOut("failed to read memory from %08x\n", dwAddr);
+ }
+
+ SetFilePointer (hGCLog, 0, 0, FILE_END);
+ DWORD dwWritten;
+ WriteFile (hGCLog, bGCLog, iLogSize, &dwWritten, NULL);
+ }
+
+ Status = S_OK;
+
+exit:
+
+ if (hGCLog != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (hGCLog);
+ }
+
+ if (Status == S_OK)
+ ExtOut("SUCCESS: Stress log dumped\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, no file written\n");
+ else
+ ExtOut("FAILURE: Stress log not dumped\n");
+
+ return Status;
+#else
+ ExtOut("Not implemented\n");
+ return S_OK;
+#endif //GC_CONFIG_DRIVEN
+}
+#endif // FEATURE_PAL
+
+#ifdef GC_CONFIG_DRIVEN
+static const char * const str_interesting_data_points[] =
+{
+ "pre short", // 0
+ "post short", // 1
+ "merged pins", // 2
+ "converted pins", // 3
+ "pre pin", // 4
+ "post pin", // 5
+ "pre and post pin", // 6
+ "pre short padded", // 7
+ "post short padded", // 7
+};
+
+static const char * const str_heap_compact_reasons[] =
+{
+ "low on ephemeral space",
+ "high fragmetation",
+ "couldn't allocate gaps",
+ "user specfied compact LOH",
+ "last GC before OOM",
+ "induced compacting GC",
+ "fragmented gen0 (ephemeral GC)",
+ "high memory load (ephemeral GC)",
+ "high memory load and frag",
+ "very high memory load and frag",
+ "no gc mode"
+};
+
+static BOOL gc_heap_compact_reason_mandatory_p[] =
+{
+ TRUE, //compact_low_ephemeral = 0,
+ FALSE, //compact_high_frag = 1,
+ TRUE, //compact_no_gaps = 2,
+ TRUE, //compact_loh_forced = 3,
+ TRUE, //compact_last_gc = 4
+ TRUE, //compact_induced_compacting = 5,
+ FALSE, //compact_fragmented_gen0 = 6,
+ FALSE, //compact_high_mem_load = 7,
+ TRUE, //compact_high_mem_frag = 8,
+ TRUE, //compact_vhigh_mem_frag = 9,
+ TRUE //compact_no_gc_mode = 10
+};
+
+static const char * const str_heap_expand_mechanisms[] =
+{
+ "reused seg with normal fit",
+ "reused seg with best fit",
+ "expand promoting eph",
+ "expand with a new seg",
+ "no memory for a new seg",
+ "expand in next full GC"
+};
+
+static const char * const str_bit_mechanisms[] =
+{
+ "using mark list",
+ "demotion"
+};
+
+static const char * const str_gc_global_mechanisms[] =
+{
+ "concurrent GCs",
+ "compacting GCs",
+ "promoting GCs",
+ "GCs that did demotion",
+ "card bundles",
+ "elevation logic"
+};
+
+void PrintInterestingGCInfo(DacpGCInterestingInfoData* dataPerHeap)
+{
+ ExtOut("Interesting data points\n");
+ size_t* data = dataPerHeap->interestingDataPoints;
+ for (int i = 0; i < NUM_GC_DATA_POINTS; i++)
+ {
+ ExtOut("%20s: %d\n", str_interesting_data_points[i], data[i]);
+ }
+
+ ExtOut("\nCompacting reasons\n");
+ data = dataPerHeap->compactReasons;
+ for (int i = 0; i < MAX_COMPACT_REASONS_COUNT; i++)
+ {
+ ExtOut("[%s]%35s: %d\n", (gc_heap_compact_reason_mandatory_p[i] ? "M" : "W"), str_heap_compact_reasons[i], data[i]);
+ }
+
+ ExtOut("\nExpansion mechanisms\n");
+ data = dataPerHeap->expandMechanisms;
+ for (int i = 0; i < MAX_EXPAND_MECHANISMS_COUNT; i++)
+ {
+ ExtOut("%30s: %d\n", str_heap_expand_mechanisms[i], data[i]);
+ }
+
+ ExtOut("\nOther mechanisms enabled\n");
+ data = dataPerHeap->bitMechanisms;
+ for (int i = 0; i < MAX_GC_MECHANISM_BITS_COUNT; i++)
+ {
+ ExtOut("%20s: %d\n", str_bit_mechanisms[i], data[i]);
+ }
+}
+#endif //GC_CONFIG_DRIVEN
+
+DECLARE_API(DumpGCData)
+{
+ INIT_API();
+
+#ifdef GC_CONFIG_DRIVEN
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (!InitializeHeapData ())
+ {
+ ExtOut("GC Heap not initialized yet.\n");
+ return S_OK;
+ }
+
+ DacpGCInterestingInfoData interestingInfo;
+ interestingInfo.RequestGlobal(g_sos);
+ for (int i = 0; i < MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
+ {
+ ExtOut("%-30s: %d\n", str_gc_global_mechanisms[i], interestingInfo.globalMechanisms[i]);
+ }
+
+ ExtOut("\n[info per heap]\n");
+
+ if (!IsServerBuild())
+ {
+ if (interestingInfo.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting interesting GC info\n");
+ return E_FAIL;
+ }
+
+ PrintInterestingGCInfo(&interestingInfo);
+ }
+ else
+ {
+ DWORD dwNHeaps = GetGcHeapCount();
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ if (interestingInfo.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Heap %d: Error requesting interesting GC info\n", n);
+ return E_FAIL;
+ }
+
+ ExtOut("--------info for heap %d--------\n", n);
+ PrintInterestingGCInfo(&interestingInfo);
+ ExtOut("\n");
+ }
+ }
+
+ return S_OK;
+#else
+ ExtOut("Not implemented\n");
+ return S_OK;
+#endif //GC_CONFIG_DRIVEN
+}
+
+#ifndef FEATURE_PAL
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the build number and type of the *
+* mscoree.dll *
+* *
+\**********************************************************************/
+DECLARE_API (EEVersion)
+{
+ INIT_API();
+
+ EEFLAVOR eef = GetEEFlavor();
+ if (eef == UNKNOWNEE) {
+ ExtOut("CLR not loaded\n");
+ return Status;
+ }
+
+ if (g_ExtSymbols2) {
+ VS_FIXEDFILEINFO version;
+
+ BOOL ret = GetEEVersion(&version);
+
+ if (ret)
+ {
+ if (version.dwFileVersionMS != (DWORD)-1)
+ {
+ ExtOut("%u.%u.%u.%u",
+ HIWORD(version.dwFileVersionMS),
+ LOWORD(version.dwFileVersionMS),
+ HIWORD(version.dwFileVersionLS),
+ LOWORD(version.dwFileVersionLS));
+ if (version.dwFileFlags & VS_FF_DEBUG)
+ {
+ ExtOut(" Checked or debug build");
+ }
+ else
+ {
+ BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr);
+
+ if (fRet)
+ ExtOut(" retail");
+ else
+ ExtOut(" free");
+ }
+
+ ExtOut("\n");
+ }
+ }
+ }
+
+ if (!InitializeHeapData ())
+ ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
+ else if (IsServerBuild())
+ ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount());
+ else
+ ExtOut("Workstation mode\n");
+
+ if (!GetGcStructuresValid())
+ {
+ ExtOut("In plan phase of garbage collection\n");
+ }
+
+ // Print SOS version
+ VS_FIXEDFILEINFO sosVersion;
+ if (GetSOSVersion(&sosVersion))
+ {
+ if (sosVersion.dwFileVersionMS != (DWORD)-1)
+ {
+ ExtOut("SOS Version: %u.%u.%u.%u",
+ HIWORD(sosVersion.dwFileVersionMS),
+ LOWORD(sosVersion.dwFileVersionMS),
+ HIWORD(sosVersion.dwFileVersionLS),
+ LOWORD(sosVersion.dwFileVersionLS));
+ if (sosVersion.dwFileFlags & VS_FF_DEBUG)
+ {
+ ExtOut(" Checked or debug build");
+ }
+ else
+ {
+ ExtOut(" retail build");
+ }
+
+ ExtOut("\n");
+ }
+ }
+ return Status;
+}
+#endif // FEATURE_PAL
+
+#ifndef FEATURE_PAL
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to print the environment setting for *
+* the current process. *
+* *
+\**********************************************************************/
+DECLARE_API (ProcInfo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (IsDumpFile())
+ {
+ ExtOut("!ProcInfo is not supported on a dump file.\n");
+ return Status;
+ }
+
+#define INFO_ENV 0x00000001
+#define INFO_TIME 0x00000002
+#define INFO_MEM 0x00000004
+#define INFO_ALL 0xFFFFFFFF
+
+ DWORD fProcInfo = INFO_ALL;
+
+ if (_stricmp (args, "-env") == 0) {
+ fProcInfo = INFO_ENV;
+ }
+
+ if (_stricmp (args, "-time") == 0) {
+ fProcInfo = INFO_TIME;
+ }
+
+ if (_stricmp (args, "-mem") == 0) {
+ fProcInfo = INFO_MEM;
+ }
+
+ if (fProcInfo & INFO_ENV) {
+ ExtOut("---------------------------------------\n");
+ ExtOut("Environment\n");
+ ULONG64 pPeb;
+ g_ExtSystem->GetCurrentProcessPeb(&pPeb);
+
+ static ULONG Offset_ProcessParam = -1;
+ static ULONG Offset_Environment = -1;
+ if (Offset_ProcessParam == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase)))
+ {
+ if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "ProcessParameters", &Offset_ProcessParam)))
+ Offset_ProcessParam = -1;
+ }
+ if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "Environment", &Offset_Environment)))
+ Offset_Environment = -1;
+ }
+ }
+ }
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_ProcessParam == -1)
+ Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters);
+
+ if (Offset_Environment == -1)
+ Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment);
+
+
+ ULONG64 addr = pPeb + Offset_ProcessParam;
+ DWORD_PTR value;
+ g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
+ addr = value + Offset_Environment;
+ g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
+
+ static WCHAR buffer[DT_OS_PAGE_SIZE/2];
+ ULONG readBytes = DT_OS_PAGE_SIZE;
+ ULONG64 Page;
+ if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK
+ && Page > 0)
+ {
+ ULONG uPageSize = (ULONG)(ULONG_PTR)Page;
+ if (readBytes > uPageSize) {
+ readBytes = uPageSize;
+ }
+ }
+ addr = value;
+ while (1) {
+ if (IsInterrupt())
+ return Status;
+ if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL)))
+ break;
+ addr += readBytes;
+ WCHAR *pt = buffer;
+ WCHAR *end = pt;
+ while (pt < &buffer[DT_OS_PAGE_SIZE/2]) {
+ end = _wcschr (pt, L'\0');
+ if (end == NULL) {
+ char format[20];
+ sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
+ ExtOut(format, pt);
+ break;
+ }
+ else if (end == pt) {
+ break;
+ }
+ ExtOut("%S\n", pt);
+ pt = end + 1;
+ }
+ if (end == pt) {
+ break;
+ }
+ }
+ }
+
+ HANDLE hProcess = INVALID_HANDLE_VALUE;
+ if (fProcInfo & (INFO_TIME | INFO_MEM)) {
+ ULONG64 handle;
+ g_ExtSystem->GetCurrentProcessHandle(&handle);
+ hProcess = (HANDLE)handle;
+ }
+
+ if (!IsDumpFile() && fProcInfo & INFO_TIME) {
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+
+ typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
+ static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
+ if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
+ HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
+ if (hstat != 0)
+ {
+ pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
+ FreeLibrary (hstat);
+ }
+ else
+ pFntGetProcessTimes = NULL;
+ }
+
+ if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) {
+ ExtOut("---------------------------------------\n");
+ ExtOut("Process Times\n");
+ static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"};
+ SYSTEMTIME SystemTime;
+ FILETIME LocalFileTime;
+ if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime)
+ && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) {
+ ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n",
+ SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay,
+ SystemTime.wHour, SystemTime.wMinute,
+ SystemTime.wSecond, SystemTime.wMilliseconds/10);
+ }
+
+ DWORD nDay = 0;
+ DWORD nHour = 0;
+ DWORD nMin = 0;
+ DWORD nSec = 0;
+ DWORD nHundred = 0;
+
+ ULONG64 totalTime;
+
+ totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32);
+ nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
+ totalTime %= 24*3600*10000000ui64;
+ nHour = (DWORD)(totalTime/(3600*10000000ui64));
+ totalTime %= 3600*10000000ui64;
+ nMin = (DWORD)(totalTime/(60*10000000));
+ totalTime %= 60*10000000;
+ nSec = (DWORD)(totalTime/10000000);
+ totalTime %= 10000000;
+ nHundred = (DWORD)(totalTime/100000);
+ ExtOut("Kernel CPU time : %d days %02d:%02d:%02d.%02d\n",
+ nDay, nHour, nMin, nSec, nHundred);
+
+ DWORD sDay = nDay;
+ DWORD sHour = nHour;
+ DWORD sMin = nMin;
+ DWORD sSec = nSec;
+ DWORD sHundred = nHundred;
+
+ totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32);
+ nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
+ totalTime %= 24*3600*10000000ui64;
+ nHour = (DWORD)(totalTime/(3600*10000000ui64));
+ totalTime %= 3600*10000000ui64;
+ nMin = (DWORD)(totalTime/(60*10000000));
+ totalTime %= 60*10000000;
+ nSec = (DWORD)(totalTime/10000000);
+ totalTime %= 10000000;
+ nHundred = (DWORD)(totalTime/100000);
+ ExtOut("User CPU time : %d days %02d:%02d:%02d.%02d\n",
+ nDay, nHour, nMin, nSec, nHundred);
+
+ sDay += nDay;
+ sHour += nHour;
+ sMin += nMin;
+ sSec += nSec;
+ sHundred += nHundred;
+ if (sHundred >= 100) {
+ sSec += sHundred/100;
+ sHundred %= 100;
+ }
+ if (sSec >= 60) {
+ sMin += sSec/60;
+ sSec %= 60;
+ }
+ if (sMin >= 60) {
+ sHour += sMin/60;
+ sMin %= 60;
+ }
+ if (sHour >= 24) {
+ sDay += sHour/24;
+ sHour %= 24;
+ }
+ ExtOut("Total CPU time : %d days %02d:%02d:%02d.%02d\n",
+ sDay, sHour, sMin, sSec, sHundred);
+ }
+ }
+
+ if (!IsDumpFile() && fProcInfo & INFO_MEM) {
+ typedef
+ NTSTATUS
+ (NTAPI
+ *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+
+ static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
+ if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
+ HINSTANCE hstat = LoadLibrary ("ntdll.dll");
+ if (hstat != 0)
+ {
+ pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
+ FreeLibrary (hstat);
+ }
+ else
+ pFntNtQueryInformationProcess = NULL;
+ }
+ VM_COUNTERS memory;
+ if (pFntNtQueryInformationProcess &&
+ NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) {
+ ExtOut("---------------------------------------\n");
+ ExtOut("Process Memory\n");
+ ExtOut("WorkingSetSize: %8d KB PeakWorkingSetSize: %8d KB\n",
+ memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024);
+ ExtOut("VirtualSize: %8d KB PeakVirtualSize: %8d KB\n",
+ memory.VirtualSize/1024, memory.PeakVirtualSize/1024);
+ ExtOut("PagefileUsage: %8d KB PeakPagefileUsage: %8d KB\n",
+ memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024);
+ }
+
+ MEMORYSTATUS memstat;
+ GlobalMemoryStatus (&memstat);
+ ExtOut("---------------------------------------\n");
+ ExtOut("%ld percent of memory is in use.\n\n",
+ memstat.dwMemoryLoad);
+ ExtOut("Memory Availability (Numbers in MB)\n\n");
+ ExtOut(" %8s %8s\n", "Total", "Avail");
+ ExtOut("Physical Memory %8d %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024);
+ ExtOut("Page File %8d %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024);
+ ExtOut("Virtual Memory %8d %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024);
+ }
+
+ return Status;
+}
+#endif // FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the address of EE data for a *
+* metadata token. *
+* *
+\**********************************************************************/
+DECLARE_API(Token2EE)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder DllName;
+ ULONG64 token = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&DllName.data, COSTRING},
+ {&token, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg!=2)
+ {
+ ExtOut("Usage: !Token2EE module_name mdToken\n");
+ ExtOut(" You can pass * for module_name to search all modules.\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ int numModule;
+ ArrayHolder<DWORD_PTR> moduleList = NULL;
+
+ if (strcmp(DllName.data, "*") == 0)
+ {
+ moduleList = ModuleFromName(NULL, &numModule);
+ }
+ else
+ {
+ moduleList = ModuleFromName(DllName.data, &numModule);
+ }
+
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n");
+ }
+ else
+ {
+ for (int i = 0; i < numModule; i ++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (i > 0)
+ {
+ ExtOut("--------------------------------------\n");
+ }
+
+ DWORD_PTR dwAddr = moduleList[i];
+ WCHAR FileName[MAX_LONGPATH];
+ FileNameForModule(dwAddr, FileName);
+
+ // We'd like a short form for this output
+ LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pszFilename == NULL)
+ {
+ pszFilename = FileName;
+ }
+ else
+ {
+ pszFilename++; // skip past the last "\" character
+ }
+
+ DMLOut("Module: %s\n", DMLModule(dwAddr));
+ ExtOut("Assembly: %S\n", pszFilename);
+
+ GetInfoFromModule(dwAddr, (ULONG)token);
+ }
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the address of EE data for a *
+* metadata token. *
+* *
+\**********************************************************************/
+DECLARE_API(Name2EE)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder DllName, TypeName;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&DllName.data, COSTRING},
+ {&TypeName.data, COSTRING}
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if (nArg == 1)
+ {
+ // The input may be in the form <modulename>!<type>
+ // If so, do some surgery on the input params.
+
+ // There should be only 1 ! character
+ LPSTR pszSeperator = strchr (DllName.data, '!');
+ if (pszSeperator != NULL)
+ {
+ if (strchr (pszSeperator + 1, '!') == NULL)
+ {
+ size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1;
+ TypeName.data = new NOTHROW char[capacity_TypeName_data];
+ if (TypeName.data)
+ {
+ // get the type name,
+ strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1);
+ // and truncate DllName
+ *pszSeperator = '\0';
+
+ // Do some extra validation
+ if (strlen (DllName.data) >= 1 &&
+ strlen (TypeName.data) > 1)
+ {
+ nArg = 2;
+ }
+ }
+ }
+ }
+ }
+
+ if (nArg != 2)
+ {
+ ExtOut("Usage: " SOSPrefix "name2ee module_name item_name\n");
+ ExtOut(" or " SOSPrefix "name2ee module_name!item_name\n");
+ ExtOut(" use * for module_name to search all loaded modules\n");
+ ExtOut("Examples: " SOSPrefix "name2ee mscorlib.dll System.String.ToString\n");
+ ExtOut(" " SOSPrefix "name2ee *!System.String\n");
+ return Status;
+ }
+
+ int numModule;
+ ArrayHolder<DWORD_PTR> moduleList = NULL;
+ if (strcmp(DllName.data, "*") == 0)
+ {
+ moduleList = ModuleFromName(NULL, &numModule);
+ }
+ else
+ {
+ moduleList = ModuleFromName(DllName.data, &numModule);
+ }
+
+
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n", DllName.data);
+ }
+ else
+ {
+ for (int i = 0; i < numModule; i ++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (i > 0)
+ {
+ ExtOut("--------------------------------------\n");
+ }
+
+ DWORD_PTR dwAddr = moduleList[i];
+ WCHAR FileName[MAX_LONGPATH];
+ FileNameForModule (dwAddr, FileName);
+
+ // We'd like a short form for this output
+ LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pszFilename == NULL)
+ {
+ pszFilename = FileName;
+ }
+ else
+ {
+ pszFilename++; // skip past the last "\" character
+ }
+
+ DMLOut("Module: %s\n", DMLModule(dwAddr));
+ ExtOut("Assembly: %S\n", pszFilename);
+ GetInfoFromName(dwAddr, TypeName.data);
+ }
+ }
+
+ return Status;
+}
+
+
+#ifndef FEATURE_PAL
+DECLARE_API(PathTo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR root = NULL;
+ DWORD_PTR target = NULL;
+ BOOL dml = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&root, COHEX},
+ {&target, COHEX},
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (root == 0 || target == 0)
+ {
+ ExtOut("Invalid argument %s\n", args);
+ return Status;
+ }
+
+ GCRootImpl gcroot;
+ bool result = gcroot.PrintPathToObject(root, target);
+
+ if (!result)
+ ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target));
+
+ return Status;
+}
+#endif
+
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function finds all roots (on stack or in handles) for a *
+* given object. *
+* *
+\**********************************************************************/
+DECLARE_API(GCRoot)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bNoStacks = FALSE;
+ DWORD_PTR obj = NULL;
+ BOOL dml = FALSE;
+ BOOL all = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-nostacks", &bNoStacks, COBOOL, FALSE},
+ {"-all", &all, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+
+ { // vptr, type
+ {&obj, COHEX}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (obj == 0)
+ {
+ ExtOut("Invalid argument %s\n", args);
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ GCRootImpl gcroot;
+ int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE);
+
+ if (IsInterrupt())
+ ExtOut("Interrupted, data may be incomplete.\n");
+
+ if (all)
+ ExtOut("Found %d roots.\n", i);
+ else
+ ExtOut("Found %d unique roots (run '!GCRoot -all' to see all roots).\n", i);
+
+ return Status;
+}
+
+DECLARE_API(GCWhere)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ BOOL bGetBrick;
+ BOOL bGetCard;
+ TADDR taddrObj = 0;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-brick", &bGetBrick, COBOOL, FALSE},
+ {"-card", &bGetCard, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taddrObj, COHEX}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ // Obtain allocation context for each managed thread.
+ AllocInfo allocInfo;
+ allocInfo.Init();
+
+ TADDR_SEGINFO trngSeg = { 0, 0, 0 };
+ TADDR_RANGE allocCtx = { 0, 0 };
+ int gen = -1;
+ BOOL bLarge = FALSE;
+ BOOL bFound = FALSE;
+
+ size_t size = 0;
+ if (sos::IsObject(taddrObj))
+ {
+ TADDR taddrMT;
+ BOOL bContainsPointers;
+ if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
+ !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers))
+ {
+ ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n",
+ SOS_PTR(taddrObj));
+ }
+ }
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting gc heap details\n");
+ return Status;
+ }
+
+ if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
+ {
+ ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated " WIN64_8SPACES " size\n");
+ ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
+ SOS_PTR(taddrObj), gen, 0, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
+ bFound = TRUE;
+ }
+ }
+ else
+ {
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return Status;
+ }
+
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = gcheap.HeapCount;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
+ {
+ ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n");
+ ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
+ SOS_PTR(taddrObj), gen, n, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
+ bFound = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!bFound)
+ {
+ ExtOut("Address %#p not found in the managed heap.\n", SOS_PTR(taddrObj));
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+DECLARE_API(FindRoots)
+{
+#ifndef FEATURE_PAL
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (IsDumpFile())
+ {
+ ExtOut("!FindRoots is not supported on a dump file.\n");
+ return Status;
+ }
+
+ LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2]
+ StringHolder sgen;
+ TADDR taObj = NULL;
+ BOOL dml = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-gen", &sgen.data, COSTRING, TRUE},
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taObj, COHEX}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (sgen.data != NULL)
+ {
+ if (_stricmp(sgen.data, "any") == 0)
+ {
+ gen = -1;
+ }
+ else
+ {
+ gen = GetExpression(sgen.data);
+ }
+ }
+ if ((gen < -1 || gen > 2) && (taObj == 0))
+ {
+ ExtOut("Incorrect options. Usage:\n\t!FindRoots -gen <N>\n\t\twhere N is 0, 1, 2, or \"any\". OR\n\t!FindRoots <obj>\n");
+ return Status;
+ }
+
+ if (gen >= -1 && gen <= 2)
+ {
+ IXCLRDataProcess2* idp2 = NULL;
+ if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
+ {
+ ExtOut("Your version of the runtime/DAC do not support this command.\n");
+ return Status;
+ }
+
+ // Request GC_MARK_END notifications from debuggee
+ GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } };
+ idp2->SetGcNotification(gea);
+ // ... and register the notification handler
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
+ // the above notification is removed in CNotification::OnGcEvent()
+ }
+ else
+ {
+ // verify that the last event in the debugger was indeed a CLRN exception
+ DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
+ CNotification Notification;
+
+ if (!CheckCLRNotificationEvent(&dle))
+ {
+ ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n");
+ ExtOut("At this time !GCRoot should be used instead.\n");
+ return Status;
+ }
+ // validate argument
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ if (g_snapshot.GetHeap(taObj) == NULL)
+ {
+ ExtOut("Address %#p is not in the managed heap.\n", SOS_PTR(taObj));
+ return Status;
+ }
+
+ int ogen = g_snapshot.GetGeneration(taObj);
+ if (ogen > CNotification::GetCondemnedGen())
+ {
+ DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
+ DMLObject(taObj), SOS_PTR(taObj), ogen, CNotification::GetCondemnedGen());
+ return Status;
+ }
+
+ GCRootImpl gcroot;
+ int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj);
+
+ ExtOut("Found %d roots.\n", roots);
+ }
+
+ return Status;
+#else
+ return E_NOTIMPL;
+#endif
+}
+
+class GCHandleStatsForDomains
+{
+public:
+ const static int SHARED_DOMAIN_INDEX = 0;
+ const static int SYSTEM_DOMAIN_INDEX = 1;
+
+ GCHandleStatsForDomains()
+ : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL)
+ {
+ }
+
+ ~GCHandleStatsForDomains()
+ {
+ if (m_pStatistics)
+ {
+ if (m_singleDomainMode)
+ delete m_pStatistics;
+ else
+ delete [] m_pStatistics;
+ }
+
+ if (m_pDomainPointers)
+ delete [] m_pDomainPointers;
+ }
+
+ BOOL Init(BOOL singleDomainMode)
+ {
+ m_singleDomainMode = singleDomainMode;
+ if (m_singleDomainMode)
+ {
+ m_numDomains = 1;
+ m_pStatistics = new NOTHROW GCHandleStatistics();
+ if (m_pStatistics == NULL)
+ return FALSE;
+ }
+ else
+ {
+ DacpAppDomainStoreData adsData;
+ if (adsData.Request(g_sos) != S_OK)
+ return FALSE;
+
+ m_numDomains = adsData.DomainCount + 2;
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount + 2];
+ if (pArray == NULL)
+ return FALSE;
+
+ pArray[SHARED_DOMAIN_INDEX] = adsData.sharedDomain;
+ pArray[SYSTEM_DOMAIN_INDEX] = adsData.systemDomain;
+
+ if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+2, NULL) != S_OK)
+ return FALSE;
+
+ m_pDomainPointers = pArray.Detach();
+ m_pStatistics = new NOTHROW GCHandleStatistics[adsData.DomainCount + 2];
+ if (m_pStatistics == NULL)
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const
+ {
+ if (m_singleDomainMode)
+ {
+ // You can pass NULL appDomainPtr if you are in singleDomainMode
+ return m_pStatistics;
+ }
+ else
+ {
+ for (int i=0; i < m_numDomains; i++)
+ if (m_pDomainPointers[i] == appDomainPtr)
+ return m_pStatistics + i;
+ }
+
+ return NULL;
+ }
+
+
+ GCHandleStatistics *GetStatistics(int appDomainIndex) const
+ {
+ SOS_Assert(appDomainIndex >= 0);
+ SOS_Assert(appDomainIndex < m_numDomains);
+
+ return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex;
+ }
+
+ int GetNumDomains() const
+ {
+ return m_numDomains;
+ }
+
+ CLRDATA_ADDRESS GetDomain(int index) const
+ {
+ SOS_Assert(index >= 0);
+ SOS_Assert(index < m_numDomains);
+ return m_pDomainPointers[index];
+ }
+
+private:
+ BOOL m_singleDomainMode;
+ int m_numDomains;
+ GCHandleStatistics *m_pStatistics;
+ CLRDATA_ADDRESS *m_pDomainPointers;
+};
+
+class GCHandlesImpl
+{
+public:
+ GCHandlesImpl(PCSTR args)
+ : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0)
+ {
+ ArrayHolder<char> type = NULL;
+ CMDOption option[] =
+ {
+ {"-perdomain", &mPerDomain, COBOOL, FALSE},
+ {"-stat", &mStat, COBOOL, FALSE},
+ {"-type", &type, COSTRING, TRUE},
+ {"/d", &mDML, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
+ sos::Throw<sos::Exception>("Failed to parse command line arguments.");
+
+ if (type != NULL)
+ if (_stricmp(type, "Pinned") == 0)
+ mType = HNDTYPE_PINNED;
+ else if (_stricmp(type, "RefCounted") == 0)
+ mType = HNDTYPE_REFCOUNTED;
+ else if (_stricmp(type, "WeakShort") == 0)
+ mType = HNDTYPE_WEAK_SHORT;
+ else if (_stricmp(type, "WeakLong") == 0)
+ mType = HNDTYPE_WEAK_LONG;
+ else if (_stricmp(type, "Strong") == 0)
+ mType = HNDTYPE_STRONG;
+ else if (_stricmp(type, "Variable") == 0)
+ mType = HNDTYPE_VARIABLE;
+ else if (_stricmp(type, "AsyncPinned") == 0)
+ mType = HNDTYPE_ASYNCPINNED;
+ else if (_stricmp(type, "SizedRef") == 0)
+ mType = HNDTYPE_SIZEDREF;
+ else if (_stricmp(type, "Dependent") == 0)
+ mType = HNDTYPE_DEPENDENT;
+ else if (_stricmp(type, "WeakWinRT") == 0)
+ mType = HNDTYPE_WEAK_WINRT;
+ else
+ sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr());
+ }
+
+ void Run()
+ {
+ EnableDMLHolder dmlHolder(mDML);
+
+ mOut.ReInit(6, POINTERSIZE_HEX, AlignRight);
+ mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX);
+ mOut.SetColAlignment(1, AlignLeft);
+
+ if (mHandleStat.Init(!mPerDomain) == FALSE)
+ sos::Throw<sos::Exception>("Error getting per-appdomain handle information");
+
+ if (!mStat)
+ mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type");
+
+ WalkHandles();
+
+ for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++)
+ {
+ GCHandleStatistics *pStats = mHandleStat.GetStatistics(i);
+
+ if (mPerDomain)
+ {
+ Print( "------------------------------------------------------------------------------\n");
+ Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i)));
+
+ if (i == GCHandleStatsForDomains::SHARED_DOMAIN_INDEX)
+ Print(" (Shared Domain)\n");
+ else if (i == GCHandleStatsForDomains::SYSTEM_DOMAIN_INDEX)
+ Print(" (System Domain)\n");
+ else
+ Print("\n");
+ }
+
+ if (!mStat)
+ Print("\n");
+ PrintGCStat(&pStats->hs);
+
+ // Don't print handle stats if the user has filtered by type. All handles will be the same
+ // type, and the total count will be displayed by PrintGCStat.
+ if (mType == (unsigned int)~0)
+ {
+ Print("\n");
+ PrintGCHandleStats(pStats);
+ }
+ }
+ }
+
+private:
+ void WalkHandles()
+ {
+ ToRelease<ISOSHandleEnum> handles;
+ if (FAILED(g_sos->GetHandleEnum(&handles)))
+ {
+ if (IsMiniDumpFile())
+ sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information.");
+ else
+ sos::Throw<sos::Exception>("Failed to walk the handle table.");
+ }
+
+ // GCC can't handle stacks which are too large.
+#ifndef FEATURE_PAL
+ SOSHandleData data[256];
+#else
+ SOSHandleData data[4];
+#endif
+
+ unsigned int fetched = 0;
+ HRESULT hr = S_OK;
+ do
+ {
+ if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
+ {
+ ExtOut("Error %x while walking the handle table.\n", hr);
+ break;
+ }
+
+ WalkHandles(data, fetched);
+ } while (_countof(data) == fetched);
+ }
+
+ void WalkHandles(SOSHandleData data[], unsigned int count)
+ {
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ sos::CheckInterrupt();
+
+ if (mType != (unsigned int)~0 && mType != data[i].Type)
+ continue;
+
+ GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain);
+ TADDR objAddr = 0;
+ TADDR mtAddr = 0;
+ size_t size = 0;
+ const WCHAR *mtName = 0;
+ const char *type = 0;
+
+ if (FAILED(MOVE(objAddr, data[i].Handle)))
+ {
+ objAddr = 0;
+ mtName = W("<error>");
+ }
+ else
+ {
+ sos::Object obj(TO_TADDR(objAddr));
+ mtAddr = obj.GetMT();
+ if (sos::MethodTable::IsFreeMT(mtAddr))
+ {
+ mtName = W("<free>");
+ }
+ else if (!sos::MethodTable::IsValid(mtAddr))
+ {
+ mtName = W("<error>");
+ }
+ else
+ {
+ size = obj.GetSize();
+ if (mType == (unsigned int)~0 || mType == data[i].Type)
+ pStats->hs.Add(obj.GetMT(), (DWORD)size);
+ }
+ }
+
+ switch(data[i].Type)
+ {
+ case HNDTYPE_PINNED:
+ type = "Pinned";
+ if (pStats) pStats->pinnedHandleCount++;
+ break;
+ case HNDTYPE_REFCOUNTED:
+ type = "RefCounted";
+ if (pStats) pStats->refCntHandleCount++;
+ break;
+ case HNDTYPE_STRONG:
+ type = "Strong";
+ if (pStats) pStats->strongHandleCount++;
+ break;
+ case HNDTYPE_WEAK_SHORT:
+ type = "WeakShort";
+ if (pStats) pStats->weakShortHandleCount++;
+ break;
+ case HNDTYPE_WEAK_LONG:
+ type = "WeakLong";
+ if (pStats) pStats->weakLongHandleCount++;
+ break;
+ case HNDTYPE_ASYNCPINNED:
+ type = "AsyncPinned";
+ if (pStats) pStats->asyncPinnedHandleCount++;
+ break;
+ case HNDTYPE_VARIABLE:
+ type = "Variable";
+ if (pStats) pStats->variableCount++;
+ break;
+ case HNDTYPE_SIZEDREF:
+ type = "SizedRef";
+ if (pStats) pStats->sizedRefCount++;
+ break;
+ case HNDTYPE_DEPENDENT:
+ type = "Dependent";
+ if (pStats) pStats->dependentCount++;
+ break;
+ case HNDTYPE_WEAK_WINRT:
+ type = "WeakWinRT";
+ if (pStats) pStats->weakWinRTHandleCount++;
+ break;
+ default:
+ DebugBreak();
+ type = "Unknown";
+ pStats->unknownHandleCount++;
+ break;
+ }
+
+ if (type && !mStat)
+ {
+ sos::MethodTable mt = mtAddr;
+ if (mtName == 0)
+ mtName = mt.GetName();
+
+ if (data[i].Type == HNDTYPE_REFCOUNTED)
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName);
+ else if (data[i].Type == HNDTYPE_DEPENDENT)
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName);
+ else if (data[i].Type == HNDTYPE_WEAK_WINRT)
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName);
+ else
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName);
+ }
+ }
+ }
+
+ inline void PrintHandleRow(const char *text, int count)
+ {
+ if (count)
+ mOut.WriteRow(text, Decimal(count));
+ }
+
+ void PrintGCHandleStats(GCHandleStatistics *pStats)
+ {
+ Print("Handles:\n");
+ mOut.ReInit(2, 21, AlignLeft, 4);
+
+ PrintHandleRow("Strong Handles:", pStats->strongHandleCount);
+ PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount);
+ PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount);
+ PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount);
+ PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount);
+ PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount);
+ PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount);
+ PrintHandleRow("Variable Handles:", pStats->variableCount);
+ PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount);
+ PrintHandleRow("Dependent Handles:", pStats->dependentCount);
+ PrintHandleRow("Other Handles:", pStats->unknownHandleCount);
+ }
+
+private:
+ BOOL mPerDomain, mStat, mDML;
+ unsigned int mType;
+ TableOutput mOut;
+ GCHandleStatsForDomains mHandleStat;
+};
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps GC Handle statistics *
+* *
+\**********************************************************************/
+DECLARE_API(GCHandles)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ try
+ {
+ GCHandlesImpl gchandles(args);
+ gchandles.Run();
+ }
+ catch(const sos::Exception &e)
+ {
+ Print(e.what());
+ }
+
+ return Status;
+}
+
+BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
+{
+ // We want to follow back until we get the mt for System.Exception
+ DacpMethodTableData dmtd;
+ CLRDATA_ADDRESS walkMT = mtObj;
+ while(walkMT != NULL)
+ {
+ if (dmtd.Request(g_sos, walkMT) != S_OK)
+ {
+ break;
+ }
+ NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);
+ if (_wcscmp (baseString, g_mdName) == 0)
+ {
+ return TRUE;
+ }
+ walkMT = dmtd.ParentMethodTable;
+ }
+ return FALSE;
+}
+
+// This is an experimental and undocumented SOS API that attempts to step through code
+// stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
+// to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
+// kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/
+#ifndef FEATURE_PAL
+DECLARE_API(TraceToCode)
+{
+ INIT_API_NOEE();
+
+ static ULONG64 g_clrBaseAddr = 0;
+
+
+ while(true)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("Interrupted\n");
+ return S_FALSE;
+ }
+
+ ULONG64 Offset;
+ g_ExtRegisters->GetInstructionOffset(&Offset);
+
+ DWORD codeType = 0;
+ ULONG64 base = 0;
+ CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset);
+ DacpMethodDescData MethodDescData;
+ if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK)
+ {
+ if(g_clrBaseAddr == 0)
+ {
+ g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
+ &g_clrBaseAddr);
+ }
+ if(g_clrBaseAddr == base)
+ {
+ ExtOut("Compiled code in CLR\n");
+ codeType = 4;
+ }
+ else
+ {
+ ExtOut("Compiled code in module @ 0x%I64x\n", base);
+ codeType = 8;
+ }
+ }
+ else if (g_sos != NULL || LoadClrDebugDll()==S_OK)
+ {
+ CLRDATA_ADDRESS addr;
+ if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
+ {
+ WCHAR wszNameBuffer[1024]; // should be large enough
+
+ // get the MethodDesc name
+ if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) &&
+ _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
+ {
+ ExtOut("ILStub\n");
+ codeType = 2;
+ }
+ else
+ {
+ ExtOut("Jitted code\n");
+ codeType = 1;
+ }
+ }
+ else
+ {
+ ExtOut("Not compiled or jitted, assuming stub\n");
+ codeType = 16;
+ }
+ }
+ else
+ {
+ // not compiled but CLR isn't loaded... some other code generator?
+ return E_FAIL;
+ }
+
+ if(codeType == 1)
+ {
+ return S_OK;
+ }
+ else
+ {
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error tracing instruction\n");
+ return Status;
+ }
+ }
+ }
+
+ return Status;
+
+}
+#endif // FEATURE_PAL
+
+// This is an experimental and undocumented API that sets a debugger pseudo-register based
+// on the type of code at the given IP. It can be used in scripts to keep stepping until certain
+// kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it
+// cancels much better
+#ifndef FEATURE_PAL
+DECLARE_API(GetCodeTypeFlags)
+{
+ INIT_API();
+
+
+ char buffer[100+mdNameLen];
+ size_t ip;
+ StringHolder PReg;
+
+ CMDValue arg[] = {
+ // vptr, type
+ {&ip, COSIZE_T},
+ {&PReg.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ size_t preg = 1; // by default
+ if (nArg == 2)
+ {
+ preg = GetExpression(PReg.data);
+ if (preg > 19)
+ {
+ ExtOut("Pseudo-register number must be between 0 and 19\n");
+ return Status;
+ }
+ }
+
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=0",
+ preg);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error initialized register $t%d to zero\n", preg);
+ return Status;
+ }
+
+ ULONG64 base = 0;
+ CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip);
+ DWORD codeType = 0;
+ CLRDATA_ADDRESS addr;
+ if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
+ {
+ WCHAR wszNameBuffer[1024]; // should be large enough
+
+ // get the MethodDesc name
+ if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK &&
+ _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
+ {
+ ExtOut("ILStub\n");
+ codeType = 2;
+ }
+ else
+ {
+ ExtOut("Jitted code");
+ codeType = 1;
+ }
+ }
+ else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK)
+ {
+ ULONG64 clrBaseAddr = 0;
+ if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr)
+ {
+ ExtOut("Compiled code in CLR");
+ codeType = 4;
+ }
+ else
+ {
+ ExtOut("Compiled code in module @ 0x%I64x\n", base);
+ codeType = 8;
+ }
+ }
+ else
+ {
+ ExtOut("Not compiled or jitted, assuming stub\n");
+ codeType = 16;
+ }
+
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=%x",
+ preg, codeType);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error setting register $t%d\n", preg);
+ return Status;
+ }
+ return Status;
+
+}
+#endif // FEATURE_PAL
+
+DECLARE_API(StopOnException)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ char buffer[100+mdNameLen];
+
+ BOOL fDerived = FALSE;
+ BOOL fCreate1 = FALSE;
+ BOOL fCreate2 = FALSE;
+
+ CMDOption option[] = {
+ // name, vptr, type, hasValue
+ {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions
+ {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler
+ {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler
+ };
+
+ StringHolder TypeName,PReg;
+
+ CMDValue arg[] = {
+ // vptr, type
+ {&TypeName.data, COSTRING},
+ {&PReg.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (IsDumpFile())
+ {
+ ExtOut("Live debugging session required\n");
+ return Status;
+ }
+ if (nArg < 1 || nArg > 2)
+ {
+ ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n");
+ ExtOut(" [<pseudo-register number for result>]\n");
+ ExtOut("ex: StopOnException -create System.OutOfMemoryException 1\n");
+ return Status;
+ }
+
+ size_t preg = 1; // by default
+ if (nArg == 2)
+ {
+ preg = GetExpression(PReg.data);
+ if (preg > 19)
+ {
+ ExtOut("Pseudo-register number must be between 0 and 19\n");
+ return Status;
+ }
+ }
+
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=0",
+ preg);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error initialized register $t%d to zero\n", preg);
+ return Status;
+ }
+
+ if (fCreate1 || fCreate2)
+ {
+ sprintf_s(buffer,_countof (buffer),
+ "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
+ fCreate1 ? "-c" : "-c2",
+ fDerived ? "-derived" : "",
+ TypeName.data,
+ preg,
+ preg,
+ TypeName.data,
+ EXCEPTION_COMPLUS
+ );
+
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error setting breakpoint: %s\n", buffer);
+ return Status;
+ }
+
+ ExtOut("Breakpoint set\n");
+ return Status;
+ }
+
+ // Find the last thrown exception on this thread.
+ // Does it match? If so, set the register.
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ TADDR taLTOH;
+ if (!SafeReadMemory(Thread.lastThrownObjectHandle,
+ &taLTOH,
+ sizeof(taLTOH), NULL))
+ {
+ ExtOut("There is no current managed exception on this thread\n");
+ return Status;
+ }
+
+ if (taLTOH)
+ {
+ LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen);
+
+ TADDR taMT;
+ if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
+ {
+ NameForMT_s (taMT, g_mdName, mdNameLen);
+ if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
+ (fDerived && derivedFrom(taMT, typeNameWide)))
+ {
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=1",
+ preg);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Failed to execute the following command: %s\n", buffer);
+ }
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function finds the size of an object or all roots. *
+* *
+\**********************************************************************/
+DECLARE_API(ObjSize)
+{
+#ifndef FEATURE_PAL
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ StringHolder str_Object;
+
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&str_Object.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ TADDR obj = GetExpression(str_Object.data);
+
+ GCRootImpl gcroot;
+ if (obj == 0)
+ {
+ gcroot.ObjSize();
+ }
+ else
+ {
+ if(!sos::IsObject(obj))
+ {
+ ExtOut("%p is not a valid object.\n", SOS_PTR(obj));
+ return Status;
+ }
+
+ size_t size = gcroot.ObjSize(obj);
+ TADDR mt = 0;
+ MOVE(mt, obj);
+ sos::MethodTable methodTable = mt;
+ ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName());
+ }
+ return Status;
+#else
+ return E_NOTIMPL;
+#endif
+
+}
+
+#ifndef FEATURE_PAL
+// For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO?
+DECLARE_API(GCHandleLeaks)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ ExtOut("-------------------------------------------------------------------------------\n");
+ ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory. \n");
+ ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n");
+ ExtOut("memory scan with Control-C or Control-Break. \n");
+ ExtOut("-------------------------------------------------------------------------------\n");
+
+ static DWORD_PTR array[2000];
+ UINT i;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR));
+ ExtOut("Found %d handles:\n",iFinal);
+ for (i=1;i<=iFinal;i++)
+ {
+ ExtOut("%p\t", SOS_PTR(array[i-1]));
+ if ((i % 4) == 0)
+ ExtOut("\n");
+ }
+
+ ExtOut("\nSearching memory\n");
+ // Now search memory for this:
+ DWORD_PTR buffer[1024];
+ ULONG64 memCur = 0x0;
+ BOOL bAbort = FALSE;
+
+ //find out memory used by stress log
+ StressLogMem stressLog;
+ CLRDATA_ADDRESS StressLogAddress = NULL;
+ if (LoadClrDebugDll() != S_OK)
+ {
+ // Try to find stress log symbols
+ DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
+ StressLogAddress = dwAddr;
+ g_bDacBroken = TRUE;
+ }
+ else
+ {
+ if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
+ {
+ ExtOut("Unable to find stress log via DAC\n");
+ }
+ g_bDacBroken = FALSE;
+ }
+
+ if (stressLog.Init (StressLogAddress, g_ExtData))
+ {
+ ExtOut("Reference found in stress log will be ignored\n");
+ }
+ else
+ {
+ ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n");
+ }
+
+
+ while (!bAbort)
+ {
+ NTSTATUS status;
+ MEMORY_BASIC_INFORMATION64 memInfo;
+
+ status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo);
+
+ if( !NT_SUCCESS(status) )
+ {
+ break;
+ }
+
+ if (memInfo.State == MEM_COMMIT)
+ {
+ for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer))
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("Quitting at %p due to user abort\n", SOS_PTR(memIter));
+ bAbort = TRUE;
+ break;
+ }
+
+ if ((memIter % 0x10000000)==0x0)
+ {
+ ExtOut("Searching %p...\n", SOS_PTR(memIter));
+ }
+
+ ULONG size = 0;
+ HRESULT ret;
+ ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size);
+ if (ret == S_OK)
+ {
+ for (UINT x=0;x<1024;x++)
+ {
+ DWORD_PTR value = buffer[x];
+ // We don't care about the low bit. Also, the GCHandle class turns on the
+ // low bit for pinned handles, so without the statement below, we wouldn't
+ // notice pinned handles.
+ value = value & ~1;
+ for (i=0;i<iFinal;i++)
+ {
+ ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR));
+ if ((array[i] & ~1) == value)
+ {
+ if (stressLog.IsInStressLog (addrInDebugee))
+ {
+ ExtOut("Found %p in stress log at location %p, reference not counted\n", SOS_PTR(value), addrInDebugee);
+ }
+ else
+ {
+ ExtOut("Found %p at location %p\n", SOS_PTR(value), addrInDebugee);
+ array[i] |= 0x1;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (size > 0)
+ {
+ ExtOut("only read %x bytes at %p\n", size, SOS_PTR(memIter));
+ }
+ }
+ }
+ }
+
+ memCur += memInfo.RegionSize;
+ }
+
+ int numNotFound = 0;
+ for (i=0;i<iFinal;i++)
+ {
+ if ((array[i] & 0x1) == 0)
+ {
+ numNotFound++;
+ // ExtOut("WARNING: %p not found\n", SOS_PTR(array[i]));
+ }
+ }
+
+ if (numNotFound > 0)
+ {
+ ExtOut("------------------------------------------------------------------------------\n");
+ ExtOut("Some handles were not found. If the number of not-found handles grows over the\n");
+ ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause \n");
+ ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only \n");
+ ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n");
+ ExtOut("may be some noise in this output, as an unmanaged application may be storing \n");
+ ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory \n");
+ ExtOut("scan wouldn't be able to find those. \n");
+ ExtOut("------------------------------------------------------------------------------\n");
+
+ ExtOut("Didn't find %d handles:\n", numNotFound);
+ int numPrinted=0;
+ for (i=0;i<iFinal;i++)
+ {
+ if ((array[i] & 0x1) == 0)
+ {
+ numPrinted++;
+ ExtOut("%p\t", SOS_PTR(array[i]));
+ if ((numPrinted % 4) == 0)
+ ExtOut("\n");
+ }
+ }
+ ExtOut("\n");
+ }
+ else
+ {
+ ExtOut("------------------------------------------------------------------------------\n");
+ ExtOut("All handles found");
+ if (bAbort)
+ ExtOut(" even though you aborted.\n");
+ else
+ ExtOut(".\n");
+ ExtOut("A leak may still exist because in a general scan of process memory SOS can't \n");
+ ExtOut("differentiate between garbage and valid structures, so you may have false \n");
+ ExtOut("positives. If you still suspect a leak, use this function over time to \n");
+ ExtOut("identify a possible trend. \n");
+ ExtOut("------------------------------------------------------------------------------\n");
+ }
+
+ return Status;
+}
+#endif // FEATURE_PAL
+
+#endif // FEATURE_PAL
+
+class ClrStackImplWithICorDebug
+{
+private:
+ static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
+ {
+ HRESULT Status = S_OK;
+ *ppOutputValue = NULL;
+ if(pIsNull != NULL) *pIsNull = FALSE;
+
+ ToRelease<ICorDebugReferenceValue> pReferenceValue;
+ Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
+ if (SUCCEEDED(Status))
+ {
+ BOOL isNull = FALSE;
+ IfFailRet(pReferenceValue->IsNull(&isNull));
+ if(!isNull)
+ {
+ ToRelease<ICorDebugValue> pDereferencedValue;
+ IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
+ return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
+ }
+ else
+ {
+ if(pIsNull != NULL) *pIsNull = TRUE;
+ *ppOutputValue = pValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+ }
+ }
+
+ ToRelease<ICorDebugBoxValue> pBoxedValue;
+ Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
+ if (SUCCEEDED(Status))
+ {
+ ToRelease<ICorDebugObjectValue> pUnboxedValue;
+ IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
+ return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
+ }
+ *ppOutputValue = pValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+ }
+
+ static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
+ {
+ if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
+
+ size_t varToExpandLen = _wcslen(varToExpand);
+ size_t currentExpansionLen = _wcslen(currentExpansion);
+ if(currentExpansionLen > varToExpandLen) return FALSE;
+ if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE;
+ if(_wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE;
+
+ return TRUE;
+ }
+
+ static BOOL IsEnum(ICorDebugValue * pInputValue)
+ {
+ ToRelease<ICorDebugValue> pValue;
+ if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
+
+ WCHAR baseTypeName[mdNameLen];
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugType> pBaseType;
+
+ if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
+ if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
+ if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
+ if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return FALSE;
+
+ return (_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0);
+ }
+
+ static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
+ {
+ bool isFirst = true;
+ ToRelease<ICorDebugTypeEnum> pTypeEnum;
+ if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
+ {
+ ULONG numTypes = 0;
+ ToRelease<ICorDebugType> pCurrentTypeParam;
+
+ while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
+ {
+ if(numTypes == 0) break;
+
+ if(isFirst)
+ {
+ isFirst = false;
+ wcsncat_s(typeName, typeNameLen, W("&lt;"), typeNameLen);
+ }
+ else wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
+
+ WCHAR typeParamName[mdNameLen];
+ typeParamName[0] = L'\0';
+ GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen);
+ wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
+ }
+ if(!isFirst)
+ wcsncat_s(typeName, typeNameLen, W("&gt;"), typeNameLen);
+ }
+
+ return S_OK;
+ }
+
+ static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
+ {
+ HRESULT Status = S_OK;
+
+ CorElementType corElemType;
+ IfFailRet(pType->GetType(&corElemType));
+
+ switch (corElemType)
+ {
+ //List of unsupported CorElementTypes:
+ //ELEMENT_TYPE_END = 0x0,
+ //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1>
+ //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type
+ //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1>
+ //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle>
+ //ELEMENT_TYPE_MAX = 0x22, // first invalid element type
+ //ELEMENT_TYPE_MODIFIER = 0x40,
+ //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
+ //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER,
+ //ELEMENT_TYPE_R4_HFA = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types
+ //ELEMENT_TYPE_R8_HFA = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types
+ default:
+ swprintf_s(typeName, typeNameLen, W("(Unhandled CorElementType: 0x%x)\0"), corElemType);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ {
+ //Defaults in case we fail...
+ if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, W("struct\0"));
+ else swprintf_s(typeName, typeNameLen, W("class\0"));
+
+ mdTypeDef typeDef;
+ ToRelease<ICorDebugClass> pClass;
+ if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
+ {
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
+ swprintf_s(typeName, typeNameLen, W("%s\0"), g_mdName);
+ }
+ AddGenericArgs(pType, typeName, typeNameLen);
+ }
+ break;
+ case ELEMENT_TYPE_VOID:
+ swprintf_s(typeName, typeNameLen, W("void\0"));
+ break;
+ case ELEMENT_TYPE_BOOLEAN:
+ swprintf_s(typeName, typeNameLen, W("bool\0"));
+ break;
+ case ELEMENT_TYPE_CHAR:
+ swprintf_s(typeName, typeNameLen, W("char\0"));
+ break;
+ case ELEMENT_TYPE_I1:
+ swprintf_s(typeName, typeNameLen, W("signed byte\0"));
+ break;
+ case ELEMENT_TYPE_U1:
+ swprintf_s(typeName, typeNameLen, W("byte\0"));
+ break;
+ case ELEMENT_TYPE_I2:
+ swprintf_s(typeName, typeNameLen, W("short\0"));
+ break;
+ case ELEMENT_TYPE_U2:
+ swprintf_s(typeName, typeNameLen, W("unsigned short\0"));
+ break;
+ case ELEMENT_TYPE_I4:
+ swprintf_s(typeName, typeNameLen, W("int\0"));
+ break;
+ case ELEMENT_TYPE_U4:
+ swprintf_s(typeName, typeNameLen, W("unsigned int\0"));
+ break;
+ case ELEMENT_TYPE_I8:
+ swprintf_s(typeName, typeNameLen, W("long\0"));
+ break;
+ case ELEMENT_TYPE_U8:
+ swprintf_s(typeName, typeNameLen, W("unsigned long\0"));
+ break;
+ case ELEMENT_TYPE_R4:
+ swprintf_s(typeName, typeNameLen, W("float\0"));
+ break;
+ case ELEMENT_TYPE_R8:
+ swprintf_s(typeName, typeNameLen, W("double\0"));
+ break;
+ case ELEMENT_TYPE_OBJECT:
+ swprintf_s(typeName, typeNameLen, W("object\0"));
+ break;
+ case ELEMENT_TYPE_STRING:
+ swprintf_s(typeName, typeNameLen, W("string\0"));
+ break;
+ case ELEMENT_TYPE_I:
+ swprintf_s(typeName, typeNameLen, W("IntPtr\0"));
+ break;
+ case ELEMENT_TYPE_U:
+ swprintf_s(typeName, typeNameLen, W("UIntPtr\0"));
+ break;
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ {
+ ToRelease<ICorDebugType> pFirstParameter;
+ if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
+ GetTypeOfValue(pFirstParameter, typeName, typeNameLen);
+ else
+ swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
+
+ switch(corElemType)
+ {
+ case ELEMENT_TYPE_SZARRAY:
+ wcsncat_s(typeName, typeNameLen, W("[]\0"), typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_ARRAY:
+ {
+ ULONG32 rank = 0;
+ pType->GetRank(&rank);
+ wcsncat_s(typeName, typeNameLen, W("["), typeNameLen);
+ for(ULONG32 i = 0; i < rank - 1; i++)
+ {
+ //
+ wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
+ }
+ wcsncat_s(typeName, typeNameLen, W("]\0"), typeNameLen);
+ }
+ return S_OK;
+ case ELEMENT_TYPE_BYREF:
+ wcsncat_s(typeName, typeNameLen, W("&\0"), typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_PTR:
+ wcsncat_s(typeName, typeNameLen, W("*\0"), typeNameLen);
+ return S_OK;
+ default:
+ // note we can never reach here as this is a nested switch
+ // and corElemType can only be one of the values above
+ break;
+ }
+ }
+ break;
+ case ELEMENT_TYPE_FNPTR:
+ swprintf_s(typeName, typeNameLen, W("*(...)\0"));
+ break;
+ case ELEMENT_TYPE_TYPEDBYREF:
+ swprintf_s(typeName, typeNameLen, W("typedbyref\0"));
+ break;
+ }
+ return S_OK;
+ }
+
+ static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
+ {
+ HRESULT Status = S_OK;
+
+ CorElementType corElemType;
+ IfFailRet(pValue->GetType(&corElemType));
+
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugValue2> pValue2;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
+ return GetTypeOfValue(pType, typeName, typeNameLen);
+ else
+ swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
+
+ return S_OK;
+ }
+
+ static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
+ {
+ HRESULT Status = S_OK;
+
+ ToRelease<ICorDebugValue> pValue;
+ IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL));
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+
+ //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
+ //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ DWORD fieldAttr = 0;
+ PCCOR_SIGNATURE pSignatureBlob = NULL;
+ ULONG sigBlobLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
+ {
+ if((fieldAttr & fdStatic) == 0)
+ {
+ CorSigUncompressCallingConv(pSignatureBlob);
+ enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
+ break;
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+
+ //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
+ fEnum = NULL;
+ bool isFirst = true;
+ ULONG64 remainingValue = *((ULONG64*)enumValue);
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ UVCP_CONSTANT pRawValue = NULL;
+ ULONG rawValueLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
+ {
+ DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
+ if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
+ continue;
+
+ ULONG64 currentConstValue = 0;
+ switch (enumUnderlyingType)
+ {
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U1:
+ currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I2:
+ currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U2:
+ currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I4:
+ currentConstValue = (ULONG64)(*((INT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U4:
+ currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I8:
+ currentConstValue = (ULONG64)(*((LONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U8:
+ currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I:
+ currentConstValue = (ULONG64)(*((int*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
+ default:
+ currentConstValue = 0;
+ }
+
+ if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
+ {
+ remainingValue &= ~currentConstValue;
+ if(isFirst)
+ {
+ ExtOut(" = %S", mdName);
+ isFirst = false;
+ }
+ else ExtOut(" | %S", mdName);
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+ return S_OK;
+ }
+
+ static HRESULT PrintStringValue(ICorDebugValue * pValue)
+ {
+ HRESULT Status;
+
+ ToRelease<ICorDebugStringValue> pStringValue;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
+
+ ULONG32 cchValue;
+ IfFailRet(pStringValue->GetLength(&cchValue));
+ cchValue++; // Allocate one more for null terminator
+
+ CQuickString quickString;
+ quickString.Alloc(cchValue);
+
+ ULONG32 cchValueReturned;
+ IfFailRet(pStringValue->GetString(
+ cchValue,
+ &cchValueReturned,
+ quickString.String()));
+
+ ExtOut(" = \"%S\"\n", quickString.String());
+
+ return S_OK;
+ }
+
+ static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
+ {
+ HRESULT Status = S_OK;
+
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ return E_UNEXPECTED;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+
+ if (cElements == 0) ExtOut(" (empty)\n");
+ else if (cElements == 1) ExtOut(" (1 element)\n");
+ else ExtOut(" (%d elements)\n", cElements);
+
+ if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
+ size_t currentExpansionLen = _wcslen(currentExpansion);
+
+ for (ULONG32 i=0; i < cElements; i++)
+ {
+ for(int j = 0; j <= indent; j++) ExtOut(" ");
+ currentExpansion[currentExpansionLen] = L'\0';
+ swprintf_s(currentExpansion, mdNameLen, W("%s.[%d]\0"), currentExpansion, i);
+
+ bool printed = false;
+ CorElementType corElemType;
+ ToRelease<ICorDebugType> pFirstParameter;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
+ {
+ if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType)))
+ {
+ switch(corElemType)
+ {
+ //If the array element is something that we can expand with !clrstack, show information about the type of this element
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_SZARRAY:
+ {
+ WCHAR typeOfElement[mdNameLen];
+ GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen);
+ DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement);
+ printed = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i));
+
+ ToRelease<ICorDebugValue> pElementValue;
+ IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
+ IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame));
+ }
+
+ return S_OK;
+ }
+
+ static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
+ {
+ HRESULT Status = S_OK;
+
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pValue;
+ IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
+
+ if(isNull)
+ {
+ ExtOut(" = null\n");
+ return S_OK;
+ }
+
+ ULONG32 cbSize;
+ IfFailRet(pValue->GetSize(&cbSize));
+ ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize];
+ if (rgbValue == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
+
+ CorElementType corElemType;
+ IfFailRet(pValue->GetType(&corElemType));
+ if (corElemType == ELEMENT_TYPE_STRING)
+ {
+ return PrintStringValue(pValue);
+ }
+
+ if (corElemType == ELEMENT_TYPE_SZARRAY)
+ {
+ return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ }
+
+ ToRelease<ICorDebugGenericValue> pGenericValue;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
+ IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
+
+ if(IsEnum(pValue))
+ {
+ Status = PrintEnumValue(pValue, rgbValue);
+ ExtOut("\n");
+ return Status;
+ }
+
+ switch (corElemType)
+ {
+ default:
+ ExtOut(" (Unhandled CorElementType: 0x%x)\n", corElemType);
+ break;
+
+ case ELEMENT_TYPE_PTR:
+ ExtOut(" = <pointer>\n");
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ {
+ CORDB_ADDRESS addr = 0;
+ ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
+ pReferenceValue->GetValue(&addr);
+ ExtOut(" = <function pointer 0x%x>\n", addr);
+ }
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ CORDB_ADDRESS addr;
+ if(SUCCEEDED(pValue->GetAddress(&addr)))
+ {
+ ExtOut(" @ 0x%I64x\n", addr);
+ }
+ else
+ {
+ ExtOut("\n");
+ }
+ ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ ExtOut(" = %s\n", rgbValue[0] == 0 ? "false" : "true");
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ ExtOut(" = '%C'\n", *(WCHAR *) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I1:
+ ExtOut(" = %d\n", *(char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U1:
+ ExtOut(" = %d\n", *(unsigned char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I2:
+ ExtOut(" = %hd\n", *(short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U2:
+ ExtOut(" = %hu\n", *(unsigned short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I:
+ ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U:
+ ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I4:
+ ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U4:
+ ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I8:
+ ExtOut(" = %I64d\n", *(__int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U8:
+ ExtOut(" = %I64u\n", *(unsigned __int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R4:
+ ExtOut(" = %f\n", (double) *(float*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R8:
+ ExtOut(" = %f\n", *(double*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_OBJECT:
+ ExtOut(" = object\n");
+ break;
+
+ // TODO: The following corElementTypes are not yet implemented here. Array
+ // might be interesting to add, though the others may be of rather limited use:
+ // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
+ //
+ // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ }
+
+ return S_OK;
+ }
+
+ static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
+ {
+ HRESULT Status = S_OK;
+
+ ULONG cParams = 0;
+ ToRelease<ICorDebugValueEnum> pParamEnum;
+ IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
+ IfFailRet(pParamEnum->GetCount(&cParams));
+ if (cParams > 0 && bParams)
+ {
+ DWORD methAttr = 0;
+ IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
+
+ ExtOut("\nPARAMETERS:\n");
+ for (ULONG i=0; i < cParams; i++)
+ {
+ ULONG paramNameLen = 0;
+ mdParamDef paramDef;
+ WCHAR paramName[mdNameLen] = W("\0");
+
+ if(i == 0 && (methAttr & mdStatic) == 0)
+ swprintf_s(paramName, mdNameLen, W("this\0"));
+ else
+ {
+ int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
+ if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, &paramDef)))
+ pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, &paramNameLen, NULL, NULL, NULL, NULL);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, W("param_%d\0"), i);
+
+ ToRelease<ICorDebugValue> pValue;
+ ULONG cArgsFetched;
+ Status = pParamEnum->Next(1, &pValue, &cArgsFetched);
+
+ if (FAILED(Status))
+ {
+ ExtOut(" + (Error 0x%x retrieving parameter '%S')\n", Status, paramName);
+ continue;
+ }
+
+ if (Status == S_FALSE)
+ {
+ break;
+ }
+
+ WCHAR typeName[mdNameLen] = W("\0");
+ GetTypeOfValue(pValue, typeName, mdNameLen);
+ DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
+
+ ToRelease<ICorDebugReferenceValue> pRefValue;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
+ {
+ BOOL bIsNull = TRUE;
+ pRefValue->IsNull(&bIsNull);
+ if(bIsNull)
+ {
+ ExtOut(" = null\n");
+ continue;
+ }
+ }
+
+ WCHAR currentExpansion[mdNameLen];
+ swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
+ if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
+ ExtOut(" + (Error 0x%x printing parameter %d)\n", Status, i);
+ }
+ }
+ else if (cParams == 0 && bParams)
+ ExtOut("\nPARAMETERS: (none)\n");
+
+ ULONG cLocals = 0;
+ ToRelease<ICorDebugValueEnum> pLocalsEnum;
+ IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
+ IfFailRet(pLocalsEnum->GetCount(&cLocals));
+ if (cLocals > 0 && bLocals)
+ {
+ bool symbolsAvailable = false;
+ SymbolReader symReader;
+ if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
+ symbolsAvailable = true;
+ ExtOut("\nLOCALS:\n");
+ for (ULONG i=0; i < cLocals; i++)
+ {
+ ULONG paramNameLen = 0;
+ WCHAR paramName[mdNameLen] = W("\0");
+
+ ToRelease<ICorDebugValue> pValue;
+ if(symbolsAvailable)
+ {
+ Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
+ }
+ else
+ {
+ ULONG cArgsFetched;
+ Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, W("local_%d\0"), i);
+
+ if (FAILED(Status))
+ {
+ ExtOut(" + (Error 0x%x retrieving local variable '%S')\n", Status, paramName);
+ continue;
+ }
+
+ if (Status == S_FALSE)
+ {
+ break;
+ }
+
+ WCHAR typeName[mdNameLen] = W("\0");
+ GetTypeOfValue(pValue, typeName, mdNameLen);
+ DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
+
+ ToRelease<ICorDebugReferenceValue> pRefValue = NULL;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
+ {
+ BOOL bIsNull = TRUE;
+ pRefValue->IsNull(&bIsNull);
+ if(bIsNull)
+ {
+ ExtOut(" = null\n");
+ continue;
+ }
+ }
+
+ WCHAR currentExpansion[mdNameLen];
+ swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
+ if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
+ ExtOut(" + (Error 0x%x printing local variable %d)\n", Status, i);
+ }
+ }
+ else if (cLocals == 0 && bLocals)
+ ExtOut("\nLOCALS: (none)\n");
+
+ if(bParams || bLocals)
+ ExtOut("\n");
+
+ return S_OK;
+ }
+
+ static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
+ {
+ if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
+ size_t currentExpansionLen = _wcslen(currentExpansion);
+
+ HRESULT Status = S_OK;
+
+ BOOL isNull = FALSE;
+ ToRelease<ICorDebugValue> pValue;
+ IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
+
+ if(isNull) return S_OK;
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ if(pTypeCast == NULL)
+ IfFailRet(pValue2->GetExactType(&pType));
+ else
+ {
+ pType = pTypeCast;
+ pType->AddRef();
+ }
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ WCHAR baseTypeName[mdNameLen] = W("\0");
+ ToRelease<ICorDebugType> pBaseType;
+ if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen)))
+ {
+ if(_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0)
+ return S_OK;
+ else if(_wcsncmp(baseTypeName, W("System.Object"), 13) != 0 && _wcsncmp(baseTypeName, W("System.ValueType"), 16) != 0)
+ {
+ currentExpansion[currentExpansionLen] = W('\0');
+ wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
+ wcscat_s(currentExpansion, currentExpansionSize, W("[basetype]"));
+ for(int i = 0; i < indent; i++) ExtOut(" ");
+ DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, W("[basetype]")));
+
+ if(ShouldExpandVariable(varToExpand, currentExpansion))
+ ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ }
+ }
+
+
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL)))
+ {
+ currentExpansion[currentExpansionLen] = W('\0');
+ wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
+ wcscat_s(currentExpansion, currentExpansionSize, mdName);
+
+ ToRelease<ICorDebugValue> pFieldVal;
+ if(fieldAttr & fdLiteral)
+ {
+ //TODO: Is it worth it??
+ //ExtOut(" |- const %S", mdName);
+ }
+ else
+ {
+ for(int i = 0; i < indent; i++) ExtOut(" ");
+
+ if (fieldAttr & fdStatic)
+ pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
+ else
+ {
+ ToRelease<ICorDebugObjectValue> pObjValue;
+ if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
+ pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
+ }
+
+ if(pFieldVal != NULL)
+ {
+ typeName[0] = L'\0';
+ GetTypeOfValue(pFieldVal, typeName, mdNameLen);
+ DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName));
+ PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ }
+ else if(!(fieldAttr & fdLiteral))
+ ExtOut(" |- < unknown type > %S\n", mdName);
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+ return S_OK;
+ }
+
+public:
+
+ // This is the main worker function used if !clrstack is called with "-i" to indicate
+ // that the public ICorDebug* should be used instead of the private DAC interface. NOTE:
+ // Currently only bParams is supported. NOTE: This is a work in progress and the
+ // following would be good to do:
+ // * More thorough testing with interesting stacks, especially with transitions into
+ // and out of managed code.
+ // * Consider interleaving this code back into the main body of !clrstack if it turns
+ // out that there's a lot of duplication of code between these two functions.
+ // (Still unclear how things will look once locals is implemented.)
+ static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
+ {
+ HRESULT Status;
+
+ IfFailRet(InitCorDebugInterface());
+
+ ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n");
+ ExtOut("=============================================================================\n");
+
+ ToRelease<ICorDebugThread> pThread;
+ ToRelease<ICorDebugThread3> pThread3;
+ ToRelease<ICorDebugStackWalk> pStackWalk;
+ ULONG ulThreadID = 0;
+ g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
+
+ IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
+ IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
+ IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
+
+ InternalFrameManager internalFrameManager;
+ IfFailRet(internalFrameManager.Init(pThread3));
+
+ #if defined(_AMD64_)
+ ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site");
+ #elif defined(_X86_)
+ ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site");
+ #endif
+
+ int currentFrame = -1;
+
+ for (Status = S_OK; ; Status = pStackWalk->Next())
+ {
+ currentFrame++;
+
+ if (Status == CORDBG_S_AT_END_OF_STACK)
+ {
+ ExtOut("Stack walk complete.\n");
+ break;
+ }
+ IfFailRet(Status);
+
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ break;
+ }
+
+ CROSS_PLATFORM_CONTEXT context;
+ ULONG32 cbContextActual;
+ if ((Status=pStackWalk->GetContext(
+ DT_CONTEXT_FULL,
+ sizeof(context),
+ &cbContextActual,
+ (BYTE *)&context))!=S_OK)
+ {
+ ExtOut("GetFrameContext failed: %lx\n",Status);
+ break;
+ }
+
+ // First find the info for the Frame object, if the current frame has an associated clr!Frame.
+ CLRDATA_ADDRESS sp = GetSP(context);
+ CLRDATA_ADDRESS ip = GetIP(context);
+
+ ToRelease<ICorDebugFrame> pFrame;
+ IfFailRet(pStackWalk->GetFrame(&pFrame));
+ if (Status == S_FALSE)
+ {
+ DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip));
+ continue;
+ }
+
+ // TODO: What about internal frames preceding the above native stack frame?
+ // Should I just exclude the above native stack frame from the output?
+ // TODO: Compare caller frame (instead of current frame) against internal frame,
+ // to deal with issues of current frame's current SP being closer to leaf than
+ // EE Frames it pushes. By "caller" I mean not just managed caller, but the
+ // very next non-internal frame dbi would return (native or managed). OR...
+ // perhaps I should use GetStackRange() instead, to see if the internal frame
+ // appears leafier than the base-part of the range of the currently iterated
+ // stack frame? I think I like that better.
+ _ASSERTE(pFrame != NULL);
+ IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame));
+
+ // Print the stack and instruction pointers.
+ DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip));
+
+ ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame;
+ Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame);
+ if (SUCCEEDED(Status))
+ {
+ ExtOut("[RuntimeUnwindableFrame]\n");
+ continue;
+ }
+
+ // Print the method/Frame info
+
+ // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES
+ // CAN BE FOUND VIA GetActiveInternalFrames?
+ ToRelease<ICorDebugInternalFrame> pInternalFrame;
+ Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame);
+ if (SUCCEEDED(Status))
+ {
+ // This is a clr!Frame.
+ LPCWSTR pwszFrameName = W("TODO: Implement GetFrameName");
+ ExtOut("[%S: p] ", pwszFrameName);
+ }
+
+ // Print the frame's associated function info, if it has any.
+ ToRelease<ICorDebugILFrame> pILFrame;
+ HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
+
+ if (SUCCEEDED(hrILFrame))
+ {
+ ToRelease<ICorDebugFunction> pFunction;
+ Status = pFrame->GetFunction(&pFunction);
+ if (FAILED(Status))
+ {
+ // We're on a JITted frame, but there's no Function for it. So it must
+ // be...
+ ExtOut("[IL Stub or LCG]\n");
+ continue;
+ }
+
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugModule> pModule;
+ mdMethodDef methodDef;
+ IfFailRet(pFunction->GetClass(&pClass));
+ IfFailRet(pFunction->GetModule(&pModule));
+ IfFailRet(pFunction->GetToken(&methodDef));
+
+ WCHAR wszModuleName[100];
+ ULONG32 cchModuleNameActual;
+ IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ ToRelease<IMDInternalImport> pMDInternal;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+ IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal));
+
+ mdTypeDef typeDef;
+ IfFailRet(pClass->GetToken(&typeDef));
+
+ // Note that we don't need to pretty print the class, as class name is
+ // already printed from GetMethodName below
+
+ CQuickBytes functionName;
+ // TODO: WARNING: GetMethodName() appears to include lots of unexercised
+ // code, as evidenced by some fundamental bugs I found. It should either be
+ // thoroughly reviewed, or some other more exercised code path to grab the
+ // name should be used.
+ // TODO: If we do stay with GetMethodName, it should be updated to print
+ // generics properly. Today, it does not show generic type parameters, and
+ // if any arguments have a generic type, those arguments are just shown as
+ // "__Canon", even when they're value types.
+ GetMethodName(methodDef, pMD, &functionName);
+
+ DMLOut(DMLManagedVar(W("-a"), currentFrame, (LPWSTR)functionName.Ptr()));
+ ExtOut(" (%S)\n", wszModuleName);
+
+ if (SUCCEEDED(hrILFrame) && (bParams || bLocals))
+ {
+ if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame))
+ IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame));
+ }
+ }
+ }
+ ExtOut("=============================================================================\n");
+
+#ifdef FEATURE_PAL
+ // Temporary until we get a process exit notification plumbed from lldb
+ UninitCorDebugInterface();
+#endif
+ return S_OK;
+ }
+};
+
+WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj)
+{
+ WString res;
+
+ if (ref.HasRegisterInformation)
+ {
+ WCHAR reg[32];
+ HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
+ if (SUCCEEDED(hr))
+ res = reg;
+ else
+ res = W("<unknown register>");
+
+ if (ref.Offset)
+ {
+ int offset = ref.Offset;
+ if (offset > 0)
+ {
+ res += W("+");
+ }
+ else
+ {
+ res += W("-");
+ offset = -offset;
+ }
+
+ res += Hex(offset);
+ }
+
+ res += W(": ");
+ }
+
+ if (ref.Address)
+ res += WString(Pointer(ref.Address));
+
+ if (printObj)
+ {
+ if (ref.Address)
+ res += W(" -> ");
+
+ res += WString(ObjectPtr(ref.Object));
+ }
+
+ if (ref.Flags & SOSRefPinned)
+ {
+ res += W(" (pinned)");
+ }
+
+ if (ref.Flags & SOSRefInterior)
+ {
+ res += W(" (interior)");
+ }
+
+ return res;
+}
+
+void PrintRef(const SOSStackRefData &ref, TableOutput &out)
+{
+ WString res = BuildRegisterOutput(ref);
+
+ if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
+ {
+ WCHAR type[128];
+ sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
+
+ res += WString(W(" - ")) + type;
+ }
+
+ out.WriteColumn(2, res);
+}
+
+
+class ClrStackImpl
+{
+public:
+ static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bFull, BOOL bDisplayRegVals)
+ {
+ // Symbols variables
+ ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+
+ if (symlines == 0)
+ bSuppressLines = TRUE;
+
+ ToRelease<IXCLRDataStackWalk> pStackWalk;
+
+ HRESULT hr = CreateStackWalk(osID, &pStackWalk);
+ if (FAILED(hr) || pStackWalk == NULL)
+ {
+ ExtOut("Failed to start stack walk: %lx\n", hr);
+ return;
+ }
+
+#ifdef _TARGET_WIN64_
+ PDEBUG_STACK_FRAME currentNativeFrame = NULL;
+ ULONG numNativeFrames = 0;
+ if (bFull)
+ {
+ hr = GetContextStackTrace(&numNativeFrames);
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to get native stack frames: %lx\n", hr);
+ return;
+ }
+ currentNativeFrame = &g_Frames[0];
+ }
+#endif // _TARGET_WIN64_
+
+ unsigned int refCount = 0, errCount = 0;
+ ArrayHolder<SOSStackRefData> pRefs = NULL;
+ ArrayHolder<SOSStackRefError> pErrs = NULL;
+ if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount)))
+ refCount = 0;
+
+ TableOutput out(3, POINTERSIZE_HEX, AlignRight);
+ out.WriteRow("Child SP", "IP", "Call Site");
+
+ do
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ break;
+ }
+ CLRDATA_ADDRESS ip = 0, sp = 0;
+ hr = GetFrameLocation(pStackWalk, &ip, &sp);
+
+ DacpFrameData FrameData;
+ HRESULT frameDataResult = FrameData.Request(pStackWalk);
+ if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
+ sp = FrameData.frameAddr;
+
+#ifdef _TARGET_WIN64_
+ while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp))
+ {
+ if (currentNativeFrame->StackOffset != sp)
+ {
+ PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
+ }
+ currentNativeFrame++;
+ numNativeFrames--;
+ }
+#endif // _TARGET_WIN64_
+
+ // Print the stack pointer.
+ out.WriteColumn(0, sp);
+
+ // Print the method/Frame info
+ if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
+ {
+ // Skip the instruction pointer because it doesn't really mean anything for method frames
+ out.WriteColumn(1, bFull ? String("") : NativePtr(ip));
+
+ // This is a clr!Frame.
+ out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk, bFull));
+
+ // Print out gc references for the Frame.
+ for (unsigned int i = 0; i < refCount; ++i)
+ if (pRefs[i].Source == sp)
+ PrintRef(pRefs[i], out);
+
+ // Print out an error message if we got one.
+ for (unsigned int i = 0; i < errCount; ++i)
+ if (pErrs[i].Source == sp)
+ out.WriteColumn(2, "Failed to enumerate GC references.");
+ }
+ else
+ {
+ out.WriteColumn(1, InstructionPtr(ip));
+ out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull));
+
+ // Print out gc references. refCount will be zero if bGC is false (or if we
+ // failed to fetch gc reference information).
+ for (unsigned int i = 0; i < refCount; ++i)
+ if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp)
+ PrintRef(pRefs[i], out);
+
+ // Print out an error message if we got one.
+ for (unsigned int i = 0; i < errCount; ++i)
+ if (pErrs[i].Source == sp)
+ out.WriteColumn(2, "Failed to enumerate GC references.");
+
+ if (bParams || bLocals)
+ PrintArgsAndLocals(pStackWalk, bParams, bLocals);
+ }
+
+ if (bDisplayRegVals)
+ PrintManagedFrameContext(pStackWalk);
+
+ } while (pStackWalk->Next() == S_OK);
+
+#ifdef _TARGET_WIN64_
+ while (numNativeFrames > 0)
+ {
+ PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
+ currentNativeFrame++;
+ numNativeFrames--;
+ }
+#endif // _TARGET_WIN64_
+ }
+
+ static HRESULT PrintManagedFrameContext(IXCLRDataStackWalk *pStackWalk)
+ {
+ CROSS_PLATFORM_CONTEXT context;
+ HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
+ if (FAILED(hr) || hr == S_FALSE)
+ {
+ // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
+ ExtOut("GetFrameContext failed: %lx\n", hr);
+ return E_FAIL;
+ }
+
+#if defined(SOS_TARGET_AMD64)
+ String outputFormat3 = " %3s=%016x %3s=%016x %3s=%016x\n";
+ String outputFormat2 = " %3s=%016x %3s=%016x\n";
+ ExtOut(outputFormat3, "rsp", context.Amd64Context.Rsp, "rbp", context.Amd64Context.Rbp, "rip", context.Amd64Context.Rip);
+ ExtOut(outputFormat3, "rax", context.Amd64Context.Rax, "rbx", context.Amd64Context.Rbx, "rcx", context.Amd64Context.Rcx);
+ ExtOut(outputFormat3, "rdx", context.Amd64Context.Rdx, "rsi", context.Amd64Context.Rsi, "rdi", context.Amd64Context.Rdi);
+ ExtOut(outputFormat3, "r8", context.Amd64Context.R8, "r9", context.Amd64Context.R9, "r10", context.Amd64Context.R10);
+ ExtOut(outputFormat3, "r11", context.Amd64Context.R11, "r12", context.Amd64Context.R12, "r13", context.Amd64Context.R13);
+ ExtOut(outputFormat2, "r14", context.Amd64Context.R14, "r15", context.Amd64Context.R15);
+#elif defined(SOS_TARGET_X86)
+ String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
+ String outputFormat2 = " %3s=%08x %3s=%08x\n";
+ ExtOut(outputFormat3, "esp", context.X86Context.Esp, "ebp", context.X86Context.Ebp, "eip", context.X86Context.Eip);
+ ExtOut(outputFormat3, "eax", context.X86Context.Eax, "ebx", context.X86Context.Ebx, "ecx", context.X86Context.Ecx);
+ ExtOut(outputFormat3, "edx", context.X86Context.Edx, "esi", context.X86Context.Esi, "edi", context.X86Context.Edi);
+#elif defined(SOS_TARGET_ARM)
+ String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
+ String outputFormat2 = " %s=%08x %s=%08x\n";
+ String outputFormat1 = " %s=%08x\n";
+ ExtOut(outputFormat3, "r0", context.ArmContext.R0, "r1", context.ArmContext.R1, "r2", context.ArmContext.R2);
+ ExtOut(outputFormat3, "r3", context.ArmContext.R3, "r4", context.ArmContext.R4, "r5", context.ArmContext.R5);
+ ExtOut(outputFormat3, "r6", context.ArmContext.R6, "r7", context.ArmContext.R7, "r8", context.ArmContext.R8);
+ ExtOut(outputFormat3, "r9", context.ArmContext.R9, "r10", context.ArmContext.R10, "r11", context.ArmContext.R11);
+ ExtOut(outputFormat1, "r12", context.ArmContext.R12);
+ ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
+ ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
+#elif defined(SOS_TARGET_ARM64)
+ String outputXRegFormat3 = " x%d=%016x x%d=%016x x%d=%016x\n";
+ String outputXRegFormat1 = " x%d=%016x\n";
+ String outputFormat3 = " %s=%016x %s=%016x %s=%016x\n";
+ String outputFormat2 = " %s=%08x %s=%08x\n";
+ DWORD64 *X = context.Arm64Context.X;
+ for (int i = 0; i < 9; i++)
+ {
+ ExtOut(outputXRegFormat3, i + 0, X[i + 0], i + 1, X[i + 1], i + 2, X[i + 2]);
+ }
+ ExtOut(outputXRegFormat1, 28, X[28]);
+ ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
+ ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
+#else
+ ExtOut("Can't display register values for this platform\n");
+#endif
+ return S_OK;
+
+ }
+
+ static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp)
+ {
+ CROSS_PLATFORM_CONTEXT context;
+ HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
+ if (FAILED(hr) || hr == S_FALSE)
+ {
+ // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
+ ExtOut("GetFrameContext failed: %lx\n", hr);
+ return E_FAIL;
+ }
+
+ // First find the info for the Frame object, if the current frame has an associated clr!Frame.
+ *ip = GetIP(context);
+ *sp = GetSP(context);
+
+ if (IsDbgTargetArm())
+ *ip = *ip & ~THUMB_CODE;
+
+ return S_OK;
+ }
+
+ static void PrintNativeStackFrame(TableOutput out, PDEBUG_STACK_FRAME frame, BOOL bSuppressLines)
+ {
+ char filename[MAX_LONGPATH + 1];
+ char symbol[1024];
+ ULONG64 displacement;
+
+ ULONG64 ip = frame->InstructionOffset;
+
+ out.WriteColumn(0, frame->StackOffset);
+ out.WriteColumn(1, NativePtr(ip));
+
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
+ if (SUCCEEDED(hr) && symbol[0] != '\0')
+ {
+ String frameOutput;
+ frameOutput += symbol;
+
+ if (displacement)
+ {
+ frameOutput += " + ";
+ frameOutput += Decimal(displacement);
+ }
+
+ if (!bSuppressLines)
+ {
+ ULONG line;
+ hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
+ if (SUCCEEDED(hr))
+ {
+ frameOutput += " at ";
+ frameOutput += filename;
+ frameOutput += ":";
+ frameOutput += Decimal(line);
+ }
+ }
+
+ out.WriteColumn(2, frameOutput);
+ }
+ else
+ {
+ out.WriteColumn(2, "");
+ }
+ }
+
+ static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
+ {
+ ULONG id = 0;
+ ULONG osid = 0;
+
+ g_ExtSystem->GetCurrentThreadSystemId(&osid);
+ ExtOut("OS Thread Id: 0x%x ", osid);
+ g_ExtSystem->GetCurrentThreadId(&id);
+ ExtOut("(%d)\n", id);
+
+ PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
+ }
+private:
+
+ static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
+ {
+ HRESULT hr = S_OK;
+ ToRelease<IXCLRDataTask> pTask;
+
+ if ((hr = g_ExtSystem->GetCurrentThreadSystemId(&osID)) != S_OK ||
+ (hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
+ {
+ ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
+ ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
+ ExtOut("the process\n");
+ return hr;
+ }
+
+ return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
+ CLRDATA_SIMPFRAME_MANAGED_METHOD |
+ CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
+ CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
+ ppStackwalk);
+ }
+
+ /* Prints the args and locals of for a thread's stack.
+ * Params:
+ * pStackWalk - the stack we are printing
+ * bArgs - whether to print args
+ * bLocals - whether to print locals
+ */
+ static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals)
+ {
+ ToRelease<IXCLRDataFrame> pFrame;
+ ToRelease<IXCLRDataValue> pVal;
+ ULONG32 argCount = 0;
+ ULONG32 localCount = 0;
+ HRESULT hr = S_OK;
+
+ hr = pStackWalk->GetFrame(&pFrame);
+
+ // Print arguments
+ if (SUCCEEDED(hr) && bArgs)
+ hr = pFrame->GetNumArguments(&argCount);
+
+ if (SUCCEEDED(hr) && bArgs)
+ hr = ShowArgs(argCount, pFrame, pVal);
+
+ // Print locals
+ if (SUCCEEDED(hr) && bLocals)
+ hr = pFrame->GetNumLocalVariables(&localCount);
+
+ if (SUCCEEDED(hr) && bLocals)
+ ShowLocals(localCount, pFrame, pVal);
+
+ ExtOut("\n");
+ }
+
+
+
+ /* Displays the arguments to a function
+ * Params:
+ * argy - the number of arguments the function has
+ * pFramey - the frame we are inspecting
+ * pVal - a pointer to the CLRDataValue we use to query for info about the args
+ */
+ static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
+ {
+ CLRDATA_ADDRESS addr = 0;
+ BOOL fPrintedLocation = FALSE;
+ ULONG64 outVar = 0;
+ ULONG32 tmp;
+ HRESULT hr = S_OK;
+
+ ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen];
+ if (!argName)
+ {
+ ReportOOM();
+ return E_FAIL;
+ }
+
+ for (ULONG32 i=0; i < argy; i++)
+ {
+ if (i == 0)
+ {
+ ExtOut(" PARAMETERS:\n");
+ }
+
+ hr = pFramey->GetArgumentByIndex(i,
+ &pVal,
+ mdNameLen,
+ &tmp,
+ argName);
+
+ if (FAILED(hr))
+ return hr;
+
+ ExtOut(" ");
+
+ if (argName[0] != L'\0')
+ {
+ ExtOut("%S ", argName.GetPtr());
+ }
+
+ // At times we cannot print the value of a parameter (most
+ // common case being a non-primitive value type). In these
+ // cases we need to print the location of the parameter,
+ // so that we can later examine it (e.g. using !dumpvc)
+ {
+ bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1;
+ if (result)
+ result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr));
+
+ if (result)
+ {
+ if (tmp == CLRDATA_VLOC_REGISTER)
+ {
+ ExtOut("(<CLR reg>) ");
+ }
+ else
+ {
+ ExtOut("(0x%p) ", SOS_PTR(CDA_TO_UL64(addr)));
+ }
+ fPrintedLocation = TRUE;
+ }
+ }
+
+ if (argName[0] != L'\0' || fPrintedLocation)
+ {
+ ExtOut("= ");
+ }
+
+ if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW)
+ {
+ ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1];
+ if (pByte == NULL)
+ {
+ ReportOOM();
+ return E_FAIL;
+ }
+
+ hr = pVal->GetBytes(tmp, &tmp, pByte);
+
+ if (FAILED(hr))
+ {
+ ExtOut("<unable to retrieve data>\n");
+ }
+ else
+ {
+ switch(tmp)
+ {
+ case 1: outVar = *((BYTE *)pByte.GetPtr()); break;
+ case 2: outVar = *((short *)pByte.GetPtr()); break;
+ case 4: outVar = *((DWORD *)pByte.GetPtr()); break;
+ case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break;
+ default: outVar = 0;
+ }
+
+ if (outVar)
+ DMLOut("0x%s\n", DMLObject(outVar));
+ else
+ ExtOut("0x%p\n", SOS_PTR(outVar));
+ }
+
+ }
+ else
+ {
+ ExtOut("<no data>\n");
+ }
+
+ pVal->Release();
+ }
+
+ return S_OK;
+ }
+
+
+ /* Prints the locals of a frame.
+ * Params:
+ * localy - the number of locals in the frame
+ * pFramey - the frame we are inspecting
+ * pVal - a pointer to the CLRDataValue we use to query for info about the args
+ */
+ static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
+ {
+ for (ULONG32 i=0; i < localy; i++)
+ {
+ if (i == 0)
+ ExtOut(" LOCALS:\n");
+
+ HRESULT hr;
+ ExtOut(" ");
+
+ // local names don't work in Whidbey.
+ hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ ULONG32 numLocations;
+ if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) &&
+ numLocations == 1)
+ {
+ ULONG32 flags;
+ CLRDATA_ADDRESS addr;
+ if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr)))
+ {
+ if (flags == CLRDATA_VLOC_REGISTER)
+ {
+ ExtOut("<CLR reg> ");
+ }
+ else
+ {
+ ExtOut("0x%p ", SOS_PTR(CDA_TO_UL64(addr)));
+ }
+ }
+
+ // Can I get a name for the item?
+
+ ExtOut("= ");
+ }
+ ULONG32 dwSize = 0;
+ hr = pVal->GetBytes(0, &dwSize, NULL);
+
+ if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW)
+ {
+ ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1];
+ if (pByte == NULL)
+ {
+ ReportOOM();
+ return E_FAIL;
+ }
+
+ hr = pVal->GetBytes(dwSize,&dwSize,pByte);
+
+ if (FAILED(hr))
+ {
+ ExtOut("<unable to retrieve data>\n");
+ }
+ else
+ {
+ ULONG64 outVar = 0;
+ switch(dwSize)
+ {
+ case 1: outVar = *((BYTE *) pByte.GetPtr()); break;
+ case 2: outVar = *((short *) pByte.GetPtr()); break;
+ case 4: outVar = *((DWORD *) pByte.GetPtr()); break;
+ case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break;
+ default: outVar = 0;
+ }
+
+ if (outVar)
+ DMLOut("0x%s\n", DMLObject(outVar));
+ else
+ ExtOut("0x%p\n", SOS_PTR(outVar));
+ }
+ }
+ else
+ {
+ ExtOut("<no data>\n");
+ }
+
+ pVal->Release();
+ }
+
+ return S_OK;
+ }
+
+};
+
+#ifndef FEATURE_PAL
+
+WatchCmd g_watchCmd;
+
+// The grand new !Watch command, private to Apollo for now
+DECLARE_API(Watch)
+{
+ INIT_API_NOEE();
+ BOOL bExpression = FALSE;
+ StringHolder addExpression;
+ StringHolder aExpression;
+ StringHolder saveName;
+ StringHolder sName;
+ StringHolder expression;
+ StringHolder filterName;
+ StringHolder renameOldName;
+ size_t expandIndex = -1;
+ size_t removeIndex = -1;
+ BOOL clear = FALSE;
+
+ size_t nArg = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-add", &addExpression.data, COSTRING, TRUE},
+ {"-a", &aExpression.data, COSTRING, TRUE},
+ {"-save", &saveName.data, COSTRING, TRUE},
+ {"-s", &sName.data, COSTRING, TRUE},
+ {"-clear", &clear, COBOOL, FALSE},
+ {"-c", &clear, COBOOL, FALSE},
+ {"-expand", &expandIndex, COSIZE_T, TRUE},
+ {"-filter", &filterName.data, COSTRING, TRUE},
+ {"-r", &removeIndex, COSIZE_T, TRUE},
+ {"-remove", &removeIndex, COSIZE_T, TRUE},
+ {"-rename", &renameOldName.data, COSTRING, TRUE},
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&expression.data, COSTRING}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if(addExpression.data != NULL || aExpression.data != NULL)
+ {
+ WCHAR pAddExpression[MAX_EXPRESSION];
+ swprintf_s(pAddExpression, MAX_EXPRESSION, W("%S"), addExpression.data != NULL ? addExpression.data : aExpression.data);
+ Status = g_watchCmd.Add(pAddExpression);
+ }
+ else if(removeIndex != -1)
+ {
+ if(removeIndex <= 0)
+ {
+ ExtOut("Index must be a postive decimal number\n");
+ }
+ else
+ {
+ Status = g_watchCmd.Remove((int)removeIndex);
+ if(Status == S_OK)
+ ExtOut("Watch expression #%d has been removed\n", removeIndex);
+ else if(Status == S_FALSE)
+ ExtOut("There is no watch expression with index %d\n", removeIndex);
+ else
+ ExtOut("Unknown failure 0x%x removing watch expression\n", Status);
+ }
+ }
+ else if(saveName.data != NULL || sName.data != NULL)
+ {
+ WCHAR pSaveName[MAX_EXPRESSION];
+ swprintf_s(pSaveName, MAX_EXPRESSION, W("%S"), saveName.data != NULL ? saveName.data : sName.data);
+ Status = g_watchCmd.SaveList(pSaveName);
+ }
+ else if(clear)
+ {
+ g_watchCmd.Clear();
+ }
+ else if(renameOldName.data != NULL)
+ {
+ if(nArg != 1)
+ {
+ ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n");
+ return S_FALSE;
+ }
+ WCHAR pOldName[MAX_EXPRESSION];
+ swprintf_s(pOldName, MAX_EXPRESSION, W("%S"), renameOldName.data);
+ WCHAR pNewName[MAX_EXPRESSION];
+ swprintf_s(pNewName, MAX_EXPRESSION, W("%S"), expression.data);
+ g_watchCmd.RenameList(pOldName, pNewName);
+ }
+ // print the tree, possibly with filtering and/or expansion
+ else if(expandIndex != -1 || expression.data == NULL)
+ {
+ WCHAR pExpression[MAX_EXPRESSION];
+ pExpression[0] = '\0';
+
+ if(expandIndex != -1)
+ {
+ if(expression.data != NULL)
+ {
+ swprintf_s(pExpression, MAX_EXPRESSION, W("%S"), expression.data);
+ }
+ else
+ {
+ ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n");
+ return S_FALSE;
+ }
+ }
+ WCHAR pFilterName[MAX_EXPRESSION];
+ pFilterName[0] = '\0';
+
+ if(filterName.data != NULL)
+ {
+ swprintf_s(pFilterName, MAX_EXPRESSION, W("%S"), filterName.data);
+ }
+
+ g_watchCmd.Print((int)expandIndex, pExpression, pFilterName);
+ }
+ else
+ {
+ ExtOut("Unrecognized argument: %s\n", expression.data);
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(ClrStack)
+{
+ INIT_API();
+
+ BOOL bAll = FALSE;
+ BOOL bParams = FALSE;
+ BOOL bLocals = FALSE;
+ BOOL bSuppressLines = FALSE;
+ BOOL bICorDebug = FALSE;
+ BOOL bGC = FALSE;
+ BOOL dml = FALSE;
+ BOOL bFull = FALSE;
+ BOOL bDisplayRegVals = FALSE;
+ DWORD frameToDumpVariablesFor = -1;
+ StringHolder cvariableName;
+ ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
+ if (wvariableName == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ memset(wvariableName, 0, sizeof(wvariableName));
+
+ size_t nArg = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-a", &bAll, COBOOL, FALSE},
+ {"-p", &bParams, COBOOL, FALSE},
+ {"-l", &bLocals, COBOOL, FALSE},
+ {"-n", &bSuppressLines, COBOOL, FALSE},
+ {"-i", &bICorDebug, COBOOL, FALSE},
+ {"-gc", &bGC, COBOOL, FALSE},
+ {"-f", &bFull, COBOOL, FALSE},
+ {"-r", &bDisplayRegVals, COBOOL, FALSE },
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&cvariableName.data, COSTRING},
+ {&frameToDumpVariablesFor, COSIZE_T},
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (bAll || bParams || bLocals)
+ {
+ // No parameter or local supports for minidump case!
+ MINIDUMP_NOT_SUPPORTED();
+ }
+
+ if (bAll)
+ {
+ bParams = bLocals = TRUE;
+ }
+
+ if (bICorDebug)
+ {
+ if(nArg > 0)
+ {
+ bool firstParamIsNumber = true;
+ for(DWORD i = 0; i < strlen(cvariableName.data); i++)
+ firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]);
+
+ if(firstParamIsNumber && nArg == 1)
+ {
+ frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data);
+ cvariableName.data[0] = '\0';
+ }
+ }
+ if(cvariableName.data != NULL && strlen(cvariableName.data) > 0)
+ swprintf_s(wvariableName, mdNameLen, W("%S\0"), cvariableName.data);
+
+ if(_wcslen(wvariableName) > 0)
+ bParams = bLocals = TRUE;
+
+ EnableDMLHolder dmlHolder(TRUE);
+ return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
+ }
+
+ ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+
+ return S_OK;
+}
+
+#ifndef FEATURE_PAL
+
+BOOL IsMemoryInfoAvailable()
+{
+ ULONG Class;
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ if (Qualifier == DEBUG_DUMP_SMALL)
+ {
+ g_ExtControl->GetDumpFormatFlags(&Qualifier);
+ if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
+ {
+ if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0)
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+DECLARE_API( VMMap )
+{
+ INIT_API();
+
+ if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
+ {
+ ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n");
+ }
+ else
+ {
+ vmmap();
+ }
+
+ return Status;
+} // DECLARE_API( vmmap )
+
+DECLARE_API( SOSFlush )
+{
+ INIT_API();
+
+ g_clrData->Flush();
+
+ return Status;
+} // DECLARE_API( SOSFlush )
+
+DECLARE_API( VMStat )
+{
+ INIT_API();
+
+ if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
+ {
+ ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n");
+ }
+ else
+ {
+ vmstat();
+ }
+
+ return Status;
+} // DECLARE_API( vmmap )
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function saves a dll to a file. *
+* *
+\**********************************************************************/
+DECLARE_API(SaveModule)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder Location;
+ DWORD_PTR moduleAddr = NULL;
+ BOOL bIsImage;
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&moduleAddr, COHEX},
+ {&Location.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg != 2)
+ {
+ ExtOut("Usage: SaveModule <address> <file to save>\n");
+ return Status;
+ }
+ if (moduleAddr == 0) {
+ ExtOut ("Invalid arg\n");
+ return Status;
+ }
+
+ char* ptr = Location.data;
+
+ DWORD_PTR dllBase = 0;
+ ULONG64 base;
+ if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK)
+ {
+ dllBase = TO_TADDR(base);
+ }
+ else if (IsModule(moduleAddr))
+ {
+ DacpModuleData module;
+ module.Request(g_sos, TO_CDADDR(moduleAddr));
+ dllBase = TO_TADDR(module.ilBase);
+ if (dllBase == 0)
+ {
+ ExtOut ("Module does not have base address\n");
+ return Status;
+ }
+ }
+ else
+ {
+ ExtOut ("%p is not a Module or base address\n", SOS_PTR(moduleAddr));
+ return Status;
+ }
+
+ MEMORY_BASIC_INFORMATION64 mbi;
+ if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi)))
+ {
+ ExtOut("Failed to retrieve information about segment %p", SOS_PTR(dllBase));
+ return Status;
+ }
+
+ // module loaded as an image or mapped as a flat file?
+ bIsImage = (mbi.Type == MEM_IMAGE);
+
+ IMAGE_DOS_HEADER DosHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
+ return S_FALSE;
+
+ IMAGE_NT_HEADERS Header;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
+ return S_FALSE;
+
+ DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader)
+ + Header.FileHeader.SizeOfOptionalHeader;
+
+ IMAGE_SECTION_HEADER section;
+ struct MemLocation
+ {
+ DWORD_PTR VAAddr;
+ DWORD_PTR VASize;
+ DWORD_PTR FileAddr;
+ DWORD_PTR FileSize;
+ };
+
+ int nSection = Header.FileHeader.NumberOfSections;
+ ExtOut("%u sections in file\n",nSection);
+ MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation));
+ int indxSec = -1;
+ int slot;
+ for (int n = 0; n < nSection; n++)
+ {
+ if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), &section, sizeof(section), NULL) == S_OK)
+ {
+ for (slot = 0; slot <= indxSec; slot ++)
+ if (section.PointerToRawData < memLoc[slot].FileAddr)
+ break;
+
+ for (int k = indxSec; k >= slot; k --)
+ memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation));
+
+ memLoc[slot].VAAddr = section.VirtualAddress;
+ memLoc[slot].VASize = section.Misc.VirtualSize;
+ memLoc[slot].FileAddr = section.PointerToRawData;
+ memLoc[slot].FileSize = section.SizeOfRawData;
+ ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n",
+ n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr,
+ memLoc[slot].FileSize);
+ indxSec ++;
+ }
+ else
+ {
+ ExtOut("Fail to read PE section info\n");
+ return Status;
+ }
+ sectionAddr += sizeof(section);
+ }
+
+ if (ptr[0] == '\0')
+ {
+ ExtOut ("File not specified\n");
+ return Status;
+ }
+
+ PCSTR file = ptr;
+ ptr += strlen(ptr)-1;
+ while (isspace(*ptr))
+ {
+ *ptr = '\0';
+ ptr --;
+ }
+
+ HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ ExtOut ("Fail to create file %s\n", file);
+ return Status;
+ }
+
+ ULONG pageSize = OSPageSize();
+ char *buffer = (char *)_alloca(pageSize);
+ DWORD nRead;
+ DWORD nWrite;
+
+ // NT PE Headers
+ TADDR dwAddr = dllBase;
+ TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders;
+ while (dwAddr < dwEnd)
+ {
+ nRead = pageSize;
+ if (dwEnd - dwAddr < nRead)
+ nRead = (ULONG)(dwEnd - dwAddr);
+
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
+ {
+ WriteFile(hFile,buffer,nRead,&nWrite,NULL);
+ }
+ else
+ {
+ ExtOut ("Fail to read memory\n");
+ goto end;
+ }
+ dwAddr += nRead;
+ }
+
+ for (slot = 0; slot <= indxSec; slot ++)
+ {
+ dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr);
+ dwEnd = memLoc[slot].FileSize + dwAddr - 1;
+
+ while (dwAddr <= dwEnd)
+ {
+ nRead = pageSize;
+ if (dwEnd - dwAddr + 1 < pageSize)
+ nRead = (ULONG)(dwEnd - dwAddr + 1);
+
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
+ {
+ WriteFile(hFile,buffer,nRead,&nWrite,NULL);
+ }
+ else
+ {
+ ExtOut ("Fail to read memory\n");
+ goto end;
+ }
+ dwAddr += pageSize;
+ }
+ }
+end:
+ CloseHandle (hFile);
+ return Status;
+}
+
+#ifdef _DEBUG
+DECLARE_API(dbgout)
+{
+ INIT_API();
+
+ BOOL bOff = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-off", &bOff, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ Output::SetDebugOutputEnabled(!bOff);
+ return Status;
+}
+DECLARE_API(filthint)
+{
+ INIT_API();
+
+ BOOL bOff = FALSE;
+ DWORD_PTR filter = 0;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-off", &bOff, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&filter, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option),
+ arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (bOff)
+ {
+ g_filterHint = 0;
+ return Status;
+ }
+
+ g_filterHint = filter;
+ return Status;
+}
+#endif // _DEBUG
+
+#endif // FEATURE_PAL
+
+static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp,
+ ULONG64 IPAddr, StringOutput& so)
+{
+#define DOAPPEND(str) \
+ do { \
+ if (!so.Append((str))) { \
+ return E_OUTOFMEMORY; \
+ }} while (0)
+
+ // Should we skip explicit frames? They are characterized by Esp = 0, && Eip = 0 or 1.
+ // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0
+ if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1))
+ {
+ return S_FALSE;
+ }
+
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
+ {
+ return E_FAIL;
+ }
+
+ ArrayHolder<WCHAR> wszNameBuffer = new WCHAR[MAX_LONGPATH+1];
+
+ if (Flags & SOS_STACKTRACE_SHOWADDRESSES)
+ {
+ _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("%p %p "), (void*)(size_t) Esp, (void*)(size_t) IPAddr); // _TRUNCATE
+ DOAPPEND(wszNameBuffer);
+ }
+
+ DacpModuleData dmd;
+ BOOL bModuleNameWorked = FALSE;
+ ULONG64 addrInModule = IPAddr;
+ if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK)
+ {
+ CLRDATA_ADDRESS base = 0;
+ if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK)
+ {
+ if (base)
+ {
+ addrInModule = base;
+ }
+ }
+ }
+ ULONG Index;
+ ULONG64 base;
+ if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &base) == S_OK)
+ {
+ ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
+ if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
+ {
+ MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, wszNameBuffer, MAX_LONGPATH);
+ DOAPPEND (wszNameBuffer);
+ bModuleNameWorked = TRUE;
+ }
+ }
+#ifdef FEATURE_PAL
+ else
+ {
+ if (g_sos->GetPEFileName(dmd.File, MAX_LONGPATH, wszNameBuffer, NULL) == S_OK)
+ {
+ if (wszNameBuffer[0] != W('\0'))
+ {
+ WCHAR *pJustName = _wcsrchr(wszNameBuffer, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pJustName == NULL)
+ pJustName = wszNameBuffer - 1;
+
+ DOAPPEND(pJustName + 1);
+ bModuleNameWorked = TRUE;
+ }
+ }
+ }
+#endif // FEATURE_PAL
+
+ // Under certain circumstances DacpMethodDescData::GetMethodDescName()
+ // returns a module qualified method name
+ HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, MAX_LONGPATH, wszNameBuffer, NULL);
+
+ WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : _wcschr(wszNameBuffer, L'!'));
+ if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL)
+ {
+ // if we weren't able to get the module name, but GetMethodDescName returned
+ // the module as part of the returned method name, use this data
+ DOAPPEND(wszNameBuffer);
+ }
+ else
+ {
+ if (!bModuleNameWorked)
+ {
+ DOAPPEND (W("UNKNOWN"));
+ }
+ DOAPPEND(W("!"));
+ if (hr == S_OK)
+ {
+ // the module name we retrieved above from debugger will take
+ // precedence over the name possibly returned by GetMethodDescName()
+ DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : (WCHAR *)wszNameBuffer);
+ }
+ else
+ {
+ DOAPPEND(W("UNKNOWN"));
+ }
+ }
+
+ ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr);
+ if (Displacement)
+ {
+ _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("+%#x"), Displacement); // _TRUNCATE
+ DOAPPEND (wszNameBuffer);
+ }
+
+ return S_OK;
+#undef DOAPPEND
+}
+
+BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext,
+ CROSS_PLATFORM_CONTEXT *context)
+{
+ if (pTransitionContexts == NULL || *pcurCount >= maxCount)
+ {
+ ++(*pcurCount);
+ return FALSE;
+ }
+ if (uiSizeOfContext == sizeof(StackTrace_SimpleContext))
+ {
+ StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts;
+ g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context);
+ }
+ else if (uiSizeOfContext == g_targetMachine->GetContextSize())
+ {
+ // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts
+ // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun).
+ g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount));
+ }
+ else
+ {
+ return FALSE;
+ }
+ ++(*pcurCount);
+ return TRUE;
+}
+
+HRESULT CALLBACK ImplementEFNStackTrace(
+ PDEBUG_CLIENT client,
+ __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags)
+{
+
+#define DOAPPEND(str) if (!so.Append((str))) { \
+ Status = E_OUTOFMEMORY; \
+ goto Exit; \
+}
+
+ HRESULT Status = E_FAIL;
+ StringOutput so;
+ size_t transitionContextCount = 0;
+
+ if (puiTextLength == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ if (pTransitionContexts)
+ {
+ if (puiTransitionContextCount == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ // Do error checking on context size
+ if ((uiSizeOfContext != g_targetMachine->GetContextSize()) &&
+ (uiSizeOfContext != sizeof(StackTrace_SimpleContext)))
+ {
+ return E_INVALIDARG;
+ }
+ }
+
+ IXCLRDataStackWalk *pStackWalk = NULL;
+ IXCLRDataTask* Task;
+ ULONG ThreadId;
+
+ if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK ||
+ (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK)
+ {
+ // Not a managed thread.
+ return SOS_E_NOMANAGEDCODE;
+ }
+
+ Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
+ CLRDATA_SIMPFRAME_MANAGED_METHOD |
+ CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
+ CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
+ &pStackWalk);
+
+ Task->Release();
+
+ if (Status != S_OK)
+ {
+ if (Status == E_FAIL)
+ {
+ return SOS_E_NOMANAGEDCODE;
+ }
+ return Status;
+ }
+
+#ifdef _TARGET_WIN64_
+ ULONG numFrames = 0;
+ BOOL bInNative = TRUE;
+
+ Status = GetContextStackTrace(&numFrames);
+ if (FAILED(Status))
+ {
+ goto Exit;
+ }
+
+ for (ULONG i = 0; i < numFrames; i++)
+ {
+ PDEBUG_STACK_FRAME pCur = g_Frames + i;
+
+ CLRDATA_ADDRESS pMD;
+ if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK)
+ {
+ if (bInNative || transitionContextCount==0)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = FALSE;
+
+ DOAPPEND (W("(TransitionMU)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+
+ Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
+ pCur->StackOffset, pCur->InstructionOffset, so);
+ if (FAILED(Status))
+ {
+ goto Exit;
+ }
+ else if (Status == S_OK)
+ {
+ DOAPPEND (W("\n"));
+ }
+ // for S_FALSE do not append anything
+
+ }
+ else
+ {
+ if (!bInNative)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = TRUE;
+
+ DOAPPEND (W("(TransitionUM)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+ }
+ }
+
+Exit:
+#else // _TARGET_WIN64_
+
+#ifdef _DEBUG
+ size_t prevLength = 0;
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+ wcscpy_s(wszNameBuffer, 1024, W("Frame")); // default value
+#endif
+
+ BOOL bInNative = TRUE;
+
+ UINT frameCount = 0;
+ do
+ {
+ DacpFrameData FrameData;
+ if ((Status = FrameData.Request(pStackWalk)) != S_OK)
+ {
+ goto Exit;
+ }
+
+ CROSS_PLATFORM_CONTEXT context;
+ if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(),
+ NULL, (BYTE *)&context))!=S_OK)
+ {
+ goto Exit;
+ }
+
+ ExtDbgOut ( " * Ctx[BSI]: %08x %08x %08x ", GetBP(context), GetSP(context), GetIP(context) );
+
+ CLRDATA_ADDRESS pMD;
+ if (!FrameData.frameAddr)
+ {
+ if (bInNative || transitionContextCount==0)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = FALSE;
+
+ DOAPPEND (W("(TransitionMU)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, &context);
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+
+ // we may have a method, try to get the methoddesc
+ if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK)
+ {
+ Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
+ GetSP(context), GetIP(context), so);
+ if (FAILED(Status))
+ {
+ goto Exit;
+ }
+ else if (Status == S_OK)
+ {
+ DOAPPEND (W("\n"));
+ }
+ // for S_FALSE do not append anything
+ }
+ }
+ else
+ {
+#ifdef _DEBUG
+ if (Output::IsDebugOutputEnabled())
+ {
+ DWORD_PTR vtAddr;
+ MOVE(vtAddr, TO_TADDR(FrameData.frameAddr));
+ if (g_sos->GetFrameName(TO_CDADDR(vtAddr), 1024, wszNameBuffer, NULL) == S_OK)
+ ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr);
+ else
+ ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr);
+ }
+#endif
+ if (!bInNative)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = TRUE;
+
+ DOAPPEND (W("(TransitionUM)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, &context);
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+ }
+
+#ifdef _DEBUG
+ if (so.Length() > prevLength)
+ {
+ ExtDbgOut ( "%ls", so.String()+prevLength );
+ prevLength = so.Length();
+ }
+ else
+ ExtDbgOut ( "\n" );
+#endif
+
+ }
+ while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK);
+
+ Status = S_OK;
+
+Exit:
+#endif // _TARGET_WIN64_
+
+ if (pStackWalk)
+ {
+ pStackWalk->Release();
+ pStackWalk = NULL;
+ }
+
+ // We have finished. Does the user want to copy this data to a buffer?
+ if (Status == S_OK)
+ {
+ if(wszTextOut)
+ {
+ // They want at least partial output
+ wcsncpy_s (wszTextOut, *puiTextLength, so.String(), *puiTextLength-1); // _TRUNCATE
+ }
+ else
+ {
+ *puiTextLength = _wcslen (so.String()) + 1;
+ }
+
+ if (puiTransitionContextCount)
+ {
+ *puiTransitionContextCount = transitionContextCount;
+ }
+ }
+
+ return Status;
+}
+
+#ifdef FEATURE_PAL
+#define PAL_TRY_NAKED PAL_CPP_TRY
+#define PAL_EXCEPT_NAKED(disp) PAL_CPP_CATCH_ALL
+#define PAL_ENDTRY_NAKED PAL_CPP_ENDTRY
+#endif
+
+// TODO: Convert PAL_TRY_NAKED to something that works on the Mac.
+HRESULT CALLBACK ImplementEFNStackTraceTry(
+ PDEBUG_CLIENT client,
+ __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags)
+{
+ HRESULT Status = E_FAIL;
+
+ PAL_TRY_NAKED
+ {
+ Status = ImplementEFNStackTrace(client, wszTextOut, puiTextLength,
+ pTransitionContexts, puiTransitionContextCount,
+ uiSizeOfContext, Flags);
+ }
+ PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ PAL_ENDTRY_NAKED
+
+ return Status;
+}
+
+// See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments.
+HRESULT CALLBACK _EFN_StackTrace(
+ PDEBUG_CLIENT client,
+ __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags)
+{
+ INIT_API();
+
+ Status = ImplementEFNStackTraceTry(client, wszTextOut, puiTextLength,
+ pTransitionContexts, puiTransitionContextCount,
+ uiSizeOfContext, Flags);
+
+ return Status;
+}
+
+
+BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString)
+{
+ BOOL bRet = FALSE;
+
+ wszBuffer[0] = L'\0';
+
+ DacpObjectData objData;
+ if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK)
+ {
+ return bRet;
+ }
+
+ strobjInfo stInfo;
+
+ if (MOVE(stInfo, strObjPointer) != S_OK)
+ {
+ return bRet;
+ }
+
+ DWORD dwBufLength = 0;
+ if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength))
+ {
+ ExtOut("<integer overflow>\n");
+ return bRet;
+ }
+
+ LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength];
+ if (pwszBuf == NULL)
+ {
+ return bRet;
+ }
+
+ if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
+ {
+ delete [] pwszBuf;
+ return bRet;
+ }
+
+ // String is in format
+ // <SP><SP><SP>at <function name>(args,...)\n
+ // ...
+ // Parse and copy just <function name>(args,...)
+
+ LPWSTR pwszPointer = pwszBuf;
+
+ WCHAR PSZSEP[] = W(" at ");
+
+ UINT Length = 0;
+ while(1)
+ {
+ if (_wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
+ {
+ delete [] pwszBuf;
+ return bRet;
+ }
+
+ pwszPointer += _wcslen(PSZSEP);
+ LPWSTR nextPos = _wcsstr(pwszPointer, PSZSEP);
+ if (nextPos == NULL)
+ {
+ // Done! Note that we are leaving the function before we add the last
+ // line of stack trace to the output string. This is on purpose because
+ // this string needs to be merged with a real trace, and the last line
+ // of the trace will be common to the real trace.
+ break;
+ }
+ WCHAR c = *nextPos;
+ *nextPos = L'\0';
+
+ // Buffer is calculated for sprintf below (" %p %p %S\n");
+ WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
+
+ // Note that we don't add a newline because we have this embedded in wszLineBuffer
+ swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %p %p %s"), (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer);
+ Length += (UINT)_wcslen(wszLineBuffer);
+
+ if (wszBuffer)
+ {
+ wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE);
+ }
+
+ *nextPos = c;
+ // Move to the next line.
+ pwszPointer = nextPos;
+ }
+
+ delete [] pwszBuf;
+
+ // Return TRUE only if the stack string had any information that was successfully parsed.
+ // (Length > 0) is a good indicator of that.
+ bRet = (Length > 0);
+ return bRet;
+}
+
+HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString,
+ BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off
+{
+ DacpObjectData objData;
+ if (objData.Request(g_sos, cdaObj) != S_OK)
+ {
+ return E_FAIL;
+ }
+
+ // Make sure it is an exception object, and get the MT of Exception
+ CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
+ if (exceptionMT == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ // First try to get exception object data using ISOSDacInterface2
+ DacpExceptionObjectData excData;
+ BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj));
+
+ int iOffset;
+ // Is there a _remoteStackTraceString? We'll want to prepend that data.
+ // We only have string data, so IP/SP info has to be set to -1.
+ DWORD_PTR strPointer;
+ if (bGotExcData)
+ {
+ strPointer = TO_TADDR(excData.RemoteStackTraceString);
+ }
+ else
+ {
+ iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_remoteStackTraceString"));
+ MOVE (strPointer, TO_TADDR(cdaObj) + iOffset);
+ }
+ if (strPointer)
+ {
+ WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString];
+ if (pwszBuffer == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (FormatFromRemoteString(strPointer, pwszBuffer, cchString))
+ {
+ // Prepend this stuff to the string for the user
+ wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
+ }
+ delete[] pwszBuffer;
+ }
+
+ BOOL bAsync = bGotExcData ? IsAsyncException(excData)
+ : IsAsyncException(TO_TADDR(cdaObj), TO_TADDR(objData.MethodTable));
+
+ DWORD_PTR arrayPtr;
+ if (bGotExcData)
+ {
+ arrayPtr = TO_TADDR(excData.StackTrace);
+ }
+ else
+ {
+ iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_stackTrace"));
+ MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset);
+ }
+
+ if (arrayPtr)
+ {
+ DWORD arrayLen;
+ MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR));
+
+ if (arrayLen)
+ {
+#ifdef _TARGET_WIN64_
+ DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
+#else
+ DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD);
+#endif // _TARGET_WIN64_
+ size_t stackTraceSize = 0;
+ MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case
+
+ DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
+ dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
+
+ if (stackTraceSize != 0)
+ {
+ size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase);
+ WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1];
+ if (pwszBuffer)
+ {
+ FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase);
+ wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
+ delete[] pwszBuffer;
+ }
+ else
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ }
+ }
+ return S_OK;
+}
+
+HRESULT ImplementEFNGetManagedExcepStack(
+ CLRDATA_ADDRESS cdaStackObj,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString)
+{
+ HRESULT Status = E_FAIL;
+
+ if (wszStackString == NULL || cchString == 0)
+ {
+ return E_INVALIDARG;
+ }
+
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+ BOOL bCanUseThreadContext = TRUE;
+
+ ZeroMemory(&Thread, sizeof(DacpThreadData));
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ // The current thread is unmanaged
+ bCanUseThreadContext = FALSE;
+ }
+
+ if (cdaStackObj == NULL)
+ {
+ if (!bCanUseThreadContext)
+ {
+ return E_INVALIDARG;
+ }
+
+ TADDR taLTOH = NULL;
+ if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &taLTOH,
+ sizeof(taLTOH), NULL)) || (taLTOH==NULL))
+ {
+ return Status;
+ }
+ else
+ {
+ cdaStackObj = TO_CDADDR(taLTOH);
+ }
+ }
+
+ // Put the stack trace header on
+ AddExceptionHeader(wszStackString, cchString);
+
+ // First is there a nested exception?
+ if (bCanUseThreadContext && Thread.firstNestedException)
+ {
+ CLRDATA_ADDRESS obj = 0, next = 0;
+ CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
+ do
+ {
+ Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
+
+ // deal with the inability to read a nested exception gracefully
+ if (Status != S_OK)
+ {
+ break;
+ }
+
+ Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE);
+ currentNested = next;
+ }
+ while(currentNested != NULL);
+ }
+
+ Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE);
+
+ return Status;
+}
+
+// TODO: Enable this when ImplementEFNStackTraceTry is fixed.
+// This function, like VerifyDAC, exists for the purpose of testing
+// hard-to-get-to SOS APIs.
+DECLARE_API(VerifyStackTrace)
+{
+ INIT_API();
+
+ BOOL bVerifyManagedExcepStack = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL))
+ {
+ return Status;
+ }
+
+ if (bVerifyManagedExcepStack)
+ {
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ TADDR taExc = NULL;
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ TADDR taLTOH = NULL;
+ if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &taLTOH,
+ sizeof(taLTOH), NULL)) || (taLTOH == NULL))
+ {
+ ExtOut("There is no current managed exception on this thread\n");
+ return Status;
+ }
+ else
+ {
+ taExc = taLTOH;
+ }
+
+ const SIZE_T cchStr = 4096;
+ WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR));
+ if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK)
+ {
+ ExtOut("Error!\n");
+ return Status;
+ }
+
+ ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", SOS_PTR(taExc));
+ ExtOut("%S\n", wszStr);
+
+ if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK)
+ {
+ ExtOut("Error!\n");
+ return Status;
+ }
+
+ ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n");
+ ExtOut("%S\n", wszStr);
+ }
+ else
+ {
+ size_t textLength = 0;
+ size_t contextLength = 0;
+ Status = ImplementEFNStackTraceTry(client,
+ NULL,
+ &textLength,
+ NULL,
+ &contextLength,
+ 0,
+ 0);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error: %lx\n", Status);
+ return Status;
+ }
+
+ ExtOut("Number of characters requested: %d\n", textLength);
+ WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1];
+ if (wszBuffer == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ // For the transition contexts buffer the callers are expected to allocate
+ // contextLength * sizeof(TARGET_CONTEXT), and not
+ // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for
+ // details.
+ LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()];
+
+ if (pContexts == NULL)
+ {
+ ReportOOM();
+ delete[] wszBuffer;
+ return Status;
+ }
+
+ Status = ImplementEFNStackTrace(client,
+ wszBuffer,
+ &textLength,
+ pContexts,
+ &contextLength,
+ g_targetMachine->GetContextSize(),
+ 0);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error: %lx\n", Status);
+ delete[] wszBuffer;
+ delete [] pContexts;
+ return Status;
+ }
+
+ ExtOut("%S\n", wszBuffer);
+
+ ExtOut("Context information:\n");
+ if (IsDbgTargetX86())
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Ebp", "Esp", "Eip");
+ }
+ else if (IsDbgTargetAmd64())
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Rbp", "Rsp", "Rip");
+ }
+ else if (IsDbgTargetArm())
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "FP", "SP", "PC");
+ }
+ else
+ {
+ ExtOut("Unsupported platform");
+ delete [] pContexts;
+ delete[] wszBuffer;
+ return S_FALSE;
+ }
+
+ for (size_t j=0; j < contextLength; j++)
+ {
+ CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize());
+ ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx));
+ }
+
+ delete [] pContexts;
+
+ StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength];
+ if (pSimple == NULL)
+ {
+ ReportOOM();
+ delete[] wszBuffer;
+ return Status;
+ }
+
+ Status = ImplementEFNStackTrace(client,
+ wszBuffer,
+ &textLength,
+ pSimple,
+ &contextLength,
+ sizeof(StackTrace_SimpleContext),
+ 0);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error: %lx\n", Status);
+ delete[] wszBuffer;
+ delete [] pSimple;
+ return Status;
+ }
+
+ ExtOut("Simple Context information:\n");
+ if (IsDbgTargetX86())
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Ebp", "Esp", "Eip");
+ else if (IsDbgTargetAmd64())
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Rbp", "Rsp", "Rip");
+ else if (IsDbgTargetArm())
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "FP", "SP", "PC");
+ else
+ {
+ ExtOut("Unsupported platform");
+ delete[] wszBuffer;
+ delete [] pSimple;
+ return S_FALSE;
+ }
+ for (size_t j=0; j < contextLength; j++)
+ {
+ ExtOut("%p %p %p\n", SOS_PTR(pSimple[j].FrameOffset),
+ SOS_PTR(pSimple[j].StackOffset),
+ SOS_PTR(pSimple[j].InstructionOffset));
+ }
+ delete [] pSimple;
+ delete[] wszBuffer;
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+// This is an internal-only Apollo extension to de-optimize the code
+DECLARE_API(SuppressJitOptimization)
+{
+ INIT_API_NOEE();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder onOff;
+ CMDValue arg[] =
+ { // vptr, type
+ {&onOff.data, COSTRING},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return E_FAIL;
+ }
+
+ if(nArg == 1 && (_stricmp(onOff.data, "On") == 0))
+ {
+ // if CLR is already loaded, try to change the flags now
+ if(CheckEEDll() == S_OK)
+ {
+ SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION);
+ }
+
+ if(!g_fAllowJitOptimization)
+ ExtOut("JIT optimization is already suppressed\n");
+ else
+ {
+ g_fAllowJitOptimization = FALSE;
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
+ ExtOut("JIT optimization will be suppressed\n");
+ }
+
+
+ }
+ else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0))
+ {
+ // if CLR is already loaded, try to change the flags now
+ if(CheckEEDll() == S_OK)
+ {
+ SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT);
+ }
+
+ if(g_fAllowJitOptimization)
+ ExtOut("JIT optimization is already permitted\n");
+ else
+ {
+ g_fAllowJitOptimization = TRUE;
+ ExtOut("JIT optimization will be permitted\n");
+ }
+ }
+ else
+ {
+ ExtOut("Usage: !SuppressJitOptimization <on|off>\n");
+ }
+
+ return S_OK;
+}
+
+// Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized
+// code
+HRESULT SetNGENCompilerFlags(DWORD flags)
+{
+ HRESULT hr;
+
+ ToRelease<ICorDebugProcess2> proc2;
+ if(FAILED(hr = InitCorDebugInterface()))
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr);
+ }
+ else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2)))
+ {
+ if(flags != CORDEBUG_JIT_DEFAULT)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags)))
+ {
+ // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL.
+ // This was first supported in the clr_triton branch around 4/1/12, Apollo release
+ // It will likely be supported in desktop CLR during Dev12
+ if(hr == E_FAIL)
+ {
+ if(flags != CORDEBUG_JIT_DEFAULT)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED)
+ {
+ if(flags != CORDEBUG_JIT_DEFAULT)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS)
+ {
+ DWORD currentFlags = 0;
+ if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(&currentFlags)))
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr);
+ }
+ else if(currentFlags != flags)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr);
+ }
+ }
+
+ return hr;
+}
+
+
+// This is an internal-only Apollo extension to save breakpoint/watch state
+DECLARE_API(SaveState)
+{
+ INIT_API_NOEE();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder filePath;
+ CMDValue arg[] =
+ { // vptr, type
+ {&filePath.data, COSTRING},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return E_FAIL;
+ }
+
+ if(nArg == 0)
+ {
+ ExtOut("Usage: !SaveState <file_path>\n");
+ }
+
+ FILE* pFile;
+ errno_t error = fopen_s(&pFile, filePath.data, "w");
+ if(error != 0)
+ {
+ ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error);
+ return E_FAIL;
+ }
+
+ g_bpoints.SaveBreakpoints(pFile);
+ g_watchCmd.SaveListToFile(pFile);
+
+ fclose(pFile);
+ ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data);
+ return S_OK;
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(StopOnCatch)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ g_stopOnNextCatch = TRUE;
+ ULONG32 flags = 0;
+ g_clrData->GetOtherNotificationFlags(&flags);
+ flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER;
+ g_clrData->SetOtherNotificationFlags(flags);
+ ExtOut("Debuggee will break the next time a managed exception is caught during execution\n");
+ return S_OK;
+}
+
+// This is an undocumented SOS extension command intended to help test SOS
+// It causes the Dml output to be printed to the console uninterpretted so
+// that a test script can read the commands which are hidden in the markup
+DECLARE_API(ExposeDML)
+{
+ Output::SetDMLExposed(true);
+ return S_OK;
+}
+
+// According to kksharma the Windows debuggers always sign-extend
+// arguments when calling externally, therefore StackObjAddr
+// conforms to CLRDATA_ADDRESS contract.
+HRESULT CALLBACK
+_EFN_GetManagedExcepStack(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount (cbString) PSTR szStackString,
+ ULONG cbString
+ )
+{
+ INIT_API();
+
+ ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString];
+ if (tmpStr == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString)))
+ {
+ return Status;
+ }
+
+ if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+// same as _EFN_GetManagedExcepStack, but returns the stack as a wide string.
+HRESULT CALLBACK
+_EFN_GetManagedExcepStackW(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString
+ )
+{
+ INIT_API();
+
+ return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString);
+}
+
+// According to kksharma the Windows debuggers always sign-extend
+// arguments when calling externally, therefore objAddr
+// conforms to CLRDATA_ADDRESS contract.
+HRESULT CALLBACK
+_EFN_GetManagedObjectName(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount (cbName) PSTR szName,
+ ULONG cbName
+ )
+{
+ INIT_API ();
+
+ if (!sos::IsObject(objAddr, false))
+ {
+ return E_INVALIDARG;
+ }
+
+ sos::Object obj = TO_TADDR(objAddr);
+
+ if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (_wcslen(obj.GetTypeName()) + 1),
+ szName, cbName, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ return S_OK;
+}
+
+// According to kksharma the Windows debuggers always sign-extend
+// arguments when calling externally, therefore objAddr
+// conforms to CLRDATA_ADDRESS contract.
+HRESULT CALLBACK
+_EFN_GetManagedObjectFieldInfo(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount (mdNameLen) PSTR szFieldName,
+ PULONG64 pValue,
+ PULONG pOffset
+ )
+{
+ INIT_API();
+ DacpObjectData objData;
+ LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+
+ if (szFieldName == NULL || *szFieldName == '\0' ||
+ objAddr == NULL)
+ {
+ return E_FAIL;
+ }
+
+ if (pOffset == NULL && pValue == NULL)
+ {
+ // One of these needs to be valid
+ return E_FAIL;
+ }
+
+ if (FAILED(objData.Request(g_sos, objAddr)))
+ {
+ return E_FAIL;
+ }
+
+ MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen);
+
+ int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName);
+ if (iOffset <= 0)
+ {
+ return E_FAIL;
+ }
+
+ if (pOffset)
+ {
+ *pOffset = (ULONG) iOffset;
+ }
+
+ if (pValue)
+ {
+ if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL)))
+ {
+ return E_FAIL;
+ }
+ }
+
+ return S_OK;
+}
+
+void PrintHelp (__in_z LPCSTR pszCmdName)
+{
+ static LPSTR pText = NULL;
+
+ if (pText == NULL) {
+#ifndef FEATURE_PAL
+ HGLOBAL hResource = NULL;
+ HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT"));
+ if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo);
+ if (hResource) pText = (LPSTR) LockResource (hResource);
+ if (pText == NULL)
+ {
+ ExtOut("Error loading documentation resource\n");
+ return;
+ }
+#else
+ int err = PAL_InitializeDLL();
+ if(err != 0)
+ {
+ ExtOut("Error initializing PAL\n");
+ return;
+ }
+ char lpFilename[MAX_LONGPATH + 12]; // + 12 to make enough room for strcat function.
+ strcpy_s(lpFilename, _countof(lpFilename), g_ExtServices->GetCoreClrDirectory());
+ strcat_s(lpFilename, _countof(lpFilename), "sosdocsunix.txt");
+
+ HANDLE hSosDocFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if (hSosDocFile == INVALID_HANDLE_VALUE) {
+ ExtOut("Error finding documentation file\n");
+ return;
+ }
+
+ HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ CloseHandle(hSosDocFile);
+ if (hMappedSosDocFile == NULL) {
+ ExtOut("Error mapping documentation file\n");
+ return;
+ }
+
+ pText = (LPSTR)MapViewOfFile(hMappedSosDocFile, FILE_MAP_READ, 0, 0, 0);
+ CloseHandle(hMappedSosDocFile);
+ if (pText == NULL)
+ {
+ ExtOut("Error loading documentation file\n");
+ return;
+ }
+#endif
+ }
+
+ // Find our line in the text file
+ char searchString[MAX_LONGPATH];
+ sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
+
+ LPSTR pStart = strstr(pText, searchString);
+ LPSTR pEnd = NULL;
+ if (!pStart)
+ {
+ ExtOut("Documentation for %s not found.\n", pszCmdName);
+ return;
+ }
+
+ // Go to the end of this line:
+ pStart = strchr(pStart, '\n');
+ if (!pStart)
+ {
+ ExtOut("Expected newline in documentation resource.\n");
+ return;
+ }
+
+ // Bypass the newline that pStart points to and setup pEnd for the loop below. We set
+ // pEnd to be the old pStart since we add one to it when we call strstr.
+ pEnd = pStart++;
+
+ // Find the first occurrence of \\ followed by an \r or an \n on a line by itself.
+ do
+ {
+ pEnd = strstr(pEnd+1, "\\\\");
+ } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n')));
+
+ if (pEnd)
+ {
+ // We have found a \\ followed by a \r or \n. Do not print out the character pEnd points
+ // to, as this will be the first \ (this is why we don't add one to the second parameter).
+ ExtOut("%.*s", pEnd - pStart, pStart);
+ }
+ else
+ {
+ // If pEnd is false then we have run to the end of the document. However, we did find
+ // the command to print, so we should simply print to the end of the file. We'll add
+ // an extra newline here in case the file does not contain one.
+ ExtOut("%s\n", pStart);
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function displays the commands available in strike and the *
+* arguments passed into each.
+* *
+\**********************************************************************/
+DECLARE_API(Help)
+{
+ // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help.
+ HRESULT Status;
+ __ExtensionCleanUp __extensionCleanUp;
+ if ((Status = ExtQuery(client)) != S_OK) return Status;
+ ControlC = FALSE;
+
+ StringHolder commandName;
+ CMDValue arg[] =
+ {
+ {&commandName.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ ExtOut("-------------------------------------------------------------------------------\n");
+
+ if (nArg == 1)
+ {
+ // Convert commandName to lower-case
+ LPSTR curChar = commandName.data;
+ while (*curChar != '\0')
+ {
+ if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar))
+ {
+ *curChar = (CHAR) tolower(*curChar);
+ }
+ curChar++;
+ }
+
+ // Strip off leading "!" if the user put that.
+ curChar = commandName.data;
+ if (*curChar == '!')
+ curChar++;
+
+ PrintHelp (curChar);
+ }
+ else
+ {
+ PrintHelp ("contents");
+ }
+
+ return S_OK;
+}
diff --git a/src/ToolBox/SOS/Strike/strike.h b/src/ToolBox/SOS/Strike/strike.h
new file mode 100644
index 0000000000..e070898ff5
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/strike.h
@@ -0,0 +1,144 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __strike_h__
+#define __strike_h__
+
+#ifndef _countof
+#define _countof(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4245) // signed/unsigned mismatch
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4127) // conditional expression is constant
+#pragma warning(disable:6255) // Prefast: alloca indicates failure by raising a stack overflow exception
+#endif
+
+#ifdef PAL_STDCPP_COMPAT
+#define _iswprint PAL_iswprint
+#define _wcslen PAL_wcslen
+#define _wcsncmp PAL_wcsncmp
+#define _wcsrchr PAL_wcsrchr
+#define _wcscmp PAL_wcscmp
+#define _wcschr PAL_wcschr
+#define _wcscspn PAL_wcscspn
+#define _wcscat PAL_wcscat
+#define _wcsstr PAL_wcsstr
+#else // PAL_STDCPP_COMPAT
+#define _iswprint iswprint
+#define _wcslen wcslen
+#define _wcsncmp wcsncmp
+#define _wcsrchr wcsrchr
+#define _wcscmp wcscmp
+#define _wcschr wcschr
+#define _wcscspn wcscspn
+#define _wcscat wcscat
+#define _wcsstr wcsstr
+#endif // !PAL_STDCPP_COMPAT
+
+#ifdef PLATFORM_UNIX
+#define _vsnprintf vsnprintf
+#endif
+
+#define ___in _SAL1_Source_(__in, (), _In_)
+#define ___out _SAL1_Source_(__out, (), _Out_)
+
+#define _max(a, b) (((a) > (b)) ? (a) : (b))
+#define _min(a, b) (((a) < (b)) ? (a) : (b))
+
+#include <winternl.h>
+#include <winver.h>
+#include <windows.h>
+
+#include <wchar.h>
+
+//#define NOEXTAPI
+#define KDEXT_64BIT
+#include <wdbgexts.h>
+#undef DECLARE_API
+#undef GetContext
+#undef SetContext
+#undef ReadMemory
+#undef WriteMemory
+#undef GetFieldValue
+#undef StackTrace
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+
+#ifndef PAL_STDCPP_COMPAT
+#include <malloc.h>
+#endif
+
+#ifdef FEATURE_PAL
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif
+#ifndef _alloca
+#define _alloca __builtin_alloca
+#endif
+#endif // FEATURE_PAL
+
+#include <stddef.h>
+
+#ifndef FEATURE_PAL
+#include <basetsd.h>
+#endif
+
+#define CORHANDLE_MASK 0x1
+
+#include "static_assert.h"
+
+// exts.h includes dbgeng.h which has a bunch of IIDs we need instantiated.
+#define INITGUID
+#include "guiddef.h"
+
+#ifdef FEATURE_PAL
+#define SOS_PTR(x) (size_t)(x)
+#else // FEATURE_PAL
+#define SOS_PTR(x) (unsigned __int64)(x)
+#endif // FEATURE_PAL else
+
+#include "exts.h"
+
+//Alignment constant for allocation
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+#define ALIGNCONST 3
+#else
+#define ALIGNCONST 7
+#endif
+
+//The large object heap uses a different alignment
+#define ALIGNCONSTLARGE 7
+
+#ifdef _WIN64
+#define SIZEOF_OBJHEADER 8
+#else // !_WIN64
+#define SIZEOF_OBJHEADER 4
+#endif // !_WIN64
+
+#define plug_skew SIZEOF_OBJHEADER
+#define min_obj_size (sizeof(BYTE*)+plug_skew+sizeof(size_t))
+
+extern BOOL CallStatus;
+
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
+HRESULT SetNGENCompilerFlags(DWORD flags);
+
+
+#endif // __strike_h__
diff --git a/src/ToolBox/SOS/Strike/util.cpp b/src/ToolBox/SOS/Strike/util.cpp
new file mode 100644
index 0000000000..9eec76e42c
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/util.cpp
@@ -0,0 +1,6975 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#include "sos.h"
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+#include "sospriv.h"
+#include "corerror.h"
+#include "safemath.h"
+
+#include <psapi.h>
+#include <cordebug.h>
+#include <xcordebug.h>
+#include <metahost.h>
+#include <mscoree.h>
+#include <tchar.h>
+#include "debugshim.h"
+
+#ifdef FEATURE_PAL
+#include "datatarget.h"
+#endif // FEATURE_PAL
+#include "gcinfo.h"
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+
+#ifndef FEATURE_PAL
+#define MAX_SYMBOL_LEN 4096
+#define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL) + MAX_SYMBOL_LEN)
+char symBuffer[SYM_BUFFER_SIZE];
+PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
+#else
+#include <sys/stat.h>
+#include <coreruncommon.h>
+#include <dlfcn.h>
+#endif // !FEATURE_PAL
+
+#include <coreclrhost.h>
+#include <set>
+
+LoadSymbolsForModuleDelegate SymbolReader::loadSymbolsForModuleDelegate;
+DisposeDelegate SymbolReader::disposeDelegate;
+ResolveSequencePointDelegate SymbolReader::resolveSequencePointDelegate;
+GetLocalVariableName SymbolReader::getLocalVariableNameDelegate;
+GetLineByILOffsetDelegate SymbolReader::getLineByILOffsetDelegate;
+
+const char * const CorElementTypeName[ELEMENT_TYPE_MAX]=
+{
+#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) c,
+#include "cortypeinfo.h"
+#undef TYPEINFO
+};
+
+const char * const CorElementTypeNamespace[ELEMENT_TYPE_MAX]=
+{
+#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) ns,
+#include "cortypeinfo.h"
+#undef TYPEINFO
+};
+
+IXCLRDataProcess *g_clrData = NULL;
+ISOSDacInterface *g_sos = NULL;
+ICorDebugProcess *g_pCorDebugProcess = NULL;
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+#ifndef IfFailGoto
+#define IfFailGoto(EXPR, label) do { Status = (EXPR); if(FAILED(Status)) { goto label; } } while (0)
+#endif // IfFailGoto
+
+#ifndef IfFailGo
+#define IfFailGo(EXPR) IfFailGoto(EXPR, Error)
+#endif // IfFailGo
+
+// Max number of reverted rejit versions that !dumpmd and !ip2md will print
+const UINT kcMaxRevertedRejitData = 10;
+
+#ifndef FEATURE_PAL
+
+// ensure we always allocate on the process heap
+void* __cdecl operator new(size_t size) throw()
+{ return HeapAlloc(GetProcessHeap(), 0, size); }
+void __cdecl operator delete(void* pObj) throw()
+{ HeapFree(GetProcessHeap(), 0, pObj); }
+
+void* __cdecl operator new[](size_t size) throw()
+{ return HeapAlloc(GetProcessHeap(), 0, size); }
+void __cdecl operator delete[](void* pObj) throw()
+{ HeapFree(GetProcessHeap(), 0, pObj); }
+
+/**********************************************************************\
+* Here we define types and functions that support custom COM *
+* activation rules, as defined by the CIOptions enum. *
+* *
+\**********************************************************************/
+
+typedef unsigned __int64 QWORD;
+
+namespace com_activation
+{
+ //
+ // Forward declarations for the implementation methods
+ //
+
+ HRESULT CreateInstanceCustomImpl(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf);
+ HRESULT ClrCreateInstance(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf);
+ HRESULT CreateInstanceFromPath(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR path,
+ void** ppItf);
+ BOOL GetPathFromModule(
+ HMODULE hModule,
+ __in_ecount(cFqPath) LPWSTR fqPath,
+ DWORD cFqPath);
+ HRESULT PickClrRuntimeInfo(
+ ICLRMetaHost *pMetaHost,
+ CIOptions cciOptions,
+ ICLRRuntimeInfo** ppClr);
+ QWORD VerString2Qword(LPCWSTR vStr);
+ void CleanupClsidHmodMap();
+
+ // Helper structures for defining the CLSID -> HMODULE hash table we
+ // use for caching already activated objects
+ class hash_compareGUID
+ {
+ public:
+ static const size_t bucket_size = 4;
+ static const size_t min_buckets = 8;
+ hash_compareGUID()
+ { }
+
+ size_t operator( )(const GUID& _Key) const
+ {
+ DWORD *pdw = (DWORD*)&_Key;
+ return (size_t)(pdw[0] ^ pdw[1] ^ pdw[2] ^ pdw[3]);
+ }
+
+ bool operator( )(const GUID& _Key1, const GUID& _Key2) const
+ { return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1; }
+ };
+
+ static std::unordered_map<GUID, HMODULE, hash_compareGUID> *g_pClsidHmodMap = NULL;
+
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceCustomImpl() provides a way to activate a COM object *
+* w/o triggering the FeatureOnDemand dialog. In order to do this we *
+* must avoid using the CoCreateInstance() API, which, on a machine *
+* with v4+ installed and w/o v2, would trigger this. *
+* CreateInstanceCustom() activates the requested COM object according *
+* to the specified passed in CIOptions, in the following order *
+* (skipping the steps not enabled in the CIOptions flags passed in): *
+* 1. Attempt to activate the COM object using a framework install: *
+* a. If the debugger machine has a V4+ shell shim use the shim *
+* to activate the object *
+* b. Otherwise simply call CoCreateInstance *
+* 2. If unsuccessful attempt to activate looking for the dllName in *
+* the same folder as the DAC was loaded from *
+* 3. If unsuccessful attempt to activate the COM object looking in *
+* every path specified in the debugger's .exepath and .sympath *
+\**********************************************************************/
+HRESULT CreateInstanceCustomImpl(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf)
+{
+ _ASSERTE(ppItf != NULL);
+
+ if (ppItf == NULL)
+ return E_POINTER;
+
+ WCHAR wszClsid[64] = W("<CLSID>");
+
+ // Step 1: Attempt activation using an installed runtime
+ if ((cciOptions & cciFxMask) != 0)
+ {
+ CIOptions opt = cciOptions & cciFxMask;
+ if (SUCCEEDED(ClrCreateInstance(clsid, iid, dllName, opt, ppItf)))
+ return S_OK;
+
+ ExtDbgOut("Failed to instantiate {%ls} from installed .NET framework locations.\n", wszClsid);
+ }
+
+ if ((cciOptions & cciDbiColocated) != 0)
+ {
+ // if we institute a way to retrieve the module for the current DBI we
+ // can perform the same steps as for the DAC.
+ }
+
+ // Step 2: attempt activation using the folder the DAC was loaded from
+ if ((cciOptions & cciDacColocated) != 0)
+ {
+ _ASSERTE(dllName != NULL);
+ HMODULE hDac = NULL;
+ WCHAR path[MAX_LONGPATH];
+
+ if (SUCCEEDED(g_sos->GetDacModuleHandle(&hDac))
+ && GetPathFromModule(hDac, path, _countof(path)))
+ {
+ // build the fully qualified file name and attempt instantiation
+ if (wcscat_s(path, dllName) == 0
+ && SUCCEEDED(CreateInstanceFromPath(clsid, iid, path, ppItf)))
+ {
+ return S_OK;
+ }
+ }
+
+ ExtDbgOut("Failed to instantiate {%ls} from DAC location.\n", wszClsid);
+ }
+
+ // Step 3: attempt activation using the debugger's .exepath and .sympath
+ if ((cciOptions & cciDbgPath) != 0)
+ {
+ _ASSERTE(dllName != NULL);
+
+ ToRelease<IDebugSymbols3> spSym3(NULL);
+ HRESULT hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
+ if (FAILED(hr))
+ {
+ ExtDbgOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
+ goto ErrDbgPath;
+ }
+
+ typedef HRESULT (__stdcall IDebugSymbols3::*GetPathFunc)(LPWSTR , ULONG, ULONG*);
+
+ // Handle both the image path and the symbol path
+ GetPathFunc rgGetPathFuncs[] =
+ { &IDebugSymbols3::GetImagePathWide, &IDebugSymbols3::GetSymbolPathWide };
+
+ for (int i = 0; i < _countof(rgGetPathFuncs); ++i)
+ {
+ ULONG pathSize = 0;
+
+ // get the path buffer size
+ if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(NULL, 0, &pathSize) != S_OK)
+ {
+ continue;
+ }
+
+ ArrayHolder<WCHAR> imgPath = new WCHAR[pathSize+MAX_LONGPATH+1];
+ if (imgPath == NULL)
+ {
+ continue;
+ }
+
+ // actually get the path
+ if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(imgPath, pathSize, NULL) != S_OK)
+ {
+ continue;
+ }
+
+ LPWSTR ctx;
+ LPCWSTR pathElem = wcstok_s(imgPath, W(";"), &ctx);
+ while (pathElem != NULL)
+ {
+ WCHAR fullName[MAX_LONGPATH];
+ wcscpy_s(fullName, _countof(fullName), pathElem);
+ if (wcscat_s(fullName, W("\\")) == 0 && wcscat_s(fullName, dllName) == 0)
+ {
+ if (SUCCEEDED(CreateInstanceFromPath(clsid, iid, fullName, ppItf)))
+ {
+ return S_OK;
+ }
+ }
+
+ pathElem = wcstok_s(NULL, W(";"), &ctx);
+ }
+ }
+
+ ErrDbgPath:
+ ExtDbgOut("Failed to instantiate {%ls} from debugger's image path.\n", wszClsid);
+ }
+
+ return REGDB_E_CLASSNOTREG;
+}
+
+
+#ifdef _MSC_VER
+// SOS is essentially single-threaded. ignore "construction of local static object is not thread-safe"
+#pragma warning(push)
+#pragma warning(disable:4640)
+#endif // _MSC_VER
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* ClrCreateInstance() attempts to activate a COM object using an *
+* installed framework: *
+* a. If the debugger machine has a V4+ shell shim use the shim to *
+* activate the object *
+* b. Otherwise simply call CoCreateInstance *
+\**********************************************************************/
+HRESULT ClrCreateInstance(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf)
+{
+ _ASSERTE((cciOptions & ~cciFxMask) == 0 && (cciOptions & cciFxMask) != 0);
+ HRESULT Status = S_OK;
+
+ static CIOptions prevOpt = 0;
+ static HRESULT prevHr = S_OK;
+
+ // if we already tried to use NetFx install and failed don't try it again
+ if (prevOpt == cciOptions && FAILED(prevHr))
+ {
+ return prevHr;
+ }
+
+ prevOpt = cciOptions;
+
+ // first try usig the metahost API:
+ HRESULT (__stdcall *pfnCLRCreateInstance)(REFCLSID clsid, REFIID riid, LPVOID * ppInterface) = NULL;
+ HMODULE hMscoree = NULL;
+
+ // if there's a v4+ shim on the debugger machine
+ if (GetProcAddressT("CLRCreateInstance", W("mscoree.dll"), &pfnCLRCreateInstance, &hMscoree))
+ {
+ // attempt to create an ICLRMetaHost instance
+ ToRelease<ICLRMetaHost> spMH;
+ Status = pfnCLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (void**)&spMH);
+ if (Status == E_NOTIMPL)
+ {
+ // E_NOTIMPL means we have a v4 aware mscoree but no v4+ framework
+ IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
+ }
+ else
+ {
+ IfFailGo( Status );
+
+ // pick a runtime according to cciOptions
+ ToRelease<ICLRRuntimeInfo> spClr;
+ IfFailGo( PickClrRuntimeInfo(spMH, cciOptions, &spClr) );
+
+ // activate the COM object
+ Status = spClr->GetInterface(clsid, iid, ppItf);
+
+ if (FAILED(Status) && dllName)
+ {
+ // if we have a v4+ runtime that does not have the fix to activate the requested CLSID
+ // try activating with the path
+ WCHAR clrDir[MAX_LONGPATH];
+ DWORD cchClrDir = _countof(clrDir);
+ IfFailGo( spClr->GetRuntimeDirectory(clrDir, &cchClrDir) );
+ IfFailGo( wcscat_s(clrDir, dllName) == 0 ? S_OK : E_FAIL );
+ IfFailGo( CreateInstanceFromPath(clsid, iid, clrDir, ppItf) );
+ }
+ }
+ }
+ else
+ {
+ // otherwise fallback to regular COM activation
+ IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
+ }
+
+Error:
+ if (hMscoree != NULL)
+ {
+ FreeLibrary(hMscoree);
+ }
+
+ // remember if we succeeded or failed
+ prevHr = Status;
+
+ return Status;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceFromPath() instantiates a COM object using a passed in *
+* fully-qualified path and a CLSID. *
+* *
+* Note: *
+* *
+* It uses a unordered_map to cache the mapping between a CLSID and the *
+* HMODULE that is successfully used to activate the CLSID from. When *
+* SOS is unloaded (in DebugExtensionUninitialize()) we call *
+* FreeLibrary() for all cached HMODULEs. *
+\**********************************************************************/
+HRESULT CreateInstanceFromPath(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR path,
+ void** ppItf)
+{
+ HRESULT Status = S_OK;
+ HRESULT (__stdcall *pfnDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv) = NULL;
+
+ HMODULE hmod = NULL;
+
+ if (g_pClsidHmodMap == NULL)
+ {
+ g_pClsidHmodMap = new std::unordered_map<GUID, HMODULE, hash_compareGUID>();
+ OnUnloadTask::Register(CleanupClsidHmodMap);
+ }
+
+ auto it = g_pClsidHmodMap->find(clsid);
+ if (it != g_pClsidHmodMap->end())
+ hmod = it->second;
+
+ if (!GetProcAddressT("DllGetClassObject", path, &pfnDllGetClassObject, &hmod))
+ return REGDB_E_CLASSNOTREG;
+
+ ToRelease<IClassFactory> pFactory;
+ IfFailGo(pfnDllGetClassObject(clsid, IID_IClassFactory, (void**)&pFactory));
+
+ IfFailGo(pFactory->CreateInstance(NULL, iid, ppItf));
+
+ // only cache the HMODULE if we successfully created the COM object
+ (*g_pClsidHmodMap)[clsid] = hmod;
+
+ return S_OK;
+
+Error:
+ if (hmod != NULL)
+ FreeLibrary(hmod);
+
+ return Status;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CleanupClsidHmodMap() cleans up the CLSID -> HMODULE map used to *
+* cache successful activations from specific paths. This is registered *
+* as an OnUnloadTask in CreateInstanceFromPath(), and executes when *
+* SOS is unloaded, in DebugExtensionUninitialize(). *
+\**********************************************************************/
+void CleanupClsidHmodMap()
+{
+ if (g_pClsidHmodMap != NULL)
+ {
+ for (auto it = g_pClsidHmodMap->begin(); it != g_pClsidHmodMap->end(); ++it)
+ {
+ _ASSERTE(it->second != NULL);
+ FreeLibrary(it->second);
+ }
+
+ delete g_pClsidHmodMap;
+ g_pClsidHmodMap = NULL;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* PickClrRuntimeInfo() selects on CLR runtime from the ones installed *
+* on the debugger machine. If cciFxAny is specified in cciOptions it *
+* simply returns the first runtime enumerated by the metahost *
+* interface. If cciLatestFx is specified we pick the runtime with the *
+* highest version (parsing the string returned from *
+* ICLRRuntimeInfo::GetVersionString(). *
+\**********************************************************************/
+HRESULT PickClrRuntimeInfo(
+ ICLRMetaHost *pMetaHost,
+ CIOptions cciOptions,
+ ICLRRuntimeInfo** ppClr)
+{
+ if (ppClr == NULL)
+ return E_POINTER;
+
+ // only support "Any framework" and "latest framework"
+ if (cciOptions != cciAnyFx && cciOptions != cciLatestFx)
+ return E_INVALIDARG;
+
+ HRESULT Status = S_OK;
+ *ppClr = NULL;
+
+ // get the CLRRuntime enumerator
+ ToRelease<IEnumUnknown> spClrsEnum;
+ IfFailRet(pMetaHost->EnumerateInstalledRuntimes(&spClrsEnum));
+
+ ToRelease<ICLRRuntimeInfo> spChosenClr;
+ QWORD verMax = 0;
+
+ int cntClr = 0;
+ while (1)
+ {
+ // retrieve the next ICLRRuntimeInfo
+ ULONG cnt;
+ ToRelease<IUnknown> spClrUnk;
+ if (spClrsEnum->Next(1, &spClrUnk, &cnt) != S_OK || cnt != 1)
+ break;
+
+ ToRelease<ICLRRuntimeInfo> spClr;
+ BOOL bLoadable = FALSE;
+ // ignore un-loadable runtimes
+ if (FAILED(spClrUnk->QueryInterface(IID_ICLRRuntimeInfo, (void**)&spClr))
+ || FAILED(spClr->IsLoadable(&bLoadable))
+ || !bLoadable)
+ {
+ continue;
+ }
+
+ WCHAR vStr[128];
+ DWORD cStr = _countof(vStr);
+ if (FAILED(spClr->GetVersionString(vStr, &cStr)))
+ continue;
+
+ ++cntClr;
+
+ if ((cciOptions & cciAnyFx) != 0)
+ {
+ spChosenClr = spClr.Detach();
+ break;
+ }
+
+ QWORD ver = VerString2Qword(vStr);
+ if ((cciOptions & cciLatestFx) != 0)
+ {
+ if (ver > verMax)
+ {
+ verMax = ver;
+ spChosenClr = spClr.Detach();
+ }
+ }
+ }
+
+ if (cntClr == 0 || spChosenClr == NULL)
+ {
+ *ppClr = NULL;
+ return E_NOINTERFACE;
+ }
+ else
+ {
+ *ppClr = spChosenClr.Detach();
+ return S_OK;
+ }
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* VerString2Qword() parses a string as returned from *
+* ICLRRuntimeInfo::GetVersionString() into a QWORD, assuming every *
+* numeric element is a WORD portion in the QWORD. *
+\**********************************************************************/
+QWORD VerString2Qword(LPCWSTR vStr)
+{
+ _ASSERTE(vStr[0] == L'v' || vStr[0] == L'V');
+ QWORD result = 0;
+
+ DWORD v1, v2, v3;
+ if (swscanf_s(vStr+1, W("%d.%d.%d"), &v1, &v2, &v3) == 3)
+ {
+ result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32) | ((QWORD)v3 << 16);
+ }
+ else if (swscanf_s(vStr+1, W("%d.%d"), &v1, &v2) == 2)
+ {
+ result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32);
+ }
+ else if (swscanf_s(vStr+1, W("%d"), &v1) == 1)
+ {
+ result = ((QWORD)v1 << 48);
+ }
+
+ return result;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* GetPathFromModule() returns the name of the folder containing the *
+* file associated with hModule. *
+ \**********************************************************************/
+BOOL GetPathFromModule(
+ HMODULE hModule,
+ __in_ecount(cFqPath) LPWSTR fqPath,
+ DWORD cFqPath)
+{
+ int len = GetModuleFileNameW(hModule, fqPath, cFqPath);
+ if (len == 0 || len == cFqPath)
+ return FALSE;
+
+ WCHAR *pLastSep = _wcsrchr(fqPath, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pLastSep == NULL || pLastSep+1 >= fqPath+cFqPath)
+ return FALSE;
+
+ *(pLastSep+1) = L'\0';
+
+ return TRUE;
+}
+
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceCustom() provides a way to activate a COM object w/o *
+* triggering the FeatureOnDemand dialog. In order to do this we *
+* must avoid using the CoCreateInstance() API, which, on a machine *
+* with v4+ installed and w/o v2, would trigger this. *
+* CreateInstanceCustom() activates the requested COM object according *
+* to the specified passed in CIOptions, in the following order *
+* (skipping the steps not enabled in the CIOptions flags passed in): *
+* 1. Attempt to activate the COM object using a framework install: *
+* a. If the debugger machine has a V4+ shell shim use the shim *
+* to activate the object *
+* b. Otherwise simply call CoCreateInstance *
+* 2. If unsuccessful attempt to activate looking for the dllName in *
+* the same folder as the DAC was loaded from *
+* 3. If unsuccessful attempt to activate the COM object looking in *
+* every path specified in the debugger's .exepath and .sympath *
+\**********************************************************************/
+HRESULT CreateInstanceCustom(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf)
+{
+ return com_activation::CreateInstanceCustomImpl(clsid, iid, dllName, cciOptions, ppItf);
+}
+
+
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the memory address given a symbol *
+* name. It handles difference in symbol name between ntsd and *
+* windbg. *
+* *
+\**********************************************************************/
+DWORD_PTR GetValueFromExpression (___in __in_z const char *const instr)
+{
+ ULONG64 dwAddr;
+ const char *str = instr;
+ char name[256];
+
+ dwAddr = 0;
+ HRESULT hr = g_ExtSymbols->GetOffsetByName (str, &dwAddr);
+ if (SUCCEEDED(hr))
+ return (DWORD_PTR)dwAddr;
+ else if (hr == S_FALSE && dwAddr)
+ return (DWORD_PTR)dwAddr;
+
+ strcpy_s (name, _countof(name), str);
+ char *ptr;
+ if ((ptr = strstr (name, "__")) != NULL)
+ {
+ ptr[0] = ':';
+ ptr[1] = ':';
+ ptr += 2;
+ while ((ptr = strstr(ptr, "__")) != NULL)
+ {
+ ptr[0] = ':';
+ ptr[1] = ':';
+ ptr += 2;
+ }
+ dwAddr = 0;
+ hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
+ if (SUCCEEDED(hr))
+ return (DWORD_PTR)dwAddr;
+ else if (hr == S_FALSE && dwAddr)
+ return (DWORD_PTR)dwAddr;
+ }
+ else if ((ptr = strstr (name, "::")) != NULL)
+ {
+ ptr[0] = '_';
+ ptr[1] = '_';
+ ptr += 2;
+ while ((ptr = strstr(ptr, "::")) != NULL)
+ {
+ ptr[0] = '_';
+ ptr[1] = '_';
+ ptr += 2;
+ }
+ dwAddr = 0;
+ hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
+ if (SUCCEEDED(hr))
+ return (DWORD_PTR)dwAddr;
+ else if (hr == S_FALSE && dwAddr)
+ return (DWORD_PTR)dwAddr;
+ }
+ return 0;
+}
+
+#endif // FEATURE_PAL
+
+ModuleInfo moduleInfo[MSCOREND] = {{0,FALSE,0},{0,FALSE,0},{0,FALSE,0}};
+
+void ReportOOM()
+{
+ ExtOut("SOS Error: Out of memory\n");
+}
+
+HRESULT CheckEEDll()
+{
+#ifndef FEATURE_PAL
+ VS_FIXEDFILEINFO ee = {};
+
+ static VS_FIXEDFILEINFO sos = {};
+ static BOOL sosDataInit = FALSE;
+
+ BOOL result = GetEEVersion(&ee);
+ if (result && !sosDataInit)
+ {
+ result = GetSOSVersion(&sos);
+
+ if (result)
+ sosDataInit = TRUE;
+ }
+
+ // We will ignore errors because it's possible sos is being loaded before CLR.
+ if (result)
+ {
+ if ((ee.dwFileVersionMS != sos.dwFileVersionMS) || (ee.dwFileVersionLS != sos.dwFileVersionLS))
+ {
+ ExtOut("The version of SOS does not match the version of CLR you are debugging. Please\n");
+ ExtOut("load the matching version of SOS for the version of CLR you are debugging.\n");
+ ExtOut("CLR Version: %u.%u.%u.%u\n",
+ HIWORD(ee.dwFileVersionMS),
+ LOWORD(ee.dwFileVersionMS),
+ HIWORD(ee.dwFileVersionLS),
+ LOWORD(ee.dwFileVersionLS));
+
+ ExtOut("SOS Version: %u.%u.%u.%u\n",
+ HIWORD(sos.dwFileVersionMS),
+ LOWORD(sos.dwFileVersionMS),
+ HIWORD(sos.dwFileVersionLS),
+ LOWORD(sos.dwFileVersionLS));
+ }
+ }
+
+ DEBUG_MODULE_PARAMETERS Params;
+
+ // Do we have clr.dll
+ if (moduleInfo[MSCORWKS].baseAddr == 0)
+ {
+ g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
+ &moduleInfo[MSCORWKS].baseAddr);
+ if (moduleInfo[MSCORWKS].baseAddr != 0 && moduleInfo[MSCORWKS].hasPdb == FALSE)
+ {
+ g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
+ if (Params.SymbolType == SymDeferred)
+ {
+ g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
+ g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
+ }
+
+ if (Params.SymbolType == SymPdb || Params.SymbolType == SymDia)
+ {
+ moduleInfo[MSCORWKS].hasPdb = TRUE;
+ }
+
+ moduleInfo[MSCORWKS].size = Params.Size;
+ }
+ if (moduleInfo[MSCORWKS].baseAddr != 0 && moduleInfo[MSCORWKS].hasPdb == FALSE)
+ ExtOut("PDB symbol for clr.dll not loaded\n");
+ }
+
+ return (moduleInfo[MSCORWKS].baseAddr != 0) ? S_OK : E_FAIL;
+#else
+ return S_OK;
+#endif // FEATURE_PAL
+}
+
+EEFLAVOR GetEEFlavor ()
+{
+#ifdef FEATURE_PAL
+ return MSCORWKS;
+#else // FEATUER_PAL
+ EEFLAVOR flavor = UNKNOWNEE;
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A,0,NULL,NULL))) {
+ flavor = MSCORWKS;
+ }
+ return flavor;
+#endif // FEATURE_PAL else
+}
+
+BOOL IsDumpFile ()
+{
+ static int g_fDumpFile = -1;
+ if (g_fDumpFile == -1) {
+ ULONG Class;
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ if (Qualifier >= DEBUG_DUMP_SMALL)
+ g_fDumpFile = 1;
+ else
+ g_fDumpFile = 0;
+ }
+ return g_fDumpFile != 0;
+}
+
+BOOL g_InMinidumpSafeMode = FALSE;
+
+BOOL IsMiniDumpFileNODAC ()
+{
+#ifndef FEATURE_PAL
+ ULONG Class;
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ if (Qualifier == DEBUG_DUMP_SMALL)
+ {
+ g_ExtControl->GetDumpFormatFlags(&Qualifier);
+ if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
+ {
+ return TRUE;
+ }
+ }
+
+#endif // FEATURE_PAL
+ return FALSE;
+}
+
+
+// We use this predicate to mean the smallest, most restrictive kind of
+// minidump file. There is no heap dump, only that set of information
+// gathered to make !clrstack, !threads, !help, !eeversion and !pe work.
+BOOL IsMiniDumpFile ()
+{
+#ifndef FEATURE_PAL
+ // It is okay for this to be static, because although the debugger may debug multiple
+ // managed processes at once, I don't believe multiple dumpfiles of different
+ // types is a scenario to worry about.
+ if (IsMiniDumpFileNODAC())
+ {
+ // Beyond recognizing the dump type above, all we can rely on for this
+ // is a flag set by the user indicating they want a safe mode minidump
+ // experience. This is primarily for testing.
+ return g_InMinidumpSafeMode;
+ }
+
+#endif // FEATURE_PAL
+ return FALSE;
+}
+
+ULONG DebuggeeType()
+{
+ static ULONG Class = DEBUG_CLASS_UNINITIALIZED;
+ if (Class == DEBUG_CLASS_UNINITIALIZED) {
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ }
+ return Class;
+}
+
+#ifndef FEATURE_PAL
+
+// Check if a file exist
+BOOL FileExist (const char *filename)
+{
+ WIN32_FIND_DATA FindFileData;
+ HANDLE handle = FindFirstFile (filename, &FindFileData);
+ if (handle != INVALID_HANDLE_VALUE) {
+ FindClose (handle);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+BOOL FileExist (const WCHAR *filename)
+{
+ WIN32_FIND_DATAW FindFileData;
+ HANDLE handle = FindFirstFileW (filename, &FindFileData);
+ if (handle != INVALID_HANDLE_VALUE) {
+ FindClose (handle);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find out if a dll is bbt-ized *
+* *
+\**********************************************************************/
+BOOL IsRetailBuild (size_t base)
+{
+ IMAGE_DOS_HEADER DosHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
+ return FALSE;
+ IMAGE_NT_HEADERS32 Header32;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header32, sizeof(Header32), NULL) != S_OK)
+ return FALSE;
+ // If there is no COMHeader, this can not be managed code.
+ if (Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress == 0)
+ return FALSE;
+
+ size_t debugDirAddr = base + Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
+ size_t nSize = Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ IMAGE_DEBUG_DIRECTORY debugDir;
+ size_t nbytes = 0;
+ while (nbytes < nSize) {
+ if (g_ExtData->ReadVirtual(TO_CDADDR(debugDirAddr+nbytes), &debugDir, sizeof(debugDir), NULL) != S_OK)
+ return FALSE;
+ if (debugDir.Type == 0xA) {
+ return TRUE;
+ }
+ nbytes += sizeof(debugDir);
+ }
+ return FALSE;
+}
+
+#endif // !FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to read memory from the debugee's *
+* address space. If the initial read fails, it attempts to read *
+* only up to the edge of the page containing "offset". *
+* *
+\**********************************************************************/
+BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb,
+ PULONG lpcbBytesRead)
+{
+ BOOL bRet = FALSE;
+
+ bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
+ lpcbBytesRead));
+
+ if (!bRet)
+ {
+ cb = (ULONG)(NextOSPageAddress(offset) - offset);
+ bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
+ lpcbBytesRead));
+ }
+ return bRet;
+}
+
+ULONG OSPageSize ()
+{
+ static ULONG pageSize = 0;
+ if (pageSize == 0)
+ g_ExtControl->GetPageSize(&pageSize);
+
+ return pageSize;
+}
+
+size_t NextOSPageAddress (size_t addr)
+{
+ size_t pageSize = OSPageSize();
+ return (addr+pageSize)&(~(pageSize-1));
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the address of MethodDesc *
+* given an ip address *
+* *
+\**********************************************************************/
+void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType,
+ DWORD_PTR &gcinfoAddr)
+{
+
+ CLRDATA_ADDRESS EIP = TO_CDADDR(IP);
+ DacpCodeHeaderData codeHeaderData;
+
+ methodDesc = NULL;
+ gcinfoAddr = NULL;
+
+ if (codeHeaderData.Request(g_sos, EIP) != S_OK)
+ {
+ return;
+ }
+
+ methodDesc = (DWORD_PTR) codeHeaderData.MethodDescPtr;
+ jitType = (JITTypes) codeHeaderData.JITType;
+ gcinfoAddr = (DWORD_PTR) codeHeaderData.GCInfo;
+}
+
+BOOL IsValueField (DacpFieldDescData *pFD)
+{
+ return (pFD->Type == ELEMENT_TYPE_VALUETYPE);
+}
+
+void DisplayDataMember (DacpFieldDescData* pFD, DWORD_PTR dwAddr, BOOL fAlign=TRUE)
+{
+ if (dwAddr > 0)
+ {
+ // we must have called this function for a "real" (non-zero size) data type
+ PREFIX_ASSUME(gElementTypeInfo[pFD->Type] != 0);
+
+ DWORD_PTR dwTmp = dwAddr;
+ bool bVTStatic = (pFD->bIsStatic && pFD->Type == ELEMENT_TYPE_VALUETYPE);
+
+ if (gElementTypeInfo[pFD->Type] != NO_SIZE || bVTStatic)
+ {
+ union Value
+ {
+ char ch;
+ short Short;
+ DWORD_PTR ptr;
+ int Int;
+ unsigned int UInt;
+ __int64 Int64;
+ unsigned __int64 UInt64;
+ float Float;
+ double Double;
+ } value;
+
+ ZeroMemory(&value, sizeof(value));
+ if (bVTStatic)
+ {
+ // static VTypes are boxed
+ moveBlock (value, dwTmp, gElementTypeInfo[ELEMENT_TYPE_CLASS]);
+ }
+ else
+ {
+ moveBlock (value, dwTmp, gElementTypeInfo[pFD->Type]);
+ }
+
+ switch (pFD->Type)
+ {
+ case ELEMENT_TYPE_I1:
+ // there's no ANSI conformant type specifier for
+ // signed char, so use the next best thing,
+ // signed short (sign extending)
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hd", (short)value.ch);
+ else
+ ExtOut("%d", value.ch);
+ break;
+ case ELEMENT_TYPE_I2:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hd", value.Short);
+ else
+ ExtOut("%d", value.Short);
+ break;
+ case ELEMENT_TYPE_I4:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "d", value.Int);
+ else
+ ExtOut("%d", value.Int);
+ break;
+ case ELEMENT_TYPE_I8:
+ ExtOut("%I64d", value.Int64);
+ break;
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_BOOLEAN:
+ if (fAlign)
+ // there's no ANSI conformant type specifier for
+ // unsigned char, so use the next best thing,
+ // unsigned short, not extending the sign
+ ExtOut("%" POINTERSIZE "hu", (USHORT)value.Short);
+ else
+ ExtOut("%u", value.ch);
+ break;
+ case ELEMENT_TYPE_U2:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hu", value.Short);
+ else
+ ExtOut("%u", value.Short);
+ break;
+ case ELEMENT_TYPE_U4:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "u", value.UInt);
+ else
+ ExtOut("%u", value.UInt);
+ break;
+ case ELEMENT_TYPE_U8:
+ ExtOut("%I64u", value.UInt64);
+ break;
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "p", SOS_PTR(value.ptr));
+ else
+ ExtOut("%p", SOS_PTR(value.ptr));
+ break;
+ case ELEMENT_TYPE_R4:
+ ExtOut("%f", value.Float);
+ break;
+ case ELEMENT_TYPE_R8:
+ ExtOut("%f", value.Double);
+ break;
+ case ELEMENT_TYPE_CHAR:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hx", value.Short);
+ else
+ ExtOut("%x", value.Short);
+ break;
+ case ELEMENT_TYPE_VALUETYPE:
+ if (value.ptr)
+ DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
+ else
+ ExtOut("%p", SOS_PTR(0));
+ break;
+ default:
+ if (value.ptr)
+ DMLOut(DMLObject(value.ptr));
+ else
+ ExtOut("%p", SOS_PTR(0));
+ break;
+ }
+ }
+ else
+ {
+ if (pFD->Type == ELEMENT_TYPE_VALUETYPE)
+ DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
+ else
+ ExtOut("%p", SOS_PTR(0));
+ }
+ }
+ else
+ {
+ ExtOut("%" POINTERSIZE "s", " ");
+ }
+}
+
+void GetStaticFieldPTR(DWORD_PTR* pOutPtr, DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
+{
+ DWORD_PTR dwTmp;
+
+ if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
+ || pFDD->Type == ELEMENT_TYPE_CLASS)
+ {
+ dwTmp = (DWORD_PTR) pDLMD->pGCStaticDataStart + pFDD->dwOffset;
+ }
+ else
+ {
+ dwTmp = (DWORD_PTR) pDLMD->pNonGCStaticDataStart + pFDD->dwOffset;
+ }
+
+ *pOutPtr = 0;
+
+ if (pMTD->bIsDynamic)
+ {
+ ExtOut("dynamic statics NYI");
+ return;
+ }
+ else
+ {
+ if (pFlags && pMTD->bIsShared)
+ {
+ BYTE flags;
+ DWORD_PTR pTargetFlags = (DWORD_PTR) pDLMD->pClassData + RidFromToken(pMTD->cl) - 1;
+ move_xp (flags, pTargetFlags);
+
+ *pFlags = flags;
+ }
+
+
+ *pOutPtr = dwTmp;
+ }
+ return;
+}
+
+void GetDLMFlags(DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, BYTE* pFlags)
+{
+ if (pMTD->bIsDynamic)
+ {
+ ExtOut("dynamic statics NYI");
+ return;
+ }
+ else
+ {
+ if (pFlags)
+ {
+ BYTE flags;
+ DWORD_PTR pTargetFlags = (DWORD_PTR) pDLMD->pClassData + RidFromToken(pMTD->cl) - 1;
+ move_xp (flags, pTargetFlags);
+
+ *pFlags = flags;
+ }
+ }
+ return;
+}
+
+void GetThreadStaticFieldPTR(DWORD_PTR* pOutPtr, DacpThreadLocalModuleData* pTLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
+{
+ DWORD_PTR dwTmp;
+
+ if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
+ || pFDD->Type == ELEMENT_TYPE_CLASS)
+ {
+ dwTmp = (DWORD_PTR) pTLMD->pGCStaticDataStart + pFDD->dwOffset;
+ }
+ else
+ {
+ dwTmp = (DWORD_PTR) pTLMD->pNonGCStaticDataStart + pFDD->dwOffset;
+ }
+
+ *pOutPtr = 0;
+
+ if (pMTD->bIsDynamic)
+ {
+ ExtOut("dynamic thread statics NYI");
+ return;
+ }
+ else
+ {
+ if (pFlags)
+ {
+ BYTE flags;
+ DWORD_PTR pTargetFlags = (DWORD_PTR) pTLMD->pClassData + RidFromToken(pMTD->cl) - 1;
+ move_xp (flags, pTargetFlags);
+
+ *pFlags = flags;
+ }
+
+ *pOutPtr = dwTmp;
+ }
+ return;
+}
+
+void DisplaySharedStatic(ULONG64 dwModuleDomainID, DacpMethodTableData* pMT, DacpFieldDescData *pFD)
+{
+ DacpAppDomainStoreData adsData;
+ if (adsData.Request(g_sos)!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain information\n");
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ if (g_sos->GetAppDomainList(adsData.DomainCount,pArray, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of AppDomains\n");
+ return;
+ }
+
+#if defined(_TARGET_WIN64_)
+ ExtOut(" >> Domain:Value ");
+#else
+ ExtOut(" >> Domain:Value ");
+#endif
+ // Skip the SystemDomain and SharedDomain
+ for (int i = 0; i < adsData.DomainCount ; i ++)
+ {
+ DacpAppDomainData appdomainData;
+ if (appdomainData.Request(g_sos,pArray[i])!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain %lx\n",pArray[i]);
+ return;
+ }
+
+ DacpDomainLocalModuleData vDomainLocalModule;
+ if (g_sos->GetDomainLocalModuleDataFromAppDomain(appdomainData.AppDomainPtr, (int)dwModuleDomainID, &vDomainLocalModule) != S_OK)
+ {
+ DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
+ continue;
+ }
+
+ DWORD_PTR dwTmp;
+ BYTE Flags = 0;
+ GetStaticFieldPTR(&dwTmp, &vDomainLocalModule , pMT, pFD, &Flags);
+
+ if ((Flags&1) == 0) {
+ // We have not initialized this yet.
+ DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
+ continue;
+ }
+ else if (Flags & 2) {
+ // We have not initialized this yet.
+ DMLOut(" %s:FailInit", DMLDomain(pArray[i]));
+ continue;
+ }
+
+ DMLOut(" %s:", DMLDomain(appdomainData.AppDomainPtr));
+ DisplayDataMember(pFD, dwTmp, FALSE);
+ }
+ ExtOut(" <<\n");
+}
+
+void DisplayThreadStatic (DacpModuleData* pModule, DacpMethodTableData* pMT, DacpFieldDescData *pFD, BOOL fIsShared)
+{
+ SIZE_T dwModuleIndex = (SIZE_T)pModule->dwModuleIndex;
+ SIZE_T dwModuleDomainID = (SIZE_T)pModule->dwModuleID;
+
+ DacpThreadStoreData ThreadStore;
+ ThreadStore.Request(g_sos);
+
+ ExtOut(" >> Thread:Value");
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ DacpThreadData vThread;
+ if (vThread.Request(g_sos, CurThread) != S_OK)
+ {
+ ExtOut(" error getting thread %p, aborting this field\n", SOS_PTR(CurThread));
+ return;
+ }
+
+ if (vThread.osThreadId != 0)
+ {
+ CLRDATA_ADDRESS appDomainAddr = vThread.domain;
+
+ // Get the DLM (we need this to check the ClassInit flags).
+ // It's annoying that we have to issue one request for
+ // domain-neutral modules and domain-specific modules.
+ DacpDomainLocalModuleData vDomainLocalModule;
+ if (fIsShared)
+ {
+ if (g_sos->GetDomainLocalModuleDataFromAppDomain(appDomainAddr, (int)dwModuleDomainID, &vDomainLocalModule) != S_OK)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+ }
+ else
+ {
+ if (g_sos->GetDomainLocalModuleDataFromModule(pMT->Module, &vDomainLocalModule) != S_OK)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+ }
+
+ // Get the TLM
+ DacpThreadLocalModuleData vThreadLocalModule;
+ if (g_sos->GetThreadLocalModuleData(CurThread, (int)dwModuleIndex, &vThreadLocalModule) != S_OK)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+
+ DWORD_PTR dwTmp;
+ BYTE Flags = 0;
+ GetThreadStaticFieldPTR(&dwTmp, &vThreadLocalModule, pMT, pFD, &Flags);
+
+ if ((Flags&4) == 0)
+ {
+ // Not allocated, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+
+ Flags = 0;
+ GetDLMFlags(&vDomainLocalModule, pMT, &Flags);
+
+ if ((Flags&1) == 0)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+
+ ExtOut(" %x:", vThread.osThreadId);
+ DisplayDataMember(pFD, dwTmp, FALSE);
+ }
+
+ // Go to next thread
+ CurThread = vThread.nextThread;
+ }
+ ExtOut(" <<\n");
+}
+
+void DisplayContextStatic (DacpFieldDescData *pFD, size_t offset, BOOL fIsShared)
+{
+ ExtOut("\nDisplay of context static variables is not implemented yet\n");
+ /*
+ int numDomain;
+ DWORD_PTR *domainList = NULL;
+ GetDomainList (domainList, numDomain);
+ ToDestroy des0 ((void**)&domainList);
+ AppDomain vAppDomain;
+ Context vContext;
+
+ ExtOut(" >> Domain:Value");
+ for (int i = 0; i < numDomain; i ++)
+ {
+ DWORD_PTR dwAddr = domainList[i];
+ if (dwAddr == 0) {
+ continue;
+ }
+ vAppDomain.Fill (dwAddr);
+ if (vAppDomain.m_pDefaultContext == 0)
+ continue;
+ dwAddr = (DWORD_PTR)vAppDomain.m_pDefaultContext;
+ vContext.Fill (dwAddr);
+
+ if (fIsShared)
+ dwAddr = (DWORD_PTR)vContext.m_pSharedStaticData;
+ else
+ dwAddr = (DWORD_PTR)vContext.m_pUnsharedStaticData;
+ if (dwAddr == 0)
+ continue;
+ dwAddr += offsetof(STATIC_DATA, dataPtr);
+ dwAddr += offset;
+ if (safemove (dwAddr, dwAddr) == 0)
+ continue;
+ if (dwAddr == 0)
+ // We have not initialized this yet.
+ continue;
+
+ dwAddr += pFD->dwOffset;
+ if (pFD->Type == ELEMENT_TYPE_CLASS
+ || pFD->Type == ELEMENT_TYPE_VALUETYPE)
+ {
+ if (safemove (dwAddr, dwAddr) == 0)
+ continue;
+ }
+ if (dwAddr == 0)
+ // We have not initialized this yet.
+ continue;
+ ExtOut(" %p:", (ULONG64)domainList[i]);
+ DisplayDataMember (pFD, dwAddr, FALSE);
+ }
+ ExtOut(" <<\n");
+ */
+}
+
+const char * ElementTypeName(unsigned type)
+{
+ switch (type) {
+ case ELEMENT_TYPE_PTR:
+ return "PTR";
+ break;
+ case ELEMENT_TYPE_BYREF:
+ return "BYREF";
+ break;
+ case ELEMENT_TYPE_VALUETYPE:
+ return "VALUETYPE";
+ break;
+ case ELEMENT_TYPE_CLASS:
+ return "CLASS";
+ break;
+ case ELEMENT_TYPE_VAR:
+ return "VAR";
+ break;
+ case ELEMENT_TYPE_ARRAY:
+ return "ARRAY";
+ break;
+ case ELEMENT_TYPE_FNPTR:
+ return "FNPTR";
+ break;
+ case ELEMENT_TYPE_SZARRAY:
+ return "SZARRAY";
+ break;
+ case ELEMENT_TYPE_MVAR:
+ return "MVAR";
+ break;
+ default:
+ if ((type >= _countof(CorElementTypeName)) || (CorElementTypeName[type] == NULL))
+ {
+ return "";
+ }
+ return CorElementTypeName[type];
+ break;
+ }
+} // ElementTypeName
+
+const char * ElementTypeNamespace(unsigned type)
+{
+ if ((type >= _countof(CorElementTypeName)) || (CorElementTypeNamespace[type] == NULL))
+ {
+ return "";
+ }
+ return CorElementTypeNamespace[type];
+}
+
+void ComposeName_s(CorElementType Type, __out_ecount(capacity_buffer) LPSTR buffer, size_t capacity_buffer)
+{
+ const char *p = ElementTypeNamespace(Type);
+ if ((p) && (*p != '\0'))
+ {
+ strcpy_s(buffer,capacity_buffer,p);
+ strcat_s(buffer,capacity_buffer,".");
+ strcat_s(buffer,capacity_buffer,ElementTypeName(Type));
+ }
+ else
+ {
+ strcpy_s(buffer,capacity_buffer,ElementTypeName(Type));
+ }
+}
+
+// NOTE: pszName is changed
+// INPUT MAXCHARS RETURN
+// HelloThere 5 ...re
+// HelloThere 8 ...There
+LPWSTR FormatTypeName (__out_ecount (maxChars) LPWSTR pszName, UINT maxChars)
+{
+ UINT iStart = 0;
+ UINT iLen = (int) _wcslen(pszName);
+ if (iLen > maxChars)
+ {
+ iStart = iLen - maxChars;
+ UINT numDots = (maxChars < 3) ? maxChars : 3;
+ for (UINT i=0; i < numDots; i++)
+ pszName[iStart+i] = '.';
+ }
+ return pszName + iStart;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump all fields of a managed object. *
+* dwStartAddr specifies the beginning memory address. *
+* bFirst is used to avoid printing header everytime. *
+* *
+\**********************************************************************/
+void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD, DWORD_PTR dwStartAddr, BOOL bFirst, BOOL bValueClass)
+{
+ static DWORD numInstanceFields = 0;
+ if (bFirst)
+ {
+ ExtOutIndent();
+ ExtOut("%" POINTERSIZE "s %8s %8s %20s %2s %8s %" POINTERSIZE "s %s\n",
+ "MT", "Field", "Offset", "Type", "VT", "Attr", "Value", "Name");
+ numInstanceFields = 0;
+ }
+
+ BOOL fIsShared = pMTD->bIsShared;
+
+ if (pMTD->ParentMethodTable)
+ {
+ DacpMethodTableData vParentMethTable;
+ if (vParentMethTable.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
+ {
+ ExtOut("Invalid parent MethodTable\n");
+ return;
+ }
+
+ DacpMethodTableFieldData vParentMethTableFields;
+ if (vParentMethTableFields.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
+ {
+ ExtOut("Invalid parent EEClass\n");
+ return;
+ }
+
+ DisplayFields(pMTD->ParentMethodTable, &vParentMethTable, &vParentMethTableFields, dwStartAddr, FALSE, bValueClass);
+ }
+
+ DWORD numStaticFields = 0;
+ CLRDATA_ADDRESS dwAddr = pMTFD->FirstField;
+ DacpFieldDescData vFieldDesc;
+
+ // Get the module name
+ DacpModuleData module;
+ if (module.Request(g_sos, pMTD->Module)!=S_OK)
+ return;
+
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
+
+ while (numInstanceFields < pMTFD->wNumInstanceFields
+ || numStaticFields < pMTFD->wNumStaticFields)
+ {
+ if (IsInterrupt())
+ return;
+
+ ExtOutIndent ();
+
+ if ((vFieldDesc.Request(g_sos, dwAddr)!=S_OK) ||
+ (vFieldDesc.Type >= ELEMENT_TYPE_MAX))
+ {
+ ExtOut("Unable to display fields\n");
+ return;
+ }
+ dwAddr = vFieldDesc.NextField;
+
+ DWORD offset = vFieldDesc.dwOffset;
+ if(!((vFieldDesc.bIsThreadLocal || vFieldDesc.bIsContextLocal || fIsShared) && vFieldDesc.bIsStatic))
+ {
+ if (!bValueClass)
+ {
+ offset += sizeof(BaseObject);
+ }
+ }
+
+ DMLOut("%s %8x %8x ", DMLMethodTable(vFieldDesc.MTOfType),
+ TokenFromRid(vFieldDesc.mb, mdtFieldDef),
+ offset);
+
+ char ElementName[mdNameLen];
+ if ((vFieldDesc.Type == ELEMENT_TYPE_VALUETYPE ||
+ vFieldDesc.Type == ELEMENT_TYPE_CLASS) && vFieldDesc.MTOfType)
+ {
+ NameForMT_s((DWORD_PTR)vFieldDesc.MTOfType, g_mdName, mdNameLen);
+ ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
+ }
+ else
+ {
+ if (vFieldDesc.Type == ELEMENT_TYPE_CLASS && vFieldDesc.TokenOfType != mdTypeDefNil)
+ {
+ // Get the name from Metadata!!!
+ NameForToken_s(TokenFromRid(vFieldDesc.TokenOfType, mdtTypeDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
+ }
+ else
+ {
+ // If ET type from signature is different from fielddesc, then the signature one is more descriptive.
+ // For example, E_T_STRING in field desc will be E_T_CLASS. In minidump's case, we won't have
+ // the method table for it.
+ ComposeName_s(vFieldDesc.Type != vFieldDesc.sigType ? vFieldDesc.sigType : vFieldDesc.Type, ElementName, sizeof(ElementName)/sizeof(ElementName[0]));
+ ExtOut("%20.20s ", ElementName);
+ }
+ }
+
+ ExtOut("%2s ", (IsElementValueType(vFieldDesc.Type)) ? "1" : "0");
+
+ if (vFieldDesc.bIsStatic && (vFieldDesc.bIsThreadLocal || vFieldDesc.bIsContextLocal))
+ {
+ numStaticFields ++;
+ if (fIsShared)
+ ExtOut("%8s %" POINTERSIZE "s", "shared", vFieldDesc.bIsThreadLocal ? "TLstatic" : "CLstatic");
+ else
+ ExtOut("%8s ", vFieldDesc.bIsThreadLocal ? "TLstatic" : "CLstatic");
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut(" <no information>\n");
+ }
+ else
+ {
+ if (vFieldDesc.bIsThreadLocal)
+ {
+ DacpModuleData vModule;
+ if (vModule.Request(g_sos,pMTD->Module) == S_OK)
+ {
+ DisplayThreadStatic(&vModule, pMTD, &vFieldDesc, fIsShared);
+ }
+ }
+ else if (vFieldDesc.bIsContextLocal)
+ {
+ DisplayContextStatic(&vFieldDesc,
+ pMTFD->wContextStaticOffset,
+ fIsShared);
+ }
+ }
+
+ }
+ else if (vFieldDesc.bIsStatic)
+ {
+ numStaticFields ++;
+
+ if (fIsShared)
+ {
+ ExtOut("%8s %" POINTERSIZE "s", "shared", "static");
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut(" <no information>\n");
+ }
+ else
+ {
+ DacpModuleData vModule;
+ if (vModule.Request(g_sos,pMTD->Module) == S_OK)
+ {
+ DisplaySharedStatic(vModule.dwModuleID, pMTD, &vFieldDesc);
+ }
+ }
+ }
+ else
+ {
+ ExtOut("%8s ", "static");
+
+ DacpDomainLocalModuleData vDomainLocalModule;
+
+ // The MethodTable isn't shared, so the module must not be loaded domain neutral. We can
+ // get the specific DomainLocalModule instance without needing to know the AppDomain in advance.
+ if (g_sos->GetDomainLocalModuleDataFromModule(pMTD->Module, &vDomainLocalModule) != S_OK)
+ {
+ ExtOut(" <no information>\n");
+ }
+ else
+ {
+ DWORD_PTR dwTmp;
+ GetStaticFieldPTR(&dwTmp, &vDomainLocalModule, pMTD, &vFieldDesc);
+ DisplayDataMember(&vFieldDesc, dwTmp);
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+ }
+ }
+ }
+ else
+ {
+ numInstanceFields ++;
+
+ ExtOut("%8s ", "instance");
+
+ if (dwStartAddr > 0)
+ {
+ DWORD_PTR dwTmp = dwStartAddr + vFieldDesc.dwOffset + (bValueClass ? 0 : sizeof(BaseObject));
+ DisplayDataMember(&vFieldDesc, dwTmp);
+ }
+ else
+ {
+ ExtOut(" %8s", " ");
+ }
+
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+ }
+
+ }
+
+ return;
+}
+
+// Return value: -1 = error,
+// 0 = field not found,
+// > 0 = offset to field from objAddr
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst)
+{
+ TADDR mt = NULL;
+ if FAILED(GetMTOfObject(TO_TADDR(cdaObj), &mt))
+ return -1;
+
+ return GetObjFieldOffset(cdaObj, TO_CDADDR(mt), wszFieldName, bFirst);
+}
+
+// Return value: -1 = error,
+// 0 = field not found,
+// > 0 = offset to field from objAddr
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName,
+ BOOL bFirst/*=TRUE*/)
+{
+
+#define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0)
+
+ DacpObjectData objData;
+ DacpMethodTableData dmtd;
+ DacpMethodTableFieldData vMethodTableFields;
+ DacpFieldDescData vFieldDesc;
+ DacpModuleData module;
+ static DWORD numInstanceFields = 0; // Static due to recursion visiting parents
+
+ if (bFirst)
+ {
+ numInstanceFields = 0;
+ }
+
+ EXITPOINT(objData.Request(g_sos, cdaObj) == S_OK);
+ EXITPOINT(dmtd.Request(g_sos, cdaMT) == S_OK);
+
+ if (dmtd.ParentMethodTable)
+ {
+ DWORD retVal = GetObjFieldOffset (cdaObj, dmtd.ParentMethodTable,
+ wszFieldName, FALSE);
+ if (retVal != 0)
+ {
+ // return in case of error or success.
+ // Fall through for field-not-found.
+ return retVal;
+ }
+ }
+
+ EXITPOINT (vMethodTableFields.Request(g_sos,cdaMT) == S_OK);
+ EXITPOINT (module.Request(g_sos,dmtd.Module) == S_OK);
+
+ CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
+
+ while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
+ {
+ EXITPOINT (vFieldDesc.Request(g_sos, dwAddr) == S_OK);
+
+ if (!vFieldDesc.bIsStatic)
+ {
+ DWORD offset = vFieldDesc.dwOffset + sizeof(BaseObject);
+ NameForToken_s (TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ if (_wcscmp (wszFieldName, g_mdName) == 0)
+ {
+ return offset;
+ }
+ numInstanceFields ++;
+ }
+
+ dwAddr = vFieldDesc.NextField;
+ }
+
+ // Field name not found...
+ return 0;
+
+#undef EXITPOINT
+}
+
+// Returns an AppDomain address if AssemblyPtr is loaded into that domain only. Otherwise
+// returns NULL
+CLRDATA_ADDRESS IsInOneDomainOnly(CLRDATA_ADDRESS AssemblyPtr)
+{
+ CLRDATA_ADDRESS appDomain = NULL;
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ ExtOut("Unable to get appdomain store\n");
+ return NULL;
+ }
+
+ size_t AllocSize;
+ if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
+ {
+ ExtOut ("Failed to get appdomain list\n");
+ return NULL;
+ }
+
+ for (int i = 0; i < adstore.DomainCount; i++)
+ {
+ if (IsInterrupt())
+ return NULL;
+
+ DacpAppDomainData dadd;
+ if (dadd.Request(g_sos, pArray[i]) != S_OK)
+ {
+ ExtOut ("Unable to get AppDomain %p\n", SOS_PTR(pArray[i]));
+ return NULL;
+ }
+
+ if (dadd.AssemblyCount)
+ {
+ size_t AssemblyAllocSize;
+ if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), dadd.AssemblyCount, AssemblyAllocSize))
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> pAsmArray = new CLRDATA_ADDRESS[dadd.AssemblyCount];
+ if (pAsmArray==NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ if (g_sos->GetAssemblyList(dadd.AppDomainPtr,dadd.AssemblyCount,pAsmArray, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of Assemblies\n");
+ return NULL;
+ }
+
+ for (LONG n = 0; n < dadd.AssemblyCount; n ++)
+ {
+ if (IsInterrupt())
+ return NULL;
+
+ if (AssemblyPtr == pAsmArray[n])
+ {
+ if (appDomain != NULL)
+ {
+ // We have found more than one AppDomain that loaded this
+ // assembly, we must return NULL.
+ return NULL;
+ }
+ appDomain = dadd.AppDomainPtr;
+ }
+ }
+ }
+ }
+
+
+ return appDomain;
+}
+
+CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr)
+{
+ DacpMethodTableData mt;
+ if (mt.Request(g_sos, mtPtr) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpModuleData module;
+ if (module.Request(g_sos, mt.Module) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAssemblyData assembly;
+ if (assembly.Request(g_sos, module.Assembly) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ return NULL;
+ }
+
+ return (assembly.ParentDomain == adstore.sharedDomain) ?
+ IsInOneDomainOnly(assembly.AssemblyPtr) :
+ assembly.ParentDomain;
+}
+
+CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr)
+{
+ CLRDATA_ADDRESS appDomain = NULL;
+
+ DacpObjectData objData;
+ if (objData.Request(g_sos,objPtr) != S_OK)
+ {
+ return NULL;
+ }
+
+ // First check eeclass->module->assembly->domain.
+ // Then check the object flags word
+ // finally, search threads for a reference to the object, and look at the thread context.
+
+ DacpMethodTableData mt;
+ if (mt.Request(g_sos,objData.MethodTable) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpModuleData module;
+ if (module.Request(g_sos,mt.Module) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAssemblyData assembly;
+ if (assembly.Request(g_sos,module.Assembly) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ return NULL;
+ }
+
+ if (assembly.ParentDomain == adstore.sharedDomain)
+ {
+ sos::Object obj(TO_TADDR(objPtr));
+ ULONG value = 0;
+ if (!obj.TryGetHeader(value))
+ {
+ return NULL;
+ }
+
+ DWORD adIndex = (value >> SBLK_APPDOMAIN_SHIFT) & SBLK_MASK_APPDOMAININDEX;
+ if ( ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0) || adIndex==0)
+ {
+ // No AppDomainID information. We'll make use of a heuristic.
+ // If the assembly is in the shared domain, we can report it as
+ // being in domain X if the only other domain that has the assembly
+ // loaded is domain X.
+ appDomain = IsInOneDomainOnly(assembly.AssemblyPtr);
+ if (appDomain == NULL && ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0))
+ {
+ if ((value & BIT_SBLK_IS_HASHCODE) == 0)
+ {
+ UINT index = value & MASK_SYNCBLOCKINDEX;
+ // We have a syncblock, the appdomain ID may be in there.
+ DacpSyncBlockData syncBlockData;
+ if (syncBlockData.Request(g_sos,index) == S_OK)
+ {
+ appDomain = syncBlockData.appDomainPtr;
+ }
+ }
+ }
+ }
+ else if ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) == 0)
+ {
+ size_t AllocSize;
+ if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
+ {
+ return NULL;
+ }
+ // we know we have a non-zero adIndex. Find the appdomain.
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
+ if (pArray==NULL)
+ {
+ return NULL;
+ }
+
+ if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
+ {
+ return NULL;
+ }
+
+ for (int i = 0; i < adstore.DomainCount; i++)
+ {
+ DacpAppDomainData dadd;
+ if (dadd.Request(g_sos, pArray[i]) != S_OK)
+ {
+ return NULL;
+ }
+ if (dadd.dwId == adIndex)
+ {
+ appDomain = pArray[i];
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ appDomain = assembly.ParentDomain;
+ }
+
+ return appDomain;
+}
+
+HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
+{
+ DacpModuleData ModuleData;
+ fileName[0] = L'\0';
+
+ HRESULT hr = ModuleData.Request(g_sos, TO_CDADDR(pModuleAddr));
+ if (SUCCEEDED(hr))
+ {
+ hr = FileNameForModule(&ModuleData,fileName);
+ }
+
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the file name given a Module. *
+* *
+\**********************************************************************/
+// fileName should be at least MAX_LONGPATH
+HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
+{
+ fileName[0] = L'\0';
+
+ HRESULT hr = S_OK;
+ CLRDATA_ADDRESS dwAddr = pModule->File;
+ if (dwAddr == 0)
+ {
+ // TODO: We have dynamic module
+ return E_NOTIMPL;
+ }
+
+ CLRDATA_ADDRESS base = 0;
+ hr = g_sos->GetPEFileBase(dwAddr, &base);
+ if (SUCCEEDED(hr))
+ {
+ hr = g_sos->GetPEFileName(dwAddr, MAX_LONGPATH, fileName, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if (fileName[0] != W('\0'))
+ return hr; // done
+ }
+#ifndef FEATURE_PAL
+ // Try the base *
+ if (base)
+ {
+ hr = DllsName((ULONG_PTR) base, fileName);
+ }
+#endif // !FEATURE_PAL
+ }
+
+ // If we got here, either DllsName worked, or we couldn't find a name
+ return hr;
+}
+
+void AssemblyInfo(DacpAssemblyData *pAssembly)
+{
+ ExtOut("ClassLoader: %p\n", SOS_PTR(pAssembly->ClassLoader));
+ if ((ULONG64)pAssembly->AssemblySecDesc != NULL)
+ ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pAssembly->AssemblySecDesc));
+ ExtOut(" Module Name\n");
+
+ ArrayHolder<CLRDATA_ADDRESS> Modules = new CLRDATA_ADDRESS[pAssembly->ModuleCount];
+ if (Modules == NULL
+ || g_sos->GetAssemblyModuleList(pAssembly->AssemblyPtr, pAssembly->ModuleCount, Modules, NULL) != S_OK)
+ {
+ ReportOOM();
+ return;
+ }
+
+ for (UINT n=0;n<pAssembly->ModuleCount;n++)
+ {
+ if (IsInterrupt())
+ {
+ return;
+ }
+
+ CLRDATA_ADDRESS ModuleAddr = Modules[n];
+ DMLOut("%s " WIN86_8SPACES, DMLModule(ModuleAddr));
+ DacpModuleData moduleData;
+ if (moduleData.Request(g_sos,ModuleAddr)==S_OK)
+ {
+ WCHAR fileName[MAX_LONGPATH];
+ FileNameForModule (&moduleData, fileName);
+ if (fileName[0])
+ {
+ ExtOut("%S\n", fileName);
+ }
+ else
+ {
+ ExtOut("%S\n", (moduleData.bIsReflection) ? W("Dynamic Module") : W("Unknown Module"));
+ }
+ }
+ }
+}
+
+const char *GetStageText(DacpAppDomainDataStage stage)
+{
+ switch(stage)
+ {
+ case STAGE_CREATING:
+ return "CREATING";
+ case STAGE_READYFORMANAGEDCODE:
+ return "READYFORMANAGEDCODE";
+ case STAGE_ACTIVE:
+ return "ACTIVE";
+ case STAGE_OPEN:
+ return "OPEN";
+ case STAGE_UNLOAD_REQUESTED:
+ return "UNLOAD_REQUESTED";
+ case STAGE_EXITING:
+ return "EXITING";
+ case STAGE_EXITED:
+ return "EXITED";
+ case STAGE_FINALIZING:
+ return "FINALIZING";
+ case STAGE_FINALIZED:
+ return "FINALIZED";
+ case STAGE_HANDLETABLE_NOACCESS:
+ return "HANDLETABLE_NOACCESS";
+ case STAGE_CLEARED:
+ return "CLEARED";
+ case STAGE_COLLECTED:
+ return "COLLECTED";
+ case STAGE_CLOSED:
+ return "CLOSED";
+ }
+ return "UNKNOWN";
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a domain. *
+* *
+\**********************************************************************/
+void DomainInfo (DacpAppDomainData *pDomain)
+{
+ ExtOut("LowFrequencyHeap: %p\n", SOS_PTR(pDomain->pLowFrequencyHeap));
+ ExtOut("HighFrequencyHeap: %p\n", SOS_PTR(pDomain->pHighFrequencyHeap));
+ ExtOut("StubHeap: %p\n", SOS_PTR(pDomain->pStubHeap));
+ ExtOut("Stage: %s\n", GetStageText(pDomain->appDomainStage));
+ if ((ULONG64)pDomain->AppSecDesc != NULL)
+ ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pDomain->AppSecDesc));
+ ExtOut("Name: ");
+
+ if (g_sos->GetAppDomainName(pDomain->AppDomainPtr, mdNameLen, g_mdName, NULL)!=S_OK)
+ {
+ ExtOut("Error getting AppDomain friendly name\n");
+ }
+ else
+ {
+ ExtOut("%S\n", (g_mdName[0] != L'\0') ? g_mdName : W("None"));
+ }
+
+ if (pDomain->AssemblyCount == 0)
+ return;
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[pDomain->AssemblyCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ if (g_sos->GetAssemblyList(pDomain->AppDomainPtr,pDomain->AssemblyCount,pArray, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of Assemblies\n");
+ return;
+ }
+
+ LONG n;
+ // Assembly vAssembly;
+ for (n = 0; n < pDomain->AssemblyCount; n ++)
+ {
+ if (IsInterrupt())
+ return;
+
+ if (n != 0)
+ ExtOut("\n");
+
+ DMLOut("Assembly: %s", DMLAssembly(pArray[n]));
+ DacpAssemblyData assemblyData;
+ if (assemblyData.Request(g_sos, pArray[n], pDomain->AppDomainPtr) == S_OK)
+ {
+ if (assemblyData.isDynamic)
+ ExtOut(" (Dynamic)");
+
+ ExtOut(" [");
+ if (g_sos->GetAssemblyName(pArray[n], mdNameLen, g_mdName, NULL) == S_OK)
+ ExtOut("%S", g_mdName);
+ ExtOut("]\n");
+
+ AssemblyInfo(&assemblyData);
+ }
+ }
+
+ ExtOut("\n");
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a MethodDesc using *
+* metadata API. *
+* *
+\**********************************************************************/
+BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
+{
+ mdName[0] = L'\0';
+ CLRDATA_ADDRESS StartAddr = TO_CDADDR(pMD);
+ DacpMethodDescData MethodDescData;
+
+ // don't need to check for minidump file as all commands are seals
+ // We also do not have EEJitManager to validate anyway.
+ //
+ if (!IsMiniDumpFile() && MethodDescData.Request(g_sos,StartAddr) != S_OK)
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(StartAddr));
+ return FALSE;
+ }
+
+ if (g_sos->GetMethodDescName(StartAddr, mdNameLen, mdName, NULL) != S_OK)
+ {
+ wcscpy_s(mdName, capacity_mdName, W("UNKNOWN"));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a MethodTable using *
+* metadata API. *
+* *
+\**********************************************************************/
+BOOL NameForMT_s(DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
+{
+ HRESULT hr = g_sos->GetMethodTableName(TO_CDADDR(MTAddr), (ULONG32)capacity_mdName, mdName, NULL);
+ return SUCCEEDED(hr);
+}
+
+WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt)
+{
+ bool array = false;
+ WCHAR *res = NULL;
+
+ if (mt == sos::MethodTable::GetFreeMT())
+ {
+ res = new WCHAR[5];
+ wcscpy_s(res, 5, W("Free"));
+ return res;
+ }
+
+ if (mt == sos::MethodTable::GetArrayMT() && cmt != NULL)
+ {
+ mt = cmt;
+ array = true;
+ }
+
+ unsigned int needed = 0;
+ HRESULT hr = g_sos->GetMethodTableName(mt, 0, NULL, &needed);
+
+ // If failed, we will return null.
+ if (SUCCEEDED(hr))
+ {
+ // +2 for [], if we need it.
+ res = new WCHAR[needed+2];
+ hr = g_sos->GetMethodTableName(mt, needed, res, NULL);
+
+ if (FAILED(hr))
+ {
+ delete [] res;
+ res = NULL;
+ }
+ else if (array)
+ {
+ res[needed-1] = '[';
+ res[needed] = ']';
+ res[needed+1] = 0;
+ }
+ }
+
+ return res;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if str2 is a substring of str1 and str1 and str2 *
+* share the same file path.
+* *
+\**********************************************************************/
+BOOL IsSameModuleName (const char *str1, const char *str2)
+{
+ if (strlen (str1) < strlen (str2))
+ return FALSE;
+ const char *ptr1 = str1 + strlen(str1)-1;
+ const char *ptr2 = str2 + strlen(str2)-1;
+ while (ptr2 >= str2)
+ {
+#ifndef FEATURE_PAL
+ if (tolower(*ptr1) != tolower(*ptr2))
+#else
+ if (*ptr1 != *ptr2)
+#endif
+ {
+ return FALSE;
+ }
+ ptr2--;
+ ptr1--;
+ }
+ if (ptr1 >= str1 && *ptr1 != DIRECTORY_SEPARATOR_CHAR_A && *ptr1 != ':')
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if moduleAddr is the address of a module. *
+* *
+\**********************************************************************/
+BOOL IsModule (DWORD_PTR moduleAddr)
+{
+ DacpModuleData module;
+ return (module.Request(g_sos, TO_CDADDR(moduleAddr))==S_OK);
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if value is the address of a MethodTable. *
+* We verify that MethodTable and EEClass are right.
+* *
+\**********************************************************************/
+BOOL IsMethodTable (DWORD_PTR value)
+{
+ DacpMethodTableData mtabledata;
+ if (mtabledata.Request(g_sos, TO_CDADDR(value))!=S_OK)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if value is the address of a MethodDesc. *
+* We verify that MethodTable and EEClass are right.
+* *
+\**********************************************************************/
+BOOL IsMethodDesc (DWORD_PTR value)
+{
+ // Just by retrieving one successfully from the DAC, we know we have a MethodDesc.
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(value)) != S_OK)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+DacpUsefulGlobalsData g_special_usefulGlobals;
+
+BOOL IsObjectArray (DacpObjectData *pData)
+{
+ if (pData->ObjectType == OBJ_ARRAY)
+ return g_special_usefulGlobals.ArrayMethodTable == pData->MethodTable;
+
+ return FALSE;
+}
+
+BOOL IsObjectArray (DWORD_PTR obj)
+{
+ DWORD_PTR mtAddr = NULL;
+ if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
+ return TO_TADDR(g_special_usefulGlobals.ArrayMethodTable) == mtAddr;
+
+ return FALSE;
+}
+
+BOOL IsStringObject (size_t obj)
+{
+ DWORD_PTR mtAddr = NULL;
+
+ if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
+ return TO_TADDR(g_special_usefulGlobals.StringMethodTable) == mtAddr;
+
+ return FALSE;
+}
+
+void DumpStackObjectsOutput(const char *location, DWORD_PTR objAddr, BOOL verifyFields)
+{
+ // rule out pointers that are outside of the gc heap.
+ if (g_snapshot.GetHeap(objAddr) == NULL)
+ return;
+
+ DacpObjectData objectData;
+ if (objectData.Request(g_sos, TO_CDADDR(objAddr)) != S_OK)
+ return;
+
+ if (sos::IsObject(objAddr, verifyFields != FALSE)
+ && !sos::MethodTable::IsFreeMT(TO_TADDR(objectData.MethodTable)))
+ {
+ DMLOut("%-" POINTERSIZE "s %s ", location, DMLObject(objAddr));
+ if (g_sos->GetObjectClassName(TO_CDADDR(objAddr), mdNameLen, g_mdName, NULL)==S_OK)
+ {
+ ExtOut("%S", g_mdName);
+
+ if (IsStringObject(objAddr))
+ {
+ ExtOut(" ");
+ StringObjectContent(objAddr, FALSE, 40);
+ }
+ else if (IsObjectArray(objAddr) &&
+ (g_sos->GetMethodTableName(objectData.ElementTypeHandle, mdNameLen, g_mdName, NULL) == S_OK))
+ {
+ ExtOut(" ");
+ ExtOut("(%S[])", g_mdName);
+ }
+ }
+ else
+ {
+ ExtOut("<unknown type>");
+ }
+ ExtOut("\n");
+ }
+}
+
+void DumpStackObjectsOutput(DWORD_PTR ptr, DWORD_PTR objAddr, BOOL verifyFields)
+{
+ char location[64];
+ sprintf_s(location, 64, "%p", (DWORD_PTR *)ptr);
+
+ DumpStackObjectsOutput(location, objAddr, verifyFields);
+}
+
+void DumpStackObjectsInternal(size_t StackTop, size_t StackBottom, BOOL verifyFields)
+{
+ for (DWORD_PTR ptr = StackTop; ptr <= StackBottom; ptr += sizeof(DWORD_PTR))
+ {
+ if (IsInterrupt())
+ return;
+
+ DWORD_PTR objAddr;
+ move_xp(objAddr, ptr);
+
+ DumpStackObjectsOutput(ptr, objAddr, verifyFields);
+ }
+}
+
+void DumpRegObjectHelper(const char *regName, BOOL verifyFields)
+{
+ DWORD_PTR reg;
+#ifdef FEATURE_PAL
+ if (FAILED(g_ExtRegisters->GetValueByName(regName, &reg)))
+ return;
+#else
+ DEBUG_VALUE value;
+ ULONG IREG;
+ if (FAILED(g_ExtRegisters->GetIndexByName(regName, &IREG)) ||
+ FAILED(g_ExtRegisters->GetValue(IREG, &value)))
+ return;
+
+#if defined(SOS_TARGET_X86) || defined(SOS_TARGET_ARM)
+ reg = (DWORD_PTR) value.I32;
+#elif defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64)
+ reg = (DWORD_PTR) value.I64;
+#else
+#error Unsupported target
+#endif
+#endif // FEATURE_PAL
+
+ DumpStackObjectsOutput(regName, reg, verifyFields);
+}
+
+void DumpStackObjectsHelper (
+ TADDR StackTop,
+ TADDR StackBottom,
+ BOOL verifyFields)
+{
+ ExtOut(g_targetMachine->GetDumpStackObjectsHeading());
+
+ LPCSTR* regs;
+ unsigned int cnt;
+ g_targetMachine->GetGCRegisters(&regs, &cnt);
+
+ for (size_t i = 0; i < cnt; ++i)
+ DumpRegObjectHelper(regs[i], verifyFields);
+
+ // Make certain StackTop is dword aligned:
+ DumpStackObjectsInternal(StackTop & ~ALIGNCONST, StackBottom, verifyFields);
+}
+
+void AddToModuleList(DWORD_PTR * &moduleList, int &numModule, int &maxList,
+ DWORD_PTR dwModuleAddr)
+{
+ int i;
+ for (i = 0; i < numModule; i ++)
+ {
+ if (moduleList[i] == dwModuleAddr)
+ break;
+ }
+ if (i == numModule)
+ {
+ moduleList[numModule] = dwModuleAddr;
+ numModule ++;
+ if (numModule == maxList)
+ {
+ int listLength = 0;
+ if (!ClrSafeInt<int>::multiply(maxList, 2, listLength))
+ {
+ ExtOut("<integer overflow>\n");
+ numModule = 0;
+ ControlC = 1;
+ return;
+ }
+ DWORD_PTR *list = new DWORD_PTR [listLength];
+
+ if (list == NULL)
+ {
+ numModule = 0;
+ ControlC = 1;
+ return;
+ }
+ memcpy (list, moduleList, maxList * sizeof(PVOID));
+ delete[] moduleList;
+ moduleList = list;
+ maxList *= 2;
+ }
+ }
+}
+
+BOOL IsFusionLoadedModule (LPCSTR fusionName, LPCSTR mName)
+{
+ // The fusion name will be in this format:
+ // <module name>, Version=<version>, Culture=<culture>, PublicKeyToken=<token>
+ // If fusionName up to the comma matches mName (case insensitive),
+ // we consider that a match was found.
+ LPCSTR commaPos = strchr (fusionName, ',');
+ if (commaPos)
+ {
+ // verify that fusionName and mName match up to a comma.
+ while (*fusionName != ',')
+ {
+ if (*mName == '\0')
+ {
+ return FALSE;
+ }
+
+#ifndef FEATURE_PAL
+ if (tolower(*fusionName) != tolower(*mName))
+#else
+ if (*fusionName != *mName)
+#endif
+ {
+ return FALSE;
+ }
+ fusionName++;
+ mName++;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL DebuggerModuleNamesMatch (CLRDATA_ADDRESS PEFileAddr, ___in __in_z LPSTR mName)
+{
+ // Another way to see if a module is the same is
+ // to accept that mName may be the debugger's name for
+ // a loaded module. We can get the debugger's name for
+ // the module we are looking at right now, and compare
+ // it with mName, if they match exactly, we can add
+ // the module to the list.
+ if (PEFileAddr)
+ {
+ CLRDATA_ADDRESS pebase = 0;
+ if (g_sos->GetPEFileBase(PEFileAddr, &pebase) == S_OK)
+ {
+ if (pebase)
+ {
+ ULONG Index;
+ ULONG64 base;
+ if (g_ExtSymbols->GetModuleByOffset(pebase, 0, &Index, &base) == S_OK)
+ {
+ CHAR ModuleName[MAX_LONGPATH+1];
+
+ if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, ModuleName,
+ MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
+ {
+ if (_stricmp (ModuleName, mName) == 0)
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+DWORD_PTR *ModuleFromName(__in_opt LPSTR mName, int *numModule)
+{
+ if (numModule == NULL)
+ return NULL;
+
+ DWORD_PTR *moduleList = NULL;
+ *numModule = 0;
+
+ DacpAppDomainStoreData adsData;
+ if (adsData.Request(g_sos)!=S_OK)
+ return NULL;
+
+ ArrayHolder<CLRDATA_ADDRESS> pAssemblyArray = NULL;
+ ArrayHolder<CLRDATA_ADDRESS> pModules = NULL;
+ int arrayLength = 0;
+ if (!ClrSafeInt<int>::addition(adsData.DomainCount, 2, arrayLength))
+ {
+ ExtOut("<integer overflow>\n");
+ return NULL;
+ }
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[arrayLength];
+
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ pArray[0] = adsData.systemDomain;
+ pArray[1] = adsData.sharedDomain;
+ if (g_sos->GetAppDomainList(adsData.DomainCount, pArray.GetPtr()+2, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of AppDomains\n");
+ return NULL;
+ }
+
+ // List all domain
+ size_t AllocSize;
+ int maxList = arrayLength; // account for system and shared domains
+ if (maxList <= 0 || !ClrSafeInt<size_t>::multiply(maxList, sizeof(PVOID), AllocSize))
+ {
+ ExtOut("Integer overflow error.\n");
+ return NULL;
+ }
+
+ moduleList = new DWORD_PTR[maxList];
+ if (moduleList == NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ WCHAR StringData[MAX_LONGPATH];
+ char fileName[sizeof(StringData)/2];
+
+ // Search all domains to find a module
+ for (int n = 0; n < adsData.DomainCount+2; n++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ goto Failure;
+ }
+
+ DacpAppDomainData appDomain;
+ if (FAILED(appDomain.Request(g_sos,pArray[n])))
+ {
+ // Don't print a failure message here, there is a very normal case when checking
+ // for modules after clr is loaded but before any AppDomains or assemblies are created
+ // for example:
+ // >sxe ld:clr
+ // >g
+ // ...
+ // ModLoad: clr.dll
+ // >!bpmd Foo.dll Foo.Bar
+
+ // we will correctly give the answer that whatever module you were looking for, it isn't loaded yet
+ goto Failure;
+ }
+
+ if (appDomain.AssemblyCount)
+ {
+ pAssemblyArray = new CLRDATA_ADDRESS[appDomain.AssemblyCount];
+ if (pAssemblyArray==NULL)
+ {
+ ReportOOM();
+ goto Failure;
+ }
+
+ if (FAILED(g_sos->GetAssemblyList(appDomain.AppDomainPtr, appDomain.AssemblyCount, pAssemblyArray, NULL)))
+ {
+ ExtOut("Unable to get array of Assemblies for the given AppDomain..\n");
+ goto Failure;
+ }
+
+ for (int nAssem = 0; nAssem < appDomain.AssemblyCount; nAssem ++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ goto Failure;
+ }
+
+ DacpAssemblyData assemblyData;
+ if (FAILED(assemblyData.Request(g_sos, pAssemblyArray[nAssem])))
+ {
+ ExtOut("Failed to request assembly.\n");
+ goto Failure;
+ }
+
+ pModules = new CLRDATA_ADDRESS[assemblyData.ModuleCount];
+ if (FAILED(g_sos->GetAssemblyModuleList(assemblyData.AssemblyPtr, assemblyData.ModuleCount, pModules, NULL)))
+ {
+ ExtOut("Failed to get the modules for the given assembly.\n");
+ goto Failure;
+ }
+
+ for (UINT nModule = 0; nModule < assemblyData.ModuleCount; nModule++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ goto Failure;
+ }
+
+ CLRDATA_ADDRESS ModuleAddr = pModules[nModule];
+ DacpModuleData ModuleData;
+ if (FAILED(ModuleData.Request(g_sos,ModuleAddr)))
+ {
+ ExtOut("Failed to request Module data from assembly.\n");
+ goto Failure;
+ }
+
+ FileNameForModule ((DWORD_PTR)ModuleAddr, StringData);
+ int m;
+ for (m = 0; StringData[m] != L'\0'; m++)
+ {
+ fileName[m] = (char)StringData[m];
+ }
+ fileName[m] = '\0';
+
+ if ((mName == NULL) ||
+ IsSameModuleName(fileName, mName) ||
+ DebuggerModuleNamesMatch(ModuleData.File, mName) ||
+ IsFusionLoadedModule(fileName, mName))
+ {
+ AddToModuleList(moduleList, *numModule, maxList, (DWORD_PTR)ModuleAddr);
+ }
+ }
+
+ pModules = NULL;
+ }
+ pAssemblyArray = NULL;
+ }
+ }
+
+ return moduleList;
+
+ // We do not want to return a half-constructed list. Instead, we return NULL on a failure.
+Failure:
+ delete [] moduleList;
+ return NULL;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a name. *
+* *
+\**********************************************************************/
+void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
+{
+ ToRelease<IMetaDataImport> pImport = MDImportForModule (ModulePtr);
+ if (pImport == 0)
+ return;
+
+ static WCHAR wszName[MAX_CLASSNAME_LENGTH];
+ size_t n;
+ size_t length = strlen (name);
+ for (n = 0; n <= length; n ++)
+ wszName[n] = name[n];
+
+ // First enumerate methods. We're taking advantage of the DAC's
+ // CLRDataModule::EnumMethodDefinitionByName which can parse
+ // method names (whether in nested classes, or explicit interface
+ // method implementations).
+ ToRelease<IXCLRDataModule> ModuleDefinition;
+ if (g_sos->GetModule(ModulePtr, &ModuleDefinition) == S_OK)
+ {
+ CLRDATA_ENUM h;
+ if (ModuleDefinition->StartEnumMethodDefinitionsByName(wszName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ BOOL fStatus = FALSE;
+ while (ModuleDefinition->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ if (fStatus)
+ ExtOut("-----------------------\n");
+
+ mdTypeDef token;
+ if (pMeth->GetTokenAndScope(&token, NULL) == S_OK)
+ {
+ GetInfoFromModule(ModulePtr, token);
+ fStatus = TRUE;
+ }
+ pMeth->Release();
+ }
+ ModuleDefinition->EndEnumMethodDefinitionsByName(h);
+ if (fStatus)
+ return;
+ }
+ }
+
+ // Now look for types, type members and fields
+ mdTypeDef cl;
+ mdToken tkEnclose = mdTokenNil;
+ WCHAR *pName;
+ WCHAR *pHead = wszName;
+ while ( ((pName = _wcschr (pHead,L'+')) != NULL) ||
+ ((pName = _wcschr (pHead,L'/')) != NULL)) {
+ pName[0] = L'\0';
+ if (FAILED(pImport->FindTypeDefByName(pHead,tkEnclose,&tkEnclose)))
+ return;
+ pHead = pName+1;
+ }
+
+ pName = pHead;
+
+ // @todo: Handle Nested classes correctly.
+ if (SUCCEEDED (pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
+ {
+ GetInfoFromModule(ModulePtr, cl);
+ return;
+ }
+
+ // See if it is a method
+ WCHAR *pwzMethod;
+ if ((pwzMethod = _wcsrchr(pName, L'.')) == NULL)
+ return;
+
+ if (pwzMethod[-1] == L'.')
+ pwzMethod --;
+ pwzMethod[0] = L'\0';
+ pwzMethod ++;
+
+ // @todo: Handle Nested classes correctly.
+ if (SUCCEEDED(pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
+ {
+ mdMethodDef token;
+ ULONG cTokens;
+ HCORENUM henum = NULL;
+
+ // is Member?
+ henum = NULL;
+ if (SUCCEEDED (pImport->EnumMembersWithName (&henum, cl, pwzMethod,
+ &token, 1, &cTokens))
+ && cTokens == 1)
+ {
+ ExtOut("Member (mdToken token) of\n");
+ GetInfoFromModule(ModulePtr, cl);
+ return;
+ }
+
+ // is Field?
+ henum = NULL;
+ if (SUCCEEDED (pImport->EnumFieldsWithName (&henum, cl, pwzMethod,
+ &token, 1, &cTokens))
+ && cTokens == 1)
+ {
+ ExtOut("Field (mdToken token) of\n");
+ GetInfoFromModule(ModulePtr, cl);
+ return;
+ }
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a token. *
+* *
+\**********************************************************************/
+DWORD_PTR GetMethodDescFromModule(DWORD_PTR ModuleAddr, ULONG token)
+{
+ if (TypeFromToken(token) != mdtMethodDef)
+ return NULL;
+
+ CLRDATA_ADDRESS md = 0;
+ if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)))
+ {
+ return NULL;
+ }
+ else if (0 == md)
+ {
+ // a NULL ReturnValue means the method desc is not loaded yet
+ return MD_NOT_YET_LOADED;
+ }
+ else if ( !IsMethodDesc((DWORD_PTR)md))
+ {
+ return NULL;
+ }
+
+ return (DWORD_PTR)md;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the MethodDefinitions given a name. *
+* *
+\**********************************************************************/
+HRESULT GetMethodDefinitionsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, IXCLRDataMethodDefinition **ppOut, int numMethods, int *numMethodsNeeded)
+{
+ if (name == NULL)
+ return E_FAIL;
+
+ size_t n;
+ size_t length = strlen (name);
+ for (n = 0; n <= length; n ++)
+ g_mdName[n] = name[n];
+
+ CLRDATA_ENUM h;
+ int methodCount = 0;
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ methodCount++;
+ pMeth->Release();
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+
+ if(numMethodsNeeded != NULL)
+ *numMethodsNeeded = methodCount;
+ if(ppOut == NULL)
+ return S_OK;
+ if(numMethods > methodCount)
+ numMethods = methodCount;
+
+ if (methodCount > 0)
+ {
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ for (int i = 0; i < numMethods && mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK; i++)
+ {
+ ppOut[i] = pMeth;
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+ }
+
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a name. *
+* *
+\**********************************************************************/
+HRESULT GetMethodDescsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, DWORD_PTR **pOut,int *numMethods)
+{
+ if (name == NULL || pOut == NULL || numMethods == NULL)
+ return E_FAIL;
+
+ *pOut = NULL;
+ *numMethods = 0;
+
+ size_t n;
+ size_t length = strlen (name);
+ for (n = 0; n <= length; n ++)
+ g_mdName[n] = name[n];
+
+ CLRDATA_ENUM h;
+ int methodCount = 0;
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ methodCount++;
+ pMeth->Release();
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+
+ if (methodCount > 0)
+ {
+ *pOut = new TADDR[methodCount];
+ if (*pOut==NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ *numMethods = methodCount;
+
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ int i = 0;
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ mdTypeDef token;
+ if (pMeth->GetTokenAndScope(&token, NULL) != S_OK)
+ (*pOut)[i] = NULL;
+ (*pOut)[i] = GetMethodDescFromModule(ModulePtr, token);
+ if ((*pOut)[i] == NULL)
+ {
+ *numMethods = 0;
+ return E_FAIL;
+ }
+ i++;
+ pMeth->Release();
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+ }
+
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a token. *
+* *
+\**********************************************************************/
+void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret)
+{
+ switch (TypeFromToken(token))
+ {
+ case mdtMethodDef:
+ break;
+ case mdtTypeDef:
+ break;
+ case mdtTypeRef:
+ break;
+ case mdtFieldDef:
+ break;
+ default:
+ ExtOut("This token type is not supported\n");
+ return;
+ break;
+ }
+
+ CLRDATA_ADDRESS md = 0;
+ if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)) || !IsValidToken (ModuleAddr, token))
+ {
+ ExtOut("<invalid module token>\n");
+ return;
+ }
+
+ if (ret != NULL)
+ {
+ *ret = (DWORD_PTR)md;
+ return;
+ }
+
+ ExtOut("Token: %p\n", SOS_PTR(token));
+
+ switch (TypeFromToken(token))
+ {
+ case mdtFieldDef:
+ {
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ ExtOut("Field name: %S\n", g_mdName);
+ break;
+ }
+ case mdtMethodDef:
+ {
+ if (md)
+ {
+ DMLOut("MethodDesc: %s\n", DMLMethodDesc(md));
+
+ // Easiest to get full parameterized method name from ..::GetMethodName
+ if (g_sos->GetMethodDescName(md, mdNameLen, g_mdName, NULL) != S_OK)
+ {
+ // Fall back to just method name without parameters..
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ }
+ }
+ else
+ {
+ ExtOut("MethodDesc: <not loaded yet>\n");
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ }
+
+ ExtOut("Name: %S\n", g_mdName);
+ // Nice to have a little more data
+ if (md)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, md) == S_OK)
+ {
+ if (MethodDescData.bHasNativeCode)
+ {
+ DMLOut("JITTED Code Address: %s\n", DMLIP(MethodDescData.NativeCodeAddr));
+ }
+ else
+ {
+#ifndef FEATURE_PAL
+ if (IsDMLEnabled())
+ DMLOut("Not JITTED yet. Use <exec cmd=\"!bpmd -md %p\">!bpmd -md %p</exec> to break on run.\n",
+ SOS_PTR(md), SOS_PTR(md));
+ else
+ ExtOut("Not JITTED yet. Use !bpmd -md %p to break on run.\n", SOS_PTR(md));
+#else
+ ExtOut("Not JITTED yet. Use 'bpmd -md %p' to break on run.\n", SOS_PTR(md));
+#endif
+ }
+ }
+ else
+ {
+ ExtOut ("<Error getting MethodDesc information>\n");
+ }
+ }
+ else
+ {
+ ExtOut("Not JITTED yet.\n");
+ }
+ break;
+ }
+ case mdtTypeDef:
+ case mdtTypeRef:
+ {
+ if (md)
+ {
+ DMLOut("MethodTable: %s\n", DMLMethodTable(md));
+ DacpMethodTableData mtabledata;
+ if (mtabledata.Request(g_sos, md) == S_OK)
+ {
+ DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
+ }
+ else
+ {
+ ExtOut("EEClass: <error getting EEClass>\n");
+ }
+ }
+ else
+ {
+ ExtOut("MethodTable: <not loaded yet>\n");
+ ExtOut("EEClass: <not loaded yet>\n");
+ }
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ ExtOut("Name: %S\n", g_mdName);
+ break;
+ }
+ default:
+ break;
+ }
+ return;
+}
+
+BOOL IsMTForFreeObj(DWORD_PTR pMT)
+{
+ return (pMT == g_special_usefulGlobals.FreeMethodTable);
+}
+
+const char *EHTypeName(EHClauseType et)
+{
+ if (et == EHFault)
+ return "FAULT";
+ else if (et == EHFinally)
+ return "FINALLY";
+ else if (et == EHFilter)
+ return "FILTER";
+ else if (et == EHTyped)
+ return "TYPED";
+ else
+ return "UNKNOWN";
+}
+
+void DumpRejitData(DacpReJitData * pReJitData)
+{
+ ExtOut(" ReJITID %p: ", SOS_PTR(pReJitData->rejitID));
+ DMLOut("CodeAddr = %s", DMLIP(pReJitData->NativeCodeAddr));
+
+ LPCSTR szFlags;
+ switch (pReJitData->flags)
+ {
+ default:
+ case DacpReJitData::kUnknown:
+ szFlags = "";
+ break;
+
+ case DacpReJitData::kRequested:
+ szFlags = " (READY to jit on next call)";
+ break;
+
+ case DacpReJitData::kActive:
+ szFlags = " (CURRENT)";
+ break;
+
+ case DacpReJitData::kReverted:
+ szFlags = " (reverted)";
+ break;
+ }
+ ExtOut("%s\n", szFlags);
+}
+
+// For !ip2md requests, this function helps us ensure that rejitted version corresponding
+// to the specified IP always gets dumped. It may have already been dumped if it was the
+// current rejit version (which is always dumped) or one of the reverted versions that we
+// happened to dump before we clipped their number down to kcRejitDataRevertedMax.
+BOOL ShouldDumpRejitDataRequested(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
+{
+ if (pMethodDescData->rejitDataRequested.rejitID == 0)
+ return FALSE;
+
+ if (pMethodDescData->rejitDataRequested.rejitID == pMethodDescData->rejitDataCurrent.rejitID)
+ return FALSE;
+
+ for (ULONG i=0; i < cRevertedRejitData; i++)
+ {
+ if (pMethodDescData->rejitDataRequested.rejitID == pRevertedRejitData[i].rejitID)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void DumpAllRejitDataIfNecessary(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
+{
+ // If there's no rejit info to output, then skip
+ if ((pMethodDescData->rejitDataCurrent.rejitID == 0) &&
+ (pMethodDescData->rejitDataRequested.rejitID == 0) &&
+ (cRevertedRejitData == 0))
+ {
+ return;
+ }
+ ExtOut("ReJITed versions:\n");
+
+ // Dump CURRENT rejit info
+ DumpRejitData(&pMethodDescData->rejitDataCurrent);
+
+ // Dump reverted rejit infos
+ for (ULONG i=0; i < cRevertedRejitData; i++)
+ {
+ DumpRejitData(&pRevertedRejitData[i]);
+ }
+
+ // For !ip2md, ensure we dump the rejit version corresponding to the specified IP
+ // (if not already dumped)
+ if (ShouldDumpRejitDataRequested(pMethodDescData, pRevertedRejitData, cRevertedRejitData))
+ DumpRejitData(&pMethodDescData->rejitDataRequested);
+
+ // If we maxed out the reverted versions we dumped, let user know there may be more
+ if (cRevertedRejitData == kcMaxRevertedRejitData)
+ ExtOut(" (... possibly more reverted versions ...)\n");
+}
+
+void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData, BOOL fStackTraceFormat)
+{
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+ BOOL bFailed = FALSE;
+ if (g_sos->GetMethodDescName(pMethodDescData->MethodDescPtr, 1024, wszNameBuffer, NULL) != S_OK)
+ {
+ wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
+ bFailed = TRUE;
+ }
+
+ if (!fStackTraceFormat)
+ {
+ ExtOut("Method Name: %S\n", wszNameBuffer);
+
+ DacpMethodTableData mtdata;
+ if (SUCCEEDED(mtdata.Request(g_sos, pMethodDescData->MethodTablePtr)))
+ {
+ DMLOut("Class: %s\n", DMLClass(mtdata.Class));
+ }
+
+ DMLOut("MethodTable: %s\n", DMLMethodTable(pMethodDescData->MethodTablePtr));
+ ExtOut("mdToken: %p\n", SOS_PTR(pMethodDescData->MDToken));
+ DMLOut("Module: %s\n", DMLModule(pMethodDescData->ModulePtr));
+ ExtOut("IsJitted: %s\n", pMethodDescData->bHasNativeCode ? "yes" : "no");
+ DMLOut("CodeAddr: %s\n", DMLIP(pMethodDescData->NativeCodeAddr));
+
+ DacpMethodDescTransparencyData transparency;
+ if (SUCCEEDED(transparency.Request(g_sos, pMethodDescData->MethodDescPtr)))
+ {
+ ExtOut("Transparency: %s\n", GetTransparency(transparency));
+ }
+
+ DumpAllRejitDataIfNecessary(pMethodDescData, pRevertedRejitData, cRevertedRejitData);
+ }
+ else
+ {
+ if (!bFailed)
+ {
+ ExtOut("%S", wszNameBuffer);
+ }
+ else
+ {
+ // Only clutter the display with module/token for cases where we
+ // can't get the MethodDesc name for some reason.
+ DMLOut("Unknown MethodDesc (Module %s, mdToken %08x)",
+ DMLModule(pMethodDescData->ModulePtr),
+ pMethodDescData->MDToken);
+ }
+ }
+}
+
+void DumpMDInfo(DWORD_PTR dwMethodDescAddr, CLRDATA_ADDRESS dwRequestedIP /* = 0 */, BOOL fStackTraceFormat /* = FALSE */)
+{
+ DacpMethodDescData MethodDescData;
+ DacpReJitData revertedRejitData[kcMaxRevertedRejitData];
+ ULONG cNeededRevertedRejitData;
+ if (g_sos->GetMethodDescData(
+ TO_CDADDR(dwMethodDescAddr),
+ dwRequestedIP,
+ &MethodDescData,
+ _countof(revertedRejitData),
+ revertedRejitData,
+ &cNeededRevertedRejitData) != S_OK)
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwMethodDescAddr));
+ return;
+ }
+
+ DumpMDInfoFromMethodDescData(&MethodDescData, revertedRejitData, cNeededRevertedRejitData, fStackTraceFormat);
+}
+
+void GetDomainList (DWORD_PTR *&domainList, int &numDomain)
+{
+ DacpAppDomainStoreData adsData;
+
+ numDomain = 0;
+
+ if (adsData.Request(g_sos)!=S_OK)
+ {
+ return;
+ }
+
+ // Do prefast integer checks before the malloc.
+ size_t AllocSize;
+ LONG DomainAllocCount;
+ if (!ClrSafeInt<LONG>::addition(adsData.DomainCount, 2, DomainAllocCount) ||
+ !ClrSafeInt<size_t>::multiply(DomainAllocCount, sizeof(PVOID), AllocSize) ||
+ (domainList = new DWORD_PTR[DomainAllocCount]) == NULL)
+ {
+ return;
+ }
+
+ domainList[numDomain++] = (DWORD_PTR) adsData.systemDomain;
+ domainList[numDomain++] = (DWORD_PTR) adsData.sharedDomain;
+
+ CLRDATA_ADDRESS *pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
+ if (pArray==NULL)
+ {
+ return;
+ }
+
+ if (g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL)!=S_OK)
+ {
+ delete [] pArray;
+ return;
+ }
+
+ for (int n=0;n<adsData.DomainCount;n++)
+ {
+ if (IsInterrupt())
+ break;
+ domainList[numDomain++] = (DWORD_PTR) pArray[n];
+ }
+
+ delete [] pArray;
+}
+
+
+HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread)
+{
+ _ASSERTE(threadList != NULL);
+ _ASSERTE(numThread != NULL);
+
+ if (threadList == NULL || numThread == NULL)
+ {
+ return E_FAIL;
+ }
+
+ *numThread = 0;
+
+ DacpThreadStoreData ThreadStore;
+ if ( ThreadStore.Request(g_sos) != S_OK)
+ {
+ ExtOut("Failed to request threads from the thread store.");
+ return E_FAIL;
+ }
+
+ *threadList = new DWORD_PTR[ThreadStore.threadCount];
+ if (*threadList == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread != NULL)
+ {
+ if (IsInterrupt())
+ return S_FALSE;
+
+ DacpThreadData Thread;
+ if (Thread.Request(g_sos, CurThread) != S_OK)
+ {
+ ExtOut("Failed to request Thread at %p\n", SOS_PTR(CurThread));
+ return E_FAIL;
+ }
+
+ (*threadList)[(*numThread)++] = (DWORD_PTR)CurThread;
+ CurThread = Thread.nextThread;
+ }
+
+ return S_OK;
+}
+
+CLRDATA_ADDRESS GetCurrentManagedThread ()
+{
+ DacpThreadStoreData ThreadStore;
+ ThreadStore.Request(g_sos);
+
+ ULONG Tid;
+ g_ExtSystem->GetCurrentThreadSystemId(&Tid);
+
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ DacpThreadData Thread;
+ if (Thread.Request(g_sos, CurThread) != S_OK)
+ {
+ return NULL;
+ }
+
+ if (Thread.osThreadId == Tid)
+ {
+ return CurThread;
+ }
+
+ CurThread = Thread.nextThread;
+ }
+ return NULL;
+}
+
+
+void ReloadSymbolWithLineInfo()
+{
+#ifndef FEATURE_PAL
+ static BOOL bLoadSymbol = FALSE;
+ if (!bLoadSymbol)
+ {
+ ULONG Options;
+ g_ExtSymbols->GetSymbolOptions(&Options);
+ if (!(Options & SYMOPT_LOAD_LINES))
+ {
+ g_ExtSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES);
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MSCOREE_SHIM_A, 0, NULL, NULL)))
+ g_ExtSymbols->Reload("/f " MSCOREE_SHIM_A);
+
+ EEFLAVOR flavor = GetEEFlavor();
+ if (flavor == MSCORWKS)
+ g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
+ }
+
+ // reload mscoree.pdb and clrjit.pdb to get line info
+ bLoadSymbol = TRUE;
+ }
+#endif
+}
+
+// Return 1 if the function is our stub
+// Return MethodDesc if the function is managed
+// Otherwise return 0
+size_t FunctionType (size_t EIP)
+{
+ ULONG64 base = 0;
+ ULONG ulLoaded, ulUnloaded, ulIndex;
+
+ // Get the number of loaded and unloaded modules
+ if (FAILED(g_ExtSymbols->GetNumberModules(&ulLoaded, &ulUnloaded)))
+ return 0;
+
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(TO_CDADDR(EIP), 0, &ulIndex, &base)) && base != 0)
+ {
+ if (ulIndex < ulLoaded)
+ {
+ IMAGE_DOS_HEADER DosHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
+ return 0;
+ IMAGE_NT_HEADERS Header;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
+ return 0;
+ // If there is no COMHeader, this can not be managed code.
+ if (Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress == 0)
+ return 0;
+
+ IMAGE_COR20_HEADER ComPlusHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base + Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress),
+ &ComPlusHeader, sizeof(ComPlusHeader), NULL) != S_OK)
+ return 0;
+
+ // If there is no Precompiled image info, it can not be prejit code
+ if (ComPlusHeader.ManagedNativeHeader.VirtualAddress == 0) {
+ return 0;
+ }
+ }
+ }
+
+ CLRDATA_ADDRESS dwStartAddr = TO_CDADDR(EIP);
+ CLRDATA_ADDRESS pMD;
+ if (g_sos->GetMethodDescPtrFromIP(dwStartAddr, &pMD) != S_OK)
+ {
+ return 1;
+ }
+
+ return (size_t) pMD;
+}
+
+#ifndef FEATURE_PAL
+
+//
+// Gets version info for the CLR in the debuggee process.
+//
+BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo)
+{
+ _ASSERTE(g_ExtSymbols2);
+ _ASSERTE(pFileInfo);
+ // Grab the version info directly from the module.
+ return g_ExtSymbols2->GetModuleVersionInformation(DEBUG_ANY_ID,
+ moduleInfo[GetEEFlavor()].baseAddr,
+ "\\", pFileInfo, sizeof(VS_FIXEDFILEINFO), NULL) == S_OK;
+}
+
+extern HMODULE g_hInstance;
+BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo)
+{
+ _ASSERTE(pFileInfo);
+
+ WCHAR wszFullPath[MAX_LONGPATH];
+ DWORD cchFullPath = GetModuleFileNameW(g_hInstance, wszFullPath, _countof(wszFullPath));
+
+ DWORD dwHandle = 0;
+ DWORD infoSize = GetFileVersionInfoSizeW(wszFullPath, &dwHandle);
+ if (infoSize)
+ {
+ ArrayHolder<BYTE> pVersionInfo = new BYTE[infoSize];
+ if (pVersionInfo)
+ {
+ if (GetFileVersionInfoW(wszFullPath, NULL, infoSize, pVersionInfo))
+ {
+ VS_FIXEDFILEINFO *pTmpFileInfo = NULL;
+ UINT uLen = 0;
+ if (VerQueryValue(pVersionInfo, "\\", (LPVOID *) &pTmpFileInfo, &uLen))
+ {
+ *pFileInfo = *pTmpFileInfo; // Copy the info
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+#endif // !FEATURE_PAL
+
+size_t ObjectSize(DWORD_PTR obj,BOOL fIsLargeObject)
+{
+ DWORD_PTR dwMT;
+ MOVE(dwMT, obj);
+ return ObjectSize(obj, dwMT, FALSE, fIsLargeObject);
+}
+
+size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject)
+{
+ BOOL bContainsPointers;
+ size_t size = 0;
+ if (!GetSizeEfficient(obj, mt, fIsLargeObject, size, bContainsPointers))
+ {
+ return 0;
+ }
+ return size;
+}
+
+// This takes an array of values and sets every non-printable character
+// to be a period.
+void Flatten(__out_ecount(len) char *data, unsigned int len)
+{
+ for (unsigned int i = 0; i < len; ++i)
+ if (data[i] < 32 || data[i] > 126)
+ data[i] = '.';
+ data[len] = 0;
+}
+
+void CharArrayContent(TADDR pos, ULONG num, bool widechar)
+{
+ if (!pos || num <= 0)
+ return;
+
+ if (widechar)
+ {
+ ArrayHolder<WCHAR> data = new WCHAR[num+1];
+ if (!data)
+ {
+ ReportOOM();
+ return;
+ }
+
+ ULONG readLen = 0;
+ if (!SafeReadMemory(pos, data, num<<1, &readLen))
+ return;
+
+ Flatten(data.GetPtr(), readLen >> 1);
+ ExtOut("%S", data.GetPtr());
+ }
+ else
+ {
+ ArrayHolder<char> data = new char[num+1];
+ if (!data)
+ {
+ ReportOOM();
+ return;
+ }
+
+ ULONG readLen = 0;
+ if (!SafeReadMemory(pos, data, num, &readLen))
+ return;
+
+ _ASSERTE(readLen <= num);
+ Flatten(data, readLen);
+
+ ExtOut("%s", data.GetPtr());
+ }
+}
+
+void StringObjectContent(size_t obj, BOOL fLiteral, const int length)
+{
+ DacpObjectData objData;
+ if (objData.Request(g_sos, TO_CDADDR(obj))!=S_OK)
+ {
+ ExtOut("<Invalid Object>");
+ return;
+ }
+
+ strobjInfo stInfo;
+
+ if (MOVE(stInfo,obj) != S_OK)
+ {
+ ExtOut ("Error getting string data\n");
+ return;
+ }
+
+ if (objData.Size > 0x200000 ||
+ stInfo.m_StringLength > 0x200000)
+ {
+ ExtOut ("<String is invalid or too large to print>\n");
+ return;
+ }
+
+ ArrayHolder<WCHAR> pwszBuf = new WCHAR[stInfo.m_StringLength+1];
+ if (pwszBuf == NULL)
+ {
+ return;
+ }
+
+ DWORD_PTR dwAddr = (DWORD_PTR)pwszBuf.GetPtr();
+ if (g_sos->GetObjectStringData(TO_CDADDR(obj), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
+ {
+ ExtOut("Error getting string data\n");
+ return;
+ }
+
+ if (!fLiteral)
+ {
+ pwszBuf[stInfo.m_StringLength] = L'\0';
+ ExtOut ("%S", pwszBuf.GetPtr());
+ }
+ else
+ {
+ ULONG32 count = stInfo.m_StringLength;
+ WCHAR buffer[256];
+ WCHAR out[512];
+ while (count)
+ {
+ DWORD toRead = 255;
+ if (count < toRead)
+ toRead = count;
+
+ ULONG bytesRead;
+ wcsncpy_s(buffer,_countof(buffer),(LPWSTR) dwAddr, toRead);
+ bytesRead = toRead*sizeof(WCHAR);
+ DWORD wcharsRead = bytesRead/2;
+ buffer[wcharsRead] = L'\0';
+
+ ULONG j,k=0;
+ for (j = 0; j < wcharsRead; j ++)
+ {
+ if (_iswprint (buffer[j])) {
+ out[k] = buffer[j];
+ k ++;
+ }
+ else
+ {
+ out[k++] = L'\\';
+ switch (buffer[j]) {
+ case L'\n':
+ out[k++] = L'n';
+ break;
+ case L'\0':
+ out[k++] = L'0';
+ break;
+ case L'\t':
+ out[k++] = L't';
+ break;
+ case L'\v':
+ out[k++] = L'v';
+ break;
+ case L'\b':
+ out[k++] = L'b';
+ break;
+ case L'\r':
+ out[k++] = L'r';
+ break;
+ case L'\f':
+ out[k++] = L'f';
+ break;
+ case L'\a':
+ out[k++] = L'a';
+ break;
+ case L'\\':
+ break;
+ case L'\?':
+ out[k++] = L'?';
+ break;
+ default:
+ out[k++] = L'?';
+ break;
+ }
+ }
+ }
+
+ out[k] = L'\0';
+ ExtOut ("%S", out);
+
+ count -= wcharsRead;
+ dwAddr += bytesRead;
+ }
+ }
+}
+
+#ifdef _TARGET_WIN64_
+
+#include <limits.h>
+
+__int64 str64hex(const char *ptr)
+{
+ __int64 value = 0;
+ unsigned char nCount = 0;
+
+ if(ptr==NULL)
+ return 0;
+
+ // Ignore leading 0x if present
+ if (*ptr=='0' && toupper(*(ptr+1))=='X') {
+ ptr = ptr + 2;
+ }
+
+ while (1) {
+
+ char digit;
+
+ if (isdigit(*ptr)) {
+ digit = *ptr - '0';
+ } else if (isalpha(*ptr)) {
+ digit = (((char)toupper(*ptr)) - 'A') + 10;
+ if (digit >= 16) {
+ break; // terminate
+ }
+ } else {
+ break;
+ }
+
+ if (nCount>15) {
+ return _UI64_MAX; // would be an overflow
+ }
+
+ value = value << 4;
+ value |= digit;
+
+ ptr++;
+ nCount++;
+ }
+
+ return value;
+}
+
+#endif // _TARGET_WIN64_
+
+BOOL GetValueForCMD (const char *ptr, const char *end, ARGTYPE type, size_t *value)
+{
+ if (type == COSTRING) {
+ // Allocate memory for the length of the string. Whitespace terminates
+ // User must free the string data.
+ char *pszValue = NULL;
+ size_t dwSize = (end - ptr);
+ pszValue= new char[dwSize+1];
+ if (pszValue == NULL)
+ {
+ return FALSE;
+ }
+ strncpy_s(pszValue,dwSize+1,ptr,dwSize); // _TRUNCATE
+ *value = (size_t) pszValue;
+ } else {
+ char *last;
+ if (type == COHEX) {
+#ifdef _TARGET_WIN64_
+ *value = str64hex(ptr);
+#else
+ *value = strtoul(ptr,&last,16);
+#endif
+ }
+ else {
+#ifdef _TARGET_WIN64_
+ *value = _atoi64(ptr);
+#else
+ *value = strtoul(ptr,&last,10);
+#endif
+ }
+
+#ifdef _TARGET_WIN64_
+ last = (char *) ptr;
+ // Ignore leading 0x if present
+ if (*last=='0' && toupper(*(last+1))=='X') {
+ last = last + 2;
+ }
+
+ while (isdigit(*last) || (toupper(*last)>='A' && toupper(*last)<='F')) {
+ last++;
+ }
+#endif
+
+ if (last != end) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void SetValueForCMD (void *vptr, ARGTYPE type, size_t value)
+{
+ switch (type) {
+ case COBOOL:
+ *(BOOL*)vptr = (BOOL) value;
+ break;
+ case COSIZE_T:
+ case COSTRING:
+ case COHEX:
+ *(SIZE_T*)vptr = value;
+ break;
+ }
+}
+
+BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption,
+ CMDValue *arg, size_t maxArg, size_t *nArg)
+{
+ const char *end;
+ const char *ptr = string;
+ BOOL endofOption = FALSE;
+
+ for (size_t n = 0; n < nOption; n ++)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ option[n].hasSeen = FALSE;
+ }
+
+ if (nArg) {
+ *nArg = 0;
+ }
+
+ while (ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ // skip any space
+ if (isspace (ptr[0])) {
+ while (isspace (ptr[0]))
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ ptr ++;
+ }
+
+ continue;
+ }
+
+ end = ptr;
+
+ // Arguments can be quoted with ". We'll remove the quotes and
+ // allow spaces to exist in the string.
+ BOOL bQuotedArg = FALSE;
+ if (ptr[0] == '\'' && ptr[1] != '-')
+ {
+ bQuotedArg = TRUE;
+
+ // skip quote
+ ptr++;
+ end++;
+
+ while (end[0] != '\'' && end[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ end ++;
+ }
+ if (end[0] != '\'')
+ {
+ // Error, th ere was a start quote but no end quote
+ ExtOut ("Missing quote in %s\n", ptr);
+ return FALSE;
+ }
+ }
+ else // whitespace terminates
+ {
+ while (!isspace(end[0]) && end[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ end ++;
+ }
+ }
+
+#ifndef FEATURE_PAL
+ if (ptr[0] != '-' && ptr[0] != '/') {
+#else
+ if (ptr[0] != '-') {
+#endif
+ if (maxArg == 0) {
+ ExtOut ("Incorrect argument: %s\n", ptr);
+ return FALSE;
+ }
+ endofOption = TRUE;
+ if (*nArg >= maxArg) {
+ ExtOut ("Incorrect argument: %s\n", ptr);
+ return FALSE;
+ }
+
+ size_t value;
+ if (!GetValueForCMD (ptr,end,arg[*nArg].type,&value)) {
+
+ char oldChar = *end;
+ *(char *)end = '\0';
+ value = (size_t)GetExpression (ptr);
+ *(char *)end = oldChar;
+
+ /*
+
+ It is silly to do this, what if 0 is a valid expression for
+ the command?
+
+ if (value == 0) {
+ ExtOut ("Invalid argument: %s\n", ptr);
+ return FALSE;
+ }
+ */
+ }
+
+ SetValueForCMD (arg[*nArg].vptr, arg[*nArg].type, value);
+
+ (*nArg) ++;
+ }
+ else if (endofOption) {
+ ExtOut ("Wrong option: %s\n", ptr);
+ return FALSE;
+ }
+ else {
+ char buffer[80];
+ if (end-ptr > 79) {
+ ExtOut ("Invalid option %s\n", ptr);
+ return FALSE;
+ }
+ strncpy_s (buffer,_countof(buffer), ptr, end-ptr);
+
+ size_t n;
+ for (n = 0; n < nOption; n ++)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ if (_stricmp (buffer, option[n].name) == 0) {
+ if (option[n].hasSeen) {
+ ExtOut ("Invalid option: option specified multiple times: %s\n", buffer);
+ return FALSE;
+ }
+ option[n].hasSeen = TRUE;
+ if (option[n].hasValue) {
+ // skip any space
+ ptr = end;
+ if (isspace (ptr[0])) {
+ while (isspace (ptr[0]))
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ ptr ++;
+ }
+ }
+ if (ptr[0] == '\0') {
+ ExtOut ("Missing value for option %s\n", buffer);
+ return FALSE;
+ }
+ end = ptr;
+ while (!isspace(end[0]) && end[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ end ++;
+ }
+
+ size_t value;
+ if (!GetValueForCMD (ptr,end,option[n].type,&value)) {
+
+ char oldChar = *end;
+ *(char *)end = '\0';
+ value = (size_t)GetExpression (ptr);
+ *(char *)end = oldChar;
+ }
+
+ SetValueForCMD (option[n].vptr,option[n].type,value);
+ }
+ else {
+ SetValueForCMD (option[n].vptr,option[n].type,TRUE);
+ }
+ break;
+ }
+ }
+ if (n == nOption) {
+ ExtOut ("Unknown option: %s\n", buffer);
+ return FALSE;
+ }
+ }
+
+ ptr = end;
+ if (bQuotedArg)
+ {
+ ptr++;
+ }
+ }
+ return TRUE;
+}
+
+ReadVirtualCache g_special_rvCacheSpace;
+ReadVirtualCache *rvCache = &g_special_rvCacheSpace;
+
+void ResetGlobals(void)
+{
+ // There are some globals used in SOS that exist for efficiency in one command,
+ // but should be reset because the next execution of an SOS command could be on
+ // another managed process. Reset them to a default state here, as this command
+ // is called on every SOS entry point.
+ g_sos->GetUsefulGlobals(&g_special_usefulGlobals);
+ g_special_mtCache.Clear();
+ g_special_rvCacheSpace.Clear();
+ Output::ResetIndent();
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Loads private DAC interface, and points g_clrData to it.
+//
+// Return Value:
+// HRESULT indicating success or failure
+//
+HRESULT LoadClrDebugDll(void)
+{
+ HRESULT hr = S_OK;
+#ifdef FEATURE_PAL
+ static IXCLRDataProcess* s_clrDataProcess = NULL;
+ if (s_clrDataProcess == NULL)
+ {
+ int err = PAL_InitializeDLL();
+ if(err != 0)
+ {
+ return CORDBG_E_UNSUPPORTED;
+ }
+ char dacModulePath[MAX_LONGPATH];
+ strcpy_s(dacModulePath, _countof(dacModulePath), g_ExtServices->GetCoreClrDirectory());
+ strcat_s(dacModulePath, _countof(dacModulePath), MAKEDLLNAME_A("mscordaccore"));
+
+ HMODULE hdac = LoadLibraryA(dacModulePath);
+ if (hdac == NULL)
+ {
+ return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
+ }
+ PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
+ if (pfnCLRDataCreateInstance == NULL)
+ {
+ FreeLibrary(hdac);
+ return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
+ }
+ ICLRDataTarget *target = new DataTarget();
+ hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&s_clrDataProcess);
+ if (FAILED(hr))
+ {
+ s_clrDataProcess = NULL;
+ return hr;
+ }
+ ULONG32 flags = 0;
+ s_clrDataProcess->GetOtherNotificationFlags(&flags);
+ flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION);
+ s_clrDataProcess->SetOtherNotificationFlags(flags);
+ }
+ g_clrData = s_clrDataProcess;
+ g_clrData->AddRef();
+ g_clrData->Flush();
+#else
+ WDBGEXTS_CLR_DATA_INTERFACE Query;
+
+ Query.Iid = &__uuidof(IXCLRDataProcess);
+ if (!Ioctl(IG_GET_CLR_DATA_INTERFACE, &Query, sizeof(Query)))
+ {
+ return E_FAIL;
+ }
+
+ g_clrData = (IXCLRDataProcess*)Query.Iface;
+#endif
+ hr = g_clrData->QueryInterface(__uuidof(ISOSDacInterface), (void**)&g_sos);
+ if (FAILED(hr))
+ {
+ g_sos = NULL;
+ return hr;
+ }
+ return S_OK;
+}
+
+#ifndef FEATURE_PAL
+
+// This structure carries some input/output data to the FindFileInPathCallback below
+typedef struct _FindFileCallbackData
+{
+ DWORD timestamp;
+ DWORD filesize;
+ HMODULE hModule;
+} FindFileCallbackData;
+
+
+// A callback used by SymFindFileInPath - called once for each file that matches
+// the initial search criteria and allows the user to do arbitrary processing
+// This implementation checks that filesize and timestamp are correct, then
+// saves the loaded module handle
+// Parameters
+// filename - the full path the file which was found
+// context - a user specified pointer to arbitrary data, in this case a FindFileCallbackData
+// Return Value
+// TRUE if the search should continue (the file is no good)
+// FALSE if the search should stop (the file is good)
+BOOL
+FindFileInPathCallback(
+ ___in PCWSTR filename,
+ ___in PVOID context
+ )
+{
+ HRESULT hr;
+ FindFileCallbackData* pCallbackData;
+ pCallbackData = (FindFileCallbackData*)context;
+ if (!pCallbackData)
+ return TRUE;
+
+ pCallbackData->hModule = LoadLibraryExW(
+ filename,
+ NULL, // __reserved
+ LOAD_WITH_ALTERED_SEARCH_PATH); // Ensure we check the dir in wszFullPath first
+ if (pCallbackData->hModule == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", filename, hr);
+ return TRUE;
+ }
+
+ // Did we load the right one?
+ MODULEINFO modInfo = {0};
+ if (!GetModuleInformation(
+ GetCurrentProcess(),
+ pCallbackData->hModule,
+ &modInfo,
+ sizeof(modInfo)))
+ {
+ ExtOut("Failed to read module information for '%S'. HRESULT = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError()));
+ FreeLibrary(pCallbackData->hModule);
+ return TRUE;
+ }
+
+ IMAGE_DOS_HEADER * pDOSHeader = (IMAGE_DOS_HEADER *) modInfo.lpBaseOfDll;
+ IMAGE_NT_HEADERS * pNTHeaders = (IMAGE_NT_HEADERS *) (((LPBYTE) modInfo.lpBaseOfDll) + pDOSHeader->e_lfanew);
+ DWORD dwSizeActual = pNTHeaders->OptionalHeader.SizeOfImage;
+ DWORD dwTimeStampActual = pNTHeaders->FileHeader.TimeDateStamp;
+ if ((dwSizeActual != pCallbackData->filesize) || (dwTimeStampActual != pCallbackData->timestamp))
+ {
+ ExtOut("Found '%S', but it does not match the CLR being debugged.\n", filename);
+ ExtOut("Size: Expected '0x%x', Actual '0x%x'\n", pCallbackData->filesize, dwSizeActual);
+ ExtOut("Time stamp: Expected '0x%x', Actual '0x%x'\n", pCallbackData->timestamp, dwTimeStampActual);
+ FreeLibrary(pCallbackData->hModule);
+ return TRUE;
+ }
+
+ ExtOut("Loaded %S\n", filename);
+ return FALSE;
+}
+
+#endif // FEATURE_PAL
+
+//---------------------------------------------------------------------------------------
+// Provides a way for the public CLR debugging interface to find the appropriate
+// mscordbi.dll, DAC, etc.
+class SOSLibraryProvider : public ICLRDebuggingLibraryProvider
+{
+public:
+ SOSLibraryProvider() : m_ref(0)
+ {
+ }
+
+ virtual ~SOSLibraryProvider() {}
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID InterfaceId,
+ PVOID* pInterface)
+ {
+ if (InterfaceId == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown *>(this);
+ }
+ else if (InterfaceId == IID_ICLRDebuggingLibraryProvider)
+ {
+ *pInterface = static_cast<ICLRDebuggingLibraryProvider *>(this);
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement(&m_ref);
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+ }
+
+
+
+ // Called by the shim to locate and load mscordacwks and mscordbi
+ // Parameters:
+ // pwszFileName - the name of the file to load
+ // dwTimestamp - the expected timestamp of the file
+ // dwSizeOfImage - the expected SizeOfImage (a PE header data value)
+ // phModule - a handle to loaded module
+ //
+ // Return Value
+ // S_OK if the file was loaded, or any error if not
+ virtual HRESULT STDMETHODCALLTYPE ProvideLibrary(
+ const WCHAR * pwszFileName,
+ DWORD dwTimestamp,
+ DWORD dwSizeOfImage,
+ HMODULE * phModule)
+ {
+#ifndef FEATURE_PAL
+ HRESULT hr;
+ FindFileCallbackData callbackData = {0};
+ callbackData.timestamp = dwTimestamp;
+ callbackData.filesize = dwSizeOfImage;
+
+ if ((phModule == NULL) || (pwszFileName == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ HMODULE dacModule;
+ if(g_sos == NULL)
+ {
+ // we ensure that windbg loads DAC first so that we can be sure to use the same one
+ return E_UNEXPECTED;
+ }
+ if (FAILED(hr = g_sos->GetDacModuleHandle(&dacModule)))
+ {
+ ExtOut("Failed to get the dac module handle. hr=0x%x.\n", hr);
+ return hr;
+ }
+
+ WCHAR dacPath[MAX_LONGPATH];
+ DWORD len = GetModuleFileNameW(dacModule, dacPath, MAX_LONGPATH);
+ if(len == 0 || len == MAX_LONGPATH)
+ {
+ ExtOut("GetModuleFileName(dacModuleHandle) failed. Last error = 0x%x\n", GetLastError());
+ return E_FAIL;
+ }
+
+ // if we are looking for the DAC, just load the one windbg already found
+ if(_wcsncmp(pwszFileName, W("mscordac"), _wcslen(W("mscordac")))==0)
+ {
+ FindFileInPathCallback(dacPath, &callbackData);
+ *phModule = callbackData.hModule;
+ return hr;
+ }
+
+ ULONG64 hProcess;
+ hr = g_ExtSystem->GetCurrentProcessHandle(&hProcess);
+ if (FAILED(hr))
+ {
+ ExtOut("IDebugSystemObjects::GetCurrentProcessHandle HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ ToRelease<IDebugSymbols3> spSym3(NULL);
+ hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
+ if (FAILED(hr))
+ {
+ ExtOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ ULONG pathSize = 0;
+ hr = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
+ if(FAILED(hr)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
+ {
+ ExtOut("Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize+MAX_LONGPATH+1];
+
+
+
+ hr = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
+ if(S_OK != hr)
+ {
+ ExtOut("Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ WCHAR foundPath[MAX_LONGPATH];
+ BOOL rc = SymFindFileInPathW((HANDLE)hProcess,
+ symbolPath,
+ pwszFileName,
+ (PVOID)(ULONG_PTR) dwTimestamp,
+ dwSizeOfImage,
+ 0,
+ SSRVOPT_DWORD,
+ foundPath,
+ (PFINDFILEINPATHCALLBACKW) &FindFileInPathCallback,
+ (PVOID) &callbackData
+ );
+ if(!rc)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExtOut("SymFindFileInPath failed for %S. HRESULT=0x%x.\nPlease ensure that %S is on your symbol path.", pwszFileName, hr, pwszFileName);
+ }
+
+ *phModule = callbackData.hModule;
+ return hr;
+#else
+ WCHAR modulePath[MAX_LONGPATH];
+ int length = MultiByteToWideChar(CP_ACP, 0, g_ExtServices->GetCoreClrDirectory(), -1, modulePath, _countof(modulePath));
+ if (0 >= length)
+ {
+ ExtOut("MultiByteToWideChar(coreclrDirectory) failed. Last error = 0x%x\n", GetLastError());
+ return E_FAIL;
+ }
+ wcscat_s(modulePath, _countof(modulePath), pwszFileName);
+
+ *phModule = LoadLibraryW(modulePath);
+ if (*phModule == NULL)
+ {
+ HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
+ ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", pwszFileName, hr);
+ return hr;
+ }
+ return S_OK;
+#endif // FEATURE_PAL
+ }
+
+protected:
+ LONG m_ref;
+};
+
+//---------------------------------------------------------------------------------------
+// Data target for the debugged process. Provided to OpenVirtualProcess in order to
+// get an ICorDebugProcess back
+//
+class SOSDataTarget : public ICorDebugMutableDataTarget
+#ifdef FEATURE_PAL
+, public ICorDebugDataTarget4
+#endif
+{
+public:
+ SOSDataTarget() : m_ref(0)
+ {
+ }
+
+ virtual ~SOSDataTarget() {}
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID InterfaceId,
+ PVOID* pInterface)
+ {
+ if (InterfaceId == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugDataTarget *>(this));
+ }
+ else if (InterfaceId == IID_ICorDebugDataTarget)
+ {
+ *pInterface = static_cast<ICorDebugDataTarget *>(this);
+ }
+ else if (InterfaceId == IID_ICorDebugMutableDataTarget)
+ {
+ *pInterface = static_cast<ICorDebugMutableDataTarget *>(this);
+ }
+#ifdef FEATURE_PAL
+ else if (InterfaceId == IID_ICorDebugDataTarget4)
+ {
+ *pInterface = static_cast<ICorDebugDataTarget4 *>(this);
+ }
+#endif
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement(&m_ref);
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+ }
+
+ //
+ // ICorDebugDataTarget.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE GetPlatform(CorDebugPlatform * pPlatform)
+ {
+ ULONG platformKind = g_targetMachine->GetPlatform();
+#ifdef FEATURE_PAL
+ if(platformKind == IMAGE_FILE_MACHINE_I386)
+ *pPlatform = CORDB_PLATFORM_POSIX_X86;
+ else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
+ *pPlatform = CORDB_PLATFORM_POSIX_AMD64;
+ else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
+ *pPlatform = CORDB_PLATFORM_POSIX_ARM;
+ else
+ return E_FAIL;
+#else
+ if(platformKind == IMAGE_FILE_MACHINE_I386)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
+ else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
+ else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
+ else if(platformKind == IMAGE_FILE_MACHINE_ARM64)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
+ else
+ return E_FAIL;
+#endif
+
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
+ CORDB_ADDRESS address,
+ BYTE * pBuffer,
+ ULONG32 request,
+ ULONG32 * pcbRead)
+ {
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->ReadVirtual(address, pBuffer, request, (PULONG) pcbRead);
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
+ DWORD dwThreadOSID,
+ ULONG32 contextFlags,
+ ULONG32 contextSize,
+ BYTE * context)
+ {
+#ifdef FEATURE_PAL
+ if (g_ExtSystem == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtSystem->GetThreadContextById(dwThreadOSID, contextFlags, contextSize, context);
+#else
+ ULONG ulThreadIDOrig;
+ ULONG ulThreadIDRequested;
+ HRESULT hr;
+ HRESULT hrRet;
+
+ hr = g_ExtSystem->GetCurrentThreadId(&ulThreadIDOrig);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = g_ExtSystem->GetThreadIdBySystemId(dwThreadOSID, &ulThreadIDRequested);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = g_ExtSystem->SetCurrentThreadId(ulThreadIDRequested);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // Prepare context structure
+ ZeroMemory(context, contextSize);
+ ((CONTEXT*) context)->ContextFlags = contextFlags;
+
+ // Ok, do it!
+ hrRet = g_ExtAdvanced3->GetThreadContext((LPVOID) context, contextSize);
+
+ // This is cleanup; failure here doesn't mean GetThreadContext should fail
+ // (that's determined by hrRet).
+ g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig);
+
+ return hrRet;
+#endif // FEATURE_PAL
+ }
+
+ //
+ // ICorDebugMutableDataTarget.
+ //
+ virtual HRESULT STDMETHODCALLTYPE WriteVirtual(CORDB_ADDRESS address,
+ const BYTE * pBuffer,
+ ULONG32 bytesRequested)
+ {
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->WriteVirtual(address, (PVOID)pBuffer, bytesRequested, NULL);
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE SetThreadContext(DWORD dwThreadID,
+ ULONG32 contextSize,
+ const BYTE * pContext)
+ {
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(DWORD dwThreadId,
+ CORDB_CONTINUE_STATUS continueStatus)
+ {
+ return E_NOTIMPL;
+ }
+
+#ifdef FEATURE_PAL
+ //
+ // ICorDebugDataTarget4
+ //
+ virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
+ {
+ if (g_ExtServices == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtServices->VirtualUnwind(threadId, contextSize, context);
+
+ }
+#endif // FEATURE_PAL
+
+protected:
+ LONG m_ref;
+};
+
+HRESULT InitCorDebugInterfaceFromModule(ULONG64 ulBase, ICLRDebugging * pClrDebugging)
+{
+ HRESULT hr;
+
+ ToRelease<ICorDebugMutableDataTarget> pSOSDataTarget = new SOSDataTarget;
+ pSOSDataTarget->AddRef();
+
+ ToRelease<ICLRDebuggingLibraryProvider> pSOSLibraryProvider = new SOSLibraryProvider;
+ pSOSLibraryProvider->AddRef();
+
+ CLR_DEBUGGING_VERSION clrDebuggingVersionRequested = {0};
+ clrDebuggingVersionRequested.wMajor = 4;
+
+ CLR_DEBUGGING_VERSION clrDebuggingVersionActual = {0};
+
+ CLR_DEBUGGING_PROCESS_FLAGS clrDebuggingFlags = (CLR_DEBUGGING_PROCESS_FLAGS)0;
+
+ ToRelease<IUnknown> pUnkProcess;
+
+ hr = pClrDebugging->OpenVirtualProcess(
+ ulBase,
+ pSOSDataTarget,
+ pSOSLibraryProvider,
+ &clrDebuggingVersionRequested,
+ IID_ICorDebugProcess,
+ &pUnkProcess,
+ &clrDebuggingVersionActual,
+ &clrDebuggingFlags);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ ICorDebugProcess * pCorDebugProcess = NULL;
+ hr = pUnkProcess->QueryInterface(IID_ICorDebugProcess, (PVOID*) &pCorDebugProcess);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // Transfer memory ownership of refcount to global
+ g_pCorDebugProcess = pCorDebugProcess;
+ return S_OK;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Unloads public ICorDebug interfaces, and clears g_pCorDebugProcess
+// This is only needed once after CLR unloads, not after every InitCorDebugInterface call
+//
+VOID UninitCorDebugInterface()
+{
+ if(g_pCorDebugProcess != NULL)
+ {
+ g_pCorDebugProcess->Detach();
+ g_pCorDebugProcess->Release();
+ g_pCorDebugProcess = NULL;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Loads public ICorDebug interfaces, and points g_pCorDebugProcess to them
+// This should be called at least once per windbg stop state to ensure that
+// the interface is available and that it doesn't hold stale data. Calling it
+// more than once isn't an error, but does have perf overhead from needlessly
+// flushing memory caches.
+//
+// Return Value:
+// HRESULT indicating success or failure
+//
+
+HRESULT InitCorDebugInterface()
+{
+ HMODULE hModule = NULL;
+ HRESULT hr;
+ ToRelease<ICLRDebugging> pClrDebugging;
+
+ // we may already have an ICorDebug instance we can use
+ if(g_pCorDebugProcess != NULL)
+ {
+ // ICorDebugProcess4 is currently considered a private experimental interface on ICorDebug, it might go away so
+ // we need to be sure to handle its absense gracefully
+ ToRelease<ICorDebugProcess4> pProcess4 = NULL;
+ if(SUCCEEDED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess4), (void**)&pProcess4)))
+ {
+ // FLUSH_ALL is more expensive than PROCESS_RUNNING, but this allows us to be safe even if things
+ // like IDNA are in use where we might be looking at non-sequential snapshots of process state
+ if(SUCCEEDED(pProcess4->ProcessStateChanged(FLUSH_ALL)))
+ {
+ // we already have an ICorDebug instance loaded and flushed, nothing more to do
+ return S_OK;
+ }
+ }
+
+ // this is a very heavy handed way of reseting
+ UninitCorDebugInterface();
+ }
+
+ // SOS now has a statically linked version of the loader code that is normally found in mscoree/mscoreei.dll
+ // Its not much code and takes a big step towards 0 install dependencies
+ // Need to pick the appropriate SKU of CLR to detect
+#if defined(FEATURE_CORESYSTEM)
+ GUID skuId = CLR_ID_ONECORE_CLR;
+#elif defined(FEATURE_CORECLR)
+ GUID skuId = CLR_ID_CORECLR;
+#else
+ GUID skuId = CLR_ID_V4_DESKTOP;
+#endif
+ CLRDebuggingImpl* pDebuggingImpl = new CLRDebuggingImpl(skuId);
+ hr = pDebuggingImpl->QueryInterface(IID_ICLRDebugging, (LPVOID *)&pClrDebugging);
+ if (FAILED(hr))
+ {
+ delete pDebuggingImpl;
+ return hr;
+ }
+
+#ifndef FEATURE_PAL
+ ULONG cLoadedModules;
+ ULONG cUnloadedModules;
+ hr = g_ExtSymbols->GetNumberModules(&cLoadedModules, &cUnloadedModules);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ ULONG64 ulBase;
+ for (ULONG i = 0; i < cLoadedModules; i++)
+ {
+ hr = g_ExtSymbols->GetModuleByIndex(i, &ulBase);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // Dunno if this is a CLR module or not (or even if it's the particular one the
+ // user cares about during inproc SxS scenarios). For now, just try to use it
+ // to grab an ICorDebugProcess. If it works, great. Else, continue the loop
+ // until we find the first one that works.
+ hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
+ if (SUCCEEDED(hr))
+ {
+ return hr;
+ }
+
+ // On failure, just iterate to the next module and try again...
+ }
+
+ // Still here? Didn't find the right module.
+ // TODO: Anything useful to return or log here?
+ return E_FAIL;
+#else
+ ULONG64 ulBase;
+ hr = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, &ulBase);
+ if (SUCCEEDED(hr))
+ {
+ hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
+ }
+ return hr;
+#endif // FEATURE_PAL
+}
+
+
+typedef enum
+{
+ GC_HEAP_INVALID = 0,
+ GC_HEAP_WKS = 1,
+ GC_HEAP_SVR = 2
+} GC_HEAP_TYPE;
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find out if runtime is server build *
+* *
+\**********************************************************************/
+
+DacpGcHeapData *g_pHeapData = NULL;
+DacpGcHeapData g_HeapData;
+
+BOOL InitializeHeapData()
+{
+ if (g_pHeapData == NULL)
+ {
+ if (g_HeapData.Request(g_sos) != S_OK)
+ {
+ return FALSE;
+ }
+ g_pHeapData = &g_HeapData;
+ }
+ return TRUE;
+}
+
+BOOL IsServerBuild()
+{
+ return InitializeHeapData() ? g_pHeapData->bServerMode : FALSE;
+}
+
+UINT GetMaxGeneration()
+{
+ return InitializeHeapData() ? g_pHeapData->g_max_generation : 0;
+}
+
+UINT GetGcHeapCount()
+{
+ return InitializeHeapData() ? g_pHeapData->HeapCount : 0;
+}
+
+BOOL GetGcStructuresValid()
+{
+ // We don't want to use the cached HeapData, because this can change
+ // each time the program runs for a while.
+ DacpGcHeapData heapData;
+ if (heapData.Request(g_sos) != S_OK)
+ {
+ return FALSE;
+ }
+
+ return heapData.bGcStructuresValid;
+}
+
+void GetAllocContextPtrs(AllocInfo *pallocInfo)
+{
+ // gets the allocation contexts for all threads. This provides information about how much of
+ // the current allocation quantum has been allocated and the heap to which the quantum belongs.
+ // The allocation quantum is a fixed size chunk of zeroed memory from which allocations will come
+ // until it's filled. Each managed thread has its own allocation context.
+
+ pallocInfo->num = 0;
+ pallocInfo->array = NULL;
+
+ // get the thread store (See code:ClrDataAccess::RequestThreadStoreData for details)
+ DacpThreadStoreData ThreadStore;
+ if ( ThreadStore.Request(g_sos) != S_OK)
+ {
+ return;
+ }
+
+ int numThread = ThreadStore.threadCount;
+ if (numThread)
+ {
+ pallocInfo->array = new needed_alloc_context[numThread];
+ if (pallocInfo->array == NULL)
+ {
+ return;
+ }
+ }
+
+ // get details for each thread in the thread store
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread != NULL)
+ {
+ if (IsInterrupt())
+ return;
+
+ DacpThreadData Thread;
+ // Get information about the thread (we're getting the values of several of the
+ // fields of the Thread instance from the target) See code:ClrDataAccess::RequestThreadData for
+ // details
+ if (Thread.Request(g_sos, CurThread) != S_OK)
+ {
+ return;
+ }
+
+ if (Thread.allocContextPtr != 0)
+ {
+ // get a list of all the allocation contexts
+ int j;
+ for (j = 0; j < pallocInfo->num; j ++)
+ {
+ if (pallocInfo->array[j].alloc_ptr == (BYTE *) Thread.allocContextPtr)
+ break;
+ }
+ if (j == pallocInfo->num)
+ {
+ pallocInfo->num ++;
+ pallocInfo->array[j].alloc_ptr = (BYTE *) Thread.allocContextPtr;
+ pallocInfo->array[j].alloc_limit = (BYTE *) Thread.allocContextLimit;
+ }
+ }
+
+ CurThread = Thread.nextThread;
+ }
+}
+
+HRESULT ReadVirtualCache::Read(TADDR taOffset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead)
+{
+ // sign extend the passed in Offset so we can use it in when calling
+ // IDebugDataSpaces::ReadVirtual()
+
+ CLRDATA_ADDRESS Offset = TO_CDADDR(taOffset);
+ // Offset can be any random ULONG64, as it can come from VerifyObjectMember(), and this
+ // can pass random pointer values in case of GC heap corruption
+ HRESULT ret;
+ ULONG cbBytesRead = 0;
+
+ if (BufferSize == 0)
+ return S_OK;
+
+ if (BufferSize > CACHE_SIZE)
+ {
+ // Don't even try with the cache
+ return g_ExtData->ReadVirtual(Offset, Buffer, BufferSize, lpcbBytesRead);
+ }
+
+ if ((m_cacheValid)
+ && (taOffset >= m_startCache)
+ && (taOffset <= m_startCache + m_cacheSize - BufferSize))
+
+ {
+ // It is within the cache
+ memcpy(Buffer,(LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
+
+ if (lpcbBytesRead != NULL)
+ {
+ *lpcbBytesRead = BufferSize;
+ }
+
+ return S_OK;
+ }
+
+ m_cacheValid = FALSE;
+ m_startCache = taOffset;
+
+ // avoid an int overflow
+ if (m_startCache + CACHE_SIZE < m_startCache)
+ m_startCache = (TADDR)(-CACHE_SIZE);
+
+ ret = g_ExtData->ReadVirtual(TO_CDADDR(m_startCache), m_cache, CACHE_SIZE, &cbBytesRead);
+ if (ret != S_OK)
+ {
+ return ret;
+ }
+
+ m_cacheSize = cbBytesRead;
+ m_cacheValid = TRUE;
+ memcpy(Buffer, (LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
+
+ if (lpcbBytesRead != NULL)
+ {
+ *lpcbBytesRead = cbBytesRead;
+ }
+
+ return S_OK;
+}
+
+HRESULT GetMTOfObject(TADDR obj, TADDR *mt)
+{
+ if (!mt)
+ return E_POINTER;
+
+ // Read the MethodTable and if we succeed, get rid of the mark bits.
+ HRESULT hr = rvCache->Read(obj, mt, sizeof(TADDR), NULL);
+ if (SUCCEEDED(hr))
+ *mt &= ~3;
+
+ return hr;
+}
+
+#ifndef FEATURE_PAL
+
+StressLogMem::~StressLogMem ()
+{
+ MemRange * range = list;
+
+ while (range)
+ {
+ MemRange * temp = range->next;
+ delete range;
+ range = temp;
+ }
+}
+
+bool StressLogMem::Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack)
+{
+ size_t ThreadStressLogAddr = NULL;
+ HRESULT hr = memCallBack->ReadVirtual(UL64_TO_CDA(stressLogAddr + offsetof (StressLog, logs)),
+ &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
+ if (hr != S_OK)
+ {
+ return false;
+ }
+
+ while(ThreadStressLogAddr != NULL)
+ {
+ size_t ChunkListHeadAddr = NULL;
+ hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfListHead ()),
+ &ChunkListHeadAddr, sizeof (ChunkListHeadAddr), 0);
+ if (hr != S_OK || ChunkListHeadAddr == NULL)
+ {
+ return false;
+ }
+
+ size_t StressLogChunkAddr = ChunkListHeadAddr;
+
+ do
+ {
+ AddRange (StressLogChunkAddr, sizeof (StressLogChunk));
+ hr = memCallBack->ReadVirtual(TO_CDADDR(StressLogChunkAddr + offsetof (StressLogChunk, next)),
+ &StressLogChunkAddr, sizeof (StressLogChunkAddr), 0);
+ if (hr != S_OK)
+ {
+ return false;
+ }
+ if (StressLogChunkAddr == NULL)
+ {
+ return true;
+ }
+ } while (StressLogChunkAddr != ChunkListHeadAddr);
+
+ hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfNext ()),
+ &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
+ if (hr != S_OK)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool StressLogMem::IsInStressLog (ULONG64 addr)
+{
+ MemRange * range = list;
+ while (range)
+ {
+ if (range->InRange (addr))
+ return true;
+ range = range->next;
+ }
+
+ return false;
+}
+
+#endif // !FEATURE_PAL
+
+unsigned int Output::g_bSuppressOutput = 0;
+unsigned int Output::g_Indent = 0;
+bool Output::g_bDbgOutput = false;
+bool Output::g_bDMLExposed = false;
+unsigned int Output::g_DMLEnable = 0;
+
+template <class T, int count, int size> const int StaticData<T, count, size>::Count = count;
+template <class T, int count, int size> const int StaticData<T, count, size>::Size = size;
+
+StaticData<char, 4, 1024> CachedString::cache;
+
+CachedString::CachedString()
+: mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
+{
+ Create();
+}
+
+CachedString::CachedString(const CachedString &rhs)
+: mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
+{
+ Copy(rhs);
+}
+
+CachedString::~CachedString()
+{
+ Clear();
+}
+
+const CachedString &CachedString::operator=(const CachedString &rhs)
+{
+ Clear();
+ Copy(rhs);
+ return *this;
+}
+
+void CachedString::Copy(const CachedString &rhs)
+{
+ if (rhs.IsOOM())
+ {
+ SetOOM();
+ }
+ else
+ {
+ mPtr = rhs.mPtr;
+ mIndex = rhs.mIndex;
+ mSize = rhs.mSize;
+
+ if (rhs.mRefCount)
+ {
+ mRefCount = rhs.mRefCount;
+ (*mRefCount)++;
+ }
+ else
+ {
+ // We only create count the first time we copy it, so
+ // we initialize it to 2.
+ mRefCount = rhs.mRefCount = new unsigned int(2);
+ if (!mRefCount)
+ SetOOM();
+ }
+ }
+}
+
+void CachedString::Clear()
+{
+ if (!mRefCount || --*mRefCount == 0)
+ {
+ if (mIndex == -1)
+ {
+ if (mPtr)
+ delete [] mPtr;
+ }
+ else if (mIndex >= 0 && mIndex < cache.Count)
+ {
+ cache.InUse[mIndex] = false;
+ }
+
+ if (mRefCount)
+ delete mRefCount;
+ }
+
+ mPtr = 0;
+ mIndex = ~0;
+ mRefCount = 0;
+ mSize = cache.Size;
+}
+
+
+void CachedString::Create()
+{
+ mIndex = -1;
+ mRefCount = 0;
+
+ // First try to find a string in the cache to use.
+ for (int i = 0; i < cache.Count; ++i)
+ if (!cache.InUse[i])
+ {
+ cache.InUse[i] = true;
+ mPtr = cache.Data[i];
+ mIndex = i;
+ break;
+ }
+
+ // We did not find a string to use, so we'll create a new one.
+ if (mIndex == -1)
+ {
+ mPtr = new char[cache.Size];
+ if (!mPtr)
+ SetOOM();
+ }
+}
+
+
+void CachedString::SetOOM()
+{
+ Clear();
+ mIndex = -2;
+}
+
+void CachedString::Allocate(int size)
+{
+ Clear();
+ mPtr = new char[size];
+
+ if (mPtr)
+ {
+ mSize = size;
+ mIndex = -1;
+ }
+ else
+ {
+ SetOOM();
+ }
+}
+
+size_t CountHexCharacters(CLRDATA_ADDRESS val)
+{
+ size_t ret = 0;
+
+ while (val)
+ {
+ val >>= 4;
+ ret++;
+ }
+
+ return ret;
+}
+
+void WhitespaceOut(int count)
+{
+ static const int FixedIndentWidth = 0x40;
+ static const char FixedIndentString[FixedIndentWidth+1] =
+ " ";
+
+ if (count <= 0)
+ return;
+
+ int mod = count & 0x3F;
+ count &= ~0x3F;
+
+ if (mod > 0)
+ g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, "%.*s", mod, FixedIndentString);
+
+ for ( ; count > 0; count -= FixedIndentWidth)
+ g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, FixedIndentString);
+}
+
+void DMLOut(PCSTR format, ...)
+{
+ if (Output::IsOutputSuppressed())
+ return;
+
+ va_list args;
+ va_start(args, format);
+ ExtOutIndent();
+
+#ifndef FEATURE_PAL
+ if (IsDMLEnabled() && !Output::IsDMLExposed())
+ {
+ g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
+ }
+ else
+#endif
+ {
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, format, args);
+ }
+
+ va_end(args);
+}
+
+void IfDMLOut(PCSTR format, ...)
+{
+#ifndef FEATURE_PAL
+ if (Output::IsOutputSuppressed() || !IsDMLEnabled())
+ return;
+
+ va_list args;
+
+ va_start(args, format);
+ ExtOutIndent();
+ g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
+ va_end(args);
+#endif
+}
+
+void ExtOut(PCSTR Format, ...)
+{
+ if (Output::IsOutputSuppressed())
+ return;
+
+ va_list Args;
+
+ va_start(Args, Format);
+ ExtOutIndent();
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
+ va_end(Args);
+}
+
+void ExtWarn(PCSTR Format, ...)
+{
+ if (Output::IsOutputSuppressed())
+ return;
+
+ va_list Args;
+
+ va_start(Args, Format);
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args);
+ va_end(Args);
+}
+
+void ExtErr(PCSTR Format, ...)
+{
+ va_list Args;
+
+ va_start(Args, Format);
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args);
+ va_end(Args);
+}
+
+
+void ExtDbgOut(PCSTR Format, ...)
+{
+#ifdef _DEBUG
+ if (Output::g_bDbgOutput)
+ {
+ va_list Args;
+
+ va_start(Args, Format);
+ ExtOutIndent();
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
+ va_end(Args);
+ }
+#endif
+}
+
+const char * const DMLFormats[] =
+{
+ NULL, // DML_None (do not use)
+ "<exec cmd=\"!DumpMT /d %s\">%s</exec>", // DML_MethodTable
+ "<exec cmd=\"!DumpMD /d %s\">%s</exec>", // DML_MethodDesc
+ "<exec cmd=\"!DumpClass /d %s\">%s</exec>", // DML_EEClass
+ "<exec cmd=\"!DumpModule /d %s\">%s</exec>", // DML_Module
+ "<exec cmd=\"!U /d %s\">%s</exec>", // DML_IP
+ "<exec cmd=\"!DumpObj /d %s\">%s</exec>", // DML_Object
+ "<exec cmd=\"!DumpDomain /d %s\">%s</exec>", // DML_Domain
+ "<exec cmd=\"!DumpAssembly /d %s\">%s</exec>", // DML_Assembly
+ "<exec cmd=\"~~[%s]s\">%s</exec>", // DML_ThreadID
+ "<exec cmd=\"!DumpVC /d %s %s\">%s</exec>", // DML_ValueClass
+ "<exec cmd=\"!DumpHeap /d -mt %s\">%s</exec>", // DML_DumpHeapMT
+ "<exec cmd=\"!ListNearObj /d %s\">%s</exec>", // DML_ListNearObj
+ "<exec cmd=\"!ThreadState %s\">%s</exec>", // DML_ThreadState
+ "<exec cmd=\"!PrintException /d %s\">%s</exec>",// DML_PrintException
+ "<exec cmd=\"!DumpRCW /d %s\">%s</exec>", // DML_RCWrapper
+ "<exec cmd=\"!DumpCCW /d %s\">%s</exec>", // DML_CCWrapper
+ "<exec cmd=\"!ClrStack -i %S %d\">%S</exec>", // DML_ManagedVar
+};
+
+void ConvertToLower(__out_ecount(len) char *buffer, size_t len)
+{
+ for (size_t i = 0; i < len && buffer[i]; ++i)
+ buffer[i] = (char)tolower(buffer[i]);
+}
+
+/* Build a hex display of addr.
+ */
+int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill)
+{
+ int count = sprintf_s(out, len, fill ? "%p" : "%x", (size_t)addr);
+
+ ConvertToLower(out, len);
+
+ return count;
+}
+
+CachedString Output::BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill)
+{
+ CachedString ret;
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+
+ if (IsDMLEnabled())
+ {
+ char hex[POINTERSIZE_BYTES*2 + 1];
+ GetHex(addr, hex, _countof(hex), fill);
+ sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hex, hex);
+ }
+ else
+ {
+ GetHex(addr, ret, ret.GetStrLen(), fill);
+ }
+
+ return ret;
+}
+
+CachedString Output::BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill)
+{
+ _ASSERTE(type == DML_ValueClass);
+ CachedString ret;
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+
+ if (IsDMLEnabled())
+ {
+ char hexaddr[POINTERSIZE_BYTES*2 + 1];
+ char hexmt[POINTERSIZE_BYTES*2 + 1];
+
+ GetHex(addr, hexaddr, _countof(hexaddr), fill);
+ GetHex(mt, hexmt, _countof(hexmt), fill);
+
+ sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hexmt, hexaddr, hexaddr);
+ }
+ else
+ {
+ GetHex(addr, ret, ret.GetStrLen(), fill);
+ }
+
+ return ret;
+}
+
+CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type)
+{
+ _ASSERTE(type == DML_ManagedVar);
+ CachedString ret;
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+
+ // calculate the number of digits in frame (this assumes base-10 display of frames)
+ int numFrameDigits = 0;
+ if (frame > 0)
+ {
+ ULONG tempFrame = frame;
+ while (tempFrame > 0)
+ {
+ ++numFrameDigits;
+ tempFrame /= 10;
+ }
+ }
+ else
+ {
+ numFrameDigits = 1;
+ }
+
+ size_t totalStringLength = strlen(DMLFormats[type]) + _wcslen(expansionName) + numFrameDigits + _wcslen(simpleName) + 1;
+ if (totalStringLength > ret.GetStrLen())
+ {
+ ret.Allocate(static_cast<int>(totalStringLength));
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+ }
+
+ if (IsDMLEnabled())
+ {
+ sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], expansionName, frame, simpleName);
+ }
+ else
+ {
+ sprintf_s(ret, ret.GetStrLen(), "%S", simpleName);
+ }
+
+ return ret;
+}
+
+CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type)
+{
+ WCHAR indexString[24];
+ swprintf_s(indexString, _countof(indexString), W("[%d]"), indexInArray);
+ return BuildManagedVarValue(expansionName, frame, indexString, type);
+}
+
+EnableDMLHolder::EnableDMLHolder(BOOL enable)
+ : mEnable(enable)
+{
+#ifndef FEATURE_PAL
+ // If the user has not requested that we use DML, it's still possible that
+ // they have instead specified ".prefer_dml 1". If enable is false,
+ // we will check here for .prefer_dml. Since this class is only used once
+ // per command issued to SOS, this should only check the setting once per
+ // sos command issued.
+ if (!mEnable && Output::g_DMLEnable <= 0)
+ {
+ ULONG opts;
+ HRESULT hr = g_ExtControl->GetEngineOptions(&opts);
+ mEnable = SUCCEEDED(hr) && (opts & DEBUG_ENGOPT_PREFER_DML) == DEBUG_ENGOPT_PREFER_DML;
+ }
+
+ if (mEnable)
+ {
+ Output::g_DMLEnable++;
+ }
+#endif // FEATURE_PAL
+}
+
+EnableDMLHolder::~EnableDMLHolder()
+{
+#ifndef FEATURE_PAL
+ if (mEnable)
+ Output::g_DMLEnable--;
+#endif
+}
+
+bool IsDMLEnabled()
+{
+ return Output::g_DMLEnable > 0;
+}
+
+NoOutputHolder::NoOutputHolder(BOOL bSuppress)
+ : mSuppress(bSuppress)
+{
+ if (mSuppress)
+ Output::g_bSuppressOutput++;
+}
+
+NoOutputHolder::~NoOutputHolder()
+{
+ if (mSuppress)
+ Output::g_bSuppressOutput--;
+}
+
+//
+// Code to support mapping RVAs to managed code line numbers.
+//
+
+//
+// Retrieves the IXCLRDataMethodInstance* instance associated with the
+// passed in native offset.
+HRESULT
+GetClrMethodInstance(
+ ___in ULONG64 NativeOffset,
+ ___out IXCLRDataMethodInstance** Method)
+{
+ HRESULT Status;
+ CLRDATA_ENUM MethEnum;
+
+ Status = g_clrData->StartEnumMethodInstancesByAddress(NativeOffset, NULL, &MethEnum);
+
+ if (Status == S_OK)
+ {
+ Status = g_clrData->EnumMethodInstanceByAddress(&MethEnum, Method);
+ g_clrData->EndEnumMethodInstancesByAddress(MethEnum);
+ }
+
+ // Any alternate success is a true failure here.
+ return (Status == S_OK || FAILED(Status)) ? Status : E_NOINTERFACE;
+}
+
+//
+// Enumerates over the IL address map associated with the passed in
+// managed method, and returns the highest non-epilog offset.
+HRESULT
+GetLastMethodIlOffset(
+ ___in IXCLRDataMethodInstance* Method,
+ ___out PULONG32 MethodOffs)
+{
+ HRESULT Status;
+ CLRDATA_IL_ADDRESS_MAP MapLocal[16];
+ CLRDATA_IL_ADDRESS_MAP* Map = MapLocal;
+ ULONG32 MapCount = _countof(MapLocal);
+ ULONG32 MapNeeded;
+ ULONG32 HighestOffset;
+
+ for (;;)
+ {
+ if ((Status = Method->GetILAddressMap(MapCount, &MapNeeded, Map)) != S_OK)
+ {
+ return Status;
+ }
+
+ if (MapNeeded <= MapCount)
+ {
+ break;
+ }
+
+ // Need more map entries.
+ if (Map != MapLocal)
+ {
+ // Already went around and the answer changed,
+ // which should not be possible.
+ delete[] Map;
+ return E_UNEXPECTED;
+ }
+
+ Map = new CLRDATA_IL_ADDRESS_MAP[MapNeeded];
+ if (!Map)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ MapCount = MapNeeded;
+ }
+
+ HighestOffset = 0;
+ for (size_t i = 0; i < MapNeeded; i++)
+ {
+ if (Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_NO_MAPPING &&
+ Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_PROLOG &&
+ Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_EPILOG &&
+ Map[i].ilOffset > HighestOffset)
+ {
+ HighestOffset = Map[i].ilOffset;
+ }
+ }
+
+ if (Map != MapLocal)
+ {
+ delete[] Map;
+ }
+
+ *MethodOffs = HighestOffset;
+ return S_OK;
+}
+
+//
+// Convert a native offset (possibly already associated with a managed
+// method identified by the passed in IXCLRDataMethodInstance) to a
+// triplet (ImageInfo, MethodToken, MethodOffset) that can be used to
+// represent an "IL offset".
+HRESULT
+ConvertNativeToIlOffset(
+ ___in ULONG64 native,
+ ___out IXCLRDataModule** ppModule,
+ ___out mdMethodDef* methodToken,
+ ___out PULONG32 methodOffs)
+{
+ ToRelease<IXCLRDataMethodInstance> pMethodInst(NULL);
+ HRESULT Status;
+
+ if ((Status = GetClrMethodInstance(native, &pMethodInst)) != S_OK)
+ {
+ return Status;
+ }
+
+ if ((Status = pMethodInst->GetILOffsetsByAddress(native, 1, NULL, methodOffs)) != S_OK)
+ {
+ *methodOffs = 0;
+ }
+ else
+ {
+ switch((LONG)*methodOffs)
+ {
+ case CLRDATA_IL_OFFSET_NO_MAPPING:
+ return E_NOINTERFACE;
+
+ case CLRDATA_IL_OFFSET_PROLOG:
+ // Treat all of the prologue as part of
+ // the first source line.
+ *methodOffs = 0;
+ break;
+
+ case CLRDATA_IL_OFFSET_EPILOG:
+ // Back up until we find the last real
+ // IL offset.
+ if ((Status = GetLastMethodIlOffset(pMethodInst, methodOffs)) != S_OK)
+ {
+ return Status;
+ }
+ break;
+ }
+ }
+
+ return pMethodInst->GetTokenAndScope(methodToken, ppModule);
+}
+
+// Based on a native offset, passed in the first argument this function
+// identifies the corresponding source file name and line number.
+HRESULT
+GetLineByOffset(
+ ___in ULONG64 offset,
+ ___out ULONG *pLinenum,
+ __out_ecount(cchFileName) WCHAR* pwszFileName,
+ ___in ULONG cchFileName)
+{
+ HRESULT Status = S_OK;
+ ULONG32 methodToken;
+ ULONG32 methodOffs;
+
+ // Find the image, method token and IL offset that correspond to "offset"
+ ToRelease<IXCLRDataModule> pModule(NULL);
+ IfFailRet(ConvertNativeToIlOffset(offset, &pModule, &methodToken, &methodOffs));
+
+ ToRelease<IMetaDataImport> pMDImport(NULL);
+ IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
+
+ SymbolReader symbolReader;
+ IfFailRet(symbolReader.LoadSymbols(pMDImport, pModule));
+
+ return symbolReader.GetLineByILOffset(methodToken, methodOffs, pLinenum, pwszFileName, cchFileName);
+}
+
+void TableOutput::ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault, int indent, int padding)
+{
+ Clear();
+
+ mColumns = numColumns;
+ mDefaultWidth = defaultColumnWidth;
+ mIndent = indent;
+ mPadding = padding;
+ mCurrCol = 0;
+ mDefaultAlign = alignmentDefault;
+}
+
+void TableOutput::SetWidths(int columns, ...)
+{
+ SOS_Assert(columns > 0);
+ SOS_Assert(columns <= mColumns);
+
+ AllocWidths();
+
+ va_list list;
+ va_start(list, columns);
+
+ for (int i = 0; i < columns; ++i)
+ mWidths[i] = va_arg(list, int);
+
+ va_end(list);
+}
+
+void TableOutput::SetColWidth(int col, int width)
+{
+ SOS_Assert(col >= 0 && col < mColumns);
+ SOS_Assert(width >= 0);
+
+ AllocWidths();
+
+ mWidths[col] = width;
+}
+
+void TableOutput::SetColAlignment(int col, Alignment align)
+{
+ SOS_Assert(col >= 0 && col < mColumns);
+
+ if (!mAlignments)
+ {
+ mAlignments = new Alignment[mColumns];
+ for (int i = 0; i < mColumns; ++i)
+ mAlignments[i] = mDefaultAlign;
+ }
+
+ mAlignments[col] = align;
+}
+
+
+
+void TableOutput::Clear()
+{
+ if (mAlignments)
+ {
+ delete [] mAlignments;
+ mAlignments = 0;
+ }
+
+ if (mWidths)
+ {
+ delete [] mWidths;
+ mWidths = 0;
+ }
+}
+
+void TableOutput::AllocWidths()
+{
+ if (!mWidths)
+ {
+ mWidths = new int[mColumns];
+ for (int i = 0; i < mColumns; ++i)
+ mWidths[i] = mDefaultWidth;
+ }
+}
+
+int TableOutput::GetColumnWidth(int col)
+{
+ SOS_Assert(col < mColumns);
+
+ if (mWidths)
+ return mWidths[col];
+
+ return mDefaultWidth;
+}
+
+Alignment TableOutput::GetColAlign(int col)
+{
+ SOS_Assert(col < mColumns);
+ if (mAlignments)
+ return mAlignments[col];
+
+ return mDefaultAlign;
+}
+
+const char *TableOutput::GetWhitespace(int amount)
+{
+ static char WhiteSpace[256] = "";
+ static int count = 0;
+
+ if (count == 0)
+ {
+ count = _countof(WhiteSpace);
+ for (int i = 0; i < count-1; ++i)
+ WhiteSpace[i] = ' ';
+ WhiteSpace[count-1] = 0;
+ }
+
+ SOS_Assert(amount < count);
+ return &WhiteSpace[count-amount-1];
+}
+
+void TableOutput::OutputBlankColumns(int col)
+{
+ if (col < mCurrCol)
+ {
+ ExtOut("\n");
+ mCurrCol = 0;
+ }
+
+ int whitespace = 0;
+ for (int i = mCurrCol; i < col; ++i)
+ whitespace += GetColumnWidth(i) + mPadding;
+
+ ExtOut(GetWhitespace(whitespace));
+}
+
+void TableOutput::OutputIndent()
+{
+ if (mIndent)
+ ExtOut(GetWhitespace(mIndent));
+}
+
+#ifndef FEATURE_PAL
+
+PEOffsetMemoryReader::PEOffsetMemoryReader(TADDR moduleBaseAddress) :
+ m_moduleBaseAddress(moduleBaseAddress),
+ m_refCount(1)
+ {}
+
+HRESULT __stdcall PEOffsetMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
+{
+ if(riid == __uuidof(IDiaReadExeAtOffsetCallback))
+ {
+ *ppInterface = static_cast<IDiaReadExeAtOffsetCallback*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else if(riid == __uuidof(IUnknown))
+ {
+ *ppInterface = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall PEOffsetMemoryReader::AddRef()
+{
+ return InterlockedIncrement((volatile LONG *) &m_refCount);
+}
+
+ULONG __stdcall PEOffsetMemoryReader::Release()
+{
+ ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
+ if(count == 0)
+ {
+ delete this;
+ }
+ return count;
+}
+
+// IDiaReadExeAtOffsetCallback implementation
+HRESULT __stdcall PEOffsetMemoryReader::ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[])
+{
+ return SafeReadMemory(m_moduleBaseAddress + fileOffset, data, cbData, pcbData) ? S_OK : E_FAIL;
+}
+
+PERvaMemoryReader::PERvaMemoryReader(TADDR moduleBaseAddress) :
+ m_moduleBaseAddress(moduleBaseAddress),
+ m_refCount(1)
+ {}
+
+HRESULT __stdcall PERvaMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
+{
+ if(riid == __uuidof(IDiaReadExeAtRVACallback))
+ {
+ *ppInterface = static_cast<IDiaReadExeAtRVACallback*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else if(riid == __uuidof(IUnknown))
+ {
+ *ppInterface = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall PERvaMemoryReader::AddRef()
+{
+ return InterlockedIncrement((volatile LONG *) &m_refCount);
+}
+
+ULONG __stdcall PERvaMemoryReader::Release()
+{
+ ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
+ if(count == 0)
+ {
+ delete this;
+ }
+ return count;
+}
+
+// IDiaReadExeAtOffsetCallback implementation
+HRESULT __stdcall PERvaMemoryReader::ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[])
+{
+ return SafeReadMemory(m_moduleBaseAddress + relativeVirtualAddress, data, cbData, pcbData) ? S_OK : E_FAIL;
+}
+
+#endif // FEATURE_PAL
+
+HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule)
+{
+ HRESULT Status = S_OK;
+ BOOL isDynamic = FALSE;
+ BOOL isInMemory = FALSE;
+ IfFailRet(pModule->IsDynamic(&isDynamic));
+ IfFailRet(pModule->IsInMemory(&isInMemory));
+
+ if (isDynamic)
+ {
+ // Dynamic and in memory assemblies are a special case which we will ignore for now
+ ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
+ return E_FAIL;
+ }
+
+ ULONG64 peAddress = 0;
+ ULONG32 peSize = 0;
+ IfFailRet(pModule->GetBaseAddress(&peAddress));
+ IfFailRet(pModule->GetSize(&peSize));
+
+ ULONG32 len = 0;
+ WCHAR moduleName[MAX_LONGPATH];
+ IfFailRet(pModule->GetName(_countof(moduleName), &len, moduleName));
+
+#ifndef FEATURE_PAL
+ if (SUCCEEDED(LoadSymbolsForWindowsPDB(pMD, peAddress, moduleName, isInMemory)))
+ {
+ return S_OK;
+ }
+#endif // FEATURE_PAL
+ return LoadSymbolsForPortablePDB(moduleName, isInMemory, isInMemory, peAddress, peSize, 0, 0);
+}
+
+HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule)
+{
+ DacpGetModuleData moduleData;
+ HRESULT hr = moduleData.Request(pModule);
+ if (FAILED(hr))
+ {
+ ExtOut("LoadSymbols moduleData.Request FAILED 0x%08x\n", hr);
+ return hr;
+ }
+
+ if (moduleData.IsDynamic)
+ {
+ ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
+ return E_FAIL;
+ }
+
+ ArrayHolder<WCHAR> pModuleName = new WCHAR[MAX_LONGPATH + 1];
+ ULONG32 nameLen = 0;
+ hr = pModule->GetFileName(MAX_LONGPATH, &nameLen, pModuleName);
+ if (FAILED(hr))
+ {
+ ExtOut("LoadSymbols: IXCLRDataModule->GetFileName FAILED 0x%08x\n", hr);
+ return hr;
+ }
+
+#ifndef FEATURE_PAL
+ // TODO: in-memory windows PDB not supported
+ hr = LoadSymbolsForWindowsPDB(pMD, moduleData.LoadedPEAddress, pModuleName, moduleData.IsFileLayout);
+ if (SUCCEEDED(hr))
+ {
+ return hr;
+ }
+#endif // FEATURE_PAL
+
+ return LoadSymbolsForPortablePDB(
+ pModuleName,
+ moduleData.IsInMemory,
+ moduleData.IsFileLayout,
+ moduleData.LoadedPEAddress,
+ moduleData.LoadedPESize,
+ moduleData.InMemoryPdbAddress,
+ moduleData.InMemoryPdbSize);
+}
+
+#ifndef FEATURE_PAL
+
+HRESULT SymbolReader::LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout)
+{
+ HRESULT Status = S_OK;
+
+ if (m_pSymReader != NULL)
+ return S_OK;
+
+ IfFailRet(CoInitialize(NULL));
+
+ // We now need a binder object that will take the module and return a
+ // reader object
+ ToRelease<ISymUnmanagedBinder3> pSymBinder;
+ if (FAILED(Status = CreateInstanceCustom(CLSID_CorSymBinder_SxS,
+ IID_ISymUnmanagedBinder3,
+ W("diasymreader.dll"),
+ cciLatestFx|cciDacColocated|cciDbgPath,
+ (void**)&pSymBinder)))
+ {
+ ExtOut("SOS Error: Unable to CoCreateInstance class=CLSID_CorSymBinder_SxS, interface=IID_ISymUnmanagedBinder3, hr=0x%x\n", Status);
+ ExtOut("This usually means the installation of .Net Framework on your machine is missing or needs repair\n");
+ return Status;
+ }
+
+ ToRelease<IDebugSymbols3> spSym3(NULL);
+ Status = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
+ if (FAILED(Status))
+ {
+ ExtOut("SOS Error: Unable to query IDebugSymbols3 HRESULT=0x%x.\n", Status);
+ return Status;
+ }
+
+ ULONG pathSize = 0;
+ Status = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
+ if (FAILED(Status)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
+ {
+ ExtOut("SOS Error: Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
+ return Status;
+ }
+
+ ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize];
+ Status = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
+ if (S_OK != Status)
+ {
+ ExtOut("SOS Error: Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
+ return Status;
+ }
+
+ ToRelease<IUnknown> pCallback = NULL;
+ if (isFileLayout)
+ {
+ pCallback = (IUnknown*) new PEOffsetMemoryReader(TO_TADDR(peAddress));
+ }
+ else
+ {
+ pCallback = (IUnknown*) new PERvaMemoryReader(TO_TADDR(peAddress));
+ }
+
+ // TODO: this should be better integrated with windbg's symbol lookup
+ Status = pSymBinder->GetReaderFromCallback(pMD, pModuleName, symbolPath,
+ AllowRegistryAccess | AllowSymbolServerAccess | AllowOriginalPathAccess | AllowReferencePathAccess, pCallback, &m_pSymReader);
+
+ if (FAILED(Status) && m_pSymReader != NULL)
+ {
+ m_pSymReader->Release();
+ m_pSymReader = NULL;
+ }
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+//
+// Pass to managed helper code to read in-memory PEs/PDBs
+// Returns the number of bytes read.
+//
+int ReadMemoryForSymbols(ULONG64 address, char *buffer, int cb)
+{
+ ULONG read;
+ if (SafeReadMemory(address, (PVOID)buffer, cb, &read))
+ {
+ return read;
+ }
+ return 0;
+}
+
+HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout,
+ ___in ULONG64 peAddress, ___in ULONG64 peSize, ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize)
+{
+ HRESULT Status = S_OK;
+
+ if (loadSymbolsForModuleDelegate == nullptr)
+ {
+ IfFailRet(PrepareSymbolReader());
+ }
+
+ // The module name needs to be null for in-memory PE's.
+ ArrayHolder<char> szModuleName = nullptr;
+ if (!isInMemory && pModuleName != nullptr)
+ {
+ szModuleName = new char[MAX_LONGPATH];
+ if (WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szModuleName, MAX_LONGPATH, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ }
+
+ m_symbolReaderHandle = loadSymbolsForModuleDelegate(szModuleName, isFileLayout, peAddress,
+ (int)peSize, inMemoryPdbAddress, (int)inMemoryPdbSize, ReadMemoryForSymbols);
+
+ if (m_symbolReaderHandle == 0)
+ {
+ return E_FAIL;
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
+{
+ const char * const tpaExtensions[] = {
+ "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ "*.dll",
+ "*.ni.exe",
+ "*.exe",
+ };
+
+ std::set<std::string> addedAssemblies;
+
+ // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+ // then files with .dll extension, etc.
+ for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+ {
+ const char* ext = tpaExtensions[extIndex];
+ size_t extLength = strlen(ext);
+
+ std::string assemblyPath(directory);
+ assemblyPath.append(DIRECTORY_SEPARATOR_STR_A);
+ assemblyPath.append(tpaExtensions[extIndex]);
+
+ WIN32_FIND_DATAA data;
+ HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
+
+ if (findHandle != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+
+ std::string filename(data.cFileName);
+ size_t extPos = filename.length() - extLength;
+ std::string filenameWithoutExt(filename.substr(0, extPos));
+
+ // Make sure if we have an assembly with multiple extensions present,
+ // we insert only one version of it.
+ if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+ {
+ addedAssemblies.insert(filenameWithoutExt);
+
+ tpaList.append(directory);
+ tpaList.append(DIRECTORY_SEPARATOR_STR_A);
+ tpaList.append(filename);
+ tpaList.append(";");
+ }
+ }
+ }
+ while (0 != FindNextFileA(findHandle, &data));
+
+ FindClose(findHandle);
+ }
+ }
+}
+
+bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
+{
+ ArrayHolder<char> hostPath = new char[MAX_LONGPATH+1];
+ if (::GetModuleFileName(NULL, hostPath, MAX_LONGPATH) == 0)
+ {
+ return false;
+ }
+
+ entrypointExecutable.clear();
+ entrypointExecutable.append(hostPath);
+
+ return true;
+}
+
+#endif // FEATURE_PAL
+
+HRESULT SymbolReader::PrepareSymbolReader()
+{
+ static bool attemptedSymbolReaderPreparation = false;
+ if (attemptedSymbolReaderPreparation)
+ {
+ // If we already tried to set up the symbol reader, we won't try again.
+ return E_FAIL;
+ }
+
+ attemptedSymbolReaderPreparation = true;
+
+ std::string absolutePath;
+ std::string coreClrPath;
+ HRESULT Status;
+
+#ifdef FEATURE_PAL
+ coreClrPath = g_ExtServices->GetCoreClrDirectory();
+ if (!GetAbsolutePath(coreClrPath.c_str(), absolutePath))
+ {
+ ExtErr("Error: Failed to get coreclr absolute path\n");
+ return E_FAIL;
+ }
+ coreClrPath.append(DIRECTORY_SEPARATOR_STR_A);
+ coreClrPath.append(MAIN_CLR_DLL_NAME_A);
+#else
+ ULONG index;
+ Status = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, &index, NULL);
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Can't find coreclr module\n");
+ return Status;
+ }
+ ArrayHolder<char> szModuleName = new char[MAX_LONGPATH + 1];
+ Status = g_ExtSymbols->GetModuleNames(index, 0, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL);
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Failed to get coreclr module name\n");
+ return Status;
+ }
+ coreClrPath = szModuleName;
+
+ // Parse off the module name to get just the path
+ size_t pos = coreClrPath.rfind(DIRECTORY_SEPARATOR_CHAR_A);
+ if (pos == std::string::npos)
+ {
+ ExtErr("Error: Failed to parse coreclr module name\n");
+ return E_FAIL;
+ }
+ absolutePath.assign(coreClrPath, 0, pos);
+#endif // FEATURE_PAL
+
+ HMODULE coreclrLib = LoadLibraryA(coreClrPath.c_str());
+ if (coreclrLib == nullptr)
+ {
+ ExtErr("Error: Failed to load %s\n", coreClrPath.c_str());
+ return E_FAIL;
+ }
+
+ void *hostHandle;
+ unsigned int domainId;
+ coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)GetProcAddress(coreclrLib, "coreclr_initialize");
+ if (initializeCoreCLR == nullptr)
+ {
+ ExtErr("Error: coreclr_initialize not found\n");
+ return E_FAIL;
+ }
+
+ std::string tpaList;
+ AddFilesFromDirectoryToTpaList(absolutePath.c_str(), tpaList);
+
+ const char *propertyKeys[] = {
+ "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS",
+ "NATIVE_DLL_SEARCH_DIRECTORIES", "AppDomainCompatSwitch"};
+
+ const char *propertyValues[] = {// TRUSTED_PLATFORM_ASSEMBLIES
+ tpaList.c_str(),
+ // APP_PATHS
+ absolutePath.c_str(),
+ // APP_NI_PATHS
+ absolutePath.c_str(),
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ absolutePath.c_str(),
+ // AppDomainCompatSwitch
+ "UseLatestBehaviorWhenTFMNotSpecified"};
+
+ std::string entryPointExecutablePath;
+ if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
+ {
+ ExtErr("Could not get full path to current executable");
+ return E_FAIL;
+ }
+
+ Status = initializeCoreCLR(entryPointExecutablePath.c_str(), "sos",
+ sizeof(propertyKeys) / sizeof(propertyKeys[0]), propertyKeys, propertyValues, &hostHandle, &domainId);
+
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Fail to initialize CoreCLR %08x\n", Status);
+ return Status;
+ }
+
+ coreclr_create_delegate_ptr createDelegate = (coreclr_create_delegate_ptr)GetProcAddress(coreclrLib, "coreclr_create_delegate");
+ if (createDelegate == nullptr)
+ {
+ ExtErr("Error: coreclr_create_delegate not found\n");
+ return E_FAIL;
+ }
+
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "LoadSymbolsForModule", (void **)&loadSymbolsForModuleDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "Dispose", (void **)&disposeDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "ResolveSequencePoint", (void **)&resolveSequencePointDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLocalVariableName", (void **)&getLocalVariableNameDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLineByILOffset", (void **)&getLineByILOffsetDelegate));
+
+ return Status;
+}
+
+HRESULT SymbolReader::GetLineByILOffset(___in mdMethodDef methodToken, ___in ULONG64 ilOffset,
+ ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(getLineByILOffsetDelegate != nullptr);
+
+ BSTR bstrFileName = SysAllocStringLen(0, MAX_LONGPATH);
+ if (bstrFileName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+ // Source lines with 0xFEEFEE markers are filtered out on the managed side.
+ if ((getLineByILOffsetDelegate(m_symbolReaderHandle, methodToken, ilOffset, pLinenum, &bstrFileName) == FALSE) || (*pLinenum == 0))
+ {
+ SysFreeString(bstrFileName);
+ return E_FAIL;
+ }
+ wcscpy_s(pwszFileName, cchFileName, bstrFileName);
+ SysFreeString(bstrFileName);
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
+ ToRelease<ISymUnmanagedMethod> pSymMethod(NULL);
+ IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
+
+ ULONG32 seqPointCount = 0;
+ IfFailRet(pSymMethod->GetSequencePointCount(&seqPointCount));
+
+ if (seqPointCount == 0)
+ return E_FAIL;
+
+ // allocate memory for the objects to be fetched
+ ArrayHolder<ULONG32> offsets(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> lines(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> columns(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> endlines(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> endcolumns(new ULONG32[seqPointCount]);
+ ArrayHolder<ToRelease<ISymUnmanagedDocument>> documents(new ToRelease<ISymUnmanagedDocument>[seqPointCount]);
+
+ ULONG32 realSeqPointCount = 0;
+ IfFailRet(pSymMethod->GetSequencePoints(seqPointCount, &realSeqPointCount, offsets, &(documents[0]), lines, columns, endlines, endcolumns));
+
+ const ULONG32 HiddenLine = 0x00feefee;
+ int bestSoFar = -1;
+
+ for (int i = 0; i < (int)realSeqPointCount; i++)
+ {
+ if (offsets[i] > ilOffset)
+ break;
+
+ if (lines[i] != HiddenLine)
+ bestSoFar = i;
+ }
+
+ if (bestSoFar != -1)
+ {
+ ULONG32 cchNeeded = 0;
+ IfFailRet(documents[bestSoFar]->GetURL(cchFileName, &cchNeeded, pwszFileName));
+
+ *pLinenum = lines[bestSoFar];
+ return S_OK;
+ }
+#endif // FEATURE_PAL
+
+ return E_FAIL;
+}
+
+HRESULT SymbolReader::GetNamedLocalVariable(___in ISymUnmanagedScope * pScope, ___in ICorDebugILFrame * pILFrame, ___in mdMethodDef methodToken,
+ ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ICorDebugValue** ppValue)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(getLocalVariableNameDelegate != nullptr);
+
+ BSTR wszParamName = SysAllocStringLen(0, mdNameLen);
+ if (wszParamName == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (getLocalVariableNameDelegate(m_symbolReaderHandle, methodToken, localIndex, &wszParamName) == FALSE)
+ {
+ SysFreeString(wszParamName);
+ return E_FAIL;
+ }
+
+ wcscpy_s(paramName, paramNameLen, wszParamName);
+ SysFreeString(wszParamName);
+
+ if (FAILED(pILFrame->GetLocalVariable(localIndex, ppValue)) || (*ppValue == NULL))
+ {
+ *ppValue = NULL;
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
+ if (pScope == NULL)
+ {
+ ToRelease<ISymUnmanagedMethod> pSymMethod;
+ IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
+
+ ToRelease<ISymUnmanagedScope> pScope;
+ IfFailRet(pSymMethod->GetRootScope(&pScope));
+
+ return GetNamedLocalVariable(pScope, pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue);
+ }
+ else
+ {
+ ULONG32 numVars = 0;
+ IfFailRet(pScope->GetLocals(0, &numVars, NULL));
+
+ ArrayHolder<ISymUnmanagedVariable*> pLocals = new ISymUnmanagedVariable*[numVars];
+ IfFailRet(pScope->GetLocals(numVars, &numVars, pLocals));
+
+ for (ULONG i = 0; i < numVars; i++)
+ {
+ ULONG32 varIndexInMethod = 0;
+ if (SUCCEEDED(pLocals[i]->GetAddressField1(&varIndexInMethod)))
+ {
+ if (varIndexInMethod != localIndex)
+ continue;
+
+ ULONG32 nameLen = 0;
+ if (FAILED(pLocals[i]->GetName(paramNameLen, &nameLen, paramName)))
+ swprintf_s(paramName, paramNameLen, W("local_%d\0"), localIndex);
+
+ if (SUCCEEDED(pILFrame->GetLocalVariable(varIndexInMethod, ppValue)) && (*ppValue != NULL))
+ {
+ for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
+ return S_OK;
+ }
+ else
+ {
+ *ppValue = NULL;
+ for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
+ return E_FAIL;
+ }
+ }
+ }
+
+ ULONG32 numChildren = 0;
+ IfFailRet(pScope->GetChildren(0, &numChildren, NULL));
+
+ ArrayHolder<ISymUnmanagedScope*> pChildren = new ISymUnmanagedScope*[numChildren];
+ IfFailRet(pScope->GetChildren(numChildren, &numChildren, pChildren));
+
+ for (ULONG i = 0; i < numChildren; i++)
+ {
+ if (SUCCEEDED(GetNamedLocalVariable(pChildren[i], pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue)))
+ {
+ for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
+ return S_OK;
+ }
+ }
+
+ for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
+ }
+#endif // FEATURE_PAL
+
+ return E_FAIL;
+}
+
+HRESULT SymbolReader::GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName,
+ ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue)
+{
+ HRESULT Status = S_OK;
+
+ *ppValue = NULL;
+ paramName[0] = L'\0';
+
+ ToRelease<ICorDebugILFrame> pILFrame;
+ IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
+
+ ToRelease<ICorDebugFunction> pFunction;
+ IfFailRet(pFrame->GetFunction(&pFunction));
+
+ mdMethodDef methodDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pFunction->GetClass(&pClass));
+ IfFailRet(pFunction->GetModule(&pModule));
+ IfFailRet(pFunction->GetToken(&methodDef));
+
+ return GetNamedLocalVariable(NULL, pILFrame, methodDef, localIndex, paramName, paramNameLen, ppValue);
+}
+
+HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* pToken, ___out ULONG32* pIlOffset)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(resolveSequencePointDelegate != nullptr);
+
+ char szName[mdNameLen];
+ if (WideCharToMultiByte(CP_ACP, 0, pFilename, (int)(_wcslen(pFilename) + 1), szName, mdNameLen, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ if (resolveSequencePointDelegate(m_symbolReaderHandle, szName, lineNumber, pToken, pIlOffset) == FALSE)
+ {
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
+ ULONG32 cDocs = 0;
+ ULONG32 cDocsNeeded = 0;
+ ArrayHolder<ToRelease<ISymUnmanagedDocument>> pDocs = NULL;
+
+ IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, NULL));
+ pDocs = new ToRelease<ISymUnmanagedDocument>[cDocsNeeded];
+ cDocs = cDocsNeeded;
+ IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, &(pDocs[0])));
+
+ ULONG32 filenameLen = (ULONG32) _wcslen(pFilename);
+
+ for (ULONG32 i = 0; i < cDocs; i++)
+ {
+ ULONG32 cchUrl = 0;
+ ULONG32 cchUrlNeeded = 0;
+ ArrayHolder<WCHAR> pUrl = NULL;
+ IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
+ pUrl = new WCHAR[cchUrlNeeded];
+ cchUrl = cchUrlNeeded;
+ IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
+
+ // If the URL is exactly as long as the filename then compare the two names directly
+ if (cchUrl-1 == filenameLen)
+ {
+ if (0!=_wcsicmp(pUrl, pFilename))
+ continue;
+ }
+ // does the URL suffix match [back]slash + filename?
+ else if (cchUrl-1 > filenameLen)
+ {
+ WCHAR* slashLocation = pUrl + (cchUrl - filenameLen - 2);
+ if (*slashLocation != L'\\' && *slashLocation != L'/')
+ continue;
+ if (0 != _wcsicmp(slashLocation+1, pFilename))
+ continue;
+ }
+ // URL is too short to match
+ else
+ continue;
+
+ ULONG32 closestLine = 0;
+ if (FAILED(pDocs[i]->FindClosestLine(lineNumber, &closestLine)))
+ continue;
+
+ ToRelease<ISymUnmanagedMethod> pSymUnmanagedMethod;
+ IfFailRet(m_pSymReader->GetMethodFromDocumentPosition(pDocs[i], closestLine, 0, &pSymUnmanagedMethod));
+ IfFailRet(pSymUnmanagedMethod->GetToken(pToken));
+ IfFailRet(pSymUnmanagedMethod->GetOffset(pDocs[i], closestLine, 0, pIlOffset));
+
+ // If this IL
+ if (*pIlOffset == -1)
+ {
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+#endif // FEATURE_PAL
+
+ return E_FAIL;
+}
+
+static void AddAssemblyName(WString& methodOutput, CLRDATA_ADDRESS mdesc)
+{
+ DacpMethodDescData mdescData;
+ if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
+ {
+ DacpModuleData dmd;
+ if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
+ {
+ ToRelease<IXCLRDataModule> pModule;
+ if (SUCCEEDED(g_sos->GetModule(mdescData.ModulePtr, &pModule)))
+ {
+ ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH + 1];
+ ULONG32 nameLen = 0;
+ if (SUCCEEDED(pModule->GetFileName(MAX_LONGPATH, &nameLen, wszFileName)))
+ {
+ if (wszFileName[0] != W('\0'))
+ {
+ WCHAR *pJustName = _wcsrchr(wszFileName, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pJustName == NULL)
+ pJustName = wszFileName - 1;
+ methodOutput += (pJustName + 1);
+ methodOutput += W("!");
+ }
+ }
+ }
+ }
+ }
+}
+
+WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackWalk, BOOL bAssemblyName)
+{
+ TADDR vtAddr;
+ MOVE(vtAddr, frameAddr);
+
+ WString frameOutput;
+ frameOutput += W("[");
+
+ if (SUCCEEDED(g_sos->GetFrameName(TO_CDADDR(vtAddr), mdNameLen, g_mdName, NULL)))
+ frameOutput += g_mdName;
+ else
+ frameOutput += W("Frame");
+
+ frameOutput += WString(W(": ")) + Pointer(frameAddr) + W("] ");
+
+ // Print the frame's associated function info, if it has any.
+ CLRDATA_ADDRESS mdesc = 0;
+ if (SUCCEEDED(g_sos->GetMethodDescPtrFromFrame(frameAddr, &mdesc)))
+ {
+ if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
+ {
+ if (bAssemblyName)
+ {
+ AddAssemblyName(frameOutput, mdesc);
+ }
+
+ frameOutput += g_mdName;
+ }
+ else
+ {
+ frameOutput += W("<unknown method>");
+ }
+ }
+ else if (pStackWalk)
+ {
+ // The Frame did not have direct function info, so try to get the method instance
+ // (in this case a MethodDesc), and read the name from it.
+ ToRelease<IXCLRDataFrame> frame;
+ if (SUCCEEDED(pStackWalk->GetFrame(&frame)))
+ {
+ ToRelease<IXCLRDataMethodInstance> methodInstance;
+ if (SUCCEEDED(frame->GetMethodInstance(&methodInstance)))
+ {
+ // GetName can return S_FALSE if mdNameLen is not large enough. However we are already
+ // passing a pretty big buffer in. If this returns S_FALSE (meaning the buffer is too
+ // small) then we should not output it anyway.
+ if (methodInstance->GetName(0, mdNameLen, NULL, g_mdName) == S_OK)
+ frameOutput += g_mdName;
+ }
+ }
+ }
+
+ return frameOutput;
+}
+
+WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssemblyName, BOOL bDisplacement)
+{
+ ULONG linenum;
+ WString methodOutput;
+ CLRDATA_ADDRESS mdesc = 0;
+
+ if (FAILED(g_sos->GetMethodDescPtrFromIP(ip, &mdesc)))
+ {
+ methodOutput = W("<unknown>");
+ }
+ else
+ {
+ DacpMethodDescData mdescData;
+ if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
+ {
+ if (bAssemblyName)
+ {
+ AddAssemblyName(methodOutput, mdesc);
+ }
+
+ methodOutput += g_mdName;
+
+ if (bDisplacement)
+ {
+ if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
+ {
+ ULONG64 disp = (ip - mdescData.NativeCodeAddr);
+ if (disp)
+ {
+ methodOutput += W(" + ");
+ methodOutput += Decimal(disp);
+ }
+ }
+ }
+ }
+ else if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
+ {
+ DacpModuleData dmd;
+ BOOL bModuleNameWorked = FALSE;
+ ULONG64 addrInModule = ip;
+ if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
+ {
+ CLRDATA_ADDRESS peFileBase = 0;
+ if (SUCCEEDED(g_sos->GetPEFileBase(dmd.File, &peFileBase)))
+ {
+ if (peFileBase)
+ {
+ addrInModule = peFileBase;
+ }
+ }
+ }
+ ULONG Index;
+ ULONG64 moduleBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &moduleBase)))
+ {
+ ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleNames(Index, moduleBase, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL)))
+ {
+ MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, g_mdName, _countof(g_mdName));
+ methodOutput += g_mdName;
+ methodOutput += W("!");
+ }
+ }
+ methodOutput += W("<unknown method>");
+ }
+ else
+ {
+ methodOutput = W("<unknown>");
+ }
+
+ ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH];
+ if (!bSuppressLines &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, wszFileName, MAX_LONGPATH)))
+ {
+ methodOutput += WString(W(" [")) + wszFileName + W(" @ ") + Decimal(linenum) + W("]");
+ }
+ }
+
+ return methodOutput;
+}
+
+HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount)
+{
+ if (ppRefs == NULL || pRefCnt == NULL)
+ return E_POINTER;
+
+ if (pErrCount)
+ *pErrCount = 0;
+
+ *pRefCnt = 0;
+ unsigned int count = 0;
+ ToRelease<ISOSStackRefEnum> pEnum;
+ if (FAILED(g_sos->GetStackReferences(osID, &pEnum)) || FAILED(pEnum->GetCount(&count)))
+ {
+ ExtOut("Failed to enumerate GC references.\n");
+ return E_FAIL;
+ }
+
+ *ppRefs = new SOSStackRefData[count];
+ if (FAILED(pEnum->Next(count, *ppRefs, pRefCnt)))
+ {
+ ExtOut("Failed to enumerate GC references.\n");
+ return E_FAIL;
+ }
+
+ SOS_Assert(count == *pRefCnt);
+
+ // Enumerate errors found. Any bad HRESULT recieved while enumerating errors is NOT a fatal error.
+ // Hence we return S_FALSE if we encounter one.
+
+ if (ppErrors && pErrCount)
+ {
+ ToRelease<ISOSStackRefErrorEnum> pErrors;
+ if (FAILED(pEnum->EnumerateErrors(&pErrors)))
+ {
+ ExtOut("Failed to enumerate GC reference errors.\n");
+ return S_FALSE;
+ }
+
+ if (FAILED(pErrors->GetCount(&count)))
+ {
+ ExtOut("Failed to enumerate GC reference errors.\n");
+ return S_FALSE;
+ }
+
+ *ppErrors = new SOSStackRefError[count];
+ if (FAILED(pErrors->Next(count, *ppErrors, pErrCount)))
+ {
+ ExtOut("Failed to enumerate GC reference errors.\n");
+ *pErrCount = 0;
+ return S_FALSE;
+ }
+
+ SOS_Assert(count == *pErrCount);
+ }
+ return S_OK;
+}
+
+
+InternalFrameManager::InternalFrameManager() : m_cInternalFramesActual(0), m_iInternalFrameCur(0) {}
+
+HRESULT InternalFrameManager::Init(ICorDebugThread3 * pThread3)
+{
+ _ASSERTE(pThread3 != NULL);
+
+ return pThread3->GetActiveInternalFrames(
+ _countof(m_rgpInternalFrame2),
+ &m_cInternalFramesActual,
+ &(m_rgpInternalFrame2[0]));
+}
+
+HRESULT InternalFrameManager::PrintPrecedingInternalFrames(ICorDebugFrame * pFrame)
+{
+ HRESULT Status;
+
+ for (; m_iInternalFrameCur < m_cInternalFramesActual; m_iInternalFrameCur++)
+ {
+ BOOL bIsCloser = FALSE;
+ IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->IsCloserToLeaf(pFrame, &bIsCloser));
+
+ if (!bIsCloser)
+ {
+ // Current internal frame is now past pFrame, so we're done
+ return S_OK;
+ }
+
+ IfFailRet(PrintCurrentInternalFrame());
+ }
+
+ // Exhausted list of internal frames. Done!
+ return S_OK;
+}
+
+HRESULT InternalFrameManager::PrintCurrentInternalFrame()
+{
+ _ASSERTE(m_iInternalFrameCur < m_cInternalFramesActual);
+
+ HRESULT Status;
+
+ CORDB_ADDRESS address;
+ IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->GetAddress(&address));
+
+ ToRelease<ICorDebugInternalFrame> pInternalFrame;
+ IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame));
+
+ CorDebugInternalFrameType type;
+ IfFailRet(pInternalFrame->GetFrameType(&type));
+
+ LPCSTR szFrameType = NULL;
+ switch(type)
+ {
+ default:
+ szFrameType = "Unknown internal frame.";
+ break;
+
+ case STUBFRAME_M2U:
+ szFrameType = "Managed to Unmanaged transition";
+ break;
+
+ case STUBFRAME_U2M:
+ szFrameType = "Unmanaged to Managed transition";
+ break;
+
+ case STUBFRAME_APPDOMAIN_TRANSITION:
+ szFrameType = "AppDomain transition";
+ break;
+
+ case STUBFRAME_LIGHTWEIGHT_FUNCTION:
+ szFrameType = "Lightweight function";
+ break;
+
+ case STUBFRAME_FUNC_EVAL:
+ szFrameType = "Function evaluation";
+ break;
+
+ case STUBFRAME_INTERNALCALL:
+ szFrameType = "Internal call";
+ break;
+
+ case STUBFRAME_CLASS_INIT:
+ szFrameType = "Class initialization";
+ break;
+
+ case STUBFRAME_EXCEPTION:
+ szFrameType = "Exception";
+ break;
+
+ case STUBFRAME_SECURITY:
+ szFrameType = "Security";
+ break;
+
+ case STUBFRAME_JIT_COMPILATION:
+ szFrameType = "JIT Compilation";
+ break;
+ }
+
+ DMLOut("%p %s ", SOS_PTR(address), SOS_PTR(0));
+ ExtOut("[%s: %p]\n", szFrameType, SOS_PTR(address));
+
+ return S_OK;
+}
diff --git a/src/ToolBox/SOS/Strike/util.h b/src/ToolBox/SOS/Strike/util.h
new file mode 100644
index 0000000000..f444c9fcb2
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/util.h
@@ -0,0 +1,3292 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __util_h__
+#define __util_h__
+
+#define LIMITED_METHOD_CONTRACT
+
+// So we can use the PAL_TRY_NAKED family of macros without dependencies on utilcode.
+inline void RestoreSOToleranceState() {}
+
+#include <cor.h>
+#include <corsym.h>
+#include <clrdata.h>
+#include <palclr.h>
+#include <metahost.h>
+#include <new>
+
+#if !defined(FEATURE_PAL)
+#include <dia2.h>
+#endif
+
+#ifdef STRIKE
+#if defined(_MSC_VER)
+#pragma warning(disable:4200)
+#pragma warning(default:4200)
+#endif
+#include "data.h"
+#endif //STRIKE
+
+#include "cordebug.h"
+#include "static_assert.h"
+
+typedef LPCSTR LPCUTF8;
+typedef LPSTR LPUTF8;
+
+DECLARE_HANDLE(OBJECTHANDLE);
+
+struct IMDInternalImport;
+
+#if defined(_TARGET_WIN64_)
+#define WIN64_8SPACES ""
+#define WIN86_8SPACES " "
+#define POINTERSIZE "16"
+#define POINTERSIZE_HEX 16
+#define POINTERSIZE_BYTES 8
+#define POINTERSIZE_TYPE "I64"
+#else
+#define WIN64_8SPACES " "
+#define WIN86_8SPACES ""
+#define POINTERSIZE "8"
+#define POINTERSIZE_HEX 8
+#define POINTERSIZE_BYTES 4
+#define POINTERSIZE_TYPE "I32"
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4510 4512 4610)
+#endif
+
+#ifndef _ASSERTE
+#ifdef _DEBUG
+#define _ASSERTE(expr) \
+ do { if (!(expr) ) { ExtErr("_ASSERTE fired:\n\t%s\n", #expr); if (IsDebuggerPresent()) DebugBreak(); } } while (0)
+#else
+#define _ASSERTE(x)
+#endif
+#endif // ASSERTE
+
+#ifdef _DEBUG
+#define ASSERT_CHECK(expr, msg, reason) \
+ do { if (!(expr) ) { ExtOut(reason); ExtOut(msg); ExtOut(#expr); DebugBreak(); } } while (0)
+#endif
+
+// PREFIX macros - Begin
+
+// SOS does not have support for Contracts. Therefore we needed to duplicate
+// some of the PREFIX infrastructure from inc\check.h in here.
+
+// Issue - PREFast_:510 v4.51 does not support __assume(0)
+#if (defined(_MSC_VER) && !defined(_PREFAST_)) || defined(_PREFIX_)
+#if defined(_AMD64_)
+// Empty methods that consist of UNREACHABLE() result in a zero-sized declspec(noreturn) method
+// which causes the pdb file to make the next method declspec(noreturn) as well, thus breaking BBT
+// Remove when we get a VC compiler that fixes VSW 449170
+# define __UNREACHABLE() DebugBreak(); __assume(0);
+#else
+# define __UNREACHABLE() __assume(0)
+#endif
+#else
+#define __UNREACHABLE() do { } while(true)
+#endif
+
+
+#if defined(_PREFAST_) || defined(_PREFIX_)
+#define COMPILER_ASSUME_MSG(_condition, _message) if (!(_condition)) __UNREACHABLE();
+#else
+
+#if defined(DACCESS_COMPILE)
+#define COMPILER_ASSUME_MSG(_condition, _message) do { } while (0)
+#else
+
+#if defined(_DEBUG)
+#define COMPILER_ASSUME_MSG(_condition, _message) \
+ ASSERT_CHECK(_condition, _message, "Compiler optimization assumption invalid")
+#else
+#define COMPILER_ASSUME_MSG(_condition, _message) __assume(_condition)
+#endif // _DEBUG
+
+#endif // DACCESS_COMPILE
+
+#endif // _PREFAST_ || _PREFIX_
+
+#define PREFIX_ASSUME(_condition) \
+ COMPILER_ASSUME_MSG(_condition, "")
+
+// PREFIX macros - End
+
+class MethodTable;
+
+#define MD_NOT_YET_LOADED ((DWORD_PTR)-1)
+/*
+ * HANDLES
+ *
+ * The default type of handle is a strong handle.
+ *
+ */
+#define HNDTYPE_DEFAULT HNDTYPE_STRONG
+#define HNDTYPE_WEAK_DEFAULT HNDTYPE_WEAK_LONG
+#define HNDTYPE_WEAK_SHORT (0)
+#define HNDTYPE_WEAK_LONG (1)
+#define HNDTYPE_STRONG (2)
+#define HNDTYPE_PINNED (3)
+#define HNDTYPE_VARIABLE (4)
+#define HNDTYPE_REFCOUNTED (5)
+#define HNDTYPE_DEPENDENT (6)
+#define HNDTYPE_ASYNCPINNED (7)
+#define HNDTYPE_SIZEDREF (8)
+#define HNDTYPE_WEAK_WINRT (9)
+
+// Anything above this we consider abnormal and stop processing heap information
+const int nMaxHeapSegmentCount = 1000;
+
+class BaseObject
+{
+ MethodTable *m_pMethTab;
+};
+
+
+const BYTE gElementTypeInfo[] = {
+#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) s,
+#include "cortypeinfo.h"
+#undef TYPEINFO
+};
+
+typedef struct tagLockEntry
+{
+ tagLockEntry *pNext; // next entry
+ tagLockEntry *pPrev; // prev entry
+ DWORD dwULockID;
+ DWORD dwLLockID; // owning lock
+ WORD wReaderLevel; // reader nesting level
+} LockEntry;
+
+#define MAX_CLASSNAME_LENGTH 1024
+
+enum EEFLAVOR {UNKNOWNEE, MSCOREE, MSCORWKS, MSCOREND};
+
+#include "sospriv.h"
+extern IXCLRDataProcess *g_clrData;
+extern ISOSDacInterface *g_sos;
+
+#include "dacprivate.h"
+
+interface ICorDebugProcess;
+extern ICorDebugProcess * g_pCorDebugProcess;
+
+// This class is templated for easy modification. We may need to update the CachedString
+// or related classes to use WCHAR instead of char in the future.
+template <class T, int count, int size>
+class StaticData
+{
+public:
+ StaticData()
+ {
+ for (int i = 0; i < count; ++i)
+ InUse[i] = false;
+ }
+
+ // Whether the individual data pointers in the cache are in use.
+ bool InUse[count];
+
+ // The actual data itself.
+ T Data[count][size];
+
+ // The number of arrays in the cache.
+ static const int Count;
+
+ // The size of each individual array.
+ static const int Size;
+};
+
+class CachedString
+{
+public:
+ CachedString();
+ CachedString(const CachedString &str);
+ ~CachedString();
+
+ const CachedString &operator=(const CachedString &str);
+
+ // Returns the capacity of this string.
+ size_t GetStrLen() const
+ {
+ return mSize;
+ }
+
+ // Returns a mutable character pointer. Be sure not to write past the
+ // length of this string.
+ inline operator char *()
+ {
+ return mPtr;
+ }
+
+ // Returns a const char representation of this string.
+ inline operator const char *() const
+ {
+ return GetPtr();
+ }
+
+ // To ensure no AV's, any time a constant pointer is requested, we will
+ // return an empty string "" if we hit an OOM. This will only happen
+ // if we hit an OOM and do not check for it before using the string.
+ // If you request a non-const char pointer out of this class, it may be
+ // null (see operator char *).
+ inline const char *GetPtr() const
+ {
+ if (!mPtr || IsOOM())
+ return "";
+
+ return mPtr;
+ }
+
+ // Returns true if we ran out of memory trying to allocate the string
+ // or the refcount.
+ bool IsOOM() const
+ {
+ return mIndex == -2;
+ }
+
+ // allocate a string of the specified size. this will Clear() any
+ // previously allocated string. call IsOOM() to check for failure.
+ void Allocate(int size);
+
+private:
+ // Copies rhs into this string.
+ void Copy(const CachedString &rhs);
+
+ // Clears this string, releasing any underlying memory.
+ void Clear();
+
+ // Creates a new string.
+ void Create();
+
+ // Sets an out of memory state.
+ void SetOOM();
+
+private:
+ char *mPtr;
+
+ // The reference count. This may be null if there is only one copy
+ // of this string.
+ mutable unsigned int *mRefCount;
+
+ // mIndex contains the index of the cached pointer we are using, or:
+ // ~0 - poison value we initialize it to for debugging purposes
+ // -1 - mPtr points to a pointer we have new'ed
+ // -2 - We hit an oom trying to allocate either mCount or mPtr
+ int mIndex;
+
+ // contains the size of current string
+ int mSize;
+
+private:
+ static StaticData<char, 4, 1024> cache;
+};
+
+// Things in this namespace should not be directly accessed/called outside of
+// the output-related functions.
+namespace Output
+{
+ extern unsigned int g_bSuppressOutput;
+ extern unsigned int g_Indent;
+ extern unsigned int g_DMLEnable;
+ extern bool g_bDbgOutput;
+ extern bool g_bDMLExposed;
+
+ inline bool IsOutputSuppressed()
+ { return g_bSuppressOutput > 0; }
+
+ inline void ResetIndent()
+ { g_Indent = 0; }
+
+ inline void SetDebugOutputEnabled(bool enabled)
+ { g_bDbgOutput = enabled; }
+
+ inline bool IsDebugOutputEnabled()
+ { return g_bDbgOutput; }
+
+ inline void SetDMLExposed(bool exposed)
+ { g_bDMLExposed = exposed; }
+
+ inline bool IsDMLExposed()
+ { return g_bDMLExposed; }
+
+ enum FormatType
+ {
+ DML_None,
+ DML_MethodTable,
+ DML_MethodDesc,
+ DML_EEClass,
+ DML_Module,
+ DML_IP,
+ DML_Object,
+ DML_Domain,
+ DML_Assembly,
+ DML_ThreadID,
+ DML_ValueClass,
+ DML_DumpHeapMT,
+ DML_ListNearObj,
+ DML_ThreadState,
+ DML_PrintException,
+ DML_RCWrapper,
+ DML_CCWrapper,
+ DML_ManagedVar,
+ };
+
+ /**********************************************************************\
+ * This function builds a DML string for a ValueClass. If DML is *
+ * enabled, this function returns a DML string based on the format *
+ * type. Otherwise this returns a string containing only the hex value *
+ * of addr. *
+ * *
+ * Params: *
+ * mt - the method table of the ValueClass *
+ * addr - the address of the ValueClass *
+ * type - the format type to use to output this object *
+ * fill - whether or not to pad the hex value with zeros *
+ * *
+ \**********************************************************************/
+ CachedString BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill = true);
+
+
+ /**********************************************************************\
+ * This function builds a DML string for an object. If DML is enabled, *
+ * this function returns a DML string based on the format type. *
+ * Otherwise this returns a string containing only the hex value of *
+ * addr. *
+ * *
+ * Params: *
+ * addr - the address of the object *
+ * type - the format type to use to output this object *
+ * fill - whether or not to pad the hex value with zeros *
+ * *
+ \**********************************************************************/
+ CachedString BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill = true);
+
+ /**********************************************************************\
+ * This function builds a DML string for an managed variable name. *
+ * If DML is enabled, this function returns a DML string that will *
+ * enable the expansion of that managed variable using the !ClrStack *
+ * command to display the variable's fields, otherwise it will just *
+ * return the variable's name as a string.
+ * *
+ * Params: *
+ * expansionName - the current variable expansion string *
+ * frame - the frame that contains the variable of interest *
+ * simpleName - simple name of the managed variable *
+ * *
+ \**********************************************************************/
+ CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type);
+ CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type); //used for array indices (simpleName = "[<indexInArray>]")
+}
+
+class NoOutputHolder
+{
+public:
+ NoOutputHolder(BOOL bSuppress = TRUE);
+ ~NoOutputHolder();
+
+private:
+ BOOL mSuppress;
+};
+
+class EnableDMLHolder
+{
+public:
+ EnableDMLHolder(BOOL enable);
+ ~EnableDMLHolder();
+
+private:
+ BOOL mEnable;
+};
+
+size_t CountHexCharacters(CLRDATA_ADDRESS val);
+
+// Normal output.
+void DMLOut(PCSTR format, ...); /* Prints out DML strings. */
+void IfDMLOut(PCSTR format, ...); /* Prints given DML string ONLY if DML is enabled; prints nothing otherwise. */
+void ExtOut(PCSTR Format, ...); /* Prints out to ExtOut (no DML). */
+void ExtWarn(PCSTR Format, ...); /* Prints out to ExtWarn (no DML). */
+void ExtErr(PCSTR Format, ...); /* Prints out to ExtErr (no DML). */
+void ExtDbgOut(PCSTR Format, ...); /* Prints out to ExtOut in a checked build (no DML). */
+void WhitespaceOut(int count); /* Prints out "count" number of spaces in the output. */
+
+// Change indent for ExtOut
+inline void IncrementIndent() { Output::g_Indent++; }
+inline void DecrementIndent() { if (Output::g_Indent > 0) Output::g_Indent--; }
+inline void ExtOutIndent() { WhitespaceOut(Output::g_Indent << 2); }
+
+// DML Generation Methods
+#define DMLListNearObj(addr) Output::BuildHexValue(addr, Output::DML_ListNearObj).GetPtr()
+#define DMLDumpHeapMT(addr) Output::BuildHexValue(addr, Output::DML_DumpHeapMT).GetPtr()
+#define DMLMethodTable(addr) Output::BuildHexValue(addr, Output::DML_MethodTable).GetPtr()
+#define DMLMethodDesc(addr) Output::BuildHexValue(addr, Output::DML_MethodDesc).GetPtr()
+#define DMLClass(addr) Output::BuildHexValue(addr, Output::DML_EEClass).GetPtr()
+#define DMLModule(addr) Output::BuildHexValue(addr, Output::DML_Module).GetPtr()
+#define DMLIP(ip) Output::BuildHexValue(ip, Output::DML_IP).GetPtr()
+#define DMLObject(addr) Output::BuildHexValue(addr, Output::DML_Object).GetPtr()
+#define DMLDomain(addr) Output::BuildHexValue(addr, Output::DML_Domain).GetPtr()
+#define DMLAssembly(addr) Output::BuildHexValue(addr, Output::DML_Assembly).GetPtr()
+#define DMLThreadID(id) Output::BuildHexValue(id, Output::DML_ThreadID, false).GetPtr()
+#define DMLValueClass(mt, addr) Output::BuildVCValue(mt, addr, Output::DML_ValueClass).GetPtr()
+#define DMLRCWrapper(addr) Output::BuildHexValue(addr, Output::DML_RCWrapper).GetPtr()
+#define DMLCCWrapper(addr) Output::BuildHexValue(addr, Output::DML_CCWrapper).GetPtr()
+#define DMLManagedVar(expansionName,frame,simpleName) Output::BuildManagedVarValue(expansionName, frame, simpleName, Output::DML_ManagedVar).GetPtr()
+
+bool IsDMLEnabled();
+
+
+#ifndef SOS_Assert
+#define SOS_Assert(x)
+#endif
+
+void ConvertToLower(__out_ecount(len) char *buffer, size_t len);
+
+extern const char * const DMLFormats[];
+int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill);
+
+// A simple string class for mutable strings. We cannot use STL, so this is a stand in replacement
+// for std::string (though it doesn't use the same interface).
+template <class T, size_t (__cdecl *LEN)(const T *), errno_t (__cdecl *COPY)(T *, size_t, const T * _Src)>
+class BaseString
+{
+public:
+ BaseString()
+ : mStr(0), mSize(0), mLength(0)
+ {
+ const size_t size = 64;
+
+ mStr = new T[size];
+ mSize = size;
+ mStr[0] = 0;
+ }
+
+ BaseString(const T *str)
+ : mStr(0), mSize(0), mLength(0)
+ {
+ CopyFrom(str, LEN(str));
+ }
+
+ BaseString(const BaseString<T, LEN, COPY> &rhs)
+ : mStr(0), mSize(0), mLength(0)
+ {
+ *this = rhs;
+ }
+
+ ~BaseString()
+ {
+ Clear();
+ }
+
+ const BaseString<T, LEN, COPY> &operator=(const BaseString<T, LEN, COPY> &rhs)
+ {
+ Clear();
+ CopyFrom(rhs.mStr, rhs.mLength);
+ return *this;
+ }
+
+ const BaseString<T, LEN, COPY> &operator=(const T *str)
+ {
+ Clear();
+ CopyFrom(str, LEN(str));
+ return *this;
+ }
+
+ const BaseString<T, LEN, COPY> &operator +=(const T *str)
+ {
+ size_t len = LEN(str);
+ CopyFrom(str, len);
+ return *this;
+ }
+
+ const BaseString<T, LEN, COPY> &operator +=(const BaseString<T, LEN, COPY> &str)
+ {
+ CopyFrom(str.mStr, str.mLength);
+ return *this;
+ }
+
+ BaseString<T, LEN, COPY> operator+(const T *str) const
+ {
+ return BaseString<T, LEN, COPY>(mStr, mLength, str, LEN(str));
+ }
+
+ BaseString<T, LEN, COPY> operator+(const BaseString<T, LEN, COPY> &str) const
+ {
+ return BaseString<T, LEN, COPY>(mStr, mLength, str.mStr, str.mLength);
+ }
+
+ operator const T *() const
+ {
+ return mStr;
+ }
+
+ const T *c_str() const
+ {
+ return mStr;
+ }
+
+ size_t GetLength() const
+ {
+ return mLength;
+ }
+
+private:
+ BaseString(const T * str1, size_t len1, const T * str2, size_t len2)
+ : mStr(0), mSize(0), mLength(0)
+ {
+ const size_t size = len1 + len2 + 1 + ((len1 + len2) >> 1);
+ mStr = new T[size];
+ mSize = size;
+
+ CopyFrom(str1, len1);
+ CopyFrom(str2, len2);
+ }
+
+ void Clear()
+ {
+ mLength = 0;
+ mSize = 0;
+ if (mStr)
+ {
+ delete [] mStr;
+ mStr = 0;
+ }
+ }
+
+ void CopyFrom(const T *str, size_t len)
+ {
+ if (mLength + len + 1 >= mSize)
+ Resize(mLength + len + 1);
+
+ COPY(mStr+mLength, mSize-mLength, str);
+ mLength += len;
+ }
+
+ void Resize(size_t size)
+ {
+ /* We always resize at least one half bigger than we need. When CopyFrom requests a resize
+ * it asks for the exact size that's needed to concatenate strings. However in practice
+ * it's common to add multiple strings together in a row, e.g.:
+ * String foo = "One " + "Two " + "Three " + "Four " + "\n";
+ * Ensuring the size of the string is bigger than we need, and that the minimum size is 64,
+ * we will cut down on a lot of needless resizes at the cost of a few bytes wasted in some
+ * cases.
+ */
+ size += size >> 1;
+ if (size < 64)
+ size = 64;
+
+ T *newStr = new T[size];
+
+ if (mStr)
+ {
+ COPY(newStr, size, mStr);
+ delete [] mStr;
+ }
+ else
+ {
+ newStr[0] = 0;
+ }
+
+ mStr = newStr;
+ mSize = size;
+ }
+private:
+ T *mStr;
+ size_t mSize, mLength;
+};
+
+typedef BaseString<char, strlen, strcpy_s> String;
+typedef BaseString<WCHAR, _wcslen, wcscpy_s> WString;
+
+
+template<class T>
+void Flatten(__out_ecount(len) T *data, unsigned int len)
+{
+ for (unsigned int i = 0; i < len; ++i)
+ if (data[i] < 32 || (data[i] > 126 && data[i] <= 255))
+ data[i] = '.';
+ data[len] = 0;
+}
+
+void Flatten(__out_ecount(len) char *data, unsigned int len);
+
+/* Formats for the Format class. We support the following formats:
+ * Pointer - Same as %p.
+ * Hex - Same as %x (same as %p, but does not output preceding zeros.
+ * PrefixHex - Same as %x, but prepends 0x.
+ * Decimal - Same as %d.
+ * Strings and wide strings don't use this.
+ */
+class Formats
+{
+public:
+ enum Format
+ {
+ Default,
+ Pointer,
+ Hex,
+ PrefixHex,
+ Decimal,
+ };
+};
+
+enum Alignment
+{
+ AlignLeft,
+ AlignRight
+};
+
+namespace Output
+{
+ /* Defines how a value will be printed. This class understands how to format
+ * and print values according to the format and DML settings provided.
+ * The raw templated class handles the pointer/integer case. Support for
+ * character arrays and wide character arrays are handled by template
+ * specializations.
+ *
+ * Note that this class is not used directly. Instead use the typedefs and
+ * macros which define the type of data you are outputing (for example ObjectPtr,
+ * MethodTablePtr, etc).
+ */
+ template <class T>
+ class Format
+ {
+ public:
+ Format(T value)
+ : mValue(value), mFormat(Formats::Default), mDml(Output::DML_None)
+ {
+ }
+
+ Format(T value, Formats::Format format, Output::FormatType dmlType)
+ : mValue(value), mFormat(format), mDml(dmlType)
+ {
+ }
+
+ Format(const Format<T> &rhs)
+ : mValue(rhs.mValue), mFormat(rhs.mFormat), mDml(rhs.mDml)
+ {
+ }
+
+ /* Prints out the value according to the Format and DML settings provided.
+ */
+ void Output() const
+ {
+ if (IsDMLEnabled() && mDml != Output::DML_None)
+ {
+ const int len = GetDMLWidth(mDml);
+ char *buffer = (char*)alloca(len);
+
+ BuildDML(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml);
+ DMLOut(buffer);
+ }
+ else
+ {
+ if (mFormat == Formats::Default || mFormat == Formats::Pointer)
+ {
+ ExtOut("%p", SOS_PTR(mValue));
+ }
+ else
+ {
+ const char *format = NULL;
+ if (mFormat == Formats::PrefixHex)
+ {
+ format = "0x%x";
+ }
+ else if (mFormat == Formats::Hex)
+ {
+ format = "%x";
+ }
+ else if (mFormat == Formats::Decimal)
+ {
+ format = "%d";
+ }
+
+ ExtOut(format, (__int32)mValue);
+ }
+
+ }
+ }
+
+ /* Prints out the value based on a specified width and alignment.
+ * Params:
+ * align - Whether the output should be left or right justified.
+ * width - The output width to fill.
+ * Note:
+ * This function guarantees that exactly width will be printed out (so if width is 24,
+ * exactly 24 characters will be printed), even if the output wouldn't normally fit
+ * in the space provided. This function makes no guarantees as to what part of the
+ * data will be printed in the case that width isn't wide enough.
+ */
+ void OutputColumn(Alignment align, int width) const
+ {
+ bool leftAlign = align == AlignLeft;
+ if (IsDMLEnabled() && mDml != Output::DML_None)
+ {
+ const int len = GetDMLColWidth(mDml, width);
+ char *buffer = (char*)alloca(len);
+
+ BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, leftAlign, width);
+ DMLOut(buffer);
+ }
+ else
+ {
+ int precision = GetPrecision();
+ if (mFormat == Formats::Default || mFormat == Formats::Pointer)
+ {
+ if (precision > width)
+ precision = width;
+
+ ExtOut(leftAlign ? "%-*.*p" : "%*.*p", width, precision, SOS_PTR(mValue));
+ }
+ else
+ {
+ const char *format = NULL;
+ if (mFormat == Formats::PrefixHex)
+ {
+ format = leftAlign ? "0x%-*.*x" : "0x%*.*x";
+ width -= 2;
+ }
+ else if (mFormat == Formats::Hex)
+ {
+ format = leftAlign ? "%-*.*x" : "%*.*x";
+ }
+ else if (mFormat == Formats::Decimal)
+ {
+ format = leftAlign ? "%-*.*d" : "%*.*d";
+ }
+
+ if (precision > width)
+ precision = width;
+
+ ExtOut(format, width, precision, (__int32)mValue);
+ }
+ }
+ }
+
+ /* Converts this object into a Wide char string. This allows you to write the following code:
+ * WString foo = L"bar " + ObjectPtr(obj);
+ * Where ObjectPtr is a subclass/typedef of this Format class.
+ */
+ operator WString() const
+ {
+ String str = *this;
+ const char *cstr = (const char *)str;
+
+ int len = MultiByteToWideChar(CP_ACP, 0, cstr, -1, NULL, 0);
+ WCHAR *buffer = (WCHAR *)alloca(len*sizeof(WCHAR));
+
+ MultiByteToWideChar(CP_ACP, 0, cstr, -1, buffer, len);
+
+ return WString(buffer);
+ }
+
+ /* Converts this object into a String object. This allows you to write the following code:
+ * String foo = "bar " + ObjectPtr(obj);
+ * Where ObjectPtr is a subclass/typedef of this Format class.
+ */
+ operator String() const
+ {
+ if (IsDMLEnabled() && mDml != Output::DML_None)
+ {
+ const int len = GetDMLColWidth(mDml, 0);
+ char *buffer = (char*)alloca(len);
+
+ BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, false, 0);
+ return buffer;
+ }
+ else
+ {
+ char buffer[64];
+ if (mFormat == Formats::Default || mFormat == Formats::Pointer)
+ {
+ sprintf_s(buffer, _countof(buffer), "%p", (int *)(SIZE_T)mValue);
+ ConvertToLower(buffer, _countof(buffer));
+ }
+ else
+ {
+ const char *format = NULL;
+ if (mFormat == Formats::PrefixHex)
+ format = "0x%x";
+ else if (mFormat == Formats::Hex)
+ format = "%x";
+ else if (mFormat == Formats::Decimal)
+ format = "%d";
+
+ sprintf_s(buffer, _countof(buffer), format, (__int32)mValue);
+ ConvertToLower(buffer, _countof(buffer));
+ }
+
+ return buffer;
+ }
+ }
+
+ private:
+ int GetPrecision() const
+ {
+ if (mFormat == Formats::Hex || mFormat == Formats::PrefixHex)
+ {
+ ULONGLONG val = mValue;
+ int count = 0;
+ while (val)
+ {
+ val >>= 4;
+ count++;
+ }
+
+ if (count == 0)
+ count = 1;
+
+ return count;
+ }
+
+ else if (mFormat == Formats::Decimal)
+ {
+ T val = mValue;
+ int count = (val > 0) ? 0 : 1;
+ while (val)
+ {
+ val /= 10;
+ count++;
+ }
+
+ return count;
+ }
+
+ // mFormat == Formats::Pointer
+ return sizeof(int*)*2;
+ }
+
+ static inline void BuildDML(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType)
+ {
+ BuildDMLCol(result, len, value, format, dmlType, true, 0);
+ }
+
+ static int GetDMLWidth(Output::FormatType dmlType)
+ {
+ return GetDMLColWidth(dmlType, 0);
+ }
+
+ static void BuildDMLCol(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType, bool leftAlign, int width)
+ {
+ char hex[64];
+ int count = GetHex(value, hex, _countof(hex), format != Formats::Hex);
+ int i = 0;
+
+ if (!leftAlign)
+ {
+ for (; i < width - count; ++i)
+ result[i] = ' ';
+
+ result[i] = 0;
+ }
+
+ int written = sprintf_s(result+i, len - i, DMLFormats[dmlType], hex, hex);
+
+ SOS_Assert(written != -1);
+ if (written != -1)
+ {
+ for (i = i + written; i < width; ++i)
+ result[i] = ' ';
+
+ result[i] = 0;
+ }
+ }
+
+ static int GetDMLColWidth(Output::FormatType dmlType, int width)
+ {
+ return 1 + 4*sizeof(int*) + (int)strlen(DMLFormats[dmlType]) + width;
+ }
+
+ private:
+ T mValue;
+ Formats::Format mFormat;
+ Output::FormatType mDml;
+ };
+
+ /* Format class used for strings.
+ */
+ template <>
+ class Format<const char *>
+ {
+ public:
+ Format(const char *value)
+ : mValue(value)
+ {
+ }
+
+ Format(const Format<const char *> &rhs)
+ : mValue(rhs.mValue)
+ {
+ }
+
+ void Output() const
+ {
+ if (IsDMLEnabled())
+ DMLOut("%s", mValue);
+ else
+ ExtOut("%s", mValue);
+ }
+
+ void OutputColumn(Alignment align, int width) const
+ {
+ int precision = (int)strlen(mValue);
+
+ if (precision > width)
+ precision = width;
+
+ const char *format = align == AlignLeft ? "%-*.*s" : "%*.*s";
+
+ if (IsDMLEnabled())
+ DMLOut(format, width, precision, mValue);
+ else
+ ExtOut(format, width, precision, mValue);
+ }
+
+ private:
+ const char *mValue;
+ };
+
+ /* Format class for wide char strings.
+ */
+ template <>
+ class Format<const WCHAR *>
+ {
+ public:
+ Format(const WCHAR *value)
+ : mValue(value)
+ {
+ }
+
+ Format(const Format<const WCHAR *> &rhs)
+ : mValue(rhs.mValue)
+ {
+ }
+
+ void Output() const
+ {
+ if (IsDMLEnabled())
+ DMLOut("%S", mValue);
+ else
+ ExtOut("%S", mValue);
+ }
+
+ void OutputColumn(Alignment align, int width) const
+ {
+ int precision = (int)_wcslen(mValue);
+ if (precision > width)
+ precision = width;
+
+ const char *format = align == AlignLeft ? "%-*.*S" : "%*.*S";
+
+ if (IsDMLEnabled())
+ DMLOut(format, width, precision, mValue);
+ else
+ ExtOut(format, width, precision, mValue);
+ }
+
+ private:
+ const WCHAR *mValue;
+ };
+
+
+ template <class T>
+ void InternalPrint(const T &t)
+ {
+ Format<T>(t).Output();
+ }
+
+ template <class T>
+ void InternalPrint(const Format<T> &t)
+ {
+ t.Output();
+ }
+
+ inline void InternalPrint(const char t[])
+ {
+ Format<const char *>(t).Output();
+ }
+}
+
+#define DefineFormatClass(name, format, dml) \
+ template <class T> \
+ Output::Format<T> name(T value) \
+ { return Output::Format<T>(value, format, dml); }
+
+DefineFormatClass(EEClassPtr, Formats::Pointer, Output::DML_EEClass);
+DefineFormatClass(ObjectPtr, Formats::Pointer, Output::DML_Object);
+DefineFormatClass(ExceptionPtr, Formats::Pointer, Output::DML_PrintException);
+DefineFormatClass(ModulePtr, Formats::Pointer, Output::DML_Module);
+DefineFormatClass(MethodDescPtr, Formats::Pointer, Output::DML_MethodDesc);
+DefineFormatClass(AppDomainPtr, Formats::Pointer, Output::DML_Domain);
+DefineFormatClass(ThreadState, Formats::Hex, Output::DML_ThreadState);
+DefineFormatClass(ThreadID, Formats::Hex, Output::DML_ThreadID);
+DefineFormatClass(RCWrapper, Formats::Pointer, Output::DML_RCWrapper);
+DefineFormatClass(CCWrapper, Formats::Pointer, Output::DML_CCWrapper);
+DefineFormatClass(InstructionPtr, Formats::Pointer, Output::DML_IP);
+DefineFormatClass(NativePtr, Formats::Pointer, Output::DML_None);
+
+DefineFormatClass(Decimal, Formats::Decimal, Output::DML_None);
+DefineFormatClass(Pointer, Formats::Pointer, Output::DML_None);
+DefineFormatClass(PrefixHex, Formats::PrefixHex, Output::DML_None);
+DefineFormatClass(Hex, Formats::Hex, Output::DML_None);
+
+#undef DefineFormatClass
+
+template <class T0>
+void Print(const T0 &val0)
+{
+ Output::InternalPrint(val0);
+}
+
+template <class T0, class T1>
+void Print(const T0 &val0, const T1 &val1)
+{
+ Output::InternalPrint(val0);
+ Output::InternalPrint(val1);
+}
+
+template <class T0>
+void PrintLn(const T0 &val0)
+{
+ Output::InternalPrint(val0);
+ ExtOut("\n");
+}
+
+template <class T0, class T1>
+void PrintLn(const T0 &val0, const T1 &val1)
+{
+ Output::InternalPrint(val0);
+ Output::InternalPrint(val1);
+ ExtOut("\n");
+}
+
+template <class T0, class T1, class T2>
+void PrintLn(const T0 &val0, const T1 &val1, const T2 &val2)
+{
+ Output::InternalPrint(val0);
+ Output::InternalPrint(val1);
+ Output::InternalPrint(val2);
+ ExtOut("\n");
+}
+
+
+/* This class handles the formatting for output which is in a table format. To use this class you define
+ * how the table is formatted by setting the number of columns in the table, the default column width,
+ * the default column alignment, the indentation (whitespace) for the table, and the amount of padding
+ * (whitespace) between each column. Once this has been setup, you output rows at a time or individual
+ * columns to build the output instead of manually tabbing out space.
+ * Also note that this class was built to work with the Format class. When outputing data, use the
+ * predefined output types to specify the format (such as ObjectPtr, MethodDescPtr, Decimal, etc). This
+ * tells the TableOutput class how to display the data, and where applicable, it automatically generates
+ * the appropriate DML output. See the DefineFormatClass macro.
+ */
+class TableOutput
+{
+public:
+
+ TableOutput()
+ : mColumns(0), mDefaultWidth(0), mIndent(0), mPadding(0), mCurrCol(0), mDefaultAlign(AlignLeft),
+ mWidths(0), mAlignments(0)
+ {
+ }
+ /* Constructor.
+ * Params:
+ * numColumns - the number of columns the table has
+ * defaultColumnWidth - the default width of each column
+ * alignmentDefault - whether columns are by default left aligned or right aligned
+ * indent - the amount of whitespace to prefix at the start of the row (in characters)
+ * padding - the amount of whitespace to place between each column (in characters)
+ */
+ TableOutput(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1)
+ : mColumns(numColumns), mDefaultWidth(defaultColumnWidth), mIndent(indent), mPadding(padding), mCurrCol(0), mDefaultAlign(alignmentDefault),
+ mWidths(0), mAlignments(0)
+ {
+ }
+
+ ~TableOutput()
+ {
+ Clear();
+ }
+
+ /* See the documentation for the constructor.
+ */
+ void ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1);
+
+ /* Sets the amount of whitespace to prefix at the start of the row (in characters).
+ */
+ void SetIndent(int indent)
+ {
+ SOS_Assert(indent >= 0);
+
+ mIndent = indent;
+ }
+
+ /* Sets the exact widths for the the given columns.
+ * Params:
+ * columns - the number of columns you are providing the width for, starting at the first column
+ * ... - an int32 for each column (given by the number of columns in the first parameter).
+ * Example:
+ * If you have 5 columns in the table, you can set their widths like so:
+ * tableOutput.SetWidths(5, 2, 3, 5, 7, 13);
+ * Note:
+ * It's fine to pass a value for "columns" less than the number of columns in the table. This
+ * is useful when you set the default column width to be correct for most of the table, and need
+ * to make a minor adjustment to a few.
+ */
+ void SetWidths(int columns, ...);
+
+ /* Individually sets a column to the given width.
+ * Params:
+ * col - the column to set, 0 indexed
+ * width - the width of the column (note this must be non-negative)
+ */
+ void SetColWidth(int col, int width);
+
+ /* Individually sets the column alignment.
+ * Params:
+ * col - the column to set, 0 indexed
+ * align - the new alignment (left or right) for the column
+ */
+ void SetColAlignment(int col, Alignment align);
+
+
+ /* The WriteRow family of functions allows you to write an entire row of the table at once.
+ * The common use case for the TableOutput class is to individually output each column after
+ * calculating what the value should contain. However, this would be tedious if you already
+ * knew the contents of the entire row which usually happenes when you are printing out the
+ * header for the table. To use this, simply pass each column as an individual parameter,
+ * for example:
+ * tableOutput.WriteRow("First Column", "Second Column", Decimal(3), PrefixHex(4), "Fifth Column");
+ */
+ template <class T0, class T1>
+ void WriteRow(T0 t0, T1 t1)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ }
+
+ template <class T0, class T1, class T2>
+ void WriteRow(T0 t0, T1 t1, T2 t2)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ }
+
+
+ template <class T0, class T1, class T2, class T3>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ }
+
+
+ template <class T0, class T1, class T2, class T3, class T4>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ WriteColumn(4, t4);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ WriteColumn(4, t4);
+ WriteColumn(5, t5);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ WriteColumn(4, t4);
+ WriteColumn(5, t5);
+ WriteColumn(6, t6);
+ WriteColumn(7, t7);
+ WriteColumn(8, t8);
+ WriteColumn(9, t9);
+ }
+
+ /* The WriteColumn family of functions is used to output individual columns in the table.
+ * The intent is that the bulk of the table will be generated in a loop like so:
+ * while (condition) {
+ * int value1 = CalculateFirstColumn();
+ * table.WriteColumn(0, value1);
+ *
+ * String value2 = CalculateSecondColumn();
+ * table.WriteColumn(1, value2);
+ * }
+ * Params:
+ * col - the column to write, 0 indexed
+ * t - the value to write
+ * Note:
+ * You should generally use the specific instances of the Format class to generate output.
+ * For example, use the "Decimal", "Pointer", "ObjectPtr", etc. When passing data to this
+ * function. This tells the Table class how to display the value.
+ */
+ template <class T>
+ void WriteColumn(int col, const Output::Format<T> &t)
+ {
+ SOS_Assert(col >= 0);
+ SOS_Assert(col < mColumns);
+
+ if (col != mCurrCol)
+ OutputBlankColumns(col);
+
+ if (col == 0)
+ OutputIndent();
+
+ bool lastCol = col == mColumns - 1;
+
+ if (!lastCol)
+ t.OutputColumn(GetColAlign(col), GetColumnWidth(col));
+ else
+ t.Output();
+
+ ExtOut(lastCol ? "\n" : GetWhitespace(mPadding));
+
+ if (lastCol)
+ mCurrCol = 0;
+ else
+ mCurrCol = col+1;
+ }
+
+ template <class T>
+ void WriteColumn(int col, T t)
+ {
+ WriteColumn(col, Output::Format<T>(t));
+ }
+
+ void WriteColumn(int col, const String &str)
+ {
+ WriteColumn(col, Output::Format<const char *>(str));
+ }
+
+ void WriteColumn(int col, const WString &str)
+ {
+ WriteColumn(col, Output::Format<const WCHAR *>(str));
+ }
+
+ void WriteColumn(int col, __in_z WCHAR *str)
+ {
+ WriteColumn(col, Output::Format<const WCHAR *>(str));
+ }
+
+ void WriteColumn(int col, const WCHAR *str)
+ {
+ WriteColumn(col, Output::Format<const WCHAR *>(str));
+ }
+
+ inline void WriteColumn(int col, __in_z char *str)
+ {
+ WriteColumn(col, Output::Format<const char *>(str));
+ }
+
+ /* Writes a column using a printf style format. You cannot use the Format class with
+ * this function to specify how the output should look, use printf style formatting
+ * with the appropriate parameters instead.
+ */
+ void WriteColumnFormat(int col, const char *fmt, ...)
+ {
+ SOS_Assert(strstr(fmt, "%S") == NULL);
+
+ char result[128];
+
+ va_list list;
+ va_start(list, fmt);
+ vsprintf_s(result, _countof(result), fmt, list);
+ va_end(list);
+
+ WriteColumn(col, result);
+ }
+
+ void WriteColumnFormat(int col, const WCHAR *fmt, ...)
+ {
+ WCHAR result[128];
+
+ va_list list;
+ va_start(list, fmt);
+ vswprintf_s(result, _countof(result), fmt, list);
+ va_end(list);
+
+ WriteColumn(col, result);
+ }
+
+ /* This function is a shortcut for writing the next column. (That is, the one after the
+ * one you just wrote.)
+ */
+ template <class T>
+ void WriteColumn(T t)
+ {
+ WriteColumn(mCurrCol, t);
+ }
+
+private:
+ void Clear();
+ void AllocWidths();
+ int GetColumnWidth(int col);
+ Alignment GetColAlign(int col);
+ const char *GetWhitespace(int amount);
+ void OutputBlankColumns(int col);
+ void OutputIndent();
+
+private:
+ int mColumns, mDefaultWidth, mIndent, mPadding, mCurrCol;
+ Alignment mDefaultAlign;
+ int *mWidths;
+ Alignment *mAlignments;
+};
+
+HRESULT GetMethodDefinitionsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, IXCLRDataMethodDefinition **ppMethodDefinitions, int numMethods, int *numMethodsNeeded);
+HRESULT GetMethodDescsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, DWORD_PTR **pOut, int *numMethodDescs);
+
+HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName);
+HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName);
+void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType,
+ DWORD_PTR &gcinfoAddr);
+const char *ElementTypeName (unsigned type);
+void DisplayFields (CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD,
+ DWORD_PTR dwStartAddr = 0, BOOL bFirst=TRUE, BOOL bValueClass=FALSE);
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE);
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE);
+
+BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb);
+void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName=true);
+void NameForToken_s(DWORD_PTR ModuleAddr, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName=true);
+HRESULT NameForToken_s(mdTypeDef mb, IMetaDataImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName);
+HRESULT NameForTokenNew_s(mdTypeDef mb, IMDInternalImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName);
+
+void vmmap();
+void vmstat();
+
+#ifndef FEATURE_PAL
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Support for managed stack tracing
+//
+
+DWORD_PTR GetDebuggerJitInfo(DWORD_PTR md);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#endif // FEATURE_PAL
+
+template <typename SCALAR>
+inline
+int bitidx(SCALAR bitflag)
+{
+ for (int idx = 0; idx < static_cast<int>(sizeof(bitflag))*8; ++idx)
+ {
+ if (bitflag & (1 << idx))
+ {
+ _ASSERTE((bitflag & (~(1 << idx))) == 0);
+ return idx;
+ }
+ }
+ return -1;
+}
+
+HRESULT
+DllsName(
+ ULONG_PTR addrContaining,
+ __out_ecount (MAX_LONGPATH) WCHAR *dllName
+ );
+
+inline
+BOOL IsElementValueType (CorElementType cet)
+{
+ return (cet >= ELEMENT_TYPE_BOOLEAN && cet <= ELEMENT_TYPE_R8)
+ || cet == ELEMENT_TYPE_VALUETYPE || cet == ELEMENT_TYPE_I || cet == ELEMENT_TYPE_U;
+}
+
+
+#define safemove(dst, src) \
+SafeReadMemory (TO_TADDR(src), &(dst), sizeof(dst), NULL)
+
+extern "C" PDEBUG_DATA_SPACES g_ExtData;
+
+template <class T>
+class ArrayHolder
+{
+public:
+ ArrayHolder(T *ptr)
+ : mPtr(ptr)
+ {
+ }
+
+ ~ArrayHolder()
+ {
+ Clear();
+ }
+
+ ArrayHolder(const ArrayHolder &rhs)
+ {
+ mPtr = const_cast<ArrayHolder *>(&rhs)->Detach();
+ }
+
+ ArrayHolder &operator=(T *ptr)
+ {
+ Clear();
+ mPtr = ptr;
+ return *this;
+ }
+
+ const T &operator[](int i) const
+ {
+ return mPtr[i];
+ }
+
+ T &operator[](int i)
+ {
+ return mPtr[i];
+ }
+
+ operator const T *() const
+ {
+ return mPtr;
+ }
+
+ operator T *()
+ {
+ return mPtr;
+ }
+
+ T **operator&()
+ {
+ return &mPtr;
+ }
+
+ T *GetPtr()
+ {
+ return mPtr;
+ }
+
+ T *Detach()
+ {
+ T *ret = mPtr;
+ mPtr = NULL;
+ return ret;
+ }
+
+private:
+ void Clear()
+ {
+ if (mPtr)
+ {
+ delete [] mPtr;
+ mPtr = NULL;
+ }
+ }
+
+private:
+ T *mPtr;
+};
+
+
+
+// This class acts a smart pointer which calls the Release method on any object
+// you place in it when the ToRelease class falls out of scope. You may use it
+// just like you would a standard pointer to a COM object (including if (foo),
+// if (!foo), if (foo == 0), etc) except for two caveats:
+// 1. This class never calls AddRef and it always calls Release when it
+// goes out of scope.
+// 2. You should never use & to try to get a pointer to a pointer unless
+// you call Release first, or you will leak whatever this object contains
+// prior to updating its internal pointer.
+template<class T>
+class ToRelease
+{
+public:
+ ToRelease()
+ : m_ptr(NULL)
+ {}
+
+ ToRelease(T* ptr)
+ : m_ptr(ptr)
+ {}
+
+ ~ToRelease()
+ {
+ Release();
+ }
+
+ void operator=(T *ptr)
+ {
+ Release();
+
+ m_ptr = ptr;
+ }
+
+ T* operator->()
+ {
+ return m_ptr;
+ }
+
+ operator T*()
+ {
+ return m_ptr;
+ }
+
+ T** operator&()
+ {
+ return &m_ptr;
+ }
+
+ T* GetPtr() const
+ {
+ return m_ptr;
+ }
+
+ T* Detach()
+ {
+ T* pT = m_ptr;
+ m_ptr = NULL;
+ return pT;
+ }
+
+ void Release()
+ {
+ if (m_ptr != NULL)
+ {
+ m_ptr->Release();
+ m_ptr = NULL;
+ }
+ }
+
+private:
+ T* m_ptr;
+};
+
+struct ModuleInfo
+{
+ ULONG64 baseAddr;
+ ULONG64 size;
+ BOOL hasPdb;
+};
+extern ModuleInfo moduleInfo[];
+
+BOOL InitializeHeapData();
+BOOL IsServerBuild ();
+UINT GetMaxGeneration();
+UINT GetGcHeapCount();
+BOOL GetGcStructuresValid();
+
+ULONG GetILSize(DWORD_PTR ilAddr); // REturns 0 if error occurs
+HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr);
+void DecodeIL(IMetaDataImport *pImport, BYTE *buffer, ULONG bufSize);
+void DecodeDynamicIL(BYTE *data, ULONG Size, DacpObjectData& tokenArray);
+
+BOOL IsRetailBuild (size_t base);
+EEFLAVOR GetEEFlavor ();
+HRESULT InitCorDebugInterface();
+VOID UninitCorDebugInterface();
+#ifndef FEATURE_PAL
+BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo);
+BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo);
+#endif
+
+BOOL IsDumpFile ();
+
+// IsMiniDumpFile will return true if 1) we are in
+// a small format minidump, and g_InMinidumpSafeMode is true.
+extern BOOL g_InMinidumpSafeMode;
+
+BOOL IsMiniDumpFile();
+void ReportOOM();
+
+BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead);
+#if !defined(_TARGET_WIN64_) && !defined(_ARM64_)
+// on 64-bit platforms TADDR and CLRDATA_ADDRESS are identical
+inline BOOL SafeReadMemory (CLRDATA_ADDRESS offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead)
+{ return SafeReadMemory(TO_TADDR(offset), lpBuffer, cb, lpcbBytesRead); }
+#endif
+
+BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName);
+BOOL NameForMT_s (DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName);
+
+WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt = NULL);
+
+void isRetAddr(DWORD_PTR retAddr, DWORD_PTR* whereCalled);
+DWORD_PTR GetValueFromExpression (___in __in_z const char *const str);
+
+enum ModuleHeapType
+{
+ ModuleHeapType_ThunkHeap,
+ ModuleHeapType_LookupTableHeap
+};
+
+HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *size, DWORD_PTR *wasted = 0);
+DWORD_PTR PrintModuleHeapInfo(DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *wasted = 0);
+void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted);
+void DomainInfo(DacpAppDomainData *pDomain);
+void AssemblyInfo(DacpAssemblyData *pAssembly);
+DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted = 0);
+DWORD_PTR JitHeapInfo();
+DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted = 0);
+
+DWORD GetNumComponents(TADDR obj);
+
+struct GenUsageStat
+{
+ size_t allocd;
+ size_t freed;
+ size_t unrooted;
+};
+
+struct HeapUsageStat
+{
+ GenUsageStat genUsage[4]; // gen0, 1, 2, LOH
+};
+
+extern DacpUsefulGlobalsData g_special_usefulGlobals;
+BOOL GCHeapUsageStats(const DacpGcHeapDetails& heap, BOOL bIncUnreachable, HeapUsageStat *hpUsage);
+
+class HeapStat
+{
+protected:
+ struct Node
+ {
+ DWORD_PTR data;
+ DWORD count;
+ size_t totalSize;
+ Node* left;
+ Node* right;
+ Node ()
+ : data(0), count(0), totalSize(0), left(NULL), right(NULL)
+ {
+ }
+ };
+ BOOL bHasStrings;
+ Node *head;
+ BOOL fLinear;
+public:
+ HeapStat ()
+ : bHasStrings(FALSE), head(NULL), fLinear(FALSE)
+ {}
+ ~HeapStat()
+ {
+ Delete();
+ }
+ // TODO: Change the aSize argument to size_t when we start supporting
+ // TODO: object sizes above 4GB
+ void Add (DWORD_PTR aData, DWORD aSize);
+ void Sort ();
+ void Print (const char* label = NULL);
+ void Delete ();
+ void HasStrings(BOOL abHasStrings)
+ {
+ bHasStrings = abHasStrings;
+ }
+private:
+ int CompareData(DWORD_PTR n1, DWORD_PTR n2);
+ void SortAdd (Node *&root, Node *entry);
+ void LinearAdd (Node *&root, Node *entry);
+ void ReverseLeftMost (Node *root);
+ void Linearize();
+};
+
+class CGCDesc;
+
+// The information MethodTableCache returns.
+struct MethodTableInfo
+{
+ bool IsInitialized() { return BaseSize != 0; }
+
+ DWORD BaseSize; // Caching BaseSize and ComponentSize for a MethodTable
+ DWORD ComponentSize; // here has HUGE perf benefits in heap traversals.
+ BOOL bContainsPointers;
+ DWORD_PTR* GCInfoBuffer; // Start of memory of GC info
+ CGCDesc* GCInfo; // Just past GC info (which is how it is stored)
+ bool ArrayOfVC;
+};
+
+class MethodTableCache
+{
+protected:
+
+ struct Node
+ {
+ DWORD_PTR data; // This is the key (the method table pointer)
+ MethodTableInfo info; // The info associated with this MethodTable
+ Node* left;
+ Node* right;
+ Node (DWORD_PTR aData) : data(aData), left(NULL), right(NULL)
+ {
+ info.BaseSize = 0;
+ info.ComponentSize = 0;
+ info.bContainsPointers = false;
+ info.GCInfo = NULL;
+ info.ArrayOfVC = false;
+ info.GCInfoBuffer = NULL;
+ }
+ };
+ Node *head;
+public:
+ MethodTableCache ()
+ : head(NULL)
+ {}
+ ~MethodTableCache() { Clear(); }
+
+ // Always succeeds, if it is not present it adds an empty Info struct and returns that
+ // Thus you must call 'IsInitialized' on the returned value before using it
+ MethodTableInfo* Lookup(DWORD_PTR aData);
+
+ void Clear ();
+private:
+ int CompareData(DWORD_PTR n1, DWORD_PTR n2);
+ void ReverseLeftMost (Node *root);
+};
+
+extern MethodTableCache g_special_mtCache;
+
+struct DumpArrayFlags
+{
+ DWORD_PTR startIndex;
+ DWORD_PTR Length;
+ BOOL bDetail;
+ LPSTR strObject;
+ BOOL bNoFieldsForElement;
+
+ DumpArrayFlags ()
+ : startIndex(0), Length((DWORD_PTR)-1), bDetail(FALSE), strObject (0), bNoFieldsForElement(FALSE)
+ {}
+ ~DumpArrayFlags ()
+ {
+ if (strObject)
+ delete [] strObject;
+ }
+}; //DumpArrayFlags
+
+
+
+// -----------------------------------------------------------------------
+
+#define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX 0x08000000
+#define BIT_SBLK_FINALIZER_RUN 0x40000000
+#define BIT_SBLK_SPIN_LOCK 0x10000000
+#define SBLK_MASK_LOCK_THREADID 0x000003FF // special value of 0 + 1023 thread ids
+#define SBLK_MASK_LOCK_RECLEVEL 0x0000FC00 // 64 recursion levels
+#define SBLK_APPDOMAIN_SHIFT 16 // shift right this much to get appdomain index
+#define SBLK_MASK_APPDOMAININDEX 0x000007FF // 2048 appdomain indices
+#define SBLK_RECLEVEL_SHIFT 10 // shift right this much to get recursion level
+#define BIT_SBLK_IS_HASHCODE 0x04000000
+#define MASK_HASHCODE ((1<<HASHCODE_BITS)-1)
+#define SYNCBLOCKINDEX_BITS 26
+#define MASK_SYNCBLOCKINDEX ((1<<SYNCBLOCKINDEX_BITS)-1)
+
+HRESULT GetMTOfObject(TADDR obj, TADDR *mt);
+
+struct needed_alloc_context
+{
+ BYTE* alloc_ptr; // starting point for next allocation
+ BYTE* alloc_limit; // ending point for allocation region/quantum
+};
+
+struct AllocInfo
+{
+ needed_alloc_context *array;
+ int num; // number of allocation contexts in array
+
+ AllocInfo()
+ : array(NULL)
+ , num(0)
+ {}
+ void Init()
+ {
+ extern void GetAllocContextPtrs(AllocInfo *pallocInfo);
+ GetAllocContextPtrs(this);
+ }
+ ~AllocInfo()
+ {
+ if (array != NULL)
+ delete[] array;
+ }
+};
+
+struct GCHandleStatistics
+{
+ HeapStat hs;
+
+ DWORD strongHandleCount;
+ DWORD pinnedHandleCount;
+ DWORD asyncPinnedHandleCount;
+ DWORD refCntHandleCount;
+ DWORD weakLongHandleCount;
+ DWORD weakShortHandleCount;
+ DWORD variableCount;
+ DWORD sizedRefCount;
+ DWORD dependentCount;
+ DWORD weakWinRTHandleCount;
+ DWORD unknownHandleCount;
+ GCHandleStatistics()
+ : strongHandleCount(0), pinnedHandleCount(0), asyncPinnedHandleCount(0), refCntHandleCount(0),
+ weakLongHandleCount(0), weakShortHandleCount(0), variableCount(0), sizedRefCount(0),
+ dependentCount(0), weakWinRTHandleCount(0), unknownHandleCount(0)
+ {}
+ ~GCHandleStatistics()
+ {
+ hs.Delete();
+ }
+};
+
+struct SegmentLookup
+{
+ DacpHeapSegmentData *m_segments;
+ int m_iSegmentsSize;
+ int m_iSegmentCount;
+
+ SegmentLookup();
+ ~SegmentLookup();
+
+ void Clear();
+ BOOL AddSegment(DacpHeapSegmentData *pData);
+ CLRDATA_ADDRESS GetHeap(CLRDATA_ADDRESS object, BOOL& bFound);
+};
+
+class GCHeapSnapshot
+{
+private:
+ BOOL m_isBuilt;
+ DacpGcHeapDetails *m_heapDetails;
+ DacpGcHeapData m_gcheap;
+ SegmentLookup m_segments;
+
+ BOOL AddSegments(DacpGcHeapDetails& details);
+public:
+ GCHeapSnapshot();
+
+ BOOL Build();
+ void Clear();
+ BOOL IsBuilt() { return m_isBuilt; }
+
+ DacpGcHeapData *GetHeapData() { return &m_gcheap; }
+
+ int GetHeapCount() { return m_gcheap.HeapCount; }
+
+ DacpGcHeapDetails *GetHeap(CLRDATA_ADDRESS objectPointer);
+ int GetGeneration(CLRDATA_ADDRESS objectPointer);
+
+
+};
+extern GCHeapSnapshot g_snapshot;
+
+BOOL IsSameModuleName (const char *str1, const char *str2);
+BOOL IsModule (DWORD_PTR moduleAddr);
+BOOL IsMethodDesc (DWORD_PTR value);
+BOOL IsMethodTable (DWORD_PTR value);
+BOOL IsStringObject (size_t obj);
+BOOL IsObjectArray (DWORD_PTR objPointer);
+BOOL IsObjectArray (DacpObjectData *pData);
+
+/* Returns a list of all modules in the process.
+ * Params:
+ * name - The name of the module you would like. If mName is NULL the all modules are returned.
+ * numModules - The number of modules in the array returned.
+ * Returns:
+ * An array of modules whose length is *numModules, NULL if an error occurred. Note that if this
+ * function succeeds but finds no modules matching the name given, this function returns a valid
+ * array, but *numModules will equal 0.
+ * Note:
+ * You must clean up the return value of this array by calling delete [] on it, or using the
+ * ArrayHolder class.
+ */
+DWORD_PTR *ModuleFromName(__in_opt LPSTR name, int *numModules);
+void GetInfoFromName(DWORD_PTR ModuleAddr, const char* name);
+void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret=NULL);
+
+
+typedef void (*VISITGCHEAPFUNC)(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token);
+BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify=true);
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct strobjInfo
+{
+ size_t methodTable;
+ DWORD m_StringLength;
+};
+
+// Just to make figuring out which fill pointer element matches a generation
+// a bit less confusing. This gen_segment function is ported from gc.cpp.
+inline unsigned int gen_segment (int gen)
+{
+ return (DAC_NUMBERGENERATIONS - gen - 1);
+}
+
+inline CLRDATA_ADDRESS SegQueue(DacpGcHeapDetails& heapDetails, int seg)
+{
+ return heapDetails.finalization_fill_pointers[seg - 1];
+}
+
+inline CLRDATA_ADDRESS SegQueueLimit(DacpGcHeapDetails& heapDetails, int seg)
+{
+ return heapDetails.finalization_fill_pointers[seg];
+}
+
+#define FinalizerListSeg (DAC_NUMBERGENERATIONS+1)
+#define CriticalFinalizerListSeg (DAC_NUMBERGENERATIONS)
+
+void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort);
+
+CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr);
+CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr);
+void GCHeapInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size);
+BOOL GCObjInHeap(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ TADDR_SEGINFO& trngSeg, int& gen, TADDR_RANGE& allocCtx, BOOL &bLarge);
+
+BOOL VerifyObject(const DacpGcHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
+ BOOL bVerifyMember);
+BOOL VerifyObject(const DacpGcHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
+ BOOL bVerifyMember);
+
+BOOL IsMTForFreeObj(DWORD_PTR pMT);
+void DumpStackObjectsHelper (TADDR StackTop, TADDR StackBottom, BOOL verifyFields);
+
+
+enum ARGTYPE {COBOOL,COSIZE_T,COHEX,COSTRING};
+struct CMDOption
+{
+ const char* name;
+ void *vptr;
+ ARGTYPE type;
+ BOOL hasValue;
+ BOOL hasSeen;
+};
+struct CMDValue
+{
+ void *vptr;
+ ARGTYPE type;
+};
+BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption,
+ CMDValue *arg, size_t maxArg, size_t *nArg);
+
+void DumpMDInfo(DWORD_PTR dwStartAddr, CLRDATA_ADDRESS dwRequestedIP = 0, BOOL fStackTraceFormat = FALSE);
+void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, BOOL fStackTraceFormat);
+void GetDomainList(DWORD_PTR *&domainList, int &numDomain);
+HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread);
+CLRDATA_ADDRESS GetCurrentManagedThread(); // returns current managed thread if any
+void GetAllocContextPtrs(AllocInfo *pallocInfo);
+
+void ReloadSymbolWithLineInfo();
+
+size_t FunctionType (size_t EIP);
+
+size_t Align (size_t nbytes);
+// Aligns large objects
+size_t AlignLarge (size_t nbytes);
+
+ULONG OSPageSize ();
+size_t NextOSPageAddress (size_t addr);
+
+// This version of objectsize reduces the lookup of methodtables in the DAC.
+// It uses g_special_mtCache for it's work.
+BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj,
+ DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers);
+
+// ObjSize now uses the methodtable cache for its work too.
+size_t ObjectSize (DWORD_PTR obj, BOOL fIsLargeObject=FALSE);
+size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject=FALSE);
+
+void CharArrayContent(TADDR pos, ULONG num, bool widechar);
+void StringObjectContent (size_t obj, BOOL fLiteral=FALSE, const int length=-1); // length=-1: dump everything in the string object.
+
+UINT FindAllPinnedAndStrong (DWORD_PTR handlearray[],UINT arraySize);
+void PrintNotReachableInRange(TADDR rngStart, TADDR rngEnd, BOOL bExcludeReadyForFinalization,
+ HeapStat* stat, BOOL bShort);
+
+const char *EHTypeName(EHClauseType et);
+
+template<typename T>
+inline const LPCSTR GetTransparency(const T &t)
+{
+ if (!t.bHasCriticalTransparentInfo)
+ {
+ return "Not calculated";
+ }
+ else if (t.bIsCritical && !t.bIsTreatAsSafe)
+ {
+ return "Critical";
+ }
+ else if (t.bIsCritical)
+ {
+ return "Safe critical";
+ }
+ else
+ {
+ return "Transparent";
+ }
+}
+
+struct StringHolder
+{
+ LPSTR data;
+ StringHolder() : data(NULL) { }
+ ~StringHolder() { if(data) delete [] data; }
+};
+
+
+ULONG DebuggeeType();
+
+inline BOOL IsKernelDebugger ()
+{
+ return DebuggeeType() == DEBUG_CLASS_KERNEL;
+}
+
+void ResetGlobals(void);
+HRESULT LoadClrDebugDll(void);
+extern "C" void UnloadClrDebugDll(void);
+
+extern IMetaDataImport* MDImportForModule (DacpModuleData *pModule);
+extern IMetaDataImport* MDImportForModule (DWORD_PTR pModule);
+
+//*****************************************************************************
+//
+// **** CQuickBytes
+// This helper class is useful for cases where 90% of the time you allocate 512
+// or less bytes for a data structure. This class contains a 512 byte buffer.
+// Alloc() will return a pointer to this buffer if your allocation is small
+// enough, otherwise it asks the heap for a larger buffer which is freed for
+// you. No mutex locking is required for the small allocation case, making the
+// code run faster, less heap fragmentation, etc... Each instance will allocate
+// 520 bytes, so use accordinly.
+//
+//*****************************************************************************
+template <DWORD SIZE, DWORD INCREMENT>
+class CQuickBytesBase
+{
+public:
+ CQuickBytesBase() :
+ pbBuff(0),
+ iSize(0),
+ cbTotal(SIZE)
+ { }
+
+ void Destroy()
+ {
+ if (pbBuff)
+ {
+ delete[] (BYTE*)pbBuff;
+ pbBuff = 0;
+ }
+ }
+
+ void *Alloc(SIZE_T iItems)
+ {
+ iSize = iItems;
+ if (iItems <= SIZE)
+ {
+ cbTotal = SIZE;
+ return (&rgData[0]);
+ }
+ else
+ {
+ if (pbBuff)
+ delete[] (BYTE*)pbBuff;
+ pbBuff = new BYTE[iItems];
+ cbTotal = pbBuff ? iItems : 0;
+ return (pbBuff);
+ }
+ }
+
+ // This is for conformity to the CQuickBytesBase that is defined by the runtime so
+ // that we can use it inside of some GC code that SOS seems to include as well.
+ //
+ // The plain vanilla "Alloc" version on this CQuickBytesBase doesn't throw either,
+ // so we'll just forward the call.
+ void *AllocNoThrow(SIZE_T iItems)
+ {
+ return Alloc(iItems);
+ }
+
+ HRESULT ReSize(SIZE_T iItems)
+ {
+ void *pbBuffNew;
+ if (iItems <= cbTotal)
+ {
+ iSize = iItems;
+ return NOERROR;
+ }
+
+ pbBuffNew = new BYTE[iItems + INCREMENT];
+ if (!pbBuffNew)
+ return E_OUTOFMEMORY;
+ if (pbBuff)
+ {
+ memcpy(pbBuffNew, pbBuff, cbTotal);
+ delete[] (BYTE*)pbBuff;
+ }
+ else
+ {
+ _ASSERTE(cbTotal == SIZE);
+ memcpy(pbBuffNew, rgData, SIZE);
+ }
+ cbTotal = iItems + INCREMENT;
+ iSize = iItems;
+ pbBuff = pbBuffNew;
+ return NOERROR;
+
+ }
+
+ operator PVOID()
+ { return ((pbBuff) ? pbBuff : &rgData[0]); }
+
+ void *Ptr()
+ { return ((pbBuff) ? pbBuff : &rgData[0]); }
+
+ SIZE_T Size()
+ { return (iSize); }
+
+ SIZE_T MaxSize()
+ { return (cbTotal); }
+
+ void *pbBuff;
+ SIZE_T iSize; // number of bytes used
+ SIZE_T cbTotal; // total bytes allocated in the buffer
+ // use UINT64 to enforce the alignment of the memory
+ UINT64 rgData[(SIZE+sizeof(UINT64)-1)/sizeof(UINT64)];
+};
+
+#define CQUICKBYTES_BASE_SIZE 512
+#define CQUICKBYTES_INCREMENTAL_SIZE 128
+
+class CQuickBytesNoDtor : public CQuickBytesBase<CQUICKBYTES_BASE_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+class CQuickBytes : public CQuickBytesNoDtor
+{
+public:
+ CQuickBytes() { }
+
+ ~CQuickBytes()
+ {
+ Destroy();
+ }
+};
+
+template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesNoDtorSpecifySize : public CQuickBytesBase<CQUICKBYTES_BASE_SPECIFY_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySize : public CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE>
+{
+public:
+ CQuickBytesSpecifySize() { }
+
+ ~CQuickBytesSpecifySize()
+ {
+ CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE>::Destroy();
+ }
+};
+
+
+#define STRING_SIZE 10
+class CQuickString : public CQuickBytesBase<STRING_SIZE, STRING_SIZE>
+{
+public:
+ CQuickString() { }
+
+ ~CQuickString()
+ {
+ Destroy();
+ }
+
+ void *Alloc(SIZE_T iItems)
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Alloc(iItems*sizeof(WCHAR));
+ }
+
+ HRESULT ReSize(SIZE_T iItems)
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::ReSize(iItems * sizeof(WCHAR));
+ }
+
+ SIZE_T Size()
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Size() / sizeof(WCHAR);
+ }
+
+ SIZE_T MaxSize()
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::MaxSize() / sizeof(WCHAR);
+ }
+
+ WCHAR* String()
+ {
+ return (WCHAR*) Ptr();
+ }
+
+};
+
+enum GetSignatureStringResults
+{
+ GSS_SUCCESS,
+ GSS_ERROR,
+ GSS_INSUFFICIENT_DATA,
+};
+
+GetSignatureStringResults GetMethodSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString);
+GetSignatureStringResults GetSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString);
+void GetMethodName(mdMethodDef methodDef, IMetaDataImport * pImport, CQuickBytes *fullName);
+
+#ifndef _TARGET_WIN64_
+#define itoa_s_ptr _itoa_s
+#define itow_s_ptr _itow_s
+#define itoa_ptr _itoa
+#define itow_ptr _itow
+#else
+#define itoa_s_ptr _i64toa_s
+#define itow_s_ptr _i64tow_s
+#define itoa_ptr _i64toa
+#define itow_ptr _i64tow
+#endif
+
+#ifdef FEATURE_PAL
+extern "C"
+int _itoa_s( int inValue, char* outBuffer, size_t inDestBufferSize, int inRadix );
+extern "C"
+int _ui64toa_s( unsigned __int64 inValue, char* outBuffer, size_t inDestBufferSize, int inRadix );
+#endif // FEATURE_PAL
+
+struct MemRange
+{
+ MemRange (ULONG64 s = NULL, size_t l = 0, MemRange * n = NULL)
+ : start(s), len (l), next (n)
+ {}
+
+ bool InRange (ULONG64 addr)
+ {
+ return addr >= start && addr < start + len;
+ }
+
+ ULONG64 start;
+ size_t len;
+ MemRange * next;
+}; //struct MemRange
+
+#ifndef FEATURE_PAL
+
+class StressLogMem
+{
+private:
+ // use a linked list for now, could be optimazied later
+ MemRange * list;
+
+ void AddRange (ULONG64 s, size_t l)
+ {
+ list = new MemRange (s, l, list);
+ }
+
+public:
+ StressLogMem () : list (NULL)
+ {}
+ ~StressLogMem ();
+ bool Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack);
+ bool IsInStressLog (ULONG64 addr);
+}; //class StressLogMem
+
+// An adapter class that DIA consumes so that it can read PE data from the an image
+// This implementation gets the backing data from the image loaded in debuggee memory
+// that has been layed out identical to the disk format (ie not seperated by section)
+class PEOffsetMemoryReader : IDiaReadExeAtOffsetCallback
+{
+public:
+ PEOffsetMemoryReader(TADDR moduleBaseAddress);
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ // IDiaReadExeAtOffsetCallback implementation
+ HRESULT __stdcall ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[]);
+
+private:
+ TADDR m_moduleBaseAddress;
+ volatile ULONG m_refCount;
+};
+
+// An adapter class that DIA consumes so that it can read PE data from the an image
+// This implementation gets the backing data from the image loaded in debuggee memory
+// that has been layed out in LoadLibrary format
+class PERvaMemoryReader : IDiaReadExeAtRVACallback
+{
+public:
+ PERvaMemoryReader(TADDR moduleBaseAddress);
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ // IDiaReadExeAtOffsetCallback implementation
+ HRESULT __stdcall ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[]);
+
+private:
+ TADDR m_moduleBaseAddress;
+ volatile ULONG m_refCount;
+};
+
+#endif // !FEATURE_PAL
+
+static const char *SymbolReaderDllName = "SOS.NETCore";
+static const char *SymbolReaderClassName = "SOS.SymbolReader";
+
+typedef int (*ReadMemoryDelegate)(ULONG64, char *, int);
+typedef ULONG64 (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate);
+typedef void (*DisposeDelegate)(ULONG64);
+typedef BOOL (*ResolveSequencePointDelegate)(ULONG64, const char*, unsigned int, unsigned int*, unsigned int*);
+typedef BOOL (*GetLocalVariableName)(ULONG64, int, int, BSTR*);
+typedef BOOL (*GetLineByILOffsetDelegate)(ULONG64, mdMethodDef, ULONG64, ULONG *, BSTR*);
+
+class SymbolReader
+{
+private:
+#ifndef FEATURE_PAL
+ ISymUnmanagedReader* m_pSymReader;
+#endif
+ ULONG64 m_symbolReaderHandle;
+
+ static LoadSymbolsForModuleDelegate loadSymbolsForModuleDelegate;
+ static DisposeDelegate disposeDelegate;
+ static ResolveSequencePointDelegate resolveSequencePointDelegate;
+ static GetLocalVariableName getLocalVariableNameDelegate;
+ static GetLineByILOffsetDelegate getLineByILOffsetDelegate;
+ static HRESULT PrepareSymbolReader();
+
+ HRESULT GetNamedLocalVariable(___in ISymUnmanagedScope* pScope, ___in ICorDebugILFrame* pILFrame, ___in mdMethodDef methodToken, ___in ULONG localIndex,
+ __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue);
+ HRESULT LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout);
+ HRESULT LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout, ___in ULONG64 peAddress, ___in ULONG64 peSize,
+ ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize);
+
+public:
+ SymbolReader()
+ {
+#ifndef FEATURE_PAL
+ m_pSymReader = NULL;
+#endif
+ m_symbolReaderHandle = 0;
+ }
+
+ ~SymbolReader()
+ {
+#ifndef FEATURE_PAL
+ if(m_pSymReader != NULL)
+ {
+ m_pSymReader->Release();
+ m_pSymReader = NULL;
+ }
+#endif
+ if (m_symbolReaderHandle != 0)
+ {
+ disposeDelegate(m_symbolReaderHandle);
+ m_symbolReaderHandle = 0;
+ }
+ }
+
+ HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule);
+ HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule);
+ HRESULT GetLineByILOffset(___in mdMethodDef MethodToken, ___in ULONG64 IlOffset, ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName);
+ HRESULT GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue);
+ HRESULT ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* ___out pToken, ___out ULONG32* pIlOffset);
+};
+
+HRESULT
+GetLineByOffset(
+ ___in ULONG64 IP,
+ ___out ULONG *pLinenum,
+ __out_ecount(cchFileName) WCHAR* pwszFileName,
+ ___in ULONG cchFileName);
+
+/// X86 Context
+#define X86_SIZE_OF_80387_REGISTERS 80
+#define X86_MAXIMUM_SUPPORTED_EXTENSION 512
+
+typedef struct {
+ DWORD ControlWord;
+ DWORD StatusWord;
+ DWORD TagWord;
+ DWORD ErrorOffset;
+ DWORD ErrorSelector;
+ DWORD DataOffset;
+ DWORD DataSelector;
+ BYTE RegisterArea[X86_SIZE_OF_80387_REGISTERS];
+ DWORD Cr0NpxState;
+} X86_FLOATING_SAVE_AREA;
+
+typedef struct {
+
+ DWORD ContextFlags;
+ DWORD Dr0;
+ DWORD Dr1;
+ DWORD Dr2;
+ DWORD Dr3;
+ DWORD Dr6;
+ DWORD Dr7;
+
+ X86_FLOATING_SAVE_AREA FloatSave;
+
+ DWORD SegGs;
+ DWORD SegFs;
+ DWORD SegEs;
+ DWORD SegDs;
+
+ DWORD Edi;
+ DWORD Esi;
+ DWORD Ebx;
+ DWORD Edx;
+ DWORD Ecx;
+ DWORD Eax;
+
+ DWORD Ebp;
+ DWORD Eip;
+ DWORD SegCs;
+ DWORD EFlags;
+ DWORD Esp;
+ DWORD SegSs;
+
+ BYTE ExtendedRegisters[X86_MAXIMUM_SUPPORTED_EXTENSION];
+
+} X86_CONTEXT;
+
+typedef struct {
+ ULONGLONG Low;
+ LONGLONG High;
+} M128A_XPLAT;
+
+
+/// AMD64 Context
+typedef struct {
+ WORD ControlWord;
+ WORD StatusWord;
+ BYTE TagWord;
+ BYTE Reserved1;
+ WORD ErrorOpcode;
+ DWORD ErrorOffset;
+ WORD ErrorSelector;
+ WORD Reserved2;
+ DWORD DataOffset;
+ WORD DataSelector;
+ WORD Reserved3;
+ DWORD MxCsr;
+ DWORD MxCsr_Mask;
+ M128A_XPLAT FloatRegisters[8];
+
+#if defined(_WIN64)
+ M128A_XPLAT XmmRegisters[16];
+ BYTE Reserved4[96];
+#else
+ M128A_XPLAT XmmRegisters[8];
+ BYTE Reserved4[220];
+
+ DWORD Cr0NpxState;
+#endif
+
+} AMD64_XMM_SAVE_AREA32;
+
+typedef struct {
+
+ DWORD64 P1Home;
+ DWORD64 P2Home;
+ DWORD64 P3Home;
+ DWORD64 P4Home;
+ DWORD64 P5Home;
+ DWORD64 P6Home;
+
+ DWORD ContextFlags;
+ DWORD MxCsr;
+
+ WORD SegCs;
+ WORD SegDs;
+ WORD SegEs;
+ WORD SegFs;
+ WORD SegGs;
+ WORD SegSs;
+ DWORD EFlags;
+
+ DWORD64 Dr0;
+ DWORD64 Dr1;
+ DWORD64 Dr2;
+ DWORD64 Dr3;
+ DWORD64 Dr6;
+ DWORD64 Dr7;
+
+ DWORD64 Rax;
+ DWORD64 Rcx;
+ DWORD64 Rdx;
+ DWORD64 Rbx;
+ DWORD64 Rsp;
+ DWORD64 Rbp;
+ DWORD64 Rsi;
+ DWORD64 Rdi;
+ DWORD64 R8;
+ DWORD64 R9;
+ DWORD64 R10;
+ DWORD64 R11;
+ DWORD64 R12;
+ DWORD64 R13;
+ DWORD64 R14;
+ DWORD64 R15;
+
+ DWORD64 Rip;
+
+ union {
+ AMD64_XMM_SAVE_AREA32 FltSave;
+ struct {
+ M128A_XPLAT Header[2];
+ M128A_XPLAT Legacy[8];
+ M128A_XPLAT Xmm0;
+ M128A_XPLAT Xmm1;
+ M128A_XPLAT Xmm2;
+ M128A_XPLAT Xmm3;
+ M128A_XPLAT Xmm4;
+ M128A_XPLAT Xmm5;
+ M128A_XPLAT Xmm6;
+ M128A_XPLAT Xmm7;
+ M128A_XPLAT Xmm8;
+ M128A_XPLAT Xmm9;
+ M128A_XPLAT Xmm10;
+ M128A_XPLAT Xmm11;
+ M128A_XPLAT Xmm12;
+ M128A_XPLAT Xmm13;
+ M128A_XPLAT Xmm14;
+ M128A_XPLAT Xmm15;
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME;
+
+ M128A_XPLAT VectorRegister[26];
+ DWORD64 VectorControl;
+
+ DWORD64 DebugControl;
+ DWORD64 LastBranchToRip;
+ DWORD64 LastBranchFromRip;
+ DWORD64 LastExceptionToRip;
+ DWORD64 LastExceptionFromRip;
+
+} AMD64_CONTEXT;
+
+typedef struct{
+ __int64 LowPart;
+ __int64 HighPart;
+} FLOAT128_XPLAT;
+
+
+/// ARM Context
+#define ARM_MAX_BREAKPOINTS_CONST 8
+#define ARM_MAX_WATCHPOINTS_CONST 4
+typedef struct {
+
+ DWORD ContextFlags;
+
+ DWORD R0;
+ DWORD R1;
+ DWORD R2;
+ DWORD R3;
+ DWORD R4;
+ DWORD R5;
+ DWORD R6;
+ DWORD R7;
+ DWORD R8;
+ DWORD R9;
+ DWORD R10;
+ DWORD R11;
+ DWORD R12;
+
+ DWORD Sp;
+ DWORD Lr;
+ DWORD Pc;
+ DWORD Cpsr;
+
+ DWORD Fpscr;
+ union {
+ M128A_XPLAT Q[16];
+ ULONGLONG D[32];
+ DWORD S[32];
+ } DUMMYUNIONNAME;
+
+ DWORD Bvr[ARM_MAX_BREAKPOINTS_CONST];
+ DWORD Bcr[ARM_MAX_BREAKPOINTS_CONST];
+ DWORD Wvr[ARM_MAX_WATCHPOINTS_CONST];
+ DWORD Wcr[ARM_MAX_WATCHPOINTS_CONST];
+
+} ARM_CONTEXT;
+
+// On ARM this mask is or'ed with the address of code to get an instruction pointer
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+
+///ARM64 Context
+#define ARM64_MAX_BREAKPOINTS 8
+#define ARM64_MAX_WATCHPOINTS 2
+typedef struct {
+
+ DWORD ContextFlags;
+ DWORD Cpsr; // NZVF + DAIF + CurrentEL + SPSel
+ union {
+ struct {
+ DWORD64 X0;
+ DWORD64 X1;
+ DWORD64 X2;
+ DWORD64 X3;
+ DWORD64 X4;
+ DWORD64 X5;
+ DWORD64 X6;
+ DWORD64 X7;
+ DWORD64 X8;
+ DWORD64 X9;
+ DWORD64 X10;
+ DWORD64 X11;
+ DWORD64 X12;
+ DWORD64 X13;
+ DWORD64 X14;
+ DWORD64 X15;
+ DWORD64 X16;
+ DWORD64 X17;
+ DWORD64 X18;
+ DWORD64 X19;
+ DWORD64 X20;
+ DWORD64 X21;
+ DWORD64 X22;
+ DWORD64 X23;
+ DWORD64 X24;
+ DWORD64 X25;
+ DWORD64 X26;
+ DWORD64 X27;
+ DWORD64 X28;
+ };
+
+ DWORD64 X[29];
+ };
+
+ DWORD64 Fp;
+ DWORD64 Lr;
+ DWORD64 Sp;
+ DWORD64 Pc;
+
+
+ M128A_XPLAT V[32];
+ DWORD Fpcr;
+ DWORD Fpsr;
+
+ DWORD Bcr[ARM64_MAX_BREAKPOINTS];
+ DWORD64 Bvr[ARM64_MAX_BREAKPOINTS];
+ DWORD Wcr[ARM64_MAX_WATCHPOINTS];
+ DWORD64 Wvr[ARM64_MAX_WATCHPOINTS];
+
+} ARM64_CONTEXT;
+
+typedef struct _CROSS_PLATFORM_CONTEXT {
+
+ _CROSS_PLATFORM_CONTEXT() {}
+
+ union {
+ X86_CONTEXT X86Context;
+ AMD64_CONTEXT Amd64Context;
+ ARM_CONTEXT ArmContext;
+ ARM64_CONTEXT Arm64Context;
+ };
+
+} CROSS_PLATFORM_CONTEXT, *PCROSS_PLATFORM_CONTEXT;
+
+
+
+WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj = true);
+WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE);
+HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount);
+WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackwalk = NULL, BOOL bAssemblyName = FALSE);
+
+/* This cache is used to read data from the target process if the reads are known
+ * to be sequential.
+ */
+class LinearReadCache
+{
+public:
+ LinearReadCache(ULONG pageSize = 0x10000);
+ ~LinearReadCache();
+
+ /* Reads an address out of the target process, caching the page of memory read.
+ * Params:
+ * addr - The address to read out of the target process.
+ * t - A pointer to the data to stuff it in. We will read sizeof(T) data
+ * from the process and write it into the location t points to. This
+ * parameter must be non-null.
+ * Returns:
+ * True if the read succeeded. False if it did not, usually as a result
+ * of the memory simply not being present in the target process.
+ * Note:
+ * The state of *t is undefined if this function returns false. We may
+ * have written partial data to it if we return false, so you must
+ * absolutely NOT use it if Read returns false.
+ */
+ template <class T>
+ bool Read(TADDR addr, T *t, bool update = true)
+ {
+ _ASSERTE(t);
+
+ // Unfortunately the ctor can fail the alloc for the byte array. In this case
+ // we'll just fall back to non-cached reads.
+ if (mPage == NULL)
+ return MisalignedRead(addr, t);
+
+ // Is addr on the current page? If not read the page of memory addr is on.
+ // If this fails, we will fall back to a raw read out of the process (which
+ // is what MisalignedRead does).
+ if ((addr < mCurrPageStart) || (addr - mCurrPageStart > mCurrPageSize))
+ if (!update || !MoveToPage(addr))
+ return MisalignedRead(addr, t);
+
+ // If MoveToPage succeeds, we MUST be on the right page.
+ _ASSERTE(addr >= mCurrPageStart);
+
+ // However, the amount of data requested may fall off of the page. In that case,
+ // fall back to MisalignedRead.
+ TADDR offset = addr - mCurrPageStart;
+ if (offset + sizeof(T) > mCurrPageSize)
+ return MisalignedRead(addr, t);
+
+ // If we reach here we know we are on the right page of memory in the cache, and
+ // that the read won't fall off of the end of the page.
+#ifdef _DEBUG
+ mReads++;
+#endif
+
+ *t = *reinterpret_cast<T*>(mPage+offset);
+ return true;
+ }
+
+ void EnsureRangeInCache(TADDR start, unsigned int size)
+ {
+ if (mCurrPageStart == start)
+ {
+ if (size <= mCurrPageSize)
+ return;
+
+ // Total bytes to read, don't overflow buffer.
+ unsigned int total = size + mCurrPageSize;
+ if (total + mCurrPageSize > mPageSize)
+ total = mPageSize-mCurrPageSize;
+
+ // Read into the middle of the buffer, update current page size.
+ ULONG read = 0;
+ HRESULT hr = g_ExtData->ReadVirtual(mCurrPageStart+mCurrPageSize, mPage+mCurrPageSize, total, &read);
+ mCurrPageSize += read;
+
+ if (hr != S_OK)
+ {
+ mCurrPageStart = 0;
+ mCurrPageSize = 0;
+ }
+ }
+ else
+ {
+ MoveToPage(start, size);
+ }
+ }
+
+ void ClearStats()
+ {
+#ifdef _DEBUG
+ mMisses = 0;
+ mReads = 0;
+ mMisaligned = 0;
+#endif
+ }
+
+ void PrintStats(const char *func)
+ {
+#ifdef _DEBUG
+ char buffer[1024];
+ sprintf_s(buffer, _countof(buffer), "Cache (%s): %d reads (%2.1f%% hits), %d misses (%2.1f%%), %d misaligned (%2.1f%%).\n",
+ func, mReads, 100*(mReads-mMisses)/(float)(mReads+mMisaligned), mMisses,
+ 100*mMisses/(float)(mReads+mMisaligned), mMisaligned, 100*mMisaligned/(float)(mReads+mMisaligned));
+ OutputDebugStringA(buffer);
+#endif
+ }
+
+private:
+ /* Sets the cache to the page specified by addr, or false if we could not move to
+ * that page.
+ */
+ bool MoveToPage(TADDR addr, unsigned int size = 0x18);
+
+ /* Attempts to read from the target process if the data is possibly hanging off
+ * the end of a page.
+ */
+ template<class T>
+ inline bool MisalignedRead(TADDR addr, T *t)
+ {
+ ULONG fetched = 0;
+ HRESULT hr = g_ExtData->ReadVirtual(addr, (BYTE*)t, sizeof(T), &fetched);
+
+ if (FAILED(hr) || fetched != sizeof(T))
+ return false;
+
+ mMisaligned++;
+ return true;
+ }
+
+private:
+ TADDR mCurrPageStart;
+ ULONG mPageSize, mCurrPageSize;
+ BYTE *mPage;
+
+ int mMisses, mReads, mMisaligned;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+// Methods for creating a database out of the gc heap and it's roots in xml format or CLRProfiler format
+//
+
+#include <unordered_map>
+#include <unordered_set>
+#include <list>
+
+class TypeTree;
+enum { FORMAT_XML=0, FORMAT_CLRPROFILER=1 };
+enum { TYPE_START=0,TYPE_TYPES=1,TYPE_ROOTS=2,TYPE_OBJECTS=3,TYPE_HIGHEST=4};
+class HeapTraverser
+{
+private:
+ TypeTree *m_pTypeTree;
+ size_t m_curNID;
+ FILE *m_file;
+ int m_format; // from the enum above
+ size_t m_objVisited; // for UI updates
+ bool m_verify;
+ LinearReadCache mCache;
+
+ std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap;
+
+public:
+ HeapTraverser(bool verify);
+ ~HeapTraverser();
+
+ FILE *getFile() { return m_file; }
+
+ BOOL Initialize();
+ BOOL CreateReport (FILE *fp, int format);
+
+private:
+ // First all types are added to a tree
+ void insert(size_t mTable);
+ size_t getID(size_t mTable);
+
+ // Functions for writing to the output file.
+ void PrintType(size_t ID,LPCWSTR name);
+
+ void PrintObjectHead(size_t objAddr,size_t typeID,size_t Size);
+ void PrintObjectMember(size_t memberValue, bool dependentHandle);
+ void PrintObjectTail();
+
+ void PrintRootHead();
+ void PrintRoot(LPCWSTR kind,size_t Value);
+ void PrintRootTail();
+
+ void PrintSection(int Type,BOOL bOpening);
+
+ // Root and object member helper functions
+ void FindGCRootOnStacks();
+ void PrintRefs(size_t obj, size_t methodTable, size_t size);
+
+ // Callback functions used during traversals
+ static void GatherTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token);
+ static void PrintHeap(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token);
+ static void PrintOutTree(size_t methodTable, size_t ID, LPVOID token);
+ void TraceHandles();
+};
+
+
+class GCRootImpl
+{
+private:
+ struct MTInfo
+ {
+ TADDR MethodTable;
+ WCHAR *TypeName;
+
+ TADDR *Buffer;
+ CGCDesc *GCDesc;
+
+ bool ArrayOfVC;
+ bool ContainsPointers;
+ size_t BaseSize;
+ size_t ComponentSize;
+
+ const WCHAR *GetTypeName()
+ {
+ if (!TypeName)
+ TypeName = CreateMethodTableName(MethodTable);
+
+ if (!TypeName)
+ return W("<error>");
+
+ return TypeName;
+ }
+
+ MTInfo()
+ : MethodTable(0), TypeName(0), Buffer(0), GCDesc(0),
+ ArrayOfVC(false), ContainsPointers(false), BaseSize(0), ComponentSize(0)
+ {
+ }
+
+ ~MTInfo()
+ {
+ if (Buffer)
+ delete [] Buffer;
+
+ if (TypeName)
+ delete [] TypeName;
+ }
+ };
+
+ struct RootNode
+ {
+ RootNode *Next;
+ RootNode *Prev;
+ TADDR Object;
+ MTInfo *MTInfo;
+
+ bool FilledRefs;
+ bool FromDependentHandle;
+ RootNode *GCRefs;
+
+
+ const WCHAR *GetTypeName()
+ {
+ if (!MTInfo)
+ return W("<unknown>");
+
+ return MTInfo->GetTypeName();
+ }
+
+ RootNode()
+ : Next(0), Prev(0)
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ if (Next && Next->Prev == this)
+ Next->Prev = NULL;
+
+ if (Prev && Prev->Next == this)
+ Prev->Next = NULL;
+
+ Next = 0;
+ Prev = 0;
+ Object = 0;
+ MTInfo = 0;
+ FilledRefs = false;
+ FromDependentHandle = false;
+ GCRefs = 0;
+ }
+
+ void Remove(RootNode *&list)
+ {
+ RootNode *curr_next = Next;
+
+ // We've already considered this object, remove it.
+ if (Prev == NULL)
+ {
+ // If we've filtered out the head, update it.
+ list = curr_next;
+
+ if (curr_next)
+ curr_next->Prev = NULL;
+ }
+ else
+ {
+ // Otherwise remove the current item from the list
+ Prev->Next = curr_next;
+
+ if (curr_next)
+ curr_next->Prev = Prev;
+ }
+ }
+ };
+
+public:
+ static void GetDependentHandleMap(std::unordered_map<TADDR, std::list<TADDR>> &map);
+
+public:
+ // Finds all objects which root "target" and prints the path from the root
+ // to "target". If all is true, all possible paths to the object are printed.
+ // If all is false, only completely unique paths will be printed.
+ int PrintRootsForObject(TADDR obj, bool all, bool noStacks);
+
+ // Finds a path from root to target if it exists and prints it out. Returns
+ // true if it found a path, false otherwise.
+ bool PrintPathToObject(TADDR root, TADDR target);
+
+ // Calculates the size of the closure of objects kept alive by root.
+ size_t ObjSize(TADDR root);
+
+ // Walks each root, printing out the total amount of memory held alive by it.
+ void ObjSize();
+
+ // Returns the set of all live objects in the process.
+ const std::unordered_set<TADDR> &GetLiveObjects(bool excludeFQ = false);
+
+ // See !FindRoots.
+ int FindRoots(int gen, TADDR target);
+
+private:
+ // typedefs
+ typedef void (*ReportCallback)(TADDR root, RootNode *path, bool printHeader);
+
+ // Book keeping and debug.
+ void ClearAll();
+ void ClearNodes();
+ void ClearSizeData();
+
+ // Printing roots
+ int PrintRootsOnHandleTable(int gen = -1);
+ int PrintRootsOnAllThreads();
+ int PrintRootsOnThread(DWORD osThreadId);
+ int PrintRootsOnFQ(bool notReadyForFinalization = false);
+ int PrintRootsInOlderGen();
+ int PrintRootsInRange(LinearReadCache &cache, TADDR start, TADDR stop, ReportCallback func, bool printHeader);
+
+ // Calculate gc root
+ RootNode *FilterRoots(RootNode *&list);
+ RootNode *FindPathToTarget(TADDR root);
+ RootNode *GetGCRefs(RootNode *path, RootNode *node);
+
+ void InitDependentHandleMap();
+
+ //Reporting:
+ void ReportOneHandlePath(const SOSHandleData &handle, RootNode *node, bool printHeader);
+ void ReportOnePath(DWORD thread, const SOSStackRefData &stackRef, RootNode *node, bool printThread, bool printFrame);
+ static void ReportOneFQEntry(TADDR root, RootNode *path, bool printHeader);
+ static void ReportOlderGenEntry(TADDR root, RootNode *path, bool printHeader);
+ void ReportSizeInfo(const SOSHandleData &handle, TADDR obj);
+ void ReportSizeInfo(DWORD thread, const SOSStackRefData &ref, TADDR obj);
+
+ // Data reads:
+ TADDR ReadPointer(TADDR location);
+ TADDR ReadPointerCached(TADDR location);
+
+ // Object/MT data:
+ MTInfo *GetMTInfo(TADDR mt);
+ DWORD GetComponents(TADDR obj, TADDR mt);
+ size_t GetSizeOfObject(TADDR obj, MTInfo *info);
+
+ // RootNode management:
+ RootNode *NewNode(TADDR obj = 0, MTInfo *mtinfo = 0, bool fromDependent = false);
+ void DeleteNode(RootNode *node);
+
+private:
+
+ bool mAll, // Print all roots or just unique roots?
+ mSize; // Print rooting information or total size info?
+
+ std::list<RootNode*> mCleanupList; // A list of RootNode's we've newed up. This is only used to delete all of them later.
+ std::list<RootNode*> mRootNewList; // A list of unused RootNodes that are free to use instead of having to "new" up more.
+
+ std::unordered_map<TADDR, MTInfo*> mMTs; // The MethodTable cache which maps from MT -> MethodTable data (size, gcdesc, string typename)
+ std::unordered_map<TADDR, RootNode*> mTargets; // The objects that we are searching for.
+ std::unordered_set<TADDR> mConsidered; // A hashtable of objects we've already visited.
+ std::unordered_map<TADDR, size_t> mSizes; // A mapping from object address to total size of data the object roots.
+
+ std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap;
+
+ LinearReadCache mCache; // A linear cache which stops us from having to read from the target process more than 1-2 times per object.
+};
+
+//
+// Helper class used for type-safe bitflags
+// T - the enum type specifying the individual bit flags
+// U - the underlying/storage type
+// Requirement:
+// sizeof(T) <= sizeof(U)
+//
+template <typename T, typename U>
+struct Flags
+{
+ typedef T UnderlyingType;
+ typedef U BitFlagEnumType;
+
+ static_assert_no_msg(sizeof(BitFlagEnumType) <= sizeof(UnderlyingType));
+
+ Flags(UnderlyingType v)
+ : m_val(v)
+ { }
+
+ Flags(BitFlagEnumType v)
+ : m_val(v)
+ { }
+
+ Flags(const Flags& other)
+ : m_val(other.m_val)
+ { }
+
+ Flags& operator = (const Flags& other)
+ { m_val = other.m_val; return *this; }
+
+ Flags operator | (Flags other) const
+ { return Flags<T, U>(m_val | other._val); }
+
+ void operator |= (Flags other)
+ { m_val |= other.m_val; }
+
+ Flags operator & (Flags other) const
+ { return Flags<T, U>(m_val & other.m_val); }
+
+ void operator &= (Flags other)
+ { m_val &= other.m_val; }
+
+ Flags operator ^ (Flags other) const
+ { return Flags<T, U>(m_val ^ other._val); }
+
+ void operator ^= (Flags other)
+ { m_val ^= other.m_val; }
+
+ BOOL operator == (Flags other) const
+ { return m_val == other.m_val; }
+
+ BOOL operator != (Flags other) const
+ { return m_val != other.m_val; }
+
+
+private:
+ UnderlyingType m_val;
+};
+
+#ifndef FEATURE_PAL
+
+// Flags defining activation policy for COM objects
+enum CIOptionsBits
+{
+ cciLatestFx = 0x01, // look in the most recent .NETFx installation
+ cciMatchFx = 0x02, // NYI: Look in the .NETFx installation matching the debuggee's runtime
+ cciAnyFx = 0x04, // look in any .NETFx installation
+ cciFxMask = 0x0f,
+ cciDbiColocated = 0x10, // NYI: Look next to the already loaded DBI module
+ cciDacColocated = 0x20, // Look next to the already loaded DAC module
+ cciDbgPath = 0x40, // Look in all folders in the debuggers symbols and binary path
+};
+
+typedef Flags<DWORD, CIOptionsBits> CIOptions;
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceCustom() provides a way to activate a COM object w/o *
+* triggering the FeatureOnDemand dialog. In order to do this we *
+* must avoid using the CoCreateInstance() API, which, on a machine *
+* with v4+ installed and w/o v2, would trigger this. *
+* CreateInstanceCustom() activates the requested COM object according *
+* to the specified passed in CIOptions, in the following order *
+* (skipping the steps not enabled in the CIOptions flags passed in): *
+* 1. Attempt to activate the COM object using a framework install: *
+* a. If the debugger machine has a V4+ shell shim use the shim *
+* to activate the object *
+* b. Otherwise simply call CoCreateInstance *
+* 2. If unsuccessful attempt to activate looking for the dllName in *
+* the same folder as the DAC was loaded from *
+* 3. If unsuccessful attempt to activate the COM object looking in *
+* every path specified in the debugger's .exepath and .sympath *
+\**********************************************************************/
+HRESULT CreateInstanceCustom(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf);
+
+
+//------------------------------------------------------------------------
+// A typesafe version of GetProcAddress
+//------------------------------------------------------------------------
+template <typename T>
+BOOL
+GetProcAddressT(
+ ___in PCSTR FunctionName,
+ __in_opt PCWSTR DllName,
+ __inout T* OutFunctionPointer,
+ __inout HMODULE* InOutDllHandle
+ )
+{
+ _ASSERTE(InOutDllHandle != NULL);
+ _ASSERTE(OutFunctionPointer != NULL);
+
+ T FunctionPointer = NULL;
+ HMODULE DllHandle = *InOutDllHandle;
+ if (DllHandle == NULL)
+ {
+ DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (DllHandle != NULL)
+ *InOutDllHandle = DllHandle;
+ }
+ if (DllHandle != NULL)
+ {
+ FunctionPointer = (T) GetProcAddress(DllHandle, FunctionName);
+ }
+ *OutFunctionPointer = FunctionPointer;
+ return FunctionPointer != NULL;
+}
+
+
+#endif // FEATURE_PAL
+
+struct ImageInfo
+{
+ ULONG64 modBase;
+};
+
+// Helper class used in ClrStackFromPublicInterface() to keep track of explicit EE Frames
+// (i.e., "internal frames") on the stack. Call Init() with the appropriate
+// ICorDebugThread3, and this class will initialize itself with the set of internal
+// frames. You can then call PrintPrecedingInternalFrames during your stack walk to
+// have this class output any internal frames that "precede" (i.e., that are closer to
+// the leaf than) the specified ICorDebugFrame.
+class InternalFrameManager
+{
+private:
+ // TODO: Verify constructor AND destructor is called for each array element
+ // TODO: Comment about hard-coding 1000
+ ToRelease<ICorDebugInternalFrame2> m_rgpInternalFrame2[1000];
+ ULONG32 m_cInternalFramesActual;
+ ULONG32 m_iInternalFrameCur;
+
+public:
+ InternalFrameManager();
+ HRESULT Init(ICorDebugThread3 * pThread3);
+ HRESULT PrintPrecedingInternalFrames(ICorDebugFrame * pFrame);
+
+private:
+ HRESULT PrintCurrentInternalFrame();
+};
+
+#endif // __util_h__
diff --git a/src/ToolBox/SOS/Strike/vm.cpp b/src/ToolBox/SOS/Strike/vm.cpp
new file mode 100644
index 0000000000..e7e5701fc6
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/vm.cpp
@@ -0,0 +1,732 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ==++==
+//
+
+//
+// ==--==
+/*++
+
+Module Name:
+
+ vm.cxx
+
+Abstract:
+
+ This module contains an NTSD debugger extension for dumping various
+ virtual memory statistics.
+
+Revision History:
+
+--*/
+
+#ifndef FEATURE_PAL
+#include <tchar.h>
+
+
+#include "strike.h"
+#include "util.h"
+#include "gcinfo.h"
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+
+
+
+//
+// Private constants.
+//
+
+#define SMALL_REGION (64 * 1024)
+#define MEDIUM_REGION (1 * 1024 * 1024)
+
+#define IS_SMALL(c) ((c) <= SMALL_REGION)
+#define IS_MEDIUM(c) (((c) > SMALL_REGION) && ((c) <= MEDIUM_REGION))
+#define IS_LARGE(c) ((c) > MEDIUM_REGION)
+
+#define PRINTF_FORMAT_HEAD "%-7s %*s %*s %*s %*s %*s\n"
+#define PRINTF_FORMAT "%-7s %*sK %*sK %*sK %*s %*sK\n"
+
+#define CCH_ULONGLONG_COMMAS _countof("18,446,744,073,709,551,616")
+#define CCH_ULONGLONG_MINIMUM_COMMAS (CCH_ULONGLONG_COMMAS - 3)
+#define CCH_ULONGLONG_BLOCKCOUNT_COMMAS sizeof("1,000,000")
+
+
+//
+// Private types.
+//
+
+typedef struct _INDIVIDUAL_STAT
+{
+ SIZE_T MinimumSize;
+ SIZE_T MaximumSize;
+ SIZE_T TotalSize;
+ SIZE_T BlockCount;
+
+} INDIVIDUAL_STAT, *PINDIVIDUAL_STAT;
+
+typedef struct _VM_STATS
+{
+ INDIVIDUAL_STAT Summary;
+ INDIVIDUAL_STAT Small;
+ INDIVIDUAL_STAT Medium;
+ INDIVIDUAL_STAT Large;
+
+} VM_STATS, *PVM_STATS;
+
+typedef struct PROTECT_MASK
+{
+ DWORD Bit;
+ PSTR Name;
+
+} PROTECT_MASK, *PPROTECT_MASK;
+
+
+//
+// Private globals.
+//
+
+PROTECT_MASK ProtectMasks[] =
+ {
+ {
+ PAGE_NOACCESS,
+ "NA"
+ },
+
+ {
+ PAGE_NOCACHE,
+ "NC"
+ },
+
+ {
+ PAGE_GUARD,
+ "G"
+ },
+
+ {
+ PAGE_READONLY,
+ "Rd"
+ },
+
+ {
+ PAGE_READWRITE,
+ "RdWr"
+ },
+
+ {
+ PAGE_WRITECOPY,
+ "WrCp"
+ },
+
+ {
+ PAGE_EXECUTE,
+ "Ex"
+ },
+
+ {
+ PAGE_EXECUTE_READ,
+ "ExRd"
+ },
+
+ {
+ PAGE_EXECUTE_READWRITE,
+ "ExRdWr"
+ },
+
+ {
+ PAGE_EXECUTE_WRITECOPY,
+ "ExWrCp"
+ }
+ };
+
+#define NUM_PROTECT_MASKS (sizeof(ProtectMasks) / sizeof(ProtectMasks[0]))
+
+//
+// Private functions.
+//
+
+
+PSTR
+ULongLongToString(
+ IN ULONGLONG Value,
+ __out_ecount (CCH_ULONGLONG_COMMAS) OUT PSTR Buffer
+ )
+{
+
+ PSTR p1;
+ PSTR p2;
+ CHAR ch;
+ INT digit;
+ INT count;
+ BOOL needComma;
+ INT length;
+
+ //
+ // Handling zero specially makes everything else a bit easier.
+ //
+
+ if( Value == 0 ) {
+ Buffer[0] = '0';
+ Buffer[1] = '\0';
+ return Buffer;
+ }
+
+ //
+ // Pull the least signifigant digits off the value and store them
+ // into the buffer. Note that this will store the digits in the
+ // reverse order.
+ //
+
+ p1 = p2 = Buffer;
+ count = 3;
+ needComma = FALSE;
+
+ while( Value != 0 ) {
+
+ if( needComma ) {
+ *p1++ = ',';
+ needComma = FALSE;
+ }
+
+ digit = (INT)( Value % 10 );
+ Value = Value / 10;
+
+ *p1++ = '0' + (CHAR) digit;
+
+ count--;
+ if( count == 0 ) {
+ count = 3;
+ needComma = TRUE;
+ }
+
+ }
+
+ length = (INT)(((size_t)p1) - ((size_t)Buffer));
+
+ //
+ // Reverse the digits in the buffer.
+ //
+
+ *p1-- = '\0';
+
+ while( p1 > p2 ) {
+
+ ch = *p1;
+ *p1 = *p2;
+ *p2 = ch;
+
+ p2++;
+ p1--;
+
+ }
+
+ return Buffer;
+
+} // ULongLongToString
+
+VOID
+InitVmStats(
+ OUT PVM_STATS Stats
+ )
+{
+ ZeroMemory( Stats, sizeof(*Stats) );
+ Stats->Summary.MinimumSize = (SIZE_T)-1L;
+ Stats->Small.MinimumSize = (SIZE_T)-1L;
+ Stats->Medium.MinimumSize = (SIZE_T)-1L;
+ Stats->Large.MinimumSize = (SIZE_T)-1L;
+
+} // InitVmStats
+
+VOID
+UpdateIndividualStat(
+ IN OUT PINDIVIDUAL_STAT Stat,
+ IN SIZE_T BlockSize
+ )
+{
+ Stat->BlockCount++;
+ Stat->TotalSize += BlockSize;
+
+ if( BlockSize > Stat->MaximumSize ) {
+ Stat->MaximumSize = BlockSize;
+ }
+
+ if( BlockSize < Stat->MinimumSize ) {
+ Stat->MinimumSize = BlockSize;
+ }
+
+} // UpdateIndividualStat
+
+VOID
+UpdateVmStats(
+ IN OUT PVM_STATS Stats,
+ IN SIZE_T BlockSize
+ )
+{
+ UpdateIndividualStat( &Stats->Summary, BlockSize );
+
+ if( IS_SMALL(BlockSize) ) {
+ UpdateIndividualStat( &Stats->Small, BlockSize );
+ }
+
+ if( IS_MEDIUM(BlockSize) ) {
+ UpdateIndividualStat( &Stats->Medium, BlockSize );
+ }
+
+ if( IS_LARGE(BlockSize) ) {
+ UpdateIndividualStat( &Stats->Large, BlockSize );
+ }
+
+} // UpdateVmStats
+
+VOID
+PrintVmStatsHeader(
+ VOID
+ )
+{
+ ExtOut(
+ PRINTF_FORMAT_HEAD,
+ "TYPE",
+ CCH_ULONGLONG_MINIMUM_COMMAS,
+ "MINIMUM",
+ CCH_ULONGLONG_COMMAS,
+ "MAXIMUM",
+ CCH_ULONGLONG_COMMAS,
+ "AVERAGE",
+ CCH_ULONGLONG_BLOCKCOUNT_COMMAS,
+ "BLK COUNT",
+ CCH_ULONGLONG_COMMAS,
+ "TOTAL"
+ );
+
+ ExtOut(
+ PRINTF_FORMAT_HEAD,
+ "~~~~",
+ CCH_ULONGLONG_MINIMUM_COMMAS,
+ "~~~~~~~",
+ CCH_ULONGLONG_COMMAS,
+ "~~~~~~~",
+ CCH_ULONGLONG_COMMAS,
+ "~~~~~~~",
+ CCH_ULONGLONG_BLOCKCOUNT_COMMAS,
+ "~~~~~~~~~",
+ CCH_ULONGLONG_COMMAS,
+ "~~~~~"
+ );
+
+} // PrintVmStatsHeader
+
+#define BYTES_TO_K(x) (x/1024)
+
+VOID
+PrintIndividualStat(
+ ___in __in_z IN PSTR Name,
+ IN PINDIVIDUAL_STAT Stat
+ )
+{
+ SIZE_T average;
+ SIZE_T minsize;
+ CHAR minStr[CCH_ULONGLONG_COMMAS];
+ CHAR maxStr[CCH_ULONGLONG_COMMAS];
+ CHAR avgStr[CCH_ULONGLONG_COMMAS];
+ CHAR countStr[CCH_ULONGLONG_COMMAS];
+ CHAR totalStr[CCH_ULONGLONG_COMMAS];
+
+ if( Stat->BlockCount == 0 ) {
+ average = 0;
+ minsize = 0;
+ } else {
+ average = Stat->TotalSize / Stat->BlockCount;
+ minsize = Stat->MinimumSize;
+ }
+
+ ExtOut(
+ PRINTF_FORMAT,
+ Name,
+ CCH_ULONGLONG_MINIMUM_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(minsize),
+ minStr
+ ),
+ CCH_ULONGLONG_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(Stat->MaximumSize),
+ maxStr
+ ),
+ CCH_ULONGLONG_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(average),
+ avgStr
+ ),
+ CCH_ULONGLONG_BLOCKCOUNT_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)Stat->BlockCount,
+ countStr
+ ),
+ CCH_ULONGLONG_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(Stat->BlockCount * average),
+ totalStr
+ )
+ );
+
+} // PrintIndividualStat
+
+
+VOID
+PrintVmStats(
+ ___in __in_z IN PSTR Name,
+ IN PVM_STATS Stats
+ )
+{
+ ExtOut( "%s:\n", Name );
+
+ PrintIndividualStat( "Small", &Stats->Small );
+ PrintIndividualStat( "Medium", &Stats->Medium );
+ PrintIndividualStat( "Large", &Stats->Large );
+ PrintIndividualStat( "Summary", &Stats->Summary );
+
+ ExtOut( "\n" );
+
+} // PrintVmStats
+
+PSTR
+VmProtectToString(
+ IN DWORD Protect,
+ __out_ecount(capacity_Buffer) OUT PSTR Buffer,
+ size_t capacity_Buffer
+ )
+{
+ INT i;
+ PPROTECT_MASK mask;
+
+ Buffer[0] = '\0';
+
+ for( i = 0, mask = &ProtectMasks[0] ;
+ (i < NUM_PROTECT_MASKS) && (Protect != 0) ;
+ i++, mask++ ) {
+ if( mask->Bit & Protect ) {
+ Protect &= ~mask->Bit;
+ if( Buffer[0] != '\0' ) {
+ strcat_s(Buffer,capacity_Buffer, "|" );
+ }
+ strcat_s( Buffer, capacity_Buffer, mask->Name );
+ }
+ }
+
+ if( Protect != 0 ) {
+ if( Buffer[0] != '\0' ) {
+ strcat_s( Buffer, capacity_Buffer, "|" );
+ }
+ size_t len_Buffer = strlen(Buffer);
+ size_t cbSizeInBytes = 0;
+ if (!ClrSafeInt<size_t>::subtraction(capacity_Buffer, len_Buffer, cbSizeInBytes))
+ {
+ ExtOut("<integer underflow>\n");
+ return Buffer;
+ }
+ sprintf_s( Buffer + len_Buffer, cbSizeInBytes, "%08lx", Protect );
+ }
+
+ return Buffer;
+
+} // VmProtectToString
+
+PSTR
+VmStateToString(
+ IN DWORD State,
+ __out_ecount(capacity_Buffer) OUT PSTR Buffer,
+ size_t capacity_Buffer
+ )
+{
+ PSTR result;
+ CHAR invalidStr[sizeof("12345678")];
+
+ switch( State )
+ {
+ case MEM_COMMIT:
+ result = "Commit";
+ break;
+
+ case MEM_RESERVE:
+ result = "Reserve";
+ break;
+
+ case MEM_FREE:
+ result = "Free";
+ break;
+
+ default:
+ sprintf_s(invalidStr,_countof(invalidStr), "%08lx", State );
+ result = invalidStr;
+ break;
+ }
+
+ strcpy_s( Buffer, capacity_Buffer, result );
+ return Buffer;
+
+} // VmStateToString
+
+PSTR
+VmTypeToString(
+ IN DWORD Type,
+ __out_ecount(capacity_Buffer) OUT PSTR Buffer,
+ size_t capacity_Buffer
+ )
+{
+ PSTR result;
+ CHAR invalidStr[sizeof("12345678")];
+
+ switch( Type )
+ {
+ case MEM_PRIVATE:
+ result = "Private";
+ break;
+
+ case MEM_MAPPED:
+ result = "Mapped";
+ break;
+
+ case MEM_IMAGE:
+ result = "Image";
+ break;
+
+ case 0:
+ result = "";
+ break;
+
+ default:
+ sprintf_s(invalidStr,_countof(invalidStr), "%08lx", Type );
+ result = invalidStr;
+ break;
+ }
+ strcpy_s( Buffer,capacity_Buffer, result );
+ return Buffer;
+
+} // VmTypeToString
+
+/************************************************************
+ * Dump Virtual Memory Info
+ ************************************************************/
+
+void vmstat()
+
+/*++
+
+Routine Description:
+
+ This function is called as an NTSD extension to format and dump
+ virtual memory statistics.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ ULONG64 address;
+ MEMORY_BASIC_INFORMATION64 memInfo;
+ VM_STATS freeStats;
+ VM_STATS reserveStats;
+ VM_STATS commitStats;
+ VM_STATS privateStats;
+ VM_STATS mappedStats;
+ VM_STATS imageStats;
+
+ //
+ // Setup.
+ //
+
+ InitVmStats( &freeStats );
+ InitVmStats( &reserveStats );
+ InitVmStats( &commitStats );
+ InitVmStats( &privateStats );
+ InitVmStats( &mappedStats );
+ InitVmStats( &imageStats );
+
+ address = 0;
+
+ //
+ // Scan the virtual address space.
+ //
+
+ for( ; ; ) {
+ status = g_ExtData2->QueryVirtual(address, &memInfo);
+
+ if( !NT_SUCCESS(status) ) {
+ break;
+ }
+
+ //
+ // Interpret the memory state.
+ //
+
+ SIZE_T regionSize = (SIZE_T) memInfo.RegionSize;
+
+ switch( memInfo.State ) {
+
+ case MEM_FREE:
+ UpdateVmStats( &freeStats, regionSize );
+ break;
+
+ case MEM_RESERVE:
+ UpdateVmStats( &reserveStats, regionSize );
+ break;
+
+ case MEM_COMMIT:
+ UpdateVmStats( &commitStats, regionSize );
+ break;
+ }
+
+ //
+ // Interpret the memory type.
+ //
+
+ switch( memInfo.Type ) {
+ case MEM_PRIVATE:
+ UpdateVmStats( &privateStats, regionSize );
+ break;
+
+ case MEM_MAPPED:
+ UpdateVmStats( &mappedStats, regionSize );
+ break;
+
+ case MEM_IMAGE:
+ UpdateVmStats( &imageStats, regionSize );
+ break;
+ }
+
+ //
+ // Advance to the next block.
+ //
+
+ address += memInfo.RegionSize;
+ }
+
+ //
+ // Dump it.
+ //
+
+ PrintVmStatsHeader();
+ PrintVmStats( "Free", &freeStats );
+ PrintVmStats( "Reserve", &reserveStats );
+ PrintVmStats( "Commit", &commitStats );
+ PrintVmStats( "Private", &privateStats );
+ PrintVmStats( "Mapped", &mappedStats );
+ PrintVmStats( "Image", &imageStats );
+
+} // DECLARE_API( vmstat )
+
+
+void vmmap()
+
+/*++
+
+Routine Description:
+
+ This function is called as an NTSD extension to format and dump
+ the debugee's virtual memory address space.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ ULONG64 address;
+ MEMORY_BASIC_INFORMATION64 memInfo;
+ CHAR protectStr[32];
+ CHAR aprotectStr[32];
+ CHAR stateStr[16];
+ CHAR typeStr[16];
+
+ //
+ // Setup.
+ //
+
+ address = 0;
+
+ ExtOut(
+ "%-*s %-*s %-*s %-13s %-13s %-8s %-8s\n",
+ sizeof(PVOID) * 2,
+ "Start",
+ sizeof(PVOID) * 2,
+ "Stop",
+ sizeof(PVOID) * 2,
+ "Length",
+ "AllocProtect",
+ "Protect",
+ "State",
+ "Type"
+ );
+
+ //
+ // Scan the virtual address space.
+ //
+
+ for( ; ; ) {
+
+ if (IsInterrupt())
+ break;
+
+ status = g_ExtData2->QueryVirtual(address, &memInfo);
+
+ if( !NT_SUCCESS(status) ) {
+ break;
+ }
+
+ //
+ // Dump the current entry.
+ //
+
+ ExtOut(
+ "%p-%p %p %-13s %-13s %-8s %-8s\n",
+ SOS_PTR(memInfo.BaseAddress),
+ SOS_PTR(((ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize - 1)),
+ SOS_PTR(memInfo.RegionSize),
+ VmProtectToString( memInfo.AllocationProtect, aprotectStr, _countof(aprotectStr) ),
+ VmProtectToString( memInfo.Protect, protectStr, _countof(protectStr) ),
+ VmStateToString( memInfo.State, stateStr, _countof(stateStr) ),
+ VmTypeToString( memInfo.Type, typeStr , _countof(typeStr))
+ );
+
+ //
+ // Advance to the next block.
+ //
+
+ address += memInfo.RegionSize;
+ }
+
+} // DECLARE_API( vmmap )
+
+#else
+
+#include <rotor_pal.h>
+#include <assert.h>
+
+void vmstat()
+{
+ assert(false);
+}
+
+void vmmap()
+{
+
+ assert(false);
+}
+
+#endif // #ifndef FEATURE_PAL
diff --git a/src/ToolBox/SOS/Strike/xplat/.gitmirror b/src/ToolBox/SOS/Strike/xplat/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/xplat/dbgeng.h b/src/ToolBox/SOS/Strike/xplat/dbgeng.h
new file mode 100644
index 0000000000..b4562271a6
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/dbgeng.h
@@ -0,0 +1,485 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//----------------------------------------------------------------------------
+//
+// Debugger engine interface subset implemented with ILLDBServices
+//
+//----------------------------------------------------------------------------
+
+#ifndef __DBGENG_H__
+#define __DBGENG_H__
+
+#include <unknwn.h>
+#include <rpc.h>
+#include <lldbservices.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+class DebugClient
+{
+private:
+ LONG m_ref;
+ ILLDBServices *m_lldbservices;
+
+public:
+ DebugClient(ILLDBServices *lldbservices) :
+ m_ref(1),
+ m_lldbservices(lldbservices)
+ {
+ m_lldbservices->AddRef();
+ }
+
+ //----------------------------------------------------------------------------
+ // IUnknown
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface);
+
+ ULONG AddRef();
+
+ ULONG Release();
+
+ //----------------------------------------------------------------------------
+ // IDebugControl2
+ //----------------------------------------------------------------------------
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ HRESULT
+ GetInterrupt()
+ {
+ return m_lldbservices->GetInterrupt();
+ }
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ HRESULT
+ Output(
+ ULONG mask,
+ PCSTR format,
+ ...)
+ {
+ va_list args;
+ va_start (args, format);
+ HRESULT result = m_lldbservices->OutputVaList(mask, format, args);
+ va_end (args);
+ return result;
+ }
+
+ HRESULT
+ OutputVaList(
+ ULONG mask,
+ PCSTR format,
+ va_list args)
+ {
+ char str[4096];
+ int length = PAL__vsnprintf(str, sizeof(str), format, args);
+ if (length > 0)
+ {
+ return Output(mask, "%s", str);
+ }
+ return E_FAIL;
+ }
+
+ // The following methods allow direct control
+ // over the distribution of the given output
+ // for situations where something other than
+ // the default is desired. These methods require
+ // extra work in the engine so they should
+ // only be used when necessary.
+ HRESULT
+ ControlledOutput(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ ...)
+ {
+ va_list args;
+ va_start (args, format);
+ HRESULT result = ControlledOutputVaList(outputControl, mask, format, args);
+ va_end (args);
+ return result;
+ }
+
+ HRESULT
+ ControlledOutputVaList(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ va_list args)
+ {
+ return OutputVaList(mask, format, args);
+ }
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ HRESULT
+ GetDebuggeeType(
+ PULONG debugClass,
+ PULONG qualifier)
+ {
+ return m_lldbservices->GetDebuggeeType(debugClass, qualifier);
+ }
+
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ HRESULT
+ GetPageSize(
+ PULONG size)
+ {
+ return m_lldbservices->GetPageSize(size);
+ }
+
+ HRESULT
+ GetExecutingProcessorType(
+ PULONG type)
+ {
+ return m_lldbservices->GetExecutingProcessorType(type);
+ }
+
+ HRESULT
+ Execute(
+ ULONG outputControl,
+ PCSTR command,
+ ULONG flags)
+ {
+ return m_lldbservices->Execute(outputControl, command, flags);
+ }
+
+ HRESULT
+ GetLastEventInformation(
+ PULONG type,
+ PULONG processId,
+ PULONG threadId,
+ PVOID extraInformation,
+ ULONG extraInformationSize,
+ PULONG extraInformationUsed,
+ PSTR description,
+ ULONG descriptionSize,
+ PULONG descriptionUsed)
+ {
+ return m_lldbservices->GetLastEventInformation(type, processId, threadId, extraInformation,
+ extraInformationSize, extraInformationUsed, description, descriptionSize, descriptionUsed);
+ }
+
+ HRESULT
+ Disassemble(
+ ULONG64 offset,
+ ULONG flags,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG disassemblySize,
+ PULONG64 endOffset)
+ {
+ return m_lldbservices->Disassemble(offset, flags, buffer, bufferSize, disassemblySize, endOffset);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugControl4
+ //----------------------------------------------------------------------------
+
+ // Stack tracing with a full initial context
+ // and full context return for each frame.
+ // The FrameContextsSize parameter is the total
+ // byte size of FrameContexts. FrameContextsEntrySize
+ // gives the byte size of each entry in
+ // FrameContexts.
+ HRESULT
+ GetContextStackTrace(
+ PVOID startContext,
+ ULONG startContextSize,
+ PDEBUG_STACK_FRAME frames,
+ ULONG framesSize,
+ PVOID frameContexts,
+ ULONG frameContextsSize,
+ ULONG frameContextsEntrySize,
+ PULONG framesFilled)
+ {
+ return m_lldbservices->GetContextStackTrace(startContext, startContextSize, frames,
+ framesSize, frameContexts, frameContextsSize, frameContextsEntrySize, framesFilled);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugDataSpaces
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ ReadVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesRead)
+ {
+ return m_lldbservices->ReadVirtual(offset, buffer, bufferSize, bytesRead);
+ }
+
+ HRESULT
+ WriteVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesWritten)
+ {
+ return m_lldbservices->WriteVirtual(offset, buffer, bufferSize, bytesWritten);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugSymbols
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetSymbolOptions(
+ PULONG options)
+ {
+ return m_lldbservices->GetSymbolOptions(options);
+ }
+
+ HRESULT
+ GetNameByOffset(
+ ULONG64 offset,
+ PSTR nameBuffer,
+ ULONG nameBufferSize,
+ PULONG nameSize,
+ PULONG64 displacement)
+ {
+ return m_lldbservices->GetNameByOffset(offset, nameBuffer, nameBufferSize, nameSize, displacement);
+ }
+
+ HRESULT
+ GetNumberModules(
+ PULONG loaded,
+ PULONG unloaded)
+ {
+ return m_lldbservices->GetNumberModules(loaded, unloaded);
+ }
+
+ HRESULT GetModuleByIndex(
+ ULONG index,
+ PULONG64 base)
+ {
+ return m_lldbservices->GetModuleByIndex(index, base);
+ }
+
+ HRESULT
+ GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base)
+ {
+ return m_lldbservices->GetModuleByModuleName(name, startIndex, index, base);
+ }
+
+ HRESULT
+ GetModuleByOffset(
+ ULONG64 offset,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base)
+ {
+ return m_lldbservices->GetModuleByOffset(offset, startIndex, index, base);
+ }
+
+ HRESULT
+ GetModuleNames(
+ ULONG index,
+ ULONG64 base,
+ PSTR imageNameBuffer,
+ ULONG imageNameBufferSize,
+ PULONG imageNameSize,
+ PSTR moduleNameBuffer,
+ ULONG moduleNameBufferSize,
+ PULONG moduleNameSize,
+ PSTR loadedImageNameBuffer,
+ ULONG loadedImageNameBufferSize,
+ PULONG loadedImageNameSize)
+ {
+ return m_lldbservices->GetModuleNames(index, base, imageNameBuffer, imageNameBufferSize, imageNameSize, moduleNameBuffer,
+ moduleNameBufferSize, moduleNameSize, loadedImageNameBuffer, loadedImageNameBufferSize, loadedImageNameSize);
+ }
+
+ HRESULT
+ GetLineByOffset(
+ ULONG64 offset,
+ PULONG line,
+ PSTR fileBuffer,
+ ULONG fileBufferSize,
+ PULONG fileSize,
+ PULONG64 displacement)
+ {
+ return m_lldbservices->GetLineByOffset(offset, line, fileBuffer, fileBufferSize, fileSize, displacement);
+ }
+
+ HRESULT
+ GetSourceFileLineOffsets(
+ PCSTR file,
+ PULONG64 buffer,
+ ULONG bufferLines,
+ PULONG fileLines)
+ {
+ return m_lldbservices->GetSourceFileLineOffsets(file, buffer, bufferLines, fileLines);
+ }
+
+ // Uses the given file path and the source path
+ // information to try and locate an existing file.
+ // The given file path is merged with elements
+ // of the source path and checked for existence.
+ // If a match is found the element used is returned.
+ // A starting element can be specified to restrict
+ // the search to a subset of the path elements;
+ // this can be useful when checking for multiple
+ // matches along the source path.
+ // The returned element can be 1, indicating
+ // the file was found directly and not on the path.
+ HRESULT
+ FindSourceFile(
+ ULONG startElement,
+ PCSTR file,
+ ULONG flags,
+ PULONG foundElement,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG foundSize)
+ {
+ return m_lldbservices->FindSourceFile(startElement, file, flags, foundElement, buffer, bufferSize, foundSize);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugSystemObjects
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetCurrentProcessId(
+ PULONG id)
+ {
+ return m_lldbservices->GetCurrentProcessId(id);
+ }
+
+ HRESULT
+ GetCurrentThreadId(
+ PULONG id)
+ {
+ return m_lldbservices->GetCurrentThreadId(id);
+ }
+
+ HRESULT
+ SetCurrentThreadId(
+ ULONG id)
+ {
+ return m_lldbservices->SetCurrentThreadId(id);
+ }
+
+ HRESULT
+ GetCurrentThreadSystemId(
+ PULONG sysId)
+ {
+ return m_lldbservices->GetCurrentThreadSystemId(sysId);
+ }
+
+ HRESULT
+ GetThreadIdBySystemId(
+ ULONG sysId,
+ PULONG threadId)
+ {
+ return m_lldbservices->GetThreadIdBySystemId(sysId, threadId);
+ }
+
+ HRESULT
+ GetThreadContextById(
+ /* in */ ULONG32 threadID,
+ /* in */ ULONG32 contextFlags,
+ /* in */ ULONG32 contextSize,
+ /* out */ PBYTE context)
+ {
+ return m_lldbservices->GetThreadContextById(threadID, contextFlags, contextSize, context);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugRegisters
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetValueByName(
+ PCSTR name,
+ PDWORD_PTR debugValue)
+ {
+ return m_lldbservices->GetValueByName(name, debugValue);
+ }
+
+ HRESULT
+ GetInstructionOffset(
+ PULONG64 offset)
+ {
+ return m_lldbservices->GetInstructionOffset(offset);
+ }
+
+ HRESULT
+ GetStackOffset(
+ PULONG64 offset)
+ {
+ return m_lldbservices->GetStackOffset(offset);
+ }
+
+ HRESULT
+ GetFrameOffset(
+ PULONG64 offset)
+ {
+ return m_lldbservices->GetFrameOffset(offset);
+ }
+};
+
+MIDL_INTERFACE("d4366723-44df-4bed-8c7e-4c05424f4588")
+IDebugControl2 : DebugClient
+{
+};
+
+MIDL_INTERFACE("94e60ce9-9b41-4b19-9fc0-6d9eb35272b3")
+IDebugControl4 : DebugClient
+{
+};
+
+MIDL_INTERFACE("88f7dfab-3ea7-4c3a-aefb-c4e8106173aa")
+IDebugDataSpaces : DebugClient
+{
+};
+
+MIDL_INTERFACE("8c31e98c-983a-48a5-9016-6fe5d667a950")
+IDebugSymbols : DebugClient
+{
+};
+
+MIDL_INTERFACE("6b86fe2c-2c4f-4f0c-9da2-174311acc327")
+IDebugSystemObjects : DebugClient
+{
+};
+
+MIDL_INTERFACE("ce289126-9e84-45a7-937e-67bb18691493")
+IDebugRegisters : DebugClient
+{
+};
+
+typedef interface ILLDBServices* PDEBUG_CLIENT;
+typedef interface IDebugControl2* PDEBUG_CONTROL2;
+typedef interface IDebugControl4* PDEBUG_CONTROL4;
+typedef interface IDebugDataSpaces* PDEBUG_DATA_SPACES;
+typedef interface IDebugSymbols* PDEBUG_SYMBOLS;
+typedef interface IDebugSystemObjects* PDEBUG_SYSTEM_OBJECTS;
+typedef interface IDebugRegisters* PDEBUG_REGISTERS;
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // #ifndef __DBGENG_H__
diff --git a/src/ToolBox/SOS/Strike/xplat/dbghelp.h b/src/ToolBox/SOS/Strike/xplat/dbghelp.h
new file mode 100644
index 0000000000..7086b335fd
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/dbghelp.h
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*
+
+Module Name:
+
+ dbghelp.h
+
+Abstract:
+
+ Dummy include file for LLDB sos extension.
+
+Revision History:
+
+--*/ \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/xplat/wdbgexts.h b/src/ToolBox/SOS/Strike/xplat/wdbgexts.h
new file mode 100644
index 0000000000..d5a1a31c0a
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/wdbgexts.h
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+Module Name:
+
+ wdbgexts.h
+
+Abstract:
+
+ Dummy include file for LLDB sos extension.
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _WDBGEXTS_
+#define _WDBGEXTS_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#endif // _WDBGEXTS_
diff --git a/src/ToolBox/SOS/diasdk/.gitmirror b/src/ToolBox/SOS/diasdk/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/diasdk/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/diasdk/CMakeLists.txt b/src/ToolBox/SOS/diasdk/CMakeLists.txt
new file mode 100644
index 0000000000..07bbcdc9d9
--- /dev/null
+++ b/src/ToolBox/SOS/diasdk/CMakeLists.txt
@@ -0,0 +1,16 @@
+# replace backslashes in path with forward slash
+STRING(REGEX REPLACE "\\\\" "/" VSINSTALLDIR $ENV{VSINSTALLDIR} )
+
+# Generate managed type lib from dia2.idl
+add_custom_target(dialib ALL
+ # generate tlb file from idl
+ COMMAND midl.exe /I"${VSINSTALLDIR}DIA SDK/include" /tlb ${CMAKE_CURRENT_BINARY_DIR}/dia2.tlb /Zp8 "${VSINSTALLDIR}DIA SDK/idl/dia2.idl"
+ # run tlbimp to generate managed type library
+ COMMAND TlbImp.exe /silent /namespace:Dia /unsafe /strictref:nopia ${CMAKE_CURRENT_BINARY_DIR}/dia2.tlb /out:${CMAKE_CURRENT_BINARY_DIR}/dialib.dll
+ COMMENT Generating managed type library from dia2.idl
+)
+
+# In order to use dialib.dll as library target it needs to be imported into cmake
+# Target is used in ToolBox/SOS/dactablegen/cmakelists.txt
+add_library_clr(dialib_dll SHARED IMPORTED GLOBAL)
+set_property(TARGET dialib_dll PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/dialib.dll)
diff --git a/src/ToolBox/SOS/diasdk/diasdk.nativeproj b/src/ToolBox/SOS/diasdk/diasdk.nativeproj
new file mode 100644
index 0000000000..230e2d9f93
--- /dev/null
+++ b/src/ToolBox/SOS/diasdk/diasdk.nativeproj
@@ -0,0 +1,43 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <OutputName>DIALib</OutputName>
+ <TargetType>DLLFROMIDL</TargetType>
+ <AssemblyAttributeComVisible>true</AssemblyAttributeComVisible>
+ <UserIncludes>$(UserIncludes);
+ $(VCToolsIncPath)</UserIncludes>
+ <MidlTlbDir>$(IntermediateOutputDirectory)</MidlTlbDir>
+ <TlbImportFlags>/namespace:Dia /unsafe</TlbImportFlags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Idl Include="$(VCToolsIncPath)\dia2.idl" />
+ <PublishPartLinked Include="$(IntermediateOutputDirectory)\dialib.dll">
+ <Visibility>Intra</Visibility>
+ <FileType>Binary</FileType>
+ </PublishPartLinked>
+ </ItemGroup>
+
+ <ItemGroup>
+ <RCResourceFile Include="native.rc" />
+ </ItemGroup>
+
+ <!--Leaf Project Items-->
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+
+ <PropertyGroup>
+ <BuildLinkedDependsOn>
+ $(BuildLinkedDependsOn)
+ PlaceSymbols
+ </BuildLinkedDependsOn>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/ToolBox/SOS/diasdk/native.rc b/src/ToolBox/SOS/diasdk/native.rc
new file mode 100644
index 0000000000..4c1007fd9a
--- /dev/null
+++ b/src/ToolBox/SOS/diasdk/native.rc
@@ -0,0 +1,7 @@
+// 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\0"
+
+#include <fxver.h>
+#include <fxver.rc>
diff --git a/src/ToolBox/SOS/dirs.proj b/src/ToolBox/SOS/dirs.proj
new file mode 100644
index 0000000000..85bea0d39a
--- /dev/null
+++ b/src/ToolBox/SOS/dirs.proj
@@ -0,0 +1,32 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <ItemDefinitionGroup>
+ <ProjectFile>
+ <ProductGroups>FX</ProductGroups>
+ </ProjectFile>
+ </ItemDefinitionGroup>
+
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="strike\dirs.proj" />
+ </ItemGroup>
+
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(BuildArchitecture)' == 'i386' and '$(FeatureCoreClr)' != 'true'">
+ <ProjectFile Include="diasdk\diasdk.nativeproj" >
+ <ProductGroups>FX;PK</ProductGroups>
+ </ProjectFile>
+ <ProjectFile Include="dactablegen\dactablegen.csproj" >
+ <ProductGroups>FX;PK</ProductGroups>
+ </ProjectFile>
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/ToolBox/SOS/lldbplugin/.gitmirrorall b/src/ToolBox/SOS/lldbplugin/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/ToolBox/SOS/lldbplugin/CMakeLists.txt b/src/ToolBox/SOS/lldbplugin/CMakeLists.txt
new file mode 100644
index 0000000000..9f90a54056
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/CMakeLists.txt
@@ -0,0 +1,115 @@
+project(sosplugin)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+# Set the RPATH of sos so that it can find dependencies without needing to set LD_LIBRARY_PATH
+# For more information: http://www.cmake.org/Wiki/CMake_RPATH_handling.
+if (CORECLR_SET_RPATH)
+ set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+ if(CLR_CMAKE_PLATFORM_DARWIN)
+ set(CMAKE_INSTALL_RPATH "@loader_path")
+ else()
+ set(CMAKE_INSTALL_RPATH "\$ORIGIN")
+ endif(CLR_CMAKE_PLATFORM_DARWIN)
+endif (CORECLR_SET_RPATH)
+
+add_definitions(-DPAL_STDCPP_COMPAT)
+
+if(CLR_CMAKE_PLATFORM_ARCH_AMD64)
+ add_definitions(-D_TARGET_AMD64_=1)
+ add_definitions(-DDBG_TARGET_64BIT=1)
+ add_definitions(-DDBG_TARGET_AMD64=1)
+ add_definitions(-DDBG_TARGET_WIN64=1)
+ add_definitions(-DBIT64)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM)
+ add_definitions(-D_TARGET_ARM_=1)
+ add_definitions(-DDBG_TARGET_32BIT=1)
+ add_definitions(-DDBG_TARGET_ARM=1)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64)
+ message(WARNING "lldb is not supported on linux/aarch64 yet")
+ return()
+endif()
+
+set(ENABLE_LLDBPLUGIN ${CLR_CMAKE_PLATFORM_UNIX} CACHE BOOL "Enable building the SOS plugin for LLDB.")
+set(REQUIRE_LLDBPLUGIN ${CLR_CMAKE_PLATFORM_LINUX} CACHE BOOL "Require building the SOS plugin for LLDB.")
+
+set(LLVM_HOST_DIR "$ENV{LLVM_HOME}")
+set(WITH_LLDB_LIBS "${LLVM_HOST_DIR}/lib" CACHE PATH "Path to LLDB libraries")
+set(WITH_LLDB_INCLUDES "${LLVM_HOST_DIR}/include" CACHE PATH "Path to LLDB headers")
+
+if(NOT ENABLE_LLDBPLUGIN)
+ return()
+endif()
+
+# Check for LLDB library
+find_library(LLDB NAMES LLDB lldb lldb-3.8 lldb-3.6 lldb-3.5 PATHS "${WITH_LLDB_LIBS}" PATH_SUFFIXES llvm NO_DEFAULT_PATH)
+find_library(LLDB NAMES LLDB lldb lldb-3.8 lldb-3.6 lldb-3.5 PATH_SUFFIXES llvm)
+if(LLDB STREQUAL LLDB-NOTFOUND)
+ if(REQUIRE_LLDBPLUGIN)
+ message(FATAL_ERROR "Cannot find lldb-3.5, lldb-3.6 or lldb-3.8. Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ else()
+ message(WARNING "Cannot find lldb-3.5,lldb-3.6 or lldb-3.8. Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ endif()
+ return()
+endif()
+
+message(STATUS "LLDB: ${LLDB}")
+
+# Check for LLDB headers
+
+find_path(LLDB_H "lldb/API/LLDB.h" PATHS "${WITH_LLDB_INCLUDES}" NO_DEFAULT_PATH)
+find_path(LLDB_H "lldb/API/LLDB.h")
+if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.8/include")
+ if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.6/include")
+ if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.5/include")
+ if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ if(REQUIRE_LLDBPLUGIN)
+ message(FATAL_ERROR "Cannot find LLDB.h. Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ else()
+ message(WARNING "Cannot find LLDB.h Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ endif()
+ return()
+ endif()
+ endif()
+ endif()
+endif()
+
+message(STATUS "LLDB_H: ${LLDB_H}")
+
+add_compile_options(-Wno-delete-non-virtual-dtor)
+
+include_directories(inc)
+include_directories("${LLDB_H}")
+include_directories(${CLR_DIR}/src/debug/inc)
+include_directories(${CLR_DIR}/src/inc)
+include_directories(${CLR_DIR}/src/coreclr/hosts/inc)
+include_directories(${CLR_DIR}/src/coreclr/hosts/unixcoreruncommon)
+
+set(SOURCES
+ sosplugin.cpp
+ soscommand.cpp
+ setclrpathcommand.cpp
+ setsostidcommand.cpp
+ services.cpp
+)
+
+if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
+ list(APPEND SOURCES
+ coreruncommand.cpp
+ ${CLR_DIR}/src/coreclr/hosts/unixcorerun/corerun.cpp
+ ${CLR_DIR}/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp
+ )
+endif(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
+
+_add_library(sosplugin SHARED ${SOURCES})
+add_dependencies(sosplugin sos)
+
+if (CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(sosplugin ${LLDB})
+endif()
+
+# add the install targets
+install_clr(sosplugin)
diff --git a/src/ToolBox/SOS/lldbplugin/coreruncommand.cpp b/src/ToolBox/SOS/lldbplugin/coreruncommand.cpp
new file mode 100644
index 0000000000..9187906acd
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/coreruncommand.cpp
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "sosplugin.h"
+#include <dlfcn.h>
+#include <string>
+
+extern int corerun(const int argc, const char* argv[]);
+
+class corerunCommand : public lldb::SBCommandPluginInterface
+{
+public:
+ corerunCommand()
+ {
+ }
+
+ virtual bool
+ DoExecute (lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ if (arguments)
+ {
+ int argc = 0;
+ char **argv = arguments;
+ for (const char* arg = *arguments; arg; arg = *(++arguments))
+ {
+ ++argc;
+ }
+ int exitcode = corerun((const int)argc, (const char**)argv);
+ if (exitcode != 0)
+ {
+ result.SetError("corerun failed");
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+bool
+corerunCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand command = interpreter.AddCommand("corerun", new corerunCommand(), "Run a managed app inside the debugger. corerun <exe-path> <managed-program-path> <command-line-args>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h b/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h
new file mode 100644
index 0000000000..16702b116d
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h
@@ -0,0 +1,565 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//----------------------------------------------------------------------------
+//
+// LLDB debugger services for sos
+//
+//----------------------------------------------------------------------------
+
+#ifndef __LLDBSERVICES_H__
+#define __LLDBSERVICES_H__
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Output mask bits.
+// Normal output.
+#define DEBUG_OUTPUT_NORMAL 0x00000001
+// Error output.
+#define DEBUG_OUTPUT_ERROR 0x00000002
+// Warnings.
+#define DEBUG_OUTPUT_WARNING 0x00000004
+// Additional output.
+#define DEBUG_OUTPUT_VERBOSE 0x00000008
+// Prompt output.
+#define DEBUG_OUTPUT_PROMPT 0x00000010
+// Register dump before prompt.
+#define DEBUG_OUTPUT_PROMPT_REGISTERS 0x00000020
+// Warnings specific to extension operation.
+#define DEBUG_OUTPUT_EXTENSION_WARNING 0x00000040
+// Debuggee debug output, such as from OutputDebugString.
+#define DEBUG_OUTPUT_DEBUGGEE 0x00000080
+// Debuggee-generated prompt, such as from DbgPrompt.
+#define DEBUG_OUTPUT_DEBUGGEE_PROMPT 0x00000100
+// Symbol messages, such as for !sym noisy.
+#define DEBUG_OUTPUT_SYMBOLS 0x00000200
+
+// Execute and ExecuteCommandFile flags.
+// These flags only apply to the command
+// text itself; output from the executed
+// command is controlled by the output
+// control parameter.
+// Default execution. Command is logged
+// but not output.
+#define DEBUG_EXECUTE_DEFAULT 0x00000000
+// Echo commands during execution. In
+// ExecuteCommandFile also echoes the prompt
+// for each line of the file.
+#define DEBUG_EXECUTE_ECHO 0x00000001
+// Do not log or output commands during execution.
+// Overridden by DEBUG_EXECUTE_ECHO.
+#define DEBUG_EXECUTE_NOT_LOGGED 0x00000002
+// If this flag is not set an empty string
+// to Execute will repeat the last Execute
+// string.
+#define DEBUG_EXECUTE_NO_REPEAT 0x00000004
+
+// Classes of debuggee. Each class
+// has different qualifiers for specific
+// kinds of debuggees.
+#define DEBUG_CLASS_UNINITIALIZED 0
+#define DEBUG_CLASS_KERNEL 1
+#define DEBUG_CLASS_USER_WINDOWS 2
+#define DEBUG_CLASS_IMAGE_FILE 3
+
+// Generic dump types. These can be used
+// with either user or kernel sessions.
+// Session-type-specific aliases are also
+// provided.
+#define DEBUG_DUMP_SMALL 1024
+#define DEBUG_DUMP_DEFAULT 1025
+#define DEBUG_DUMP_FULL 1026
+#define DEBUG_DUMP_IMAGE_FILE 1027
+#define DEBUG_DUMP_TRACE_LOG 1028
+#define DEBUG_DUMP_WINDOWS_CE 1029
+
+#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
+#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
+#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
+
+// Execution status codes used for waiting,
+// for returning current status and for
+// event method return values.
+#define DEBUG_STATUS_NO_CHANGE 0
+#define DEBUG_STATUS_GO 1
+#define DEBUG_STATUS_GO_HANDLED 2
+#define DEBUG_STATUS_GO_NOT_HANDLED 3
+#define DEBUG_STATUS_STEP_OVER 4
+#define DEBUG_STATUS_STEP_INTO 5
+#define DEBUG_STATUS_BREAK 6
+#define DEBUG_STATUS_NO_DEBUGGEE 7
+#define DEBUG_STATUS_STEP_BRANCH 8
+#define DEBUG_STATUS_IGNORE_EVENT 9
+#define DEBUG_STATUS_RESTART_REQUESTED 10
+#define DEBUG_STATUS_REVERSE_GO 11
+#define DEBUG_STATUS_REVERSE_STEP_BRANCH 12
+#define DEBUG_STATUS_REVERSE_STEP_OVER 13
+#define DEBUG_STATUS_REVERSE_STEP_INTO 14
+#define DEBUG_STATUS_OUT_OF_SYNC 15
+#define DEBUG_STATUS_WAIT_INPUT 16
+#define DEBUG_STATUS_TIMEOUT 17
+
+#define DEBUG_STATUS_MASK 0x1f
+
+#define DEBUG_EVENT_EXCEPTION 0x00000002
+
+#ifdef DEFINE_EXCEPTION_RECORD
+
+#define EXCEPTION_MAXIMUM_PARAMETERS 15
+
+// This copy of the "64" bit record has been modified
+// by removing the alignment field to make it the same
+// as the _EXCEPTION_RECORD used in the pal defined in
+// pal.h.
+typedef struct _EXCEPTION_RECORD64 {
+ DWORD ExceptionCode;
+ DWORD ExceptionFlags;
+ DWORD64 ExceptionRecord;
+ DWORD64 ExceptionAddress;
+ DWORD NumberParameters;
+ DWORD64 ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
+} EXCEPTION_RECORD64, *PEXCEPTION_RECORD64;
+
+#endif // DEFINE_EXCEPTION_RECORD
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXCEPTION
+{
+ EXCEPTION_RECORD64 ExceptionRecord;
+ ULONG FirstChance;
+} DEBUG_LAST_EVENT_INFO_EXCEPTION, *PDEBUG_LAST_EVENT_INFO_EXCEPTION;
+
+//
+// Information about a module.
+//
+
+// Flags.
+#define DEBUG_MODULE_LOADED 0x00000000
+#define DEBUG_MODULE_UNLOADED 0x00000001
+#define DEBUG_MODULE_USER_MODE 0x00000002
+#define DEBUG_MODULE_EXPLICIT 0x00000008
+#define DEBUG_MODULE_SECONDARY 0x00000010
+#define DEBUG_MODULE_SYNTHETIC 0x00000020
+#define DEBUG_MODULE_SYM_BAD_CHECKSUM 0x00010000
+
+// Symbol types.
+#define DEBUG_SYMTYPE_NONE 0
+#define DEBUG_SYMTYPE_COFF 1
+#define DEBUG_SYMTYPE_CODEVIEW 2
+#define DEBUG_SYMTYPE_PDB 3
+#define DEBUG_SYMTYPE_EXPORT 4
+#define DEBUG_SYMTYPE_DEFERRED 5
+#define DEBUG_SYMTYPE_SYM 6
+#define DEBUG_SYMTYPE_DIA 7
+
+typedef struct _DEBUG_MODULE_PARAMETERS
+{
+ ULONG64 Base;
+ ULONG Size;
+ ULONG TimeDateStamp;
+ ULONG Checksum;
+ ULONG Flags;
+ ULONG SymbolType;
+ ULONG ImageNameSize;
+ ULONG ModuleNameSize;
+ ULONG LoadedImageNameSize;
+ ULONG SymbolFileNameSize;
+ ULONG MappedImageNameSize;
+ ULONG64 Reserved[2];
+} DEBUG_MODULE_PARAMETERS, *PDEBUG_MODULE_PARAMETERS;
+
+// FindSourceFile flags.
+#define DEBUG_FIND_SOURCE_DEFAULT 0x00000000
+// Returns fully-qualified paths only. If this
+// is not set the path returned may be relative.
+#define DEBUG_FIND_SOURCE_FULL_PATH 0x00000001
+// Scans all the path elements for a match and
+// returns the one that has the most similarity
+// between the given file and the matching element.
+#define DEBUG_FIND_SOURCE_BEST_MATCH 0x00000002
+// Do not search source server paths.
+#define DEBUG_FIND_SOURCE_NO_SRCSRV 0x00000004
+// Restrict FindSourceFileAndToken to token lookup only.
+#define DEBUG_FIND_SOURCE_TOKEN_LOOKUP 0x00000008
+
+// A special value marking an offset that should not
+// be treated as a valid offset. This is only used
+// in special situations where it is unlikely that
+// this value would be a valid offset.
+#define DEBUG_INVALID_OFFSET ((ULONG64)-1)
+
+// General unspecified ID constant.
+#define DEBUG_ANY_ID 0xffffffff
+
+typedef struct _DEBUG_STACK_FRAME
+{
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[6];
+ BOOL Virtual;
+ ULONG FrameNumber;
+} DEBUG_STACK_FRAME, *PDEBUG_STACK_FRAME;
+
+#define DBG_FRAME_DEFAULT 0 // the same as INLINE_FRAME_CONTEXT_INIT in dbghelp.h
+#define DBG_FRAME_IGNORE_INLINE 0xFFFFFFFF // the same as INLINE_FRAME_CONTEXT_IGNORE in dbghelp.h
+
+typedef struct _DEBUG_STACK_FRAME_EX
+{
+ // First DEBUG_STACK_FRAME structure
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[6];
+ BOOL Virtual;
+ ULONG FrameNumber;
+
+ // Extended DEBUG_STACK_FRAME fields.
+ ULONG InlineFrameContext;
+ ULONG Reserved1; // For alignment purpose.
+} DEBUG_STACK_FRAME_EX, *PDEBUG_STACK_FRAME_EX;
+
+// The types of inline frame context.
+#define STACK_FRAME_TYPE_INIT 0x00
+#define STACK_FRAME_TYPE_STACK 0x01
+#define STACK_FRAME_TYPE_INLINE 0x02
+#define STACK_FRAME_TYPE_RA 0x80 // Whether the instruction pointer is the current IP or a RA from callee frame.
+#define STACK_FRAME_TYPE_IGNORE 0xFF
+
+//
+// options that are set/returned by SymSetOptions() & SymGetOptions()
+// these are used as a mask
+//
+#define SYMOPT_LOAD_LINES 0x00000010
+
+interface ILLDBServices;
+typedef HRESULT (*PFN_EXCEPTION_CALLBACK)(ILLDBServices *services);
+
+//----------------------------------------------------------------------------
+// ILLDBServices
+//----------------------------------------------------------------------------
+
+MIDL_INTERFACE("2E6C569A-9E14-4DA4-9DFC-CDB73A532566")
+ILLDBServices : public IUnknown
+{
+public:
+ //----------------------------------------------------------------------------
+ // ILLDBServices
+ //----------------------------------------------------------------------------
+
+ // Returns the coreclr module directory found by lldb plugin
+ // in the target process.
+ virtual PCSTR GetCoreClrDirectory() = 0;
+
+ // Evaluates a lldb expression into a value.
+ virtual DWORD_PTR GetExpression(
+ /* [in] */ PCSTR exp) = 0;
+
+ // Unwind one native stack frame given a thread and register context
+ virtual HRESULT VirtualUnwind(
+ /* [in] */ DWORD threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, out, size_is(contextSize)] */ PBYTE context) = 0;
+
+ // Set an exception throw callback
+ virtual HRESULT SetExceptionCallback(
+ /* [in] */ PFN_EXCEPTION_CALLBACK callback) = 0;
+
+ // Clear the exception throw callback
+ virtual HRESULT ClearExceptionCallback() = 0;
+
+ //------------------------------------------------
+ // IDebugControl2
+ //------------------------------------------------
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ virtual HRESULT GetInterrupt(
+ void) = 0;
+
+ virtual HRESULT OutputVaList(
+ ULONG mask,
+ PCSTR format,
+ va_list args) = 0;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ virtual HRESULT GetDebuggeeType(
+ PULONG debugClass,
+ PULONG qualifier) = 0;
+
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ virtual HRESULT GetPageSize(
+ PULONG size) = 0;
+
+ // Returns the type of processor used in the
+ // current processor context.
+ virtual HRESULT GetExecutingProcessorType(
+ PULONG type) = 0;
+
+ // Executes the given command string.
+ // If the string has multiple commands
+ // Execute will not return until all
+ // of them have been executed. If this
+ // requires waiting for the debuggee to
+ // execute an internal wait will be done
+ // so Execute can take an arbitrary amount
+ // of time.
+ virtual HRESULT Execute(
+ ULONG outputControl,
+ PCSTR command,
+ ULONG flags) = 0;
+
+ // Retrieves information about the last event that occurred.
+ // EventType is one of the event callback mask bits.
+ // ExtraInformation contains additional event-specific
+ // information. Not all events have additional information.
+ virtual HRESULT GetLastEventInformation(
+ PULONG type,
+ PULONG processId,
+ PULONG threadId,
+ PVOID extraInformation,
+ ULONG extraInformationSize,
+ PULONG extraInformationUsed,
+ PSTR description,
+ ULONG descriptionSize,
+ PULONG descriptionUsed) = 0;
+
+ virtual HRESULT Disassemble(
+ ULONG64 offset,
+ ULONG flags,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG disassemblySize,
+ PULONG64 endOffset) = 0;
+
+ //----------------------------------------------------------------------------
+ // IDebugControl4
+ //----------------------------------------------------------------------------
+
+ // Stack tracing with a full initial context
+ // and full context return for each frame.
+ // The FrameContextsSize parameter is the total
+ // byte size of FrameContexts. FrameContextsEntrySize
+ // gives the byte size of each entry in
+ // FrameContexts.
+ virtual HRESULT GetContextStackTrace(
+ PVOID startContext,
+ ULONG startContextSize,
+ PDEBUG_STACK_FRAME frames,
+ ULONG framesSize,
+ PVOID frameContexts,
+ ULONG frameContextsSize,
+ ULONG frameContextsEntrySize,
+ PULONG framesFilled) = 0;
+
+ //------------------------------------------------
+ // IDebugDataSpaces
+ //------------------------------------------------
+
+ virtual HRESULT ReadVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesRead) = 0;
+
+ virtual HRESULT WriteVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesWritten) = 0;
+
+ //------------------------------------------------
+ // IDebugSymbols
+ //------------------------------------------------
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ virtual HRESULT GetSymbolOptions(
+ PULONG options) = 0;
+
+ virtual HRESULT GetNameByOffset(
+ ULONG64 offset,
+ PSTR nameBuffer,
+ ULONG nameBufferSize,
+ PULONG nameSize,
+ PULONG64 displacement) = 0;
+
+ // Enumerates the engines list of modules
+ // loaded for the current process. This may
+ // or may not match the system module list
+ // for the process. Reload can be used to
+ // synchronize the engines list with the system
+ // if necessary.
+ // Some sessions also track recently unloaded
+ // code modules for help in analyzing failures
+ // where an attempt is made to call unloaded code.
+ // These modules are indexed after the loaded
+ // modules.
+ virtual HRESULT GetNumberModules(
+ PULONG loaded,
+ PULONG unloaded) = 0;
+
+ virtual HRESULT GetModuleByIndex(
+ ULONG index,
+ PULONG64 base) = 0;
+
+ // The module name may not be unique.
+ // This method returns the first match.
+ virtual HRESULT GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base) = 0;
+
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ virtual HRESULT GetModuleByOffset(
+ ULONG64 offset,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base) = 0;
+
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ virtual HRESULT GetModuleNames(
+ ULONG index,
+ ULONG64 base,
+ PSTR imageNameBuffer,
+ ULONG imageNameBufferSize,
+ PULONG imageNameSize,
+ PSTR moduleNameBuffer,
+ ULONG moduleNameBufferSize,
+ PULONG moduleNameSize,
+ PSTR loadedImageNameBuffer,
+ ULONG loadedImageNameBufferSize,
+ PULONG loadedImageNameSize) = 0;
+
+ HRESULT virtual GetLineByOffset(
+ ULONG64 offset,
+ PULONG line,
+ PSTR fileBuffer,
+ ULONG fileBufferSize,
+ PULONG fileSize,
+ PULONG64 displacement) = 0;
+
+ HRESULT virtual GetSourceFileLineOffsets(
+ PCSTR file,
+ PULONG64 buffer,
+ ULONG bufferLines,
+ PULONG fileLines) = 0;
+
+ // Uses the given file path and the source path
+ // information to try and locate an existing file.
+ // The given file path is merged with elements
+ // of the source path and checked for existence.
+ // If a match is found the element used is returned.
+ // A starting element can be specified to restrict
+ // the search to a subset of the path elements;
+ // this can be useful when checking for multiple
+ // matches along the source path.
+ // The returned element can be 1, indicating
+ // the file was found directly and not on the path.
+ HRESULT virtual FindSourceFile(
+ ULONG startElement,
+ PCSTR file,
+ ULONG flags,
+ PULONG foundElement,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG foundSize) = 0;
+
+ //------------------------------------------------
+ // IDebugSystemObjects
+ //------------------------------------------------
+
+ virtual HRESULT GetCurrentProcessId(
+ PULONG id) = 0;
+
+ // Controls implicit thread used by the
+ // debug engine. The debuggers current
+ // thread is just a piece of data held
+ // by the debugger for calls which use
+ // thread-specific information. In those
+ // calls the debuggers current thread is used.
+ // The debuggers current thread is not related
+ // to any system thread attribute.
+ // IDs for threads are small integer IDs
+ // maintained by the engine. They are not
+ // related to system thread IDs.
+ virtual HRESULT GetCurrentThreadId(
+ PULONG id) = 0;
+
+ virtual HRESULT SetCurrentThreadId(
+ ULONG id) = 0;
+
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ virtual HRESULT GetCurrentThreadSystemId(
+ PULONG sysId) = 0;
+
+ // Looks up a debugger thread ID for the given
+ // system thread ID.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ virtual HRESULT GetThreadIdBySystemId(
+ ULONG sysId,
+ PULONG id) = 0;
+
+ // This is a special sos/lldb function used to implement the ICLRDataTarget interface and
+ // not actually part of dbgeng's IDebugSystemObjects interface.
+ virtual HRESULT GetThreadContextById(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context) = 0;
+
+ //------------------------------------------------
+ // IDebugRegister
+ //------------------------------------------------
+
+ // This is the combination of dbgeng's GetIndexByName and GetValue and not
+ // actually part of the dbgeng's IDebugRegister interface.
+ virtual HRESULT GetValueByName(
+ PCSTR name,
+ PDWORD_PTR value) = 0;
+
+ // Abstracted pieces of processor information.
+ // The mapping of these values to architectural
+ // registers is architecture-specific and their
+ // interpretation and existence may vary. They
+ // are intended to be directly compatible with
+ // calls which take this information, such as
+ // stack walking.
+ virtual HRESULT GetInstructionOffset(
+ PULONG64 offset) = 0;
+
+ virtual HRESULT GetStackOffset(
+ PULONG64 offset) = 0;
+
+ virtual HRESULT GetFrameOffset(
+ PULONG64 offset) = 0;
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // #ifndef __LLDBSERVICES_H__
diff --git a/src/ToolBox/SOS/lldbplugin/mstypes.h b/src/ToolBox/SOS/lldbplugin/mstypes.h
new file mode 100644
index 0000000000..b5eee921a3
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/mstypes.h
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Contains some definitions duplicated from pal.h, palrt.h, rpc.h,
+// etc. because they have various conflicits with the linux standard
+// runtime h files like wchar_t, memcpy, etc.
+
+#include <../../../pal/inc/pal_mstypes.h>
+
+#define S_OK (HRESULT)0x00000000
+#define S_FALSE (HRESULT)0x00000001
+#define E_NOTIMPL (HRESULT)0x80004001
+#define E_FAIL (HRESULT)0x80004005
+#define E_INVALIDARG (HRESULT)0x80070057
+#define E_NOINTERFACE (HRESULT)0x80004002
+
+#define MAX_PATH 260
+
+// Platform-specific library naming
+//
+#ifdef __APPLE__
+#define MAKEDLLNAME_W(name) u"lib" name u".dylib"
+#define MAKEDLLNAME_A(name) "lib" name ".dylib"
+#elif defined(_AIX)
+#define MAKEDLLNAME_W(name) L"lib" name L".a"
+#define MAKEDLLNAME_A(name) "lib" name ".a"
+#elif defined(__hppa__) || defined(_IA64_)
+#define MAKEDLLNAME_W(name) L"lib" name L".sl"
+#define MAKEDLLNAME_A(name) "lib" name ".sl"
+#else
+#define MAKEDLLNAME_W(name) u"lib" name u".so"
+#define MAKEDLLNAME_A(name) "lib" name ".so"
+#endif
+
+#if defined(_MSC_VER) || defined(__llvm__)
+#define DECLSPEC_ALIGN(x) __declspec(align(x))
+#else
+#define DECLSPEC_ALIGN(x)
+#endif
+
+#define interface struct
+#define DECLSPEC_UUID(x) __declspec(uuid(x))
+#define DECLSPEC_NOVTABLE
+#define MIDL_INTERFACE(x) struct DECLSPEC_UUID(x) DECLSPEC_NOVTABLE
+
+#ifdef __cplusplus
+#define REFGUID const GUID &
+#else
+#define REFGUID const GUID *
+#endif
+
+#ifdef __cplusplus
+extern "C++" {
+#include "string.h"
+#if !defined _SYS_GUID_OPERATOR_EQ_ && !defined _NO_SYS_GUID_OPERATOR_EQ_
+#define _SYS_GUID_OPERATOR_EQ_
+inline int IsEqualGUID(REFGUID rguid1, REFGUID rguid2)
+ { return !memcmp(&rguid1, &rguid2, sizeof(GUID)); }
+inline int operator==(REFGUID guidOne, REFGUID guidOther)
+ { return IsEqualGUID(guidOne,guidOther); }
+inline int operator!=(REFGUID guidOne, REFGUID guidOther)
+ { return !IsEqualGUID(guidOne,guidOther); }
+#endif
+};
+#endif // __cplusplus
+
+typedef GUID IID;
+#ifdef __cplusplus
+#define REFIID const IID &
+#else
+#define REFIID const IID *
+#endif
+#define IID_NULL GUID_NULL
+#define IsEqualIID(riid1, riid2) IsEqualGUID(riid1, riid2)
+
+MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
+IUnknown
+{
+public:
+ virtual HRESULT QueryInterface(
+ REFIID riid,
+ void **ppvObject) = 0;
+
+ virtual ULONG AddRef( void) = 0;
+
+ virtual ULONG Release( void) = 0;
+};
+
+EXTERN_C
+inline
+LONG
+InterlockedIncrement(
+ LONG volatile *lpAddend)
+{
+ return __sync_add_and_fetch(lpAddend, (LONG)1);
+}
+
+EXTERN_C
+inline
+LONG
+InterlockedDecrement(
+ LONG volatile *lpAddend)
+{
+ return __sync_sub_and_fetch(lpAddend, (LONG)1);
+} \ No newline at end of file
diff --git a/src/ToolBox/SOS/lldbplugin/services.cpp b/src/ToolBox/SOS/lldbplugin/services.cpp
new file mode 100644
index 0000000000..f6b42139c4
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/services.cpp
@@ -0,0 +1,1692 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <cstdarg>
+#include <cstdlib>
+#include "sosplugin.h"
+#include <string.h>
+#include <string>
+
+ULONG g_currentThreadIndex = -1;
+ULONG g_currentThreadSystemId = -1;
+char *g_coreclrDirectory;
+
+LLDBServices::LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process, lldb::SBThread *thread) :
+ m_ref(1),
+ m_debugger(debugger),
+ m_returnObject(returnObject),
+ m_currentProcess(process),
+ m_currentThread(thread)
+{
+ returnObject.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+}
+
+LLDBServices::~LLDBServices()
+{
+}
+
+//----------------------------------------------------------------------------
+// IUnknown
+//----------------------------------------------------------------------------
+
+HRESULT
+LLDBServices::QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface
+ )
+{
+ if (InterfaceId == __uuidof(IUnknown) ||
+ InterfaceId == __uuidof(ILLDBServices))
+ {
+ *Interface = (ILLDBServices*)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG
+LLDBServices::AddRef()
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+ULONG
+LLDBServices::Release()
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+}
+
+//----------------------------------------------------------------------------
+// ILLDBServices
+//----------------------------------------------------------------------------
+
+PCSTR
+LLDBServices::GetCoreClrDirectory()
+{
+ return g_coreclrDirectory;
+}
+
+DWORD_PTR
+LLDBServices::GetExpression(
+ PCSTR exp)
+{
+ if (exp == nullptr)
+ {
+ return 0;
+ }
+
+ lldb::SBFrame frame = GetCurrentFrame();
+ if (!frame.IsValid())
+ {
+ return 0;
+ }
+
+ DWORD_PTR result = 0;
+ lldb::SBError error;
+ std::string str;
+
+ // To be compatible with windbg/dbgeng, we need to emulate the default
+ // hex radix (because sos prints addresses and other hex values without
+ // the 0x) by first prepending 0x and if that fails use the actual
+ // undecorated expression.
+ str.append("0x");
+ str.append(exp);
+
+ result = GetExpression(frame, error, str.c_str());
+ if (error.Fail())
+ {
+ result = GetExpression(frame, error, exp);
+ }
+
+ return result;
+}
+
+// Internal function
+DWORD_PTR
+LLDBServices::GetExpression(
+ /* const */ lldb::SBFrame& frame,
+ lldb::SBError& error,
+ PCSTR exp)
+{
+ DWORD_PTR result = 0;
+
+ lldb::SBValue value = frame.EvaluateExpression(exp, lldb::eNoDynamicValues);
+ if (value.IsValid())
+ {
+ result = value.GetValueAsUnsigned(error);
+ }
+
+ return result;
+}
+
+//
+// lldb doesn't have a way or API to unwind an arbitary context (IP, SP)
+// and return the next frame so we have to stick with the native frames
+// lldb has found and find the closest frame to the incoming context SP.
+//
+HRESULT
+LLDBServices::VirtualUnwind(
+ DWORD threadID,
+ ULONG32 contextSize,
+ PBYTE context)
+{
+ lldb::SBProcess process;
+ lldb::SBThread thread;
+
+ if (context == NULL || contextSize < sizeof(DT_CONTEXT))
+ {
+ return E_INVALIDARG;
+ }
+
+ process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ return E_FAIL;
+ }
+
+ thread = process.GetThreadByID(threadID);
+ if (!thread.IsValid())
+ {
+ return E_FAIL;
+ }
+
+ DT_CONTEXT *dtcontext = (DT_CONTEXT*)context;
+ lldb::SBFrame frameFound;
+
+#ifdef DBG_TARGET_AMD64
+ DWORD64 spToFind = dtcontext->Rsp;
+#elif DBG_TARGET_ARM
+ DWORD spToFind = dtcontext->Sp;
+#endif
+
+ int numFrames = thread.GetNumFrames();
+ for (int i = 0; i < numFrames; i++)
+ {
+ lldb::SBFrame frame = thread.GetFrameAtIndex(i);
+ if (!frame.IsValid())
+ {
+ break;
+ }
+ lldb::addr_t sp = frame.GetSP();
+
+ if ((i + 1) < numFrames)
+ {
+ lldb::SBFrame frameNext = thread.GetFrameAtIndex(i + 1);
+ if (frameNext.IsValid())
+ {
+ lldb::addr_t spNext = frameNext.GetSP();
+
+ // An exact match of the current frame's SP would be nice
+ // but sometimes the incoming context is between lldb frames
+ if (spToFind >= sp && spToFind < spNext)
+ {
+ frameFound = frameNext;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!frameFound.IsValid())
+ {
+ return E_FAIL;
+ }
+
+ GetContextFromFrame(frameFound, dtcontext);
+
+ return S_OK;
+}
+
+bool
+ExceptionBreakpointCallback(
+ void *baton,
+ lldb::SBProcess &process,
+ lldb::SBThread &thread,
+ lldb::SBBreakpointLocation &location)
+{
+ lldb::SBDebugger debugger = process.GetTarget().GetDebugger();
+
+ // Send the normal and error output to stdout/stderr since we
+ // don't have a return object from the command interpreter.
+ lldb::SBCommandReturnObject result;
+ result.SetImmediateOutputFile(stdout);
+ result.SetImmediateErrorFile(stderr);
+
+ // Save the process and thread to be used by the current process/thread
+ // helper functions.
+ LLDBServices* client = new LLDBServices(debugger, result, &process, &thread);
+ return ((PFN_EXCEPTION_CALLBACK)baton)(client) == S_OK;
+}
+
+lldb::SBBreakpoint g_exceptionbp;
+
+HRESULT
+LLDBServices::SetExceptionCallback(
+ PFN_EXCEPTION_CALLBACK callback)
+{
+ if (!g_exceptionbp.IsValid())
+ {
+ lldb::SBTarget target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ return E_FAIL;
+ }
+ lldb::SBBreakpoint exceptionbp = target.BreakpointCreateForException(lldb::LanguageType::eLanguageTypeC_plus_plus, false, true);
+ if (!exceptionbp.IsValid())
+ {
+ return E_FAIL;
+ }
+#ifdef FLAGS_ANONYMOUS_ENUM
+ exceptionbp.AddName("DoNotDeleteOrDisable");
+#endif
+ exceptionbp.SetCallback(ExceptionBreakpointCallback, (void *)callback);
+ g_exceptionbp = exceptionbp;
+ }
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::ClearExceptionCallback()
+{
+ if (g_exceptionbp.IsValid())
+ {
+ lldb::SBTarget target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ return E_FAIL;
+ }
+ target.BreakpointDelete(g_exceptionbp.GetID());
+ g_exceptionbp = lldb::SBBreakpoint();
+ }
+ return S_OK;
+}
+
+//----------------------------------------------------------------------------
+// IDebugControl2
+//----------------------------------------------------------------------------
+
+// Checks for a user interrupt, such a Ctrl-C
+// or stop button.
+// This method is reentrant.
+HRESULT
+LLDBServices::GetInterrupt()
+{
+ return E_FAIL;
+}
+
+// Sends output through clients
+// output callbacks if the mask is allowed
+// by the current output control mask and
+// according to the output distribution
+// settings.
+HRESULT
+LLDBServices::Output(
+ ULONG mask,
+ PCSTR format,
+ ...)
+{
+ va_list args;
+ va_start (args, format);
+ HRESULT result = OutputVaList(mask, format, args);
+ va_end (args);
+ return result;
+}
+
+HRESULT
+LLDBServices::OutputVaList(
+ ULONG mask,
+ PCSTR format,
+ va_list args)
+{
+ HRESULT result = S_OK;
+ char str[1024];
+
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ // Try and format our string into a fixed buffer first and see if it fits
+ size_t length = ::vsnprintf(str, sizeof(str), format, args);
+ if (length < sizeof(str))
+ {
+ OutputString(mask, str);
+ }
+ else
+ {
+ // Our stack buffer wasn't big enough to contain the entire formatted
+ // string, so lets let vasprintf create the string for us!
+ char *str_ptr = nullptr;
+ length = ::vasprintf(&str_ptr, format, args_copy);
+ if (str_ptr)
+ {
+ OutputString(mask, str_ptr);
+ ::free (str_ptr);
+ }
+ else
+ {
+ result = E_FAIL;
+ }
+ }
+
+ va_end (args_copy);
+
+ return result;
+}
+
+// The following methods allow direct control
+// over the distribution of the given output
+// for situations where something other than
+// the default is desired. These methods require
+// extra work in the engine so they should
+// only be used when necessary.
+HRESULT
+LLDBServices::ControlledOutput(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ ...)
+{
+ va_list args;
+ va_start (args, format);
+ HRESULT result = ControlledOutputVaList(outputControl, mask, format, args);
+ va_end (args);
+ return result;
+}
+
+HRESULT
+LLDBServices::ControlledOutputVaList(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ va_list args)
+{
+ return OutputVaList(mask, format, args);
+}
+
+// Returns information about the debuggee such
+// as user vs. kernel, dump vs. live, etc.
+HRESULT
+LLDBServices::GetDebuggeeType(
+ PULONG debugClass,
+ PULONG qualifier)
+{
+ *debugClass = DEBUG_CLASS_USER_WINDOWS;
+ *qualifier = 0;
+ return S_OK;
+}
+
+// Returns the page size for the currently executing
+// processor context. The page size may vary between
+// processor types.
+HRESULT
+LLDBServices::GetPageSize(
+ PULONG size)
+{
+ *size = 4096;
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetExecutingProcessorType(
+ PULONG type)
+{
+#ifdef DBG_TARGET_AMD64
+ *type = IMAGE_FILE_MACHINE_AMD64;
+#elif DBG_TARGET_ARM
+ *type = IMAGE_FILE_MACHINE_ARMNT;
+#endif
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::Execute(
+ ULONG outputControl,
+ PCSTR command,
+ ULONG flags)
+{
+ lldb::SBCommandInterpreter interpreter = m_debugger.GetCommandInterpreter();
+
+ lldb::SBCommandReturnObject result;
+ lldb::ReturnStatus status = interpreter.HandleCommand(command, result);
+
+ return status <= lldb::eReturnStatusSuccessContinuingResult ? S_OK : E_FAIL;
+}
+
+// PAL raise exception function and exception record pointer variable name
+// See coreclr\src\pal\src\exception\seh-unwind.cpp for the details. This
+// function depends on RtlpRaisException not being inlined or optimized.
+#define FUNCTION_NAME "RtlpRaiseException"
+#define VARIABLE_NAME "ExceptionRecord"
+
+HRESULT
+LLDBServices::GetLastEventInformation(
+ PULONG type,
+ PULONG processId,
+ PULONG threadId,
+ PVOID extraInformation,
+ ULONG extraInformationSize,
+ PULONG extraInformationUsed,
+ PSTR description,
+ ULONG descriptionSize,
+ PULONG descriptionUsed)
+{
+ if (extraInformationSize < sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION) ||
+ type == NULL || processId == NULL || threadId == NULL || extraInformationUsed == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ *type = DEBUG_EVENT_EXCEPTION;
+ *processId = 0;
+ *threadId = 0;
+ *extraInformationUsed = sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION);
+
+ DEBUG_LAST_EVENT_INFO_EXCEPTION *pdle = (DEBUG_LAST_EVENT_INFO_EXCEPTION *)extraInformation;
+ pdle->FirstChance = 1;
+
+ lldb::SBProcess process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ return E_FAIL;
+ }
+ lldb::SBThread thread = GetCurrentThread();
+ if (!thread.IsValid())
+ {
+ return E_FAIL;
+ }
+
+ *processId = process.GetProcessID();
+ *threadId = thread.GetThreadID();
+
+ // Enumerate each stack frame at the special "throw"
+ // breakpoint and find the raise exception function
+ // with the exception record parameter.
+ int numFrames = thread.GetNumFrames();
+ for (int i = 0; i < numFrames; i++)
+ {
+ lldb::SBFrame frame = thread.GetFrameAtIndex(i);
+ if (!frame.IsValid())
+ {
+ break;
+ }
+
+ const char *functionName = frame.GetFunctionName();
+ if (functionName == NULL || strncmp(functionName, FUNCTION_NAME, sizeof(FUNCTION_NAME) - 1) != 0)
+ {
+ continue;
+ }
+
+ lldb::SBValue exValue = frame.FindVariable(VARIABLE_NAME);
+ if (!exValue.IsValid())
+ {
+ break;
+ }
+
+ lldb::SBError error;
+ ULONG64 pExceptionRecord = exValue.GetValueAsUnsigned(error);
+ if (error.Fail())
+ {
+ break;
+ }
+
+ process.ReadMemory(pExceptionRecord, &pdle->ExceptionRecord, sizeof(pdle->ExceptionRecord), error);
+ if (error.Fail())
+ {
+ break;
+ }
+
+ return S_OK;
+ }
+
+ return E_FAIL;
+}
+
+HRESULT
+LLDBServices::Disassemble(
+ ULONG64 offset,
+ ULONG flags,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG disassemblySize,
+ PULONG64 endOffset)
+{
+ lldb::SBInstruction instruction;
+ lldb::SBInstructionList list;
+ lldb::SBTarget target;
+ lldb::SBAddress address;
+ lldb::SBError error;
+ lldb::SBData data;
+ std::string str;
+ HRESULT hr = S_OK;
+ ULONG size = 0;
+ uint8_t byte;
+ int cch;
+
+ if (buffer == NULL)
+ {
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+ *buffer = 0;
+
+ target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+ address = target.ResolveLoadAddress(offset);
+ if (!address.IsValid())
+ {
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+ list = target.ReadInstructions(address, 1, "intel");
+ if (!list.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+ instruction = list.GetInstructionAtIndex(0);
+ if (!instruction.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+ cch = snprintf(buffer, bufferSize, "%016llx ", (unsigned long long)offset);
+ buffer += cch;
+ bufferSize -= cch;
+
+ size = instruction.GetByteSize();
+ data = instruction.GetData(target);
+ for (int i = 0; i < size && bufferSize > 0; i++)
+ {
+ byte = data.GetUnsignedInt8(error, i);
+ if (error.Fail())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+ cch = snprintf(buffer, bufferSize, "%02x", byte);
+ buffer += cch;
+ bufferSize -= cch;
+ }
+ // Pad the data bytes to 16 chars
+ cch = size * 2;
+ while (bufferSize > 0)
+ {
+ *buffer++ = ' ';
+ bufferSize--;
+ if (++cch >= 21)
+ break;
+ }
+
+ cch = snprintf(buffer, bufferSize, "%s", instruction.GetMnemonic(target));
+ buffer += cch;
+ bufferSize -= cch;
+
+ // Pad the mnemonic to 8 chars
+ while (bufferSize > 0)
+ {
+ *buffer++ = ' ';
+ bufferSize--;
+ if (++cch >= 8)
+ break;
+ }
+ snprintf(buffer, bufferSize, "%s\n", instruction.GetOperands(target));
+
+exit:
+ if (disassemblySize != NULL)
+ {
+ *disassemblySize = size;
+ }
+ if (endOffset != NULL)
+ {
+ *endOffset = offset + size;
+ }
+ return hr;
+}
+
+// Internal output string function
+void
+LLDBServices::OutputString(
+ ULONG mask,
+ PCSTR str)
+{
+ if (mask == DEBUG_OUTPUT_ERROR)
+ {
+ m_returnObject.SetStatus(lldb::eReturnStatusFailed);
+ }
+ // Can not use AppendMessage or AppendWarning because they add a newline. SetError
+ // can not be used for DEBUG_OUTPUT_ERROR mask because it caches the error strings
+ // seperately from the normal output so error/normal texts are not intermixed
+ // correctly.
+ m_returnObject.Printf("%s", str);
+}
+
+//----------------------------------------------------------------------------
+// IDebugControl4
+//----------------------------------------------------------------------------
+
+HRESULT
+LLDBServices::GetContextStackTrace(
+ PVOID startContext,
+ ULONG startContextSize,
+ PDEBUG_STACK_FRAME frames,
+ ULONG framesSize,
+ PVOID frameContexts,
+ ULONG frameContextsSize,
+ ULONG frameContextsEntrySize,
+ PULONG framesFilled)
+{
+ DT_CONTEXT *currentContext = (DT_CONTEXT*)frameContexts;
+ PDEBUG_STACK_FRAME currentFrame = frames;
+ lldb::SBThread thread;
+ lldb::SBFrame frame;
+ ULONG cFrames = 0;
+ HRESULT hr = S_OK;
+
+ // Doesn't support a starting context
+ if (startContext != NULL || frames == NULL || frameContexts == NULL || frameContextsEntrySize != sizeof(DT_CONTEXT))
+ {
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+
+ thread = GetCurrentThread();
+ if (!thread.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ frame = thread.GetFrameAtIndex(0);
+ for (int i = 0; i < thread.GetNumFrames(); i++)
+ {
+ if (!frame.IsValid() || (cFrames > framesSize) || ((char *)currentContext > ((char *)frameContexts + frameContextsSize)))
+ {
+ break;
+ }
+ lldb::SBFrame framePrevious;
+ lldb::SBFrame frameNext;
+
+ currentFrame->InstructionOffset = frame.GetPC();
+ currentFrame->StackOffset = frame.GetSP();
+
+ currentFrame->FuncTableEntry = 0;
+ currentFrame->Params[0] = 0;
+ currentFrame->Params[1] = 0;
+ currentFrame->Params[2] = 0;
+ currentFrame->Params[3] = 0;
+ currentFrame->Virtual = i == 0 ? TRUE : FALSE;
+ currentFrame->FrameNumber = frame.GetFrameID();
+
+ frameNext = thread.GetFrameAtIndex(i + 1);
+ if (frameNext.IsValid())
+ {
+ currentFrame->ReturnOffset = frameNext.GetPC();
+ }
+
+ if (framePrevious.IsValid())
+ {
+ currentFrame->FrameOffset = framePrevious.GetSP();
+ }
+ else
+ {
+ currentFrame->FrameOffset = frame.GetSP();
+ }
+
+ GetContextFromFrame(frame, currentContext);
+
+ framePrevious = frame;
+ frame = frameNext;
+ currentContext++;
+ currentFrame++;
+ cFrames++;
+ }
+
+exit:
+ if (framesFilled != NULL)
+ {
+ *framesFilled = cFrames;
+ }
+ return hr;
+}
+
+//----------------------------------------------------------------------------
+// IDebugDataSpaces
+//----------------------------------------------------------------------------
+
+HRESULT
+LLDBServices::ReadVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesRead)
+{
+ lldb::SBError error;
+ size_t read = 0;
+
+ lldb::SBProcess process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ goto exit;
+ }
+
+ read = process.ReadMemory(offset, buffer, bufferSize, error);
+
+exit:
+ if (bytesRead)
+ {
+ *bytesRead = read;
+ }
+ return error.Success() ? S_OK : E_FAIL;
+}
+
+HRESULT
+LLDBServices::WriteVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesWritten)
+{
+ lldb::SBError error;
+ size_t written = 0;
+
+ lldb::SBProcess process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ goto exit;
+ }
+
+ written = process.WriteMemory(offset, buffer, bufferSize, error);
+
+exit:
+ if (bytesWritten)
+ {
+ *bytesWritten = written;
+ }
+ return error.Success() ? S_OK : E_FAIL;
+}
+
+//----------------------------------------------------------------------------
+// IDebugSymbols
+//----------------------------------------------------------------------------
+
+HRESULT
+LLDBServices::GetSymbolOptions(
+ PULONG options)
+{
+ *options = SYMOPT_LOAD_LINES;
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetNameByOffset(
+ ULONG64 offset,
+ PSTR nameBuffer,
+ ULONG nameBufferSize,
+ PULONG nameSize,
+ PULONG64 displacement)
+{
+ ULONG64 disp = DEBUG_INVALID_OFFSET;
+ HRESULT hr = S_OK;
+
+ lldb::SBTarget target;
+ lldb::SBAddress address;
+ lldb::SBModule module;
+ lldb::SBFileSpec file;
+ lldb::SBSymbol symbol;
+ std::string str;
+
+ target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ address = target.ResolveLoadAddress(offset);
+ if (!address.IsValid())
+ {
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+
+ module = address.GetModule();
+ if (!module.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ file = module.GetFileSpec();
+ if (file.IsValid())
+ {
+ str.append(file.GetFilename());
+ }
+
+ symbol = address.GetSymbol();
+ if (symbol.IsValid())
+ {
+ lldb::SBAddress startAddress = symbol.GetStartAddress();
+ disp = address.GetOffset() - startAddress.GetOffset();
+
+ const char *name = symbol.GetName();
+ if (name)
+ {
+ if (file.IsValid())
+ {
+ str.append("!");
+ }
+ str.append(name);
+ }
+ }
+
+ str.append(1, '\0');
+
+exit:
+ if (nameSize)
+ {
+ *nameSize = str.length();
+ }
+ if (nameBuffer)
+ {
+ str.copy(nameBuffer, nameBufferSize);
+ }
+ if (displacement)
+ {
+ *displacement = disp;
+ }
+ return hr;
+}
+
+HRESULT
+LLDBServices::GetNumberModules(
+ PULONG loaded,
+ PULONG unloaded)
+{
+ ULONG numModules = 0;
+ HRESULT hr = S_OK;
+
+ lldb::SBTarget target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ numModules = target.GetNumModules();
+
+exit:
+ if (loaded)
+ {
+ *loaded = numModules;
+ }
+ if (unloaded)
+ {
+ *unloaded = 0;
+ }
+ return hr;
+}
+
+HRESULT LLDBServices::GetModuleByIndex(
+ ULONG index,
+ PULONG64 base)
+{
+ ULONG64 moduleBase = UINT64_MAX;
+
+ lldb::SBTarget target;
+ lldb::SBModule module;
+
+ target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ goto exit;
+ }
+
+ module = target.GetModuleAtIndex(index);
+ if (!module.IsValid())
+ {
+ goto exit;
+ }
+
+ moduleBase = GetModuleBase(target, module);
+
+exit:
+ if (base)
+ {
+ *base = moduleBase;
+ }
+ return moduleBase == UINT64_MAX ? E_FAIL : S_OK;
+}
+
+HRESULT
+LLDBServices::GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base)
+{
+ ULONG64 moduleBase = UINT64_MAX;
+ ULONG moduleIndex = UINT32_MAX;
+
+ lldb::SBTarget target;
+ lldb::SBModule module;
+ lldb::SBFileSpec fileSpec;
+ fileSpec.SetFilename(name);
+
+ target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ goto exit;
+ }
+
+ module = target.FindModule(fileSpec);
+ if (!module.IsValid())
+ {
+ goto exit;
+ }
+
+ moduleBase = GetModuleBase(target, module);
+
+ if (index)
+ {
+ int numModules = target.GetNumModules();
+ for (int mi = startIndex; mi < numModules; mi++)
+ {
+ lldb::SBModule mod = target.GetModuleAtIndex(mi);
+ if (module == mod)
+ {
+ moduleIndex = mi;
+ break;
+ }
+ }
+ }
+
+exit:
+ if (index)
+ {
+ *index = moduleIndex;
+ }
+ if (base)
+ {
+ *base = moduleBase;
+ }
+ return moduleBase == UINT64_MAX ? E_FAIL : S_OK;
+}
+
+HRESULT
+LLDBServices::GetModuleByOffset(
+ ULONG64 offset,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base)
+{
+ ULONG64 moduleBase = UINT64_MAX;
+ ULONG moduleIndex = UINT32_MAX;
+
+ lldb::SBTarget target;
+ int numModules;
+
+ target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ goto exit;
+ }
+
+ numModules = target.GetNumModules();
+ for (int mi = startIndex; mi < numModules; mi++)
+ {
+ lldb::SBModule module = target.GetModuleAtIndex(mi);
+
+ int numSections = module.GetNumSections();
+ for (int si = 0; si < numSections; si++)
+ {
+ lldb::SBSection section = module.GetSectionAtIndex(si);
+ if (section.IsValid())
+ {
+ lldb::addr_t baseAddress = section.GetLoadAddress(target);
+ if (baseAddress != LLDB_INVALID_ADDRESS)
+ {
+ if (offset > baseAddress)
+ {
+ if ((offset - baseAddress) < section.GetByteSize())
+ {
+ moduleIndex = mi;
+ moduleBase = baseAddress - section.GetFileOffset();
+ goto exit;
+ }
+ }
+ }
+ }
+ }
+ }
+
+exit:
+ if (index)
+ {
+ *index = moduleIndex;
+ }
+ if (base)
+ {
+ *base = moduleBase;
+ }
+ return moduleBase == UINT64_MAX ? E_FAIL : S_OK;
+}
+
+HRESULT
+LLDBServices::GetModuleNames(
+ ULONG index,
+ ULONG64 base,
+ PSTR imageNameBuffer,
+ ULONG imageNameBufferSize,
+ PULONG imageNameSize,
+ PSTR moduleNameBuffer,
+ ULONG moduleNameBufferSize,
+ PULONG moduleNameSize,
+ PSTR loadedImageNameBuffer,
+ ULONG loadedImageNameBufferSize,
+ PULONG loadedImageNameSize)
+{
+ lldb::SBTarget target;
+ lldb::SBFileSpec fileSpec;
+ HRESULT hr = S_OK;
+
+ target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ if (index != DEBUG_ANY_ID)
+ {
+ lldb::SBModule module = target.GetModuleAtIndex(index);
+ if (module.IsValid())
+ {
+ fileSpec = module.GetFileSpec();
+ }
+ }
+ else
+ {
+ int numModules = target.GetNumModules();
+ for (int mi = 0; mi < numModules; mi++)
+ {
+ lldb::SBModule module = target.GetModuleAtIndex(mi);
+ if (module.IsValid())
+ {
+ ULONG64 moduleBase = GetModuleBase(target, module);
+ if (base == moduleBase)
+ {
+ fileSpec = module.GetFileSpec();
+ break;
+ }
+ }
+ }
+ }
+
+ if (!fileSpec.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+exit:
+ if (imageNameBuffer)
+ {
+ int size = fileSpec.GetPath(imageNameBuffer, imageNameBufferSize);
+ if (imageNameSize)
+ {
+ *imageNameSize = size;
+ }
+ }
+ if (moduleNameBuffer)
+ {
+ const char *fileName = fileSpec.GetFilename();
+ if (fileName == NULL)
+ {
+ fileName = "";
+ }
+ stpncpy(moduleNameBuffer, fileName, moduleNameBufferSize);
+ if (moduleNameSize)
+ {
+ *moduleNameSize = strlen(fileName);
+ }
+ }
+ if (loadedImageNameBuffer)
+ {
+ int size = fileSpec.GetPath(loadedImageNameBuffer, loadedImageNameBufferSize);
+ if (loadedImageNameSize)
+ {
+ *loadedImageNameSize = size;
+ }
+ }
+ return hr;
+}
+
+HRESULT
+LLDBServices::GetLineByOffset(
+ ULONG64 offset,
+ PULONG fileLine,
+ PSTR fileBuffer,
+ ULONG fileBufferSize,
+ PULONG fileSize,
+ PULONG64 displacement)
+{
+ ULONG64 disp = DEBUG_INVALID_OFFSET;
+ HRESULT hr = S_OK;
+ ULONG line = 0;
+
+ lldb::SBTarget target;
+ lldb::SBAddress address;
+ lldb::SBFileSpec file;
+ lldb::SBLineEntry lineEntry;
+ std::string str;
+
+ target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ address = target.ResolveLoadAddress(offset);
+ if (!address.IsValid())
+ {
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+
+ if (displacement)
+ {
+ lldb::SBSymbol symbol = address.GetSymbol();
+ if (symbol.IsValid())
+ {
+ lldb::SBAddress startAddress = symbol.GetStartAddress();
+ disp = address.GetOffset() - startAddress.GetOffset();
+ }
+ }
+
+ lineEntry = address.GetLineEntry();
+ if (!lineEntry.IsValid())
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ line = lineEntry.GetLine();
+ file = lineEntry.GetFileSpec();
+ if (file.IsValid())
+ {
+ str.append(file.GetDirectory());
+ str.append(1, '/');
+ str.append(file.GetFilename());
+ }
+
+ str.append(1, '\0');
+
+exit:
+ if (fileLine)
+ {
+ *fileLine = line;
+ }
+ if (fileSize)
+ {
+ *fileSize = str.length();
+ }
+ if (fileBuffer)
+ {
+ str.copy(fileBuffer, fileBufferSize);
+ }
+ if (displacement)
+ {
+ *displacement = disp;
+ }
+ return hr;
+}
+
+HRESULT
+LLDBServices::GetSourceFileLineOffsets(
+ PCSTR file,
+ PULONG64 buffer,
+ ULONG bufferLines,
+ PULONG fileLines)
+{
+ if (fileLines != NULL)
+ {
+ *fileLines = (ULONG)-1;
+ }
+ return E_NOTIMPL;
+}
+
+HRESULT
+LLDBServices::FindSourceFile(
+ ULONG startElement,
+ PCSTR file,
+ ULONG flags,
+ PULONG foundElement,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG foundSize)
+{
+ return E_NOTIMPL;
+}
+
+// Internal functions
+PCSTR
+LLDBServices::GetModuleDirectory(
+ PCSTR name)
+{
+ lldb::SBTarget target = m_debugger.GetSelectedTarget();
+ if (!target.IsValid())
+ {
+ return NULL;
+ }
+
+ lldb::SBFileSpec fileSpec;
+ fileSpec.SetFilename(name);
+
+ lldb::SBModule module = target.FindModule(fileSpec);
+ if (!module.IsValid())
+ {
+ return NULL;
+ }
+
+ return module.GetFileSpec().GetDirectory();
+}
+
+ULONG64
+LLDBServices::GetModuleBase(
+ /* const */ lldb::SBTarget& target,
+ /* const */ lldb::SBModule& module)
+{
+ // Find the first section with an valid base address
+ int numSections = module.GetNumSections();
+ for (int si = 0; si < numSections; si++)
+ {
+ lldb::SBSection section = module.GetSectionAtIndex(si);
+ if (section.IsValid())
+ {
+ lldb::addr_t baseAddress = section.GetLoadAddress(target);
+ if (baseAddress != LLDB_INVALID_ADDRESS)
+ {
+ return baseAddress - section.GetFileOffset();
+ }
+ }
+ }
+
+ return UINT64_MAX;
+}
+
+//----------------------------------------------------------------------------
+// IDebugSystemObjects
+//----------------------------------------------------------------------------
+
+HRESULT
+LLDBServices::GetCurrentProcessId(
+ PULONG id)
+{
+ if (id == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ lldb::SBProcess process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ *id = 0;
+ return E_FAIL;
+ }
+
+ *id = process.GetProcessID();
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetCurrentThreadId(
+ PULONG id)
+{
+ if (id == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ lldb::SBThread thread = GetCurrentThread();
+ if (!thread.IsValid())
+ {
+ *id = 0;
+ return E_FAIL;
+ }
+
+ // This is allow the a valid current TID to be returned to
+ // workaround a bug in lldb on core dumps.
+ if (g_currentThreadIndex != -1)
+ {
+ *id = g_currentThreadIndex;
+ return S_OK;
+ }
+
+ *id = thread.GetIndexID();
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::SetCurrentThreadId(
+ ULONG id)
+{
+ lldb::SBProcess process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ return E_FAIL;
+ }
+
+ if (!process.SetSelectedThreadByIndexID(id))
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetCurrentThreadSystemId(
+ PULONG sysId)
+{
+ if (sysId == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ lldb::SBThread thread = GetCurrentThread();
+ if (!thread.IsValid())
+ {
+ *sysId = 0;
+ return E_FAIL;
+ }
+
+ // This is allow the a valid current TID to be returned to
+ // workaround a bug in lldb on core dumps.
+ if (g_currentThreadSystemId != -1)
+ {
+ *sysId = g_currentThreadSystemId;
+ return S_OK;
+ }
+
+ *sysId = thread.GetThreadID();
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetThreadIdBySystemId(
+ ULONG sysId,
+ PULONG threadId)
+{
+ HRESULT hr = E_FAIL;
+ ULONG id = 0;
+
+ lldb::SBProcess process;
+ lldb::SBThread thread;
+
+ if (threadId == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ goto exit;
+ }
+
+ // If we have a "fake" thread OS (system) id and a fake thread index,
+ // we need to return fake thread index.
+ if (g_currentThreadSystemId == sysId && g_currentThreadIndex != -1)
+ {
+ id = g_currentThreadIndex;
+ }
+ else
+ {
+ thread = process.GetThreadByID(sysId);
+ if (!thread.IsValid())
+ {
+ goto exit;
+ }
+
+ id = thread.GetIndexID();
+ }
+ hr = S_OK;
+
+exit:
+ *threadId = id;
+ return hr;
+}
+
+HRESULT
+LLDBServices::GetThreadContextById(
+ /* in */ ULONG32 threadID,
+ /* in */ ULONG32 contextFlags,
+ /* in */ ULONG32 contextSize,
+ /* out */ PBYTE context)
+{
+ lldb::SBProcess process;
+ lldb::SBThread thread;
+ lldb::SBFrame frame;
+ DT_CONTEXT *dtcontext;
+ HRESULT hr = E_FAIL;
+
+ if (context == NULL || contextSize < sizeof(DT_CONTEXT))
+ {
+ goto exit;
+ }
+ memset(context, 0, contextSize);
+
+ process = GetCurrentProcess();
+ if (!process.IsValid())
+ {
+ goto exit;
+ }
+
+ // If we have a "fake" thread OS (system) id and a fake thread index,
+ // use the fake thread index to get the context.
+ if (g_currentThreadSystemId == threadID && g_currentThreadIndex != -1)
+ {
+ thread = process.GetThreadByIndexID(g_currentThreadIndex);
+ }
+ else
+ {
+ thread = process.GetThreadByID(threadID);
+ }
+
+ if (!thread.IsValid())
+ {
+ goto exit;
+ }
+
+ frame = thread.GetFrameAtIndex(0);
+ if (!frame.IsValid())
+ {
+ goto exit;
+ }
+
+ dtcontext = (DT_CONTEXT*)context;
+ dtcontext->ContextFlags = contextFlags;
+
+ GetContextFromFrame(frame, dtcontext);
+ hr = S_OK;
+
+exit:
+ return hr;
+}
+
+// Internal function
+void
+LLDBServices::GetContextFromFrame(
+ /* const */ lldb::SBFrame& frame,
+ DT_CONTEXT *dtcontext)
+{
+#ifdef DBG_TARGET_AMD64
+ dtcontext->Rip = frame.GetPC();
+ dtcontext->Rsp = frame.GetSP();
+ dtcontext->Rbp = frame.GetFP();
+ dtcontext->EFlags = GetRegister(frame, "rflags");
+
+ dtcontext->Rax = GetRegister(frame, "rax");
+ dtcontext->Rbx = GetRegister(frame, "rbx");
+ dtcontext->Rcx = GetRegister(frame, "rcx");
+ dtcontext->Rdx = GetRegister(frame, "rdx");
+ dtcontext->Rsi = GetRegister(frame, "rsi");
+ dtcontext->Rdi = GetRegister(frame, "rdi");
+ dtcontext->R8 = GetRegister(frame, "r8");
+ dtcontext->R9 = GetRegister(frame, "r9");
+ dtcontext->R10 = GetRegister(frame, "r10");
+ dtcontext->R11 = GetRegister(frame, "r11");
+ dtcontext->R12 = GetRegister(frame, "r12");
+ dtcontext->R13 = GetRegister(frame, "r13");
+ dtcontext->R14 = GetRegister(frame, "r14");
+ dtcontext->R15 = GetRegister(frame, "r15");
+
+ dtcontext->SegCs = GetRegister(frame, "cs");
+ dtcontext->SegSs = GetRegister(frame, "ss");
+ dtcontext->SegDs = GetRegister(frame, "ds");
+ dtcontext->SegEs = GetRegister(frame, "es");
+ dtcontext->SegFs = GetRegister(frame, "fs");
+ dtcontext->SegGs = GetRegister(frame, "gs");
+#elif DBG_TARGET_ARM
+ dtcontext->Pc = frame.GetPC();
+ dtcontext->Sp = frame.GetSP();
+ dtcontext->Lr = GetRegister(frame, "lr");
+ dtcontext->Cpsr = GetRegister(frame, "cpsr");
+
+ dtcontext->R0 = GetRegister(frame, "r0");
+ dtcontext->R1 = GetRegister(frame, "r1");
+ dtcontext->R2 = GetRegister(frame, "r2");
+ dtcontext->R3 = GetRegister(frame, "r3");
+ dtcontext->R4 = GetRegister(frame, "r4");
+ dtcontext->R5 = GetRegister(frame, "r5");
+ dtcontext->R6 = GetRegister(frame, "r6");
+ dtcontext->R7 = GetRegister(frame, "r7");
+ dtcontext->R8 = GetRegister(frame, "r8");
+ dtcontext->R9 = GetRegister(frame, "r9");
+ dtcontext->R10 = GetRegister(frame, "r10");
+ dtcontext->R11 = GetRegister(frame, "r11");
+ dtcontext->R12 = GetRegister(frame, "r12");
+#endif
+}
+
+// Internal function
+DWORD_PTR
+LLDBServices::GetRegister(
+ /* const */ lldb::SBFrame& frame,
+ const char *name)
+{
+ lldb::SBValue regValue = frame.FindRegister(name);
+
+ lldb::SBError error;
+ DWORD_PTR result = regValue.GetValueAsUnsigned(error);
+
+ return result;
+}
+
+//----------------------------------------------------------------------------
+// IDebugRegisters
+//----------------------------------------------------------------------------
+
+HRESULT
+LLDBServices::GetValueByName(
+ PCSTR name,
+ PDWORD_PTR debugValue)
+{
+ lldb::SBFrame frame = GetCurrentFrame();
+ if (!frame.IsValid())
+ {
+ *debugValue = 0;
+ return E_FAIL;
+ }
+
+ lldb::SBValue value = frame.FindRegister(name);
+ if (!value.IsValid())
+ {
+ *debugValue = 0;
+ return E_FAIL;
+ }
+
+ *debugValue = value.GetValueAsUnsigned();
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetInstructionOffset(
+ PULONG64 offset)
+{
+ lldb::SBFrame frame = GetCurrentFrame();
+ if (!frame.IsValid())
+ {
+ *offset = 0;
+ return E_FAIL;
+ }
+
+ *offset = frame.GetPC();
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetStackOffset(
+ PULONG64 offset)
+{
+ lldb::SBFrame frame = GetCurrentFrame();
+ if (!frame.IsValid())
+ {
+ *offset = 0;
+ return E_FAIL;
+ }
+
+ *offset = frame.GetSP();
+ return S_OK;
+}
+
+HRESULT
+LLDBServices::GetFrameOffset(
+ PULONG64 offset)
+{
+ lldb::SBFrame frame = GetCurrentFrame();
+ if (!frame.IsValid())
+ {
+ *offset = 0;
+ return E_FAIL;
+ }
+
+ *offset = frame.GetFP();
+ return S_OK;
+}
+
+//----------------------------------------------------------------------------
+// Helper functions
+//----------------------------------------------------------------------------
+
+lldb::SBProcess
+LLDBServices::GetCurrentProcess()
+{
+ lldb::SBProcess process;
+
+ if (m_currentProcess == nullptr)
+ {
+ lldb::SBTarget target = m_debugger.GetSelectedTarget();
+ if (target.IsValid())
+ {
+ process = target.GetProcess();
+ }
+ }
+ else
+ {
+ process = *m_currentProcess;
+ }
+
+ return process;
+}
+
+lldb::SBThread
+LLDBServices::GetCurrentThread()
+{
+ lldb::SBThread thread;
+
+ if (m_currentThread == nullptr)
+ {
+ lldb::SBProcess process = GetCurrentProcess();
+ if (process.IsValid())
+ {
+ thread = process.GetSelectedThread();
+ }
+ }
+ else
+ {
+ thread = *m_currentThread;
+ }
+
+ return thread;
+}
+
+lldb::SBFrame
+LLDBServices::GetCurrentFrame()
+{
+ lldb::SBFrame frame;
+
+ lldb::SBThread thread = GetCurrentThread();
+ if (thread.IsValid())
+ {
+ frame = thread.GetSelectedFrame();
+ }
+
+ return frame;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/services.h b/src/ToolBox/SOS/lldbplugin/services.h
new file mode 100644
index 0000000000..3c384c9076
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/services.h
@@ -0,0 +1,274 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <cstdarg>
+
+class LLDBServices : public ILLDBServices
+{
+private:
+ LONG m_ref;
+ lldb::SBDebugger &m_debugger;
+ lldb::SBCommandReturnObject &m_returnObject;
+
+ lldb::SBProcess *m_currentProcess;
+ lldb::SBThread *m_currentThread;
+
+ void OutputString(ULONG mask, PCSTR str);
+ ULONG64 GetModuleBase(lldb::SBTarget& target, lldb::SBModule& module);
+ DWORD_PTR GetExpression(lldb::SBFrame& frame, lldb::SBError& error, PCSTR exp);
+ void GetContextFromFrame(lldb::SBFrame& frame, DT_CONTEXT *dtcontext);
+ DWORD_PTR GetRegister(lldb::SBFrame& frame, const char *name);
+
+ lldb::SBProcess GetCurrentProcess();
+ lldb::SBThread GetCurrentThread();
+ lldb::SBFrame GetCurrentFrame();
+
+public:
+ LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process = nullptr, lldb::SBThread *thread = nullptr);
+ ~LLDBServices();
+
+ //----------------------------------------------------------------------------
+ // IUnknown
+ //----------------------------------------------------------------------------
+
+ HRESULT QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface);
+
+ ULONG AddRef();
+
+ ULONG Release();
+
+ //----------------------------------------------------------------------------
+ // ILLDBServices
+ //----------------------------------------------------------------------------
+
+ PCSTR GetCoreClrDirectory();
+
+ DWORD_PTR GetExpression(
+ PCSTR exp);
+
+ HRESULT VirtualUnwind(
+ DWORD threadID,
+ ULONG32 contextSize,
+ PBYTE context);
+
+ HRESULT SetExceptionCallback(
+ PFN_EXCEPTION_CALLBACK callback);
+
+ HRESULT ClearExceptionCallback();
+
+ //----------------------------------------------------------------------------
+ // IDebugControl2
+ //----------------------------------------------------------------------------
+
+ HRESULT GetInterrupt();
+
+ HRESULT Output(
+ ULONG mask,
+ PCSTR format,
+ ...);
+
+ HRESULT OutputVaList(
+ ULONG mask,
+ PCSTR format,
+ va_list args);
+
+ HRESULT ControlledOutput(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ ...);
+
+ HRESULT ControlledOutputVaList(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ va_list args);
+
+ HRESULT GetDebuggeeType(
+ PULONG debugClass,
+ PULONG qualifier);
+
+ HRESULT GetPageSize(
+ PULONG size);
+
+ HRESULT GetExecutingProcessorType(
+ PULONG type);
+
+ HRESULT Execute(
+ ULONG outputControl,
+ PCSTR command,
+ ULONG flags);
+
+ HRESULT GetLastEventInformation(
+ PULONG type,
+ PULONG processId,
+ PULONG threadId,
+ PVOID extraInformation,
+ ULONG extraInformationSize,
+ PULONG extraInformationUsed,
+ PSTR description,
+ ULONG descriptionSize,
+ PULONG descriptionUsed);
+
+ HRESULT Disassemble(
+ ULONG64 offset,
+ ULONG flags,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG disassemblySize,
+ PULONG64 endOffset);
+
+ //----------------------------------------------------------------------------
+ // IDebugControl4
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetContextStackTrace(
+ PVOID startContext,
+ ULONG startContextSize,
+ PDEBUG_STACK_FRAME frames,
+ ULONG framesSize,
+ PVOID frameContexts,
+ ULONG frameContextsSize,
+ ULONG frameContextsEntrySize,
+ PULONG framesFilled);
+
+ //----------------------------------------------------------------------------
+ // IDebugDataSpaces
+ //----------------------------------------------------------------------------
+
+ HRESULT ReadVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesRead);
+
+ HRESULT WriteVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesWritten);
+
+ //----------------------------------------------------------------------------
+ // IDebugSymbols
+ //----------------------------------------------------------------------------
+
+ HRESULT GetSymbolOptions(
+ PULONG options);
+
+ HRESULT GetNameByOffset(
+ ULONG64 offset,
+ PSTR nameBuffer,
+ ULONG nameBufferSize,
+ PULONG nameSize,
+ PULONG64 displacement);
+
+ HRESULT GetNumberModules(
+ PULONG loaded,
+ PULONG unloaded);
+
+ HRESULT GetModuleByIndex(
+ ULONG index,
+ PULONG64 base);
+
+ HRESULT GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base);
+
+ HRESULT GetModuleByOffset(
+ ULONG64 offset,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base);
+
+ HRESULT GetModuleNames(
+ ULONG index,
+ ULONG64 base,
+ PSTR imageNameBuffer,
+ ULONG imageNameBufferSize,
+ PULONG imageNameSize,
+ PSTR moduleNameBuffer,
+ ULONG moduleNameBufferSize,
+ PULONG moduleNameSize,
+ PSTR loadedImageNameBuffer,
+ ULONG loadedImageNameBufferSize,
+ PULONG loadedImageNameSize);
+
+ HRESULT GetLineByOffset(
+ ULONG64 offset,
+ PULONG line,
+ PSTR fileBuffer,
+ ULONG fileBufferSize,
+ PULONG fileSize,
+ PULONG64 displacement);
+
+ HRESULT GetSourceFileLineOffsets(
+ PCSTR file,
+ PULONG64 buffer,
+ ULONG bufferLines,
+ PULONG fileLines);
+
+ HRESULT FindSourceFile(
+ ULONG startElement,
+ PCSTR file,
+ ULONG flags,
+ PULONG foundElement,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG foundSize);
+
+ //----------------------------------------------------------------------------
+ // IDebugSystemObjects
+ //----------------------------------------------------------------------------
+
+ HRESULT GetCurrentProcessId(
+ PULONG id);
+
+ HRESULT GetCurrentThreadId(
+ PULONG id);
+
+ HRESULT SetCurrentThreadId(
+ ULONG id);
+
+ HRESULT GetCurrentThreadSystemId(
+ PULONG sysId);
+
+ HRESULT GetThreadIdBySystemId(
+ ULONG sysId,
+ PULONG threadId);
+
+ HRESULT GetThreadContextById(
+ ULONG32 threadID,
+ ULONG32 contextFlags,
+ ULONG32 contextSize,
+ PBYTE context);
+
+ //----------------------------------------------------------------------------
+ // IDebugRegisters
+ //----------------------------------------------------------------------------
+
+ HRESULT GetValueByName(
+ PCSTR name,
+ PDWORD_PTR debugValue);
+
+ HRESULT GetInstructionOffset(
+ PULONG64 offset);
+
+ HRESULT GetStackOffset(
+ PULONG64 offset);
+
+ HRESULT GetFrameOffset(
+ PULONG64 offset);
+
+ //----------------------------------------------------------------------------
+ // LLDBServices (internal)
+ //----------------------------------------------------------------------------
+
+ PCSTR GetModuleDirectory(
+ PCSTR name);
+};
diff --git a/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp b/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp
new file mode 100644
index 0000000000..2208306671
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "sosplugin.h"
+#include <dlfcn.h>
+#include <string.h>
+#include <string>
+#include <stdlib.h>
+#include <limits.h>
+
+class setclrpathCommand : public lldb::SBCommandPluginInterface
+{
+public:
+ setclrpathCommand()
+ {
+ }
+
+ virtual bool
+ DoExecute (lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ if (arguments[0] == NULL)
+ {
+ result.Printf("Load path for sos/dac/dbi: '%s'\n", g_coreclrDirectory == NULL ? "<none>" : g_coreclrDirectory);
+ }
+ else {
+ if (g_coreclrDirectory != NULL)
+ {
+ free(g_coreclrDirectory);
+ }
+
+ std::string path(arguments[0]);
+ if (path[path.length() - 1] != '/')
+ {
+ path.append("/");
+ }
+
+ g_coreclrDirectory = strdup(path.c_str());
+ result.Printf("Set load path for sos/dac/dbi to '%s'\n", g_coreclrDirectory);
+ }
+ return result.Succeeded();
+ }
+};
+
+bool
+setclrpathCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand command = interpreter.AddCommand("setclrpath", new setclrpathCommand(), "Set the path to load coreclr sos/dac/dbi files. setclrpath <path>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp b/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp
new file mode 100644
index 0000000000..c7d6a1ba25
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "sosplugin.h"
+#include <dlfcn.h>
+#include <string.h>
+#include <string>
+#include <stdlib.h>
+#include <limits.h>
+
+class setsostidCommand : public lldb::SBCommandPluginInterface
+{
+public:
+ setsostidCommand()
+ {
+ }
+
+ virtual bool
+ DoExecute(lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ if (arguments[0] == NULL)
+ {
+ if (g_currentThreadSystemId == -1 || g_currentThreadIndex == -1)
+ {
+ result.Printf("sos OS tid not mapped\n");
+ }
+ else {
+ result.Printf("sos OS tid 0x%x mapped to lldb thread index %d\n",
+ g_currentThreadSystemId, g_currentThreadIndex);
+ }
+ }
+ else if (strcmp(arguments[0], "-clear") == 0) {
+ g_currentThreadIndex = -1;
+ g_currentThreadSystemId = -1;
+ result.Printf("Cleared sos OS tid/index\n");
+ }
+ else if (arguments[1] == NULL)
+ {
+ result.Printf("Need thread index parameter that maps to the OS tid\n");
+ }
+ else
+ {
+ ULONG tid = strtoul(arguments[0], NULL, 16);
+ g_currentThreadSystemId = tid;
+
+ ULONG index = strtoul(arguments[1], NULL, 16);
+ g_currentThreadIndex = index;
+
+ result.Printf("Mapped sos OS tid 0x%x to lldb thread index %d\n", tid, index);
+ }
+ return result.Succeeded();
+ }
+};
+
+bool
+setsostidCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand command = interpreter.AddCommand("setsostid", new setsostidCommand(), "Set the current os tid/thread index instead of using the one lldb provides. setsostid <tid> <index>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/soscommand.cpp b/src/ToolBox/SOS/lldbplugin/soscommand.cpp
new file mode 100644
index 0000000000..4ca7ce37f3
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/soscommand.cpp
@@ -0,0 +1,152 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "sosplugin.h"
+#include <dlfcn.h>
+#include <string.h>
+#include <string>
+
+class sosCommand : public lldb::SBCommandPluginInterface
+{
+ const char *m_command;
+ void *m_sosHandle;
+
+public:
+ sosCommand(const char *command)
+ {
+ m_command = command;
+ m_sosHandle = NULL;
+ }
+
+ virtual bool
+ DoExecute (lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ LLDBServices* services = new LLDBServices(debugger, result);
+ LoadSos(services);
+
+ if (m_sosHandle)
+ {
+ const char* sosCommand = m_command;
+ if (sosCommand == NULL)
+ {
+ if (arguments == NULL || *arguments == NULL) {
+ sosCommand = "Help";
+ }
+ else
+ {
+ sosCommand = *arguments++;
+ }
+ }
+ CommandFunc commandFunc = (CommandFunc)dlsym(m_sosHandle, sosCommand);
+ if (commandFunc)
+ {
+ std::string str;
+ if (arguments != NULL)
+ {
+ for (const char* arg = *arguments; arg; arg = *(++arguments))
+ {
+ str.append(arg);
+ str.append(" ");
+ }
+ }
+ const char* sosArgs = str.c_str();
+ HRESULT hr = commandFunc(services, sosArgs);
+ if (hr != S_OK)
+ {
+ services->Output(DEBUG_OUTPUT_ERROR, "%s %s failed\n", sosCommand, sosArgs);
+ }
+ }
+ else
+ {
+ services->Output(DEBUG_OUTPUT_ERROR, "SOS command '%s' not found %s\n", sosCommand, dlerror());
+ }
+ }
+
+ services->Release();
+ return result.Succeeded();
+ }
+
+ void
+ LoadSos(LLDBServices *services)
+ {
+ if (m_sosHandle == NULL)
+ {
+ if (g_coreclrDirectory == NULL)
+ {
+ const char *coreclrModule = MAKEDLLNAME_A("coreclr");
+ const char *directory = services->GetModuleDirectory(coreclrModule);
+ if (directory != NULL)
+ {
+ std::string path(directory);
+ path.append("/");
+ g_coreclrDirectory = strdup(path.c_str());
+ }
+ else
+ {
+ services->Output(DEBUG_OUTPUT_WARNING, "The %s module is not loaded yet in the target process\n", coreclrModule);
+ }
+ }
+
+ if (g_coreclrDirectory != NULL)
+ {
+ // Load the DAC module first explicitly because SOS and DBI
+ // have implicit references to the DAC's PAL.
+ LoadModule(services, MAKEDLLNAME_A("mscordaccore"));
+
+ m_sosHandle = LoadModule(services, MAKEDLLNAME_A("sos"));
+ }
+ }
+ }
+
+ void *
+ LoadModule(LLDBServices *services, const char *moduleName)
+ {
+ std::string modulePath(g_coreclrDirectory);
+ modulePath.append(moduleName);
+
+ void *moduleHandle = dlopen(modulePath.c_str(), RTLD_NOW);
+ if (moduleHandle == NULL)
+ {
+ services->Output(DEBUG_OUTPUT_ERROR, "dlopen(%s) failed %s\n", modulePath.c_str(), dlerror());
+ }
+
+ return moduleHandle;
+ }
+};
+
+bool
+sosCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ interpreter.AddCommand("sos", new sosCommand(NULL), "Various coreclr debugging commands. See 'soshelp' for more details. sos <command-name> <args>");
+ interpreter.AddCommand("bpmd", new sosCommand("bpmd"), "Creates a breakpoint at the specified managed method in the specified module.");
+ interpreter.AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only.");
+ interpreter.AddCommand("clrthreads", new sosCommand("Threads"), "List the managed threads running.");
+ interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method.");
+ interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address.");
+ interpreter.AddCommand("dumpheap", new sosCommand("DumpHeap"), "Displays info about the garbage-collected heap and collection statistics about objects.");
+ interpreter.AddCommand("dumpil", new sosCommand("DumpIL"), "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.");
+ interpreter.AddCommand("dumplog", new sosCommand("DumpLog"), "Writes the contents of an in-memory stress log to the specified file.");
+ interpreter.AddCommand("dumpmd", new sosCommand("DumpMD"), "Displays information about a MethodDesc structure at the specified address.");
+ interpreter.AddCommand("dumpmodule", new sosCommand("DumpModule"), "Displays information about a EE module structure at the specified address.");
+ interpreter.AddCommand("dumpmt", new sosCommand("DumpMT"), "Displays information about a method table at the specified address.");
+ interpreter.AddCommand("dumpobj", new sosCommand("DumpObj"), "Displays info about an object at the specified address.");
+ interpreter.AddCommand("dumpstack", new sosCommand("DumpStack"), "Displays a native and managed stack trace.");
+ interpreter.AddCommand("dso", new sosCommand("DumpStackObjects"), "Displays all managed objects found within the bounds of the current stack.");
+ interpreter.AddCommand("eeheap", new sosCommand("EEHeap"), "Displays info about process memory consumed by internal runtime data structures.");
+ interpreter.AddCommand("eestack", new sosCommand("EEStack"), "Runs dumpstack on all threads in the process.");
+ interpreter.AddCommand("gcroot", new sosCommand("GCRoot"), "Displays info about references (or roots) to an object at the specified address.");
+ interpreter.AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.");
+ interpreter.AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.");
+ interpreter.AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address.");
+ interpreter.AddCommand("histclear", new sosCommand("HistClear"), "Releases any resources used by the family of Hist commands.");
+ interpreter.AddCommand("histinit", new sosCommand("HistInit"), "Initializes the SOS structures from the stress log saved in the debuggee.");
+ interpreter.AddCommand("histobj", new sosCommand("HistObj"), "Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument.");
+ interpreter.AddCommand("histobjfind", new sosCommand("HistObjFind"), "Displays all the log entries that reference an object at the specified address.");
+ interpreter.AddCommand("histroot", new sosCommand("HistRoot"), "Displays information related to both promotions and relocations of the specified root.");
+ interpreter.AddCommand("soshelp", new sosCommand("Help"), "Displays all available commands when no parameter is specified, or displays detailed help information about the specified command. soshelp <command>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/sosplugin.cpp b/src/ToolBox/SOS/lldbplugin/sosplugin.cpp
new file mode 100644
index 0000000000..6d43d90823
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/sosplugin.cpp
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "sosplugin.h"
+
+namespace lldb {
+ bool PluginInitialize (lldb::SBDebugger debugger);
+}
+
+bool
+lldb::PluginInitialize (lldb::SBDebugger debugger)
+{
+#ifdef _DEBUG
+ corerunCommandInitialize(debugger);
+#endif
+ sosCommandInitialize(debugger);
+ setclrpathCommandInitialize(debugger);
+ setsostidCommandInitialize(debugger);
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/sosplugin.h b/src/ToolBox/SOS/lldbplugin/sosplugin.h
new file mode 100644
index 0000000000..4d38589e3f
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/sosplugin.h
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <lldb/API/LLDB.h>
+#include "mstypes.h"
+#define DEFINE_EXCEPTION_RECORD
+#include <lldbservices.h>
+#include <dbgtargetcontext.h>
+#include "services.h"
+
+typedef HRESULT (*CommandFunc)(ILLDBServices* services, const char *args);
+
+extern char *g_coreclrDirectory;
+extern ULONG g_currentThreadIndex;
+extern ULONG g_currentThreadSystemId;
+
+bool
+sosCommandInitialize(lldb::SBDebugger debugger);
+
+bool
+setsostidCommandInitialize(lldb::SBDebugger debugger);
+
+bool
+setclrpathCommandInitialize(lldb::SBDebugger debugger);
+
+bool
+corerunCommandInitialize(lldb::SBDebugger debugger);
diff --git a/src/ToolBox/SOS/tests/.gitmirrorall b/src/ToolBox/SOS/tests/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/ToolBox/SOS/tests/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/ToolBox/SOS/tests/OnCrash.do b/src/ToolBox/SOS/tests/OnCrash.do
new file mode 100644
index 0000000000..b1da86154a
--- /dev/null
+++ b/src/ToolBox/SOS/tests/OnCrash.do
@@ -0,0 +1,2 @@
+script open("/tmp/flag_fail", "a").close()
+q
diff --git a/src/ToolBox/SOS/tests/README.md b/src/ToolBox/SOS/tests/README.md
new file mode 100644
index 0000000000..ed4c8d062c
--- /dev/null
+++ b/src/ToolBox/SOS/tests/README.md
@@ -0,0 +1,33 @@
+Testing libsosplugin
+=====================================
+
+**Test assembly**
+The test asembly file must follow two rules:
+1. the test class must be named `Program` and
+2. it must have a static `Main` method.
+
+**Running tests**
+Make sure that python's lldb module is accessible. To run the tests, use the following command:
+`python test_libsosplugin.py --clr-args="/path/to/corerun [corerun_options] /path/to/test_assembly.exe"`
+`--clr-args` is a command that would normally be used to launch `corerun`
+This will run the test suite on the specified assembly.
+
+**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) \ No newline at end of file
diff --git a/src/ToolBox/SOS/tests/dumpil.py b/src/ToolBox/SOS/tests/dumpil.py
new file mode 100644
index 0000000000..9539b618bb
--- /dev/null
+++ b/src/ToolBox/SOS/tests/dumpil.py
@@ -0,0 +1,26 @@
+import lldb
+import lldbutil
+import re
+import os
+import testutils
+
+def runScenario(assemblyName, debugger, target):
+ process = target.GetProcess()
+ res = lldb.SBCommandReturnObject()
+ ci = debugger.GetCommandInterpreter()
+
+ testutils.stop_in_main(ci, process, assemblyName)
+ addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "MethodDesc:\s+([0-9a-fA-F]+)")
+
+ result = False
+ if addr is not None:
+ ci.HandleCommand("dumpil " + addr, res)
+ if res.Succeeded():
+ result = True
+ else:
+ print("DumpIL failed:")
+ print(res.GetOutput())
+ print(res.GetError())
+
+ process.Continue()
+ return result
diff --git a/src/ToolBox/SOS/tests/dumpmodule.py b/src/ToolBox/SOS/tests/dumpmodule.py
new file mode 100644
index 0000000000..04a5764752
--- /dev/null
+++ b/src/ToolBox/SOS/tests/dumpmodule.py
@@ -0,0 +1,26 @@
+import lldb
+import lldbutil
+import re
+import os
+import testutils
+
+def runScenario(assemblyName, debugger, target):
+ process = target.GetProcess()
+ res = lldb.SBCommandReturnObject()
+ ci = debugger.GetCommandInterpreter()
+
+ testutils.stop_in_main(ci, process, assemblyName)
+ addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "Module:\s+([0-9a-fA-F]+)")
+
+ result = False
+ if addr is not None:
+ ci.HandleCommand("dumpmodule " + addr, res)
+ if res.Succeeded():
+ result = True
+ else:
+ print("DumpModule failed:")
+ print(res.GetOutput())
+ print(res.GetError())
+
+ process.Continue()
+ return result \ No newline at end of file
diff --git a/src/ToolBox/SOS/tests/runprocess.py b/src/ToolBox/SOS/tests/runprocess.py
new file mode 100644
index 0000000000..d9367b3e6c
--- /dev/null
+++ b/src/ToolBox/SOS/tests/runprocess.py
@@ -0,0 +1,34 @@
+import os
+import lldb
+import sys
+import importlib
+from test_libsosplugin import fail_flag
+
+def run(assemblyName, moduleName):
+ global fail_flag
+
+ print(fail_flag)
+ # set the flag, if it is not set
+ if not os.access(fail_flag, os.R_OK):
+ open(fail_flag, "a").close()
+
+
+ debugger = lldb.debugger
+
+ debugger.SetAsync(False)
+ target = lldb.target
+
+ debugger.HandleCommand("process launch -s")
+ debugger.HandleCommand("breakpoint set -n LoadLibraryExW")
+
+ target.GetProcess().Continue()
+
+ debugger.HandleCommand("breakpoint delete 1")
+ #run the scenario
+ print("starting scenario...")
+ i = importlib.import_module(moduleName)
+ scenarioResult = i.runScenario(os.path.basename(assemblyName), debugger, target)
+
+ # clear the failed flag if the exit status is OK
+ if scenarioResult is True and target.GetProcess().GetExitStatus() == 0:
+ os.unlink(fail_flag)
diff --git a/src/ToolBox/SOS/tests/test_libsosplugin.py b/src/ToolBox/SOS/tests/test_libsosplugin.py
new file mode 100644
index 0000000000..e4f59ebbcf
--- /dev/null
+++ b/src/ToolBox/SOS/tests/test_libsosplugin.py
@@ -0,0 +1,84 @@
+import unittest
+import argparse
+import re
+import tempfile
+import subprocess
+import threading
+import os
+import os.path
+import sys
+
+assemblyName=''
+clrArgs=''
+fail_flag='/tmp/fail_flag'
+
+# helper functions
+
+def prepareScenarioFile(moduleName):
+ global assemblyName
+ #create a temporary scenario file
+ fd, scenarioFileName = tempfile.mkstemp()
+ scenarioFile = open(scenarioFileName, 'w')
+ scenarioFile.write('script from runprocess import run\n')
+ scenarioFile.write('script run("'+assemblyName+'", "'+moduleName+'")\n')
+ scenarioFile.write('quit\n')
+ scenarioFile.close()
+ os.close(fd)
+ return scenarioFileName
+
+def runWithTimeout(cmd, timeout):
+ d = {'process': None}
+ def run():
+ d['process'] = subprocess.Popen(cmd, shell=True)
+ d['process'].communicate()
+
+ thread = threading.Thread(target=run)
+ thread.start()
+
+ thread.join(timeout)
+ if thread.is_alive():
+ d['process'].terminate()
+ thread.join()
+
+# Test class
+class TestSosCommands(unittest.TestCase):
+
+ def do_test(self, command):
+ global clrArgs
+ global fail_flag
+ filename = prepareScenarioFile(command)
+ cmd = "lldb --source "+filename+" -b -K \"OnCrash.do\" -- "+clrArgs+" > "+command+".log 2>"+command+".log.2"
+ runWithTimeout(cmd, 120)
+ self.assertFalse(os.path.isfile(fail_flag))
+ os.unlink(filename)
+
+ def test_dumpmodule(self):
+ self.do_test("dumpmodule")
+
+ def test_dumpil(self):
+ self.do_test("dumpil")
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--clr-args', default='')
+ parser.add_argument('unittest_args', nargs='*')
+
+ args = parser.parse_args()
+
+ clrArgs = args.clr_args
+ print("ClrArgs: " + clrArgs)
+ # find assembly name among lldb arguments
+ assembly_regexp = re.compile("([^\s]+\.exe)")
+ assemblyMatch = assembly_regexp.search(clrArgs)
+ if assemblyMatch is not None:
+ assemblyName = assemblyMatch.group(1)
+ else:
+ print("Assembly not recognized")
+ exit(1)
+
+ print("Assembly name: "+assemblyName)
+ sys.argv[1:] = args.unittest_args
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestSosCommands)
+ unittest.TextTestRunner(verbosity=2).run(suite)
+ os.unlink(fail_flag) \ No newline at end of file
diff --git a/src/ToolBox/SOS/tests/testutils.py b/src/ToolBox/SOS/tests/testutils.py
new file mode 100644
index 0000000000..1ddb6560e6
--- /dev/null
+++ b/src/ToolBox/SOS/tests/testutils.py
@@ -0,0 +1,40 @@
+import lldb
+import re
+
+def checkResult(res):
+ if not res.Succeeded():
+ print(res.GetOutput())
+ print(res.GetError())
+ exit(1)
+
+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 is not None:
+ addr = match.group(1)
+ break
+
+ print("Found addr: " + str(addr))
+ return addr
+
+def stop_in_main(commandInterpreter, process, assemblyName):
+ res = lldb.SBCommandReturnObject()
+ commandInterpreter.HandleCommand("bpmd " + assemblyName + " Program.Main", res)
+ checkResult(res)
+ print(res.GetOutput())
+ print(res.GetError())
+ res.Clear()
+
+
+ # Use Python API to continue the process. The listening thread should be
+ # able to receive the state changed events.
+ process.Continue() \ No newline at end of file
diff --git a/src/ToolBox/dirs.proj b/src/ToolBox/dirs.proj
new file mode 100644
index 0000000000..9ac295d8d5
--- /dev/null
+++ b/src/ToolBox/dirs.proj
@@ -0,0 +1,100 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <ItemDefinitionGroup>
+ <ProjectFile>
+ <ProductGroups>FX</ProductGroups>
+ </ProjectFile>
+ </ItemDefinitionGroup>
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1 of the Desktop build-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreclr)' != 'true' and '$(BuildProjectName)' != 'CoreSys'">
+ <ProjectFile Include="adepends\adepends.csproj" />
+ <ProjectFile Include="allocationprofiler\clrprofiler.csproj" />
+ <ProjectFile Include="apicompat\apicompat_buildtree.csproj" />
+ <ProjectFile Include="apiconformance\apiconformance_ndpbuild.csproj" Condition="'$(BuildArchitecture)' != 'arm'" />
+ <ProjectFile Include="asmrefrewriter\asmrefrewriter.csproj" />
+ <ProjectFile Include="binaryrewriting\dirs.proj">
+ <ProductGroups>FX;PK</ProductGroups>
+ </ProjectFile>
+ <ProjectFile Include="basealloc\basealloc.csproj" />
+ <ProjectFile Include="bindingaddin\bindingaddin.csproj" />
+ <ProjectFile Include="bindingdisplayinterop\bindingdisplayinterop.nativeproj" />
+ <ProjectFile Include="caspol\caspol.csproj" />
+ <ProjectFile Include="clrconfigure\clrconfigure_ndpbuild.csproj" />
+ <ProjectFile Include="coveragerecorder\coveragerecorder.csproj" />
+ <ProjectFile Include="etw\dirs.proj" />
+ <ProjectFile Include="flux.viewer\flux.viewer_msbuild.csproj" />
+ <ProjectFile Include="flux\flux_msbuild.csproj" />
+ <ProjectFile Include="genman32\genman32.csproj" />
+ <ProjectFile Include="genpubcfg\genpubcfg.csproj" Condition="'$(BuildArchitecture)' == 'i386'" />
+ <ProjectFile Include="glee\glee.nativeproj" />
+ <ProjectFile Include="ibcmerge\ibcmerge_msbuild.csproj">
+ <ProductGroups>FX;PK</ProductGroups>
+ </ProjectFile>
+ <ProjectFile Include="isymmanagedwrapper\isymwrapper.csproj" Condition="'$(BuildArchitecture)' == 'ppc'" />
+ <ProjectFile Include="isymwrapper\isymwrapper.nativeproj" />
+ <ProjectFile Include="locstudiomanagedparser\managedparser.csproj" />
+ <ProjectFile Include="mangen\mangen.csproj" />
+ <ProjectFile Include="mda\mda.nativeproj" />
+ <ProjectFile Include="mdbg\dirs.proj" />
+ <ProjectFile Include="LaunchAppx\LaunchAppx.csproj" />
+ <ProjectFile Include="mpgo\mpgo.csproj" >
+ <ProductGroups>VS;FX</ProductGroups>
+ </ProjectFile>
+ <ProjectFile Include="LaunchAppx\LaunchAppx.csproj" >
+ <ProductGroups>VS;FX</ProductGroups>
+ </ProjectFile>
+ <ProjectFile Include="mscorlibtlb\mscorlibtlb.nativeproj" />
+ <ProjectFile Include="namesgen\namesgen.csproj" />
+ <ProjectFile Include="nirvana\dirs.proj" Condition="'$(BuildArchitecture)' == 'i386' or '$(BuildArchitecture)' == 'amd64'" />
+ <ProjectFile Include="obfus\obfus.csproj" />
+ <ProjectFile Include="permissionsets\permissionsets.csproj" />
+ <ProjectFile Include="pltypes\pltypes.csproj" />
+ <ProjectFile Include="regasm\regasm_msbuild.csproj" />
+ <ProjectFile Include="regkeydiff\regkeydiff.csproj" />
+ <ProjectFile Include="regkeyseperator\regkeyseperator.csproj" />
+ <ProjectFile Include="regsvcs\regsvcs.csproj" />
+ <ProjectFile Include="resview\resview.csproj" />
+ <ProjectFile Include="runsxs\runsxs.nativeproj" />
+ <ProjectFile Include="soapsuds\dirs.proj" />
+ <ProjectFile Include="sos\dirs.proj" >
+ <ProductGroups>FX;PK</ProductGroups>
+ </ProjectFile>
+ <ProjectFile Include="storeadm\storeadm.csproj" />
+ <ProjectFile Include="tlbexp\tlbexp_msbuild.csproj" />
+ <ProjectFile Include="tlbguid\tlbguid.csproj" />
+ <ProjectFile Include="tlbimp2\tlbimp2.csproj" />
+ <ProjectFile Include="tnt\dirs.proj" Condition="'$(BuildArchitecture)' != 'arm'" />
+ <ProjectFile Include="transparencyannotator\dirs.proj" >
+ <ProductGroups>VS;FX</ProductGroups>
+ </ProjectFile>
+ <ProjectFile Include="urtui\dirs.proj"/>
+ <ProjectFile Include="winmdexp\dirs.proj" />
+ <ProjectFile Include="winverify\dirs.proj" />
+ <ProjectFile Include="wpf\wpf.proj" Condition="'$(BuildArchitecture)' == 'i386' or '$(BuildArchitecture)' == 'amd64'" />
+ <ProjectFile Include="PdbTypeMatch\PdbTypeMatch.nativeproj" Condition="'$(BuildArchitecture)' == 'i386' and '$(_BuildType)' == 'ret'" >
+ <ProductGroups>PK</ProductGroups>
+ </ProjectFile>
+ </ItemGroup>
+
+ <!--The following projects will build during PHASE 1 of the CoreCLR build-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and ('$(FeatureCoreclr)' == 'true' or '$(BuildProjectName)' == 'CoreSys')">
+ <ProjectFile Include="sos\dirs.proj" />
+ </ItemGroup>
+
+ <!--The following projects will build during PHASE 1 of the CoreSys build-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(BuildProjectName)' == 'CoreSys'">
+ <ProjectFile Include="ibcmerge\ibcmerge_msbuild.csproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/ToolBox/superpmi/.gitmirror b/src/ToolBox/superpmi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/CMakeLists.txt b/src/ToolBox/superpmi/CMakeLists.txt
new file mode 100644
index 0000000000..d4308d2e03
--- /dev/null
+++ b/src/ToolBox/superpmi/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(superpmi)
+add_subdirectory(mcs)
+add_subdirectory(superpmi-shim-collector)
+add_subdirectory(superpmi-shim-counter)
+add_subdirectory(superpmi-shim-simple)
diff --git a/src/ToolBox/superpmi/mcs/.gitmirror b/src/ToolBox/superpmi/mcs/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/mcs/CMakeLists.txt b/src/ToolBox/superpmi/mcs/CMakeLists.txt
new file mode 100644
index 0000000000..7e6ff70471
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/CMakeLists.txt
@@ -0,0 +1,76 @@
+project(mcs)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+
+if(WIN32)
+ #use static crt
+ add_definitions(-MT)
+endif(WIN32)
+
+include_directories(.)
+include_directories(../superpmi-shared)
+
+set(MCS_SOURCES
+ commandline.cpp
+ mcs.cpp
+ verbasmdump.cpp
+ verbconcat.cpp
+ verbdump.cpp
+ verbdumpmap.cpp
+ verbdumptoc.cpp
+ verbfracture.cpp
+ verbildump.cpp
+ verbinteg.cpp
+ verbmerge.cpp
+ verbremovedup.cpp
+ verbsmarty.cpp
+ verbstat.cpp
+ verbstrip.cpp
+ verbtoc.cpp
+ ../superpmi-shared/asmdumper.cpp
+ ../superpmi-shared/callutils.cpp
+ ../superpmi-shared/compileresult.cpp
+ ../superpmi-shared/errorhandling.cpp
+ ../superpmi-shared/logging.cpp
+ ../superpmi-shared/mclist.cpp
+ ../superpmi-shared/methodcontext.cpp
+ ../superpmi-shared/methodcontextiterator.cpp
+ ../superpmi-shared/methodcontextreader.cpp
+ ../superpmi-shared/simpletimer.cpp
+ ../superpmi-shared/spmiutil.cpp
+ ../superpmi-shared/tocfile.cpp
+ ../superpmi-shared/typeutils.cpp
+)
+
+add_precompiled_header(
+ standardpch.h
+ ../superpmi-shared/standardpch.cpp
+ MCS_SOURCES
+)
+
+add_executable(mcs
+ ${MCS_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(mcs
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(mcs
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/mcs.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS mcs DESTINATION .)
diff --git a/src/ToolBox/superpmi/mcs/commandline.cpp b/src/ToolBox/superpmi/mcs/commandline.cpp
new file mode 100644
index 0000000000..ea6a4a3e72
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/commandline.cpp
@@ -0,0 +1,581 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.cpp - tiny very specific command line parser
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "commandline.h"
+#include "logging.h"
+#include "mclist.h"
+
+void CommandLine::DumpHelp(const char* program)
+{
+ printf("MCS is a utility for examining and manipulating SuperPMI MC files.\n");
+ printf("\n");
+ printf("Usage: %s [options] {verb} {verb options}", program);
+ printf("\n");
+ printf("Options:\n");
+ printf("\n");
+ printf(" -v[erbosity] messagetypes\n");
+ printf(" Controls which types of messages MCS logs. Specify a string of\n");
+ printf(" characters representing message categories to enable, where:\n");
+ printf(" e - errors (internal fatal errors that are non-recoverable)\n");
+ printf(" w - warnings (internal conditions that are unusual, but not serious)\n");
+ printf(" m - missing (failures due to missing JIT-EE interface details)\n");
+ printf(" n - information (notifications/summaries, e.g. 'Loaded 42, Saved 23')\n");
+ printf(" v - verbose (status messages, e.g. 'Jit startup took 151.12ms')\n");
+ printf(" d - debug (lots of detailed output)\n");
+ printf(" a - all (enable all message types; overrides other enable message types)\n");
+ printf(" q - quiet (disable all output; overrides all others)\n");
+ printf(" e.g. '-v ew' only writes error and warning messages to the console.\n");
+ printf(" 'q' takes precedence over any other message type specified.\n");
+ printf(" Default set of messages enabled is 'ewmnv'.\n");
+ printf("\n");
+ printf(" -writeLogFile logfile\n");
+ printf(" Write log messages to the specified file.\n");
+ printf("\n");
+ printf("Verbs:\n");
+ printf("\n");
+ printf(" -ASMDump {optional range} inputfile outputfile\n");
+ printf(" Dump out the asm file for each input methodContext.\n");
+ printf(" inputfile is read and output is written to outputfile.\n");
+ printf(" e.g. -ASMDump a.mc a.asm\n");
+ printf("\n");
+ printf(" -concat file1 file2\n");
+ printf(" Concatenate two files without regard to internal formatting.\n");
+ printf(" file2 is appended to file1.\n");
+ printf(" e.g. -concat a.mch b.mch\n");
+ printf("\n");
+ printf(" -copy range file1 file2\n");
+ printf(" Copy methodContext numbers in range from file1 to file2.\n");
+ printf(" file1 is read and file2 is written\n");
+ printf(" e.g. -copy a.mch b.mch\n");
+ printf("\n");
+ printf(" -dump {optional range} inputfile\n");
+ printf(" Dump details for each methodContext\n");
+ printf(" e.g. -dump a.mc\n");
+ printf("\n");
+ printf(" -dumpMap inputfile\n");
+ printf(" Dump a map from MC index to function name to the console, in CSV format\n");
+ printf(" e.g. -dumpMap a.mc\n");
+ printf("\n");
+ printf(" -dumpToc inputfile\n");
+ printf(" Dump a TOC file\n");
+ printf(" e.g. -dumpToc a.mct\n");
+ printf("\n");
+ printf(" -fracture range inputfile outputfile\n");
+ printf(" Break the input file into chunks sized by range.\n");
+ printf(" If '-thin' is also passed, CompileResults are stripped from the input file when written.\n");
+ printf(" e.g. '-fracture 3 a.mch b-' leads to b-0.mch, b-1.mch, etc., with 3 mc's in each file.\n");
+ printf("\n");
+ printf(" -ildump {optional range} inputfile\n");
+ printf(" Dump raw IL for each methodContext\n");
+ printf(" e.g. -ildump a.mc\n");
+ printf("\n");
+ printf(" -integ inputfile\n");
+ printf(" Check the integrity of each methodContext\n");
+ printf(" e.g. -integ a.mc\n");
+ printf("\n");
+ printf(" -merge outputfile pattern\n");
+ printf(" Merge all the input files matching the pattern.\n");
+ printf(" e.g. -merge a.mch *.mc\n");
+ printf(" e.g. -merge a.mch c:\\foo\\bar\\*.mc\n");
+ printf(" e.g. -merge a.mch relpath\\*.mc\n");
+ printf(" e.g. -merge a.mch .\n");
+ printf(" e.g. -merge a.mch onedir\n");
+ printf("\n");
+ printf(" -merge outputfile pattern -recursive\n");
+ printf(" Merge all the input files matching the pattern, in the specified and all child directories.\n");
+ printf(" e.g. -merge a.mch *.mc -recursive\n");
+ printf("\n");
+ printf(" -removeDup inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" e.g. -removeDup a.mc b.mc\n");
+ printf("\n");
+ printf(" -removeDup -legacy inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" Comparisons are performed using the legacy method and may take much longer\n");
+ printf(" e.g. -removeDup -legacy a.mc b.mc\n");
+ printf("\n");
+ printf(" -removeDup -thin inputfile outputfile\n");
+ printf(" Copy methodContexts from inputfile to outputfile, skipping duplicates.\n");
+ printf(" CompileResults are stripped from the input file when written.\n");
+ printf(" e.g. -removeDup -thin a.mc b.mc\n");
+ printf(" e.g. -removeDup -legacy -thin a.mc b.mc\n");
+ printf("\n");
+ printf(" -smarty range inputfile outputfile\n");
+ printf(" Write smarty Test IDs of the range from inputfile to outputfile.\n");
+ printf(" e.g. -smarty 2 a.mc b.mc\n");
+ printf("\n");
+ printf(" -stat {optional range} inputfile outputfile\n");
+ printf(" Report various statistics per method context.\n");
+ printf(" inputfile is read and statistics are written into outputfile\n");
+ printf(" e.g. -stat a.mc a.csv\n");
+ printf("\n");
+ printf(" -strip range inputfile outputfile\n");
+ printf(" Copy method contexts from one file to another, skipping ranged items.\n");
+ printf(" inputfile is read and records not in range are written to outputfile.\n");
+ printf(" e.g. -strip 2 a.mc b.mc\n");
+ printf("\n");
+ printf(" -toc inputfile\n");
+ printf(" Create a Table of Contents file for inputfile to allow better random access\n");
+ printf(" to the mch file.\n");
+ printf(" e.g. '-toc a.mch' creates a.mch.mct\n");
+ printf("\n");
+ printf("Range descriptions are either a single number, or a text file with .mcl extension\n");
+ printf("containing a sorted list of line delimited numbers.\n");
+ printf(" e.g. -strip 2 a.mc b.mc\n");
+ printf(" e.g. -strip list.mcl a.mc b.mc\n");
+ printf("\n");
+ printf("Note: Inputs are case insensitive.\n");
+}
+
+//Assumption: All inputs are initialized to default or real value. we'll just set the stuff in what we see on the command line.
+//Assumption: Single byte names are passed in.. mb stuff doesnt cause an obvious problem... but it might have issues...
+//Assumption: Values larger than 2^31 aren't expressible from the commandline.... (atoi) Unless you pass in negatives.. :-|
+bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
+{
+ size_t argLen = 0;
+ size_t tempLen = 0;
+
+ bool foundVerb = false;
+ bool foundFile1 = false;
+ bool foundFile2 = false;
+
+ if (argc == 1) //Print help when no args are passed
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ for (int i = 1; i<argc; i++)
+ {
+ bool isASwitch = (argv[i][0] == '-');
+#ifndef FEATURE_PAL
+ if (argv[i][0] == '/') // Also accept "/" on Windows
+ {
+ isASwitch = true;
+ }
+#endif // !FEATURE_PAL
+
+ //Process a switch
+ if (isASwitch)
+ {
+ argLen = strlen(argv[i]);
+
+ if (argLen >1)
+ argLen--; //adjust for leading switch
+ else
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if ((_strnicmp(&argv[i][1], "help", argLen) == 0) ||
+ (_strnicmp(&argv[i][1], "?", argLen) == 0))
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+ else if ((_strnicmp(&argv[i][1], "ASMDump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionASMDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "concat", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionConcat = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "copy", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionCopy = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "dump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "fracture", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionFracture = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "dumpmap", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDumpMap = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "dumptoc", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionDumpToc = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "ildump", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionILDump = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "merge", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionMerge = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "recursive", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ o->recursive = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "toc", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionTOC = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "input", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processInput:
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ printf("ERROR: CommandLine::Parse() Arg '%s' is invalid, name of file missing.\n", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (foundFile1 == false)
+ {
+ o->nameOfFile1 = new char[tempLen + 1];
+ strcpy_s(o->nameOfFile1, tempLen + 1, argv[i]);
+ foundFile1 = true;
+ }
+ else if (foundFile2 == false)
+ {
+ o->nameOfFile2 = new char[tempLen + 1];
+ strcpy_s(o->nameOfFile2, tempLen + 1, argv[i]);
+ foundFile2 = true;
+ }
+ else
+ {
+ printf("ERROR: CommandLine::Parse() Arg '%s' is invalid, too many files given.\n", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ else if ((_strnicmp(&argv[i][1], "integ", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionInteg = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "mcl", argLen) == 0))
+ {
+ if (i + 1 >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processMCL:
+ i++;
+ processMCL2:
+
+ bool isValidList = MCList::processArgAsMCL(argv[i], &o->indexCount, &o->indexes);
+ if (!isValidList)
+ i--;
+ }
+ else if ((_strnicmp(&argv[i][1], "removeDup", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionRemoveDup = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "stat", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionStat = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "strip", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ foundVerb = true;
+ o->actionStrip = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "thin", argLen) == 0))
+ {
+ o->stripCR = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "legacy", argLen) == 0))
+ {
+ o->legacyCompare = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "smarty", argLen) == 0))
+ {
+ tempLen = strlen(argv[i]);
+ o->actionSmarty = true;
+ foundVerb = true;
+ if (i + 1 < argc) //Peek to see if we have an mcl file or an integer next
+ goto processMCL;
+ }
+ else if ((_strnicmp(&argv[i][1], "verbosity", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ Logger::SetLogLevel(Logger::ParseLogLevelString(argv[i]));
+ }
+ else if ((_strnicmp(&argv[i][1], "writeLogFile", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ Logger::OpenLogFile(argv[i]);
+ }
+ else
+ {
+ LogError("CommandLine::Parse() - Unknown verb '%s'", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ }
+ //Process an input filename
+ else
+ {
+ char *lastdot = strrchr(argv[i], '.');
+ if (lastdot != nullptr)
+ {
+ if (_stricmp(lastdot, ".mcl") == 0)
+ goto processMCL2;
+ }
+ goto processInput;
+ }
+ }
+
+ if (o->recursive)
+ {
+ if (!o->actionMerge)
+ {
+ LogError("CommandLine::Parse() '-recursive' requires -merge.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+
+ if (o->actionASMDump)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -ASMDump needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionConcat)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-concat' needs two input files (second will be used as output).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionMerge)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-merge' needs an output file (the first) and a file pattern (the second).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionCopy)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-copy' needs one input and one output.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -copy requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionDump)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dump' needs one input file, but didn't see one.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionFracture)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-fracture' needs one input and one output.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -fracture requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount > 1)
+ {
+ LogWarning("CommandLine::Parse() -fracture found multiple ranges, we'll use the first one.");
+ }
+ return true;
+ }
+ if (o->actionDumpMap)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dumpMap' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionDumpToc)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-dumpToc' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionILDump)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-ildump' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionTOC)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-toc' needs one input.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionInteg)
+ {
+ if (!foundFile1)
+ {
+ LogError("CommandLine::Parse() '-integ' needs one input file, but didn't see one.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionRemoveDup)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -removeDup needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionStat)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-stat' needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionStrip)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() -strip needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount == 0)
+ {
+ LogError("CommandLine::Parse() -strip requires a range.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+ if (o->actionSmarty)
+ {
+ if ((!foundFile1) || (!foundFile2))
+ {
+ LogError("CommandLine::Parse() '-smarty' needs one input file and one output file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+ }
+
+ DumpHelp(argv[0]);
+ return false;
+}
diff --git a/src/ToolBox/superpmi/mcs/commandline.h b/src/ToolBox/superpmi/mcs/commandline.h
new file mode 100644
index 0000000000..a04969696b
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/commandline.h
@@ -0,0 +1,76 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.h - tiny very specific command line parser
+//----------------------------------------------------------
+#ifndef _CommandLine
+#define _CommandLine
+
+class CommandLine
+{
+public:
+
+ class Options
+ {
+ public:
+ Options() :
+ actionASMDump(false),
+ actionConcat(false),
+ actionCopy(false),
+ actionDump(false),
+ actionDumpMap(false),
+ actionDumpToc(false),
+ actionFracture(false),
+ actionILDump(false),
+ actionInteg(false),
+ actionMerge(false),
+ actionRemoveDup(false),
+ actionSmarty(false),
+ actionStat(false),
+ actionStrip(false),
+ actionTOC(false),
+ legacyCompare(false),
+ recursive(false),
+ stripCR(false),
+ nameOfFile1(nullptr),
+ nameOfFile2(nullptr),
+ nameOfFile3(nullptr),
+ indexCount(-1),
+ indexes(nullptr)
+ {
+ }
+
+ bool actionASMDump;
+ bool actionConcat;
+ bool actionCopy;
+ bool actionDump;
+ bool actionDumpMap;
+ bool actionDumpToc;
+ bool actionFracture;
+ bool actionILDump;
+ bool actionInteg;
+ bool actionMerge;
+ bool actionRemoveDup;
+ bool actionSmarty;
+ bool actionStat;
+ bool actionStrip;
+ bool actionTOC;
+ bool legacyCompare;
+ bool recursive;
+ bool stripCR;
+ char* nameOfFile1;
+ char* nameOfFile2;
+ char* nameOfFile3;
+ int indexCount;
+ int* indexes;
+ };
+
+ static bool Parse(int argc, char* argv[], /* OUT */ Options* o);
+
+private:
+ static void DumpHelp(const char* program);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/mcs.cpp b/src/ToolBox/superpmi/mcs/mcs.cpp
new file mode 100644
index 0000000000..d2debdd90f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/mcs.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "mcs.h"
+#include "commandline.h"
+#include "verbasmdump.h"
+#include "verbinteg.h"
+#include "verbdump.h"
+#include "verbfracture.h"
+#include "verbdumpmap.h"
+#include "verbdumptoc.h"
+#include "verbildump.h"
+#include "verbtoc.h"
+#include "verbremovedup.h"
+#include "verbstat.h"
+#include "verbconcat.h"
+#include "verbmerge.h"
+#include "verbstrip.h"
+#include "verbsmarty.h"
+#include "logging.h"
+
+int __cdecl main(int argc, char* argv[])
+{
+#ifdef FEATURE_PAL
+ if (0 != PAL_Initialize(argc, argv))
+ {
+ fprintf(stderr, "Error: Fail to PAL_Initialize\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+
+ CommandLine::Options o;
+ if(!CommandLine::Parse(argc, argv, &o))
+ {
+ return -1;
+ }
+
+ //execute the chosen command.
+ int exitCode = 0;
+ if (o.actionASMDump)
+ {
+ exitCode = verbASMDump::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+ if (o.actionConcat)
+ {
+ exitCode = verbConcat::DoWork(o.nameOfFile1, o.nameOfFile2);
+ }
+ if (o.actionMerge)
+ {
+ exitCode = verbMerge::DoWork(o.nameOfFile1, o.nameOfFile2, o.recursive);
+ }
+ if (o.actionCopy)
+ {
+ exitCode = verbStrip::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, false, o.stripCR);
+ }
+ if (o.actionDump)
+ {
+ exitCode = verbDump::DoWork(o.nameOfFile1, o.indexCount, o.indexes);
+ }
+ if (o.actionFracture)
+ {
+ exitCode = verbFracture::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, o.stripCR);
+ }
+ if (o.actionDumpMap)
+ {
+ exitCode = verbDumpMap::DoWork(o.nameOfFile1);
+ }
+ if (o.actionDumpToc)
+ {
+ exitCode = verbDumpToc::DoWork(o.nameOfFile1);
+ }
+ if (o.actionILDump)
+ {
+ exitCode = verbILDump::DoWork(o.nameOfFile1, o.indexCount, o.indexes);
+ }
+ if (o.actionInteg)
+ {
+ exitCode = verbInteg::DoWork(o.nameOfFile1);
+ }
+ if (o.actionRemoveDup)
+ {
+ exitCode = verbRemoveDup::DoWork(o.nameOfFile1, o.nameOfFile2, o.stripCR, o.legacyCompare);
+ }
+ if (o.actionStat)
+ {
+ exitCode = verbStat::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+ if (o.actionStrip)
+ {
+ exitCode = verbStrip::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes, true, o.stripCR);
+ }
+ if (o.actionTOC)
+ {
+ exitCode = verbTOC::DoWork(o.nameOfFile1);
+ }
+ if (o.actionSmarty)
+ {
+ exitCode = verbSmarty::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
+ }
+
+ Logger::Shutdown();
+ return exitCode;
+}
diff --git a/src/ToolBox/superpmi/mcs/mcs.h b/src/ToolBox/superpmi/mcs/mcs.h
new file mode 100644
index 0000000000..3eb19502db
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/mcs.h
@@ -0,0 +1,9 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _MCS
+#define _MCS
+
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbasmdump.cpp b/src/ToolBox/superpmi/mcs/verbasmdump.cpp
new file mode 100644
index 0000000000..3f018b3a45
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbasmdump.cpp
@@ -0,0 +1,68 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbasmdump.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "asmdumper.h"
+#include "errorhandling.h"
+
+#define BUFFER_SIZE 0xFFFFFF
+
+int verbASMDump::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Loading from '%s' and writing ASM output into '%s-MC#.asm'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes, true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ char buff[500];
+ sprintf_s(buff, 500, "%s-%d.asm", nameOfOutput, mci.MethodContextNumber());
+
+ HANDLE hFileOut = CreateFileA(buff, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output '%s'. GetLastError()=%u", buff, GetLastError());
+ return -1;
+ }
+
+ if (mc->cr->IsEmpty())
+ {
+ const size_t bufflen = 4096;
+ DWORD bytesWritten;
+ char buff[bufflen];
+ ZeroMemory(buff, bufflen * sizeof(char));
+ int buff_offset = sprintf_s(buff, bufflen, ";;Method context has no compile result");
+ WriteFile(hFileOut, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+ }
+ else
+ {
+ ASMDumper::DumpToFile(hFileOut, mc, mc->cr);
+ }
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ savedCount++;
+ }
+
+ LogInfo("Asm'd %d", savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbasmdump.h b/src/ToolBox/superpmi/mcs/verbasmdump.h
new file mode 100644
index 0000000000..693366ef91
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbasmdump.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbASMDump.h - verb that
+//----------------------------------------------------------
+#ifndef _verbASMDump
+#define _verbASMDump
+
+class verbASMDump
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes);
+};
+#endif
+
+
diff --git a/src/ToolBox/superpmi/mcs/verbconcat.cpp b/src/ToolBox/superpmi/mcs/verbconcat.cpp
new file mode 100644
index 0000000000..36620f11b2
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbconcat.cpp
@@ -0,0 +1,99 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbconcat.h"
+#include "simpletimer.h"
+#include "logging.h"
+
+#define BUFFER_SIZE 0xFFFFFF
+
+int verbConcat::DoWork(const char *nameOfFile1, const char *nameOfFile2)
+{
+ SimpleTimer st1;
+
+ LogVerbose("Concatenating '%s'+'%s' into %s", nameOfFile1, nameOfFile2, nameOfFile1);
+
+ LARGE_INTEGER DataTemp1;
+ LARGE_INTEGER DataTemp2;
+
+ HANDLE hFileIn1 = CreateFileA(nameOfFile1, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileIn1 == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfFile1, GetLastError());
+ return -1;
+ }
+ if(GetFileSizeEx(hFileIn1, &DataTemp1)==0)
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LONG highDWORD = 0;
+ DWORD dwPtr = SetFilePointer(hFileIn1, 0, &highDWORD, FILE_END);
+ if (dwPtr == INVALID_SET_FILE_POINTER)
+ {
+ LogError("Failed to SetFilePointer on input 1 '%s'. GetLastError()=%u", nameOfFile1, GetLastError());
+ return -1;
+ }
+
+ HANDLE hFileIn2 = CreateFileA(nameOfFile2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileIn2 == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 2 '%s'. GetLastError()=%u", nameOfFile2, GetLastError());
+ return -1;
+ }
+ if(GetFileSizeEx(hFileIn2, &DataTemp2)==0)
+ {
+ LogError("2nd GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ unsigned char* buffer = new unsigned char[BUFFER_SIZE];
+
+ st1.Start();
+ for(LONGLONG offset = 0; offset < DataTemp2.QuadPart; offset += BUFFER_SIZE)
+ {
+ DWORD bytesRead = -1;
+ BOOL res = ReadFile(hFileIn2, buffer, BUFFER_SIZE, &bytesRead, nullptr);
+ if(res == 0)
+ {
+ LogError("Failed to read '%s' from offset %lld. GetLastError()=%u", nameOfFile2, offset, GetLastError());
+ return -1;
+ }
+ DWORD bytesWritten = -1;
+ BOOL res2 = WriteFile(hFileIn1, buffer, bytesRead, &bytesWritten, nullptr);
+ if(res2 == 0)
+ {
+ LogError("Failed to write '%s' at offset %lld. GetLastError()=%u", nameOfFile1, offset, GetLastError());
+ return -1;
+ }
+ if(bytesRead!=bytesWritten)
+ {
+ LogError("Failed to read/write matching bytes %u!=%u", bytesRead, bytesWritten);
+ return -1;
+ }
+ }
+ st1.Stop();
+
+ delete[] buffer;
+
+ if(CloseHandle(hFileIn1)==0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ if(CloseHandle(hFileIn2)==0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Read/Wrote %lld MB @ %4.2f MB/s.\n",
+ DataTemp2.QuadPart/(1000*1000),
+ (((double)DataTemp2.QuadPart)/(1000*1000))/st1.GetSeconds()); //yes yes.. http://en.wikipedia.org/wiki/Megabyte_per_second#Megabyte_per_second
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbconcat.h b/src/ToolBox/superpmi/mcs/verbconcat.h
new file mode 100644
index 0000000000..26e55857bd
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbconcat.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbConcat.h - verb that concatenates two files
+//----------------------------------------------------------
+#ifndef _verbConcat
+#define _verbConcat
+
+class verbConcat
+{
+public:
+ static int DoWork(const char *nameOfFile1, const char *nameOfFile2);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdump.cpp b/src/ToolBox/superpmi/mcs/verbdump.cpp
new file mode 100644
index 0000000000..290cbdbac5
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdump.cpp
@@ -0,0 +1,36 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbdump.h"
+#include "logging.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+
+int verbDump::DoWork(const char *nameOfInput, int indexCount, const int *indexes)
+{
+ LogVerbose("Dumping '%s' to console", nameOfInput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int dumpedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ mc->dumpToConsole(mci.MethodContextNumber());
+ dumpedCount++;
+ }
+
+ LogVerbose("Dumped %d methodContexts", dumpedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdump.h b/src/ToolBox/superpmi/mcs/verbdump.h
new file mode 100644
index 0000000000..32f4b0d661
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdump.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDump.h - verb that Dumps a MC file
+//----------------------------------------------------------
+#ifndef _verbDump
+#define _verbDump
+
+class verbDump
+{
+public:
+ static int DoWork(const char *nameofInput, int indexCount, const int *indexes);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdumpmap.cpp b/src/ToolBox/superpmi/mcs/verbdumpmap.cpp
new file mode 100644
index 0000000000..3fc8268f5f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumpmap.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "verbdumpmap.h"
+#include "verbildump.h"
+
+// Dump the CSV format header for all the columns we're going to dump.
+void DumpMapHeader()
+{
+ printf("index,");
+ // printf("process name,");
+ printf("method name,");
+ printf("full signature\n");
+}
+
+void DumpMap(int index, MethodContext *mc)
+{
+ CORINFO_METHOD_INFO cmi;
+ unsigned int flags = 0;
+
+ mc->repCompileMethod(&cmi, &flags);
+
+ const char *moduleName = nullptr;
+ const char *methodName = mc->repGetMethodName(cmi.ftn, &moduleName);
+ const char *className = mc->repGetClassName(mc->repGetMethodClass(cmi.ftn));
+
+ printf("%d,", index);
+ // printf("\"%s\",", mc->cr->repProcessName());
+ printf("%s:%s,", className, methodName);
+
+ // Also, dump the full method signature
+ printf("\"");
+ DumpAttributeToConsoleBare(mc->repGetMethodAttribs(cmi.ftn));
+ DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass);
+ printf(" %s(", methodName);
+ DumpSigToConsoleBare(mc, &cmi.args);
+ printf(")\"\n");
+}
+
+int verbDumpMap::DoWork(const char *nameOfInput)
+{
+ MethodContextIterator mci;
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ DumpMapHeader();
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ DumpMap(mci.MethodContextNumber(), mc);
+ }
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdumpmap.h b/src/ToolBox/superpmi/mcs/verbdumpmap.h
new file mode 100644
index 0000000000..129b8d8440
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumpmap.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDumpMap.h - verb that dumps a map of index to function name for an MC file
+//----------------------------------------------------------
+#ifndef _verbDumpMap
+#define _verbDumpMap
+
+class verbDumpMap
+{
+public:
+ static int DoWork(const char *nameofInput);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbdumptoc.cpp b/src/ToolBox/superpmi/mcs/verbdumptoc.cpp
new file mode 100644
index 0000000000..2837f0bf3e
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumptoc.cpp
@@ -0,0 +1,32 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbdumptoc.h"
+#include "methodcontext.h"
+#include "tocfile.h"
+#include "runtimedetails.h"
+
+int verbDumpToc::DoWork(const char *nameOfInput)
+{
+ TOCFile tf;
+
+ tf.LoadToc(nameOfInput, false);
+
+ for (size_t i = 0; i < tf.GetTocCount(); i++)
+ {
+ const TOCElement* te = tf.GetElementPtr(i);
+ printf("%4u: %016llX ", te->Number, te->Offset);
+
+ for (int j = 0; j < sizeof(te->Hash); j++)
+ {
+ printf("%02x ", te->Hash[j]);
+ }
+
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbdumptoc.h b/src/ToolBox/superpmi/mcs/verbdumptoc.h
new file mode 100644
index 0000000000..c2ea880b52
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbdumptoc.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbDumpToc.h - verb that dumps a MCH Table of Context file
+//----------------------------------------------------------
+#ifndef _verbDumpToc
+#define _verbDumpToc
+
+class verbDumpToc
+{
+public:
+ static int DoWork(const char *nameOfInput1);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbfracture.cpp b/src/ToolBox/superpmi/mcs/verbfracture.cpp
new file mode 100644
index 0000000000..bcc6d04e1d
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbfracture.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbfracture.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+#include "logging.h"
+
+int verbFracture::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR)
+{
+ int rangeSize = indexes[0];
+
+ LogVerbose("Reading from '%s' copying %d MethodContexts files into each output file of '%s'", nameOfInput, rangeSize, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int fileCount = 0;
+ char fileName[512];
+
+ HANDLE hFileOut = INVALID_HANDLE_VALUE;
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ if ((hFileOut == INVALID_HANDLE_VALUE) || (((mci.MethodContextNumber() - 1) % rangeSize) == 0))
+ {
+ if (hFileOut != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("1st CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ hFileOut = INVALID_HANDLE_VALUE;
+ }
+ sprintf_s(fileName, 512, "%s-%0*d.mch", nameOfOutput, 5, fileCount++);
+ hFileOut = CreateFileA(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return -1;
+ }
+ }
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ }
+
+ if (hFileOut != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ }
+
+ LogInfo("Output fileCount %d", fileCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbfracture.h b/src/ToolBox/superpmi/mcs/verbfracture.h
new file mode 100644
index 0000000000..feddbe1908
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbfracture.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbFracture.h - verb that copies N items into each child file.
+//----------------------------------------------------------
+#ifndef _verbFracture
+#define _verbFracture
+
+class verbFracture
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbildump.cpp b/src/ToolBox/superpmi/mcs/verbildump.cpp
new file mode 100644
index 0000000000..23b68da003
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbildump.cpp
@@ -0,0 +1,625 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "verbildump.h"
+
+void DumpPrimToConsoleBare(MethodContext *mc, CorInfoType prim, DWORDLONG classHandle)
+{
+ switch(prim)
+ {
+ case CORINFO_TYPE_VOID: printf("void"); return;
+ case CORINFO_TYPE_BOOL: printf("bool"); return;
+ case CORINFO_TYPE_CHAR: printf("char"); return;
+ case CORINFO_TYPE_BYTE: printf("int8"); return;
+ case CORINFO_TYPE_UBYTE: printf("unsigned int8"); return;
+ case CORINFO_TYPE_SHORT: printf("int16"); return;
+ case CORINFO_TYPE_USHORT: printf("unsigned int16"); return;
+ case CORINFO_TYPE_INT: printf("int32"); return;
+ case CORINFO_TYPE_UINT: printf("unsigned int32"); return;
+ case CORINFO_TYPE_LONG: printf("int64"); return;
+ case CORINFO_TYPE_ULONG: printf("unsigned int64"); return;
+ case CORINFO_TYPE_NATIVEINT: printf("native int"); return;
+ case CORINFO_TYPE_NATIVEUINT: printf("native unsigned int"); return;
+ case CORINFO_TYPE_FLOAT: printf("float32"); return;
+ case CORINFO_TYPE_DOUBLE: printf("float64"); return;
+// case CORINFO_TYPE_STRING: printf("string"); return;
+ case CORINFO_TYPE_PTR: printf("ptr"); return;
+ case CORINFO_TYPE_BYREF: printf("byref"); return;
+ case CORINFO_TYPE_VALUECLASS: printf("valueclass %s", mc->repGetClassName((CORINFO_CLASS_HANDLE)classHandle)); return;
+ case CORINFO_TYPE_CLASS: printf("class %s", mc->repGetClassName((CORINFO_CLASS_HANDLE)classHandle)); return;
+ case CORINFO_TYPE_REFANY: printf("refany"); return;
+ case CORINFO_TYPE_VAR: printf("var"); return;
+ default:
+ LogWarning("unknown type in PrimToString(0x%x)",prim);
+ __debugbreak();
+ return;
+ }
+}
+void DumpSigToConsoleBare(MethodContext *mc, CORINFO_SIG_INFO *pSig)
+{
+ CORINFO_ARG_LIST_HANDLE currentItem = pSig->args;
+ DWORD exceptionCode;
+
+ for(int i=0; i < (int)pSig->numArgs; i++)
+ {
+ DWORDLONG dl;
+ CorInfoTypeWithMod type = mc->repGetArgType(pSig, currentItem, (CORINFO_CLASS_HANDLE *)&dl, &exceptionCode);
+ CorInfoType cit = strip(type);
+ if (cit == CORINFO_TYPE_CLASS)
+ dl = (DWORDLONG)mc->repGetArgClass(pSig, currentItem, &exceptionCode);
+ if ((type & CORINFO_TYPE_MOD_PINNED) == CORINFO_TYPE_MOD_PINNED)
+ printf("pinned ");
+ DumpPrimToConsoleBare(mc, cit, dl);
+ currentItem = mc->repGetArgNext(currentItem);
+ if (i + 1 < (int)pSig->numArgs)
+ printf(", ");
+ }
+}
+
+
+void DumpILToConsoleBare(unsigned char *ilCode, int len)
+{
+ int i,j,k;
+ for(i=0;i<len;i++)
+ {
+ printf("IL_%04x: ",i);
+ switch(ilCode[i])
+ {
+ case 0x00:printf("nop");continue;
+ case 0x01:printf("break");continue;
+ case 0x02:printf("ldarg.0");continue;
+ case 0x03:printf("ldarg.1");continue;
+ case 0x04:printf("ldarg.2");continue;
+ case 0x05:printf("ldarg.3");continue;
+ case 0x06:printf("ldloc.0");continue;
+ case 0x07:printf("ldloc.1");continue;
+ case 0x08:printf("ldloc.2");continue;
+ case 0x09:printf("ldloc.3");continue;
+ case 0x0a:printf("stloc.0");continue;
+ case 0x0b:printf("stloc.1");continue;
+ case 0x0c:printf("stloc.2");continue;
+ case 0x0d:printf("stloc.3");continue;
+ case 0x0e: //ldarg.s X
+ printf("ldarg.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x0f: //ldarga.s X
+ printf("ldarga.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x10: //starg.s X
+ printf("starg.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x11: //ldloc.s X
+ printf("ldloc.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x12: //ldloca.s X
+ printf("ldloca.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x13: //stloc.s X
+ printf("stloc.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x14:printf("ldnull");continue;
+ case 0x15:printf("ldc.i4.m1");continue;
+ case 0x16:printf("ldc.i4.0");continue;
+ case 0x17:printf("ldc.i4.1");continue;
+ case 0x18:printf("ldc.i4.2");continue;
+ case 0x19:printf("ldc.i4.3");continue;
+ case 0x1a:printf("ldc.i4.4");continue;
+ case 0x1b:printf("ldc.i4.5");continue;
+ case 0x1c:printf("ldc.i4.6");continue;
+ case 0x1d:printf("ldc.i4.7");continue;
+ case 0x1e:printf("ldc.i4.8");continue;
+ case 0x1f: //ldc.i4.s X
+ printf("ldc.i4.s 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x20: //ldc.i4 XXXX
+ printf("ldc.i4 0x%02x%02x%02x%02x", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x21: //ldc.i8 XXXXXXXX
+ printf("ldc.i8 0x%02x%02x%02x%02x%02x%02x%02x%02x", ilCode[i+8], ilCode[i+7], ilCode[i+6], ilCode[i+5], ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=8;
+ continue;
+ case 0x22: //ldc.r4 XXXX
+ printf("ldc.r4 float32(0x%02x%02x%02x%02x)", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x23: //ldc.r8 XXXXXXXX
+ printf("ldc.r8 float64(0x%02x%02x%02x%02x%02x%02x%02x%02x)", ilCode[i+8], ilCode[i+7], ilCode[i+6], ilCode[i+5], ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=8;
+ continue;
+ case 0x25:printf("dup");continue;
+ case 0x26:printf("pop");continue;
+ case 0x27: //JMP <T>
+ printf("jmp <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x28: //call <T>
+ printf("call <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x29: //calli <T>
+ printf("calli <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x2a:printf("ret");continue;
+ case 0x2b: //br.s X
+ printf("br.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2c: //brfalse.s X
+ printf("brfalse.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2d: //brtrue.s X
+ printf("brtrue.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2e: //beq.s X
+ printf("beq.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x2f: //bgt.s X
+ printf("bgt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x30: //bgt.s X
+ printf("bgt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x31: //ble.s X
+ printf("ble.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x32: //blt.s X
+ printf("blt.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x33: //bne.un.s X
+ printf("bne.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x34: //bge.un.s X
+ printf("bge.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x35: //bgt.un.s X
+ printf("bgt.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x36: //ble.un.s X
+ printf("ble.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x37: //blt.un.s X
+ printf("blt.un.s IL_%04x", i+2+ilCode[i+1]);;
+ i+=1;
+ continue;
+ case 0x38: //br XXXX
+ printf("br IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x39: //brfalse XXXX
+ printf("brfalse IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3a: //brtrue XXXX
+ printf("brtrue IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3b: //beq XXXX
+ printf("beq IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3c: //bgt XXXX
+ printf("bgt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3d: //bgt XXXX
+ printf("bgt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3e: //ble XXXX
+ printf("ble IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x3f: //blt XXXX
+ printf("blt IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x40: //bne.un XXXX
+ printf("bne.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x41: //bge.un XXXX
+ printf("bge.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x42: //bgt.un XXXX
+ printf("bgt.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x43: //ble.un XXXX
+ printf("ble.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x44: //blt.un XXXX
+ printf("blt.un IL_%04x", i+5+(ilCode[i+4]<<24|ilCode[i+3]<<16|ilCode[i+2]<<8|ilCode[i+1]));
+ i+=4;
+ continue;
+ case 0x45: //switch NNNN NNNN*XXXX
+ printf("switch (0x%02x%02x%02x%02x)", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ k = (ilCode[i+4]<<24)|(ilCode[i+3]<<16)|(ilCode[i+2]<<8)|(ilCode[i+1]<<0);
+ i+=4;
+ for(j=0;j<k;j++)
+ {
+ printf(" <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ }
+ continue;
+ case 0x46:printf("ldind.i1");continue;
+ case 0x47:printf("ldind.u1");continue;
+ case 0x48:printf("ldind.i2");continue;
+ case 0x49:printf("ldind.u2");continue;
+ case 0x4a:printf("ldind.i4");continue;
+ case 0x4b:printf("ldind.u4");continue;
+ case 0x4c:printf("ldind.i8");continue;
+ case 0x4d:printf("ldind.u8");continue;
+ case 0x4e:printf("ldind.r4");continue;
+ case 0x4f:printf("ldind.r8");continue;
+ case 0x50:printf("ldind.ref");continue;
+ case 0x51:printf("stind.ref");continue;
+ case 0x52:printf("stind.i1");continue;
+ case 0x53:printf("stind.i2");continue;
+ case 0x54:printf("stind.i4");continue;
+ case 0x55:printf("stind.i8");continue;
+ case 0x56:printf("stind.r4");continue;
+ case 0x57:printf("stind.r8");continue;
+ case 0x58:printf("add");continue;
+ case 0x59:printf("sub");continue;
+ case 0x5a:printf("mul");continue;
+ case 0x5b:printf("div");continue;
+ case 0x5c:printf("div.un");continue;
+ case 0x5d:printf("rem");continue;
+ case 0x5e:printf("rem.un");continue;
+ case 0x5f:printf("and");continue;
+ case 0x60:printf("or");continue;
+ case 0x61:printf("xor");continue;
+ case 0x62:printf("shl");continue;
+ case 0x63:printf("shr");continue;
+ case 0x64:printf("shr.un");continue;
+ case 0x65:printf("neg");continue;
+ case 0x66:printf("not");continue;
+ case 0x67:printf("conv.i1");continue;
+ case 0x68:printf("conv.i2");continue;
+ case 0x69:printf("conv.i4");continue;
+ case 0x6a:printf("conv.i8");continue;
+ case 0x6b:printf("conv.r4");continue;
+ case 0x6c:printf("conv.r8");continue;
+ case 0x6d:printf("conv.u4");continue;
+ case 0x6e:printf("conv.u8");continue;
+ case 0x6f: //callvirt <T>
+ printf("callvirt <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x70: //cpobj <T>
+ printf("cpobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x71: //ldobj <T>
+ printf("ldobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x72: //ldstr <T>
+ printf("ldstr <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x73: //newobj <T>
+ printf("newobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x74: //castclass <T>
+ printf("castclass <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x75: //isinst <T>
+ printf("isinst <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x76:printf("conv.r.un");continue;
+ case 0x79: //unbox <T>
+ printf("unbox <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7a:printf("throw");continue;
+ case 0x7b: //ldfld <T>
+ printf("ldfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7c: //ldflda <T>
+ printf("ldflda <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7d: //stfld <T>
+ printf("stfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7e: //ldsfld <T>
+ printf("ldsfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x7f: //ldsflda <T>
+ printf("ldsflda <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x80: //stsfld <T>
+ printf("stsfld <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x81: //stobj <T>
+ printf("stobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x82:printf("conv.ovf.i1.un");continue;
+ case 0x83:printf("conv.ovf.i2.un");continue;
+ case 0x84:printf("conv.ovf.i4.un");continue;
+ case 0x85:printf("conv.ovf.i8.un");continue;
+ case 0x86:printf("conv.ovf.u1.un");continue;
+ case 0x87:printf("conv.ovf.u2.un");continue;
+ case 0x88:printf("conv.ovf.u4.un");continue;
+ case 0x89:printf("conv.ovf.u8.un");continue;
+ case 0x8a:printf("conv.ovf.i.un");continue;
+ case 0x8b:printf("conv.ovf.u.un");continue;
+ case 0x8c: //box <T>
+ printf("box <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x8d: //newarr <T>
+ printf("newarr <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x8e:printf("ldlen");continue;
+ case 0x8f:printf("ldelema <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x90:printf("ldelem.i1");continue;
+ case 0x91:printf("ldelem.u1");continue;
+ case 0x92:printf("ldelem.i2");continue;
+ case 0x93:printf("ldelem.u2");continue;
+ case 0x94:printf("ldelem.i4");continue;
+ case 0x95:printf("ldelem.u4");continue;
+ case 0x96:printf("ldelem.i8");continue;
+ case 0x97:printf("ldelem.i");continue;
+ case 0x98:printf("ldelem.r4");continue;
+ case 0x99:printf("ldelem.r8");continue;
+ case 0x9a:printf("ldelem.ref");continue;
+ case 0x9b:printf("stelem.i");continue;
+ case 0x9c:printf("stelem.i1");continue;
+ case 0x9d:printf("stelem.i2");continue;
+ case 0x9e:printf("stelem.i4");continue;
+ case 0x9f:printf("stelem.i8");continue;
+ case 0xa0:printf("stelem.r4");continue;
+ case 0xa1:printf("stelem.r8");continue;
+ case 0xa2:printf("stelem.ref");continue;
+ case 0xa3:printf("stelem <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xa4:printf("stelem <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xa5:printf("unbox.any <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xb3:printf("conv.ovf.i1");continue;
+ case 0xb4:printf("conv.ovf.u1");continue;
+ case 0xb5:printf("conv.ovf.i2");continue;
+ case 0xb6:printf("conv.ovf.u2");continue;
+ case 0xb7:printf("conv.ovf.i4");continue;
+ case 0xb8:printf("conv.ovf.u4");continue;
+ case 0xb9:printf("conv.ovf.i8");continue;
+ case 0xba:printf("conv.ovf.u8");continue;
+ case 0xc2: //refanyval <T>
+ printf("refanyval <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xc3:printf("ckfinite");continue;
+ case 0xc6: //mkrefany <T>
+ printf("mkrefany <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xd0: //ldtoken <T>
+ printf("ldtoken <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xd1:printf("conv.u2");continue;
+ case 0xd2:printf("conv.u1");continue;
+ case 0xd3:printf("conv.i");continue;
+ case 0xd4:printf("conv.ovf.i");continue;
+ case 0xd5:printf("conv.ovf.u");continue;
+ case 0xd6:printf("add.ovf");continue;
+ case 0xd7:printf("add.ovf.un");continue;
+ case 0xd8:printf("mul.ovf");continue;
+ case 0xd9:printf("mul.ovf.un");continue;
+ case 0xda:printf("sub.ovf");continue;
+ case 0xdb:printf("sub.ovf.un");continue;
+ case 0xdc:printf("endfinally");continue;
+ case 0xdd: //leave XXXX
+ printf("leave 0x%02x%02x%02x%02x", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0xde: //leave.s X
+ printf("leave 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0xdf:printf("stind.i");continue;
+ case 0xe0:printf("conv.u");continue;
+ case 0xfe:
+ i++;
+ switch(ilCode[i])
+ {
+ case 0x00:printf("arglist");continue;
+ case 0x01:printf("ceq");continue;
+ case 0x02:printf("cgt");continue;
+ case 0x03:printf("cgt.un");continue;
+ case 0x04:printf("clt");continue;
+ case 0x05:printf("clt.un");continue;
+ case 0x06: //ldftn <T>
+ printf("ldftn <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x07: //ldvirtftn <T>
+ printf("ldvirtftn <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x09: //ldarg XX
+ printf("ldarg 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0a: //ldarga XX
+ printf("ldarga 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0b: //starg XX
+ printf("starg 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0c: //ldloc XX
+ printf("ldloc 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0d: //ldloca XX
+ printf("ldloca 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0e: //stloc XX
+ printf("stloc 0x%02x%02x", ilCode[i+2], ilCode[i+1]);
+ i+=2;
+ continue;
+ case 0x0f:printf("localloc");continue;
+ case 0x11:printf("endfilter");continue;
+ case 0x12: //unaligned X
+ printf("unaligned. 0x%02x", ilCode[i+1]);
+ i+=1;
+ continue;
+ case 0x13:printf("volatile.");continue;
+ case 0x14:printf("tail.");continue;
+ case 0x15: //initobj <T>
+ printf("initobj <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x16://incomplete?
+ printf("constrained. <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x17:printf("cpblk");continue;
+ case 0x18:printf("initblk");continue;
+ case 0x19:printf("no.");continue; //incomplete?
+ case 0x1a:printf("rethrow");continue;
+ case 0x1c: //sizeof <T>
+ printf("sizeof <0x%02x%02x%02x%02x>", ilCode[i+4], ilCode[i+3], ilCode[i+2], ilCode[i+1]);
+ i+=4;
+ continue;
+ case 0x1d:printf("refanytype");continue;
+ default:
+ LogError("unknown ilCode 0xfe%2x at offset %d in MethodGen::PrettyPrint", ilCode[i], i);
+ break;
+ }
+ default:
+ LogError("unknown ilCode 0x%02x at offset %d in MethodGen::PrettyPrint", ilCode[i], i);
+ break;
+ }
+ printf("\n");
+ }
+}
+char * DumpAttributeToConsoleBare(DWORD attribute)
+{
+ const char *s_static = "static";
+ const char *s_dontInline = "$dontInline ";
+ const char *s_constructor = "$constructor";
+ const char *s_cfnw = "$noSecurityWrap";
+
+#define ifPrint(s,t) else if((s&attribute)==s) {printf(t); printf(" ");}
+
+ if(0);
+ ifPrint(CORINFO_FLG_STATIC, s_static)
+ ifPrint(CORINFO_FLG_DONT_INLINE, s_dontInline)
+ ifPrint(CORINFO_FLG_CONSTRUCTOR, s_constructor)
+ ifPrint(CORINFO_FLG_NOSECURITYWRAP, s_cfnw)
+ else
+ {
+ LogError("unknown attribute %x", attribute);
+ __debugbreak();
+ }
+ return nullptr;
+
+#undef ifPrint
+}
+
+void DumpIL(MethodContext *mc)
+{
+ CORINFO_METHOD_INFO cmi;
+ unsigned int flags = 0;
+
+ mc->repCompileMethod(&cmi, &flags);
+
+ const char *moduleName = nullptr;
+ const char *methodName = mc->repGetMethodName(cmi.ftn, &moduleName);
+ const char *className = mc->repGetClassName(mc->repGetMethodClass(cmi.ftn));
+
+ printf("// ProcessName - '%s'\n", mc->cr->repProcessName());
+ printf(".assembly extern mscorlib{}\n");
+ printf(".assembly %s{}\n", moduleName);
+ printf(".class %s\n", className);
+ printf("{\n");
+ printf(" .method ");
+ DumpAttributeToConsoleBare(mc->repGetMethodAttribs(cmi.ftn));
+ DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass);
+ printf(" %s(", methodName);
+ DumpSigToConsoleBare(mc, &cmi.args);
+ printf(")\n");
+ printf(" {\n");
+ printf(" .maxstack %u\n", cmi.maxStack);
+ printf(" .locals%s(", (((cmi.options&CORINFO_OPT_INIT_LOCALS)==CORINFO_OPT_INIT_LOCALS)?" init ":" "));
+ DumpSigToConsoleBare(mc, &cmi.locals);
+ printf(")\n");
+ DumpILToConsoleBare(cmi.ILCode, cmi.ILCodeSize);
+ printf(" }\n");
+ printf("}\n");
+}
+
+int verbILDump::DoWork(const char *nameOfInput, int indexCount, const int *indexes)
+{
+ LogVerbose("// Reading from '%s' dumping raw IL for MC Indexes to console", nameOfInput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int dumpedCount = 0;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ DumpIL(mc);
+ dumpedCount++;
+ }
+
+ LogInfo("// Dumped %d", dumpedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbildump.h b/src/ToolBox/superpmi/mcs/verbildump.h
new file mode 100644
index 0000000000..e947fcf40c
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbildump.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbILDump.h - verb that attempts to dump the raw IL for a MC
+//----------------------------------------------------------
+#ifndef _verbILDump
+#define _verbILDump
+
+#include "methodcontext.h"
+
+class verbILDump
+{
+public:
+ static int DoWork(const char *nameOfInput1, int indexCount, const int *indexes);
+};
+
+void DumpPrimToConsoleBare(MethodContext *mc, CorInfoType prim, DWORDLONG classHandle);
+void DumpSigToConsoleBare(MethodContext *mc, CORINFO_SIG_INFO *pSig);
+char * DumpAttributeToConsoleBare(DWORD attribute);
+
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbinteg.cpp b/src/ToolBox/superpmi/mcs/verbinteg.cpp
new file mode 100644
index 0000000000..9b1057769a
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbinteg.cpp
@@ -0,0 +1,37 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbinteg.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+int verbInteg::DoWork(const char *nameOfInput)
+{
+ LogVerbose("Checking the integrity of '%s'", nameOfInput);
+
+ SimpleTimer st2;
+ st2.Start();
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+ // Nothing to do except load the current one.
+ }
+
+ st2.Stop();
+ LogInfo("Checked the integrity of %d methodContexts at %d per second",
+ mci.MethodContextNumber(), (int)((double)mci.MethodContextNumber() / st2.GetSeconds()));
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbinteg.h b/src/ToolBox/superpmi/mcs/verbinteg.h
new file mode 100644
index 0000000000..d45ea15307
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbinteg.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbInteg.h - verb that checks the integrity of a MC file
+//----------------------------------------------------------
+#ifndef _verbInteg
+#define _verbInteg
+
+class verbInteg
+{
+public:
+ static int DoWork(const char *nameofInput);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbmerge.cpp b/src/ToolBox/superpmi/mcs/verbmerge.cpp
new file mode 100644
index 0000000000..c4acfd8769
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbmerge.cpp
@@ -0,0 +1,470 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbmerge.h"
+#include "simpletimer.h"
+#include "logging.h"
+
+// Do reads/writes in large 256MB chunks.
+#define BUFFER_SIZE 0x10000000
+
+// MergePathStrings: take two file system path components, compose them together, and return the merged pathname string.
+// The caller must delete the returned string with delete[].
+//
+// static
+char* verbMerge::MergePathStrings(const char* dir, const char* file)
+{
+ size_t dirlen = strlen(dir);
+ size_t filelen = strlen(file);
+ size_t newlen = dirlen + 1 /* slash */ + filelen + 1 /* null */;
+ char* newpath = new char[newlen];
+ strcpy(newpath, dir);
+ strcat(newpath, DIRECTORY_SEPARATOR_STR_A);
+ strcat(newpath, file);
+ return newpath;
+}
+
+// AppendFile: append the file named by 'fileName' to the output file referred to by 'hFileOut'. The 'hFileOut'
+// handle is assumed to be open, and the file position is assumed to be at the correct spot for writing, to append.
+//
+// 'buffer' is memory that can be used to do reading/buffering.
+//
+// static
+int verbMerge::AppendFile(HANDLE hFileOut, const char* fileName, unsigned char* buffer, size_t bufferSize)
+{
+ int result = 0; // default to zero == success
+
+ LogInfo("Appending file '%s'", fileName);
+
+ HANDLE hFileIn = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileIn == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return -1;
+ }
+
+ LARGE_INTEGER fileSize;
+ if (GetFileSizeEx(hFileIn, &fileSize) == 0)
+ {
+ LogError("GetFileSizeEx on '%s' failed. GetLastError()=%u", fileName, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+
+ for (LONGLONG offset = 0; offset < fileSize.QuadPart; offset += bufferSize)
+ {
+ DWORD bytesRead = -1;
+ BOOL res = ReadFile(hFileIn, buffer, (DWORD)bufferSize, &bytesRead, nullptr);
+ if (!res)
+ {
+ LogError("Failed to read '%s' from offset %lld. GetLastError()=%u", fileName, offset, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ DWORD bytesWritten = -1;
+ BOOL res2 = WriteFile(hFileOut, buffer, bytesRead, &bytesWritten, nullptr);
+ if (!res2)
+ {
+ LogError("Failed to write output file at offset %lld. GetLastError()=%u", offset, GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ if (bytesRead != bytesWritten)
+ {
+ LogError("Failed to read/write matching bytes %u!=%u", bytesRead, bytesWritten);
+ result = -1;
+ goto CLEAN_UP;
+ }
+ }
+
+CLEAN_UP:
+
+ if (CloseHandle(hFileIn) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ result = -1;
+ }
+
+ return result;
+}
+
+// Return true if this is a directory
+//
+// static
+bool verbMerge::DirectoryFilterDirectories(WIN32_FIND_DATAA* findData)
+{
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ {
+ // It's a directory. See if we want to exclude it because of other reasons, such as:
+ // 1. reparse points: avoid the possibility of loops
+ // 2. system directories
+ // 3. hidden directories
+ // 4. "." or ".."
+
+#ifndef FEATURE_PAL // FILE_ATTRIBUTE_REPARSE_POINT is not defined in the PAL
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
+ return false;
+#endif // !FEATURE_PAL
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0)
+ return false;
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)
+ return false;
+
+ if (strcmp(findData->cFileName, ".") == 0)
+ return false;
+ if (strcmp(findData->cFileName, "..") == 0)
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// Return true if this is a file.
+//
+// static
+bool verbMerge::DirectoryFilterFile(WIN32_FIND_DATAA* findData)
+{
+ if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ // This is not a directory, so it must be a file.
+ return true;
+ }
+
+ return false;
+}
+
+// static
+int __cdecl verbMerge::WIN32_FIND_DATAA_qsort_helper(const void* p1, const void* p2)
+{
+ const WIN32_FIND_DATAA* file1 = (WIN32_FIND_DATAA*)p1;
+ const WIN32_FIND_DATAA* file2 = (WIN32_FIND_DATAA*)p2;
+ return strcmp(file1->cFileName, file2->cFileName);
+}
+
+// Enumerate a directory for the files specified by "searchPattern". For each element in the directory,
+// pass it to the filter function. If the filter returns true, we keep it, otherwise we ignore it. Return
+// an array of information for the files that we kept, sorted by filename.
+//
+// Returns 0 on success, non-zero on failure.
+// If success, fileArray and elemCount are set.
+//
+// static
+int verbMerge::FilterDirectory(const char* searchPattern, DirectoryFilterFunction_t filter, /* out */ WIN32_FIND_DATAA** ppFileArray, int* pElemCount)
+{
+ // First, build up a list, then create an array and sort it after we know how many elements there are.
+ struct findDataList
+ {
+ findDataList(WIN32_FIND_DATAA* newFindData, findDataList* newNext)
+ : findData(*newFindData)
+ , next(newNext)
+ {
+ }
+
+ static void DeleteList(findDataList* root)
+ {
+ for (findDataList* loop = root; loop != nullptr; )
+ {
+ findDataList* tmp = loop;
+ loop = loop->next;
+ delete tmp;
+ }
+ }
+
+ WIN32_FIND_DATAA findData;
+ findDataList* next;
+ };
+
+ WIN32_FIND_DATAA* retArray = nullptr;
+ findDataList* first = nullptr;
+
+ int result = 0; // default to zero == success
+ int elemCount = 0;
+
+ // NOTE: this function only works on Windows 7 and later.
+ WIN32_FIND_DATAA findData;
+ HANDLE hSearch;
+#ifdef FEATURE_PAL
+ // PAL doesn't have FindFirstFileEx(). So just use FindFirstFile(). The only reason we use
+ // the Ex version is potentially better performance (don't populate short name; use large fetch),
+ // not functionality.
+ hSearch = FindFirstFileA(searchPattern, &findData);
+#else // !FEATURE_PAL
+ hSearch = FindFirstFileExA(searchPattern,
+ FindExInfoBasic, // We don't care about the short names
+ &findData,
+ FindExSearchNameMatch, // standard name matching
+ NULL,
+ FIND_FIRST_EX_LARGE_FETCH);
+#endif // !FEATURE_PAL
+
+ if (hSearch == INVALID_HANDLE_VALUE)
+ {
+ DWORD lastErr = GetLastError();
+ if (lastErr == ERROR_FILE_NOT_FOUND)
+ {
+ // This is ok; there was just nothing matching the pattern.
+ }
+ else
+ {
+ LogError("Failed to find pattern '%s'. GetLastError()=%u", searchPattern, GetLastError());
+ }
+ goto CLEAN_UP;
+ }
+
+ while (true)
+ {
+ // Do something with findData...
+
+ if (filter(&findData))
+ {
+ // Prepend it to the list.
+ first = new findDataList(&findData, first);
+ ++elemCount;
+ }
+
+ BOOL ok = FindNextFileA(hSearch, &findData);
+ if (!ok)
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_NO_MORE_FILES)
+ {
+ LogError("Failed to find next file. GetLastError()=%u", GetLastError());
+ result = -1;
+ goto CLEAN_UP;
+ }
+ break;
+ }
+ }
+
+ // Now sort the list. Create an array to put everything in.
+
+ int i;
+
+ retArray = new WIN32_FIND_DATAA[elemCount];
+ i = 0;
+ for (findDataList* tmp = first; tmp != nullptr; tmp = tmp->next)
+ {
+ retArray[i++] = tmp->findData;
+ }
+
+ qsort(retArray, elemCount, sizeof(retArray[0]), WIN32_FIND_DATAA_qsort_helper);
+
+CLEAN_UP:
+
+ findDataList::DeleteList(first);
+
+ if ((hSearch != INVALID_HANDLE_VALUE) && !FindClose(hSearch))
+ {
+ LogError("Failed to close search handle. GetLastError()=%u", GetLastError());
+ delete[] retArray;
+ return -1;
+ }
+
+ *ppFileArray = retArray;
+ *pElemCount = elemCount;
+ return result;
+}
+
+// Append all files in the given directory matching the file pattern.
+//
+// static
+int verbMerge::AppendAllInDir(HANDLE hFileOut, const char* dir, const char* file, unsigned char* buffer, size_t bufferSize, bool recursive, /* out */ LONGLONG* size)
+{
+ int result = 0; // default to zero == success
+ LONGLONG totalSize = 0;
+
+ char* searchPattern = MergePathStrings(dir, file);
+
+ WIN32_FIND_DATAA* fileArray = nullptr;
+ int elemCount = 0;
+ result = FilterDirectory(searchPattern, DirectoryFilterFile, &fileArray, &elemCount);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+
+ for (int i = 0; i < elemCount; i++)
+ {
+ const WIN32_FIND_DATAA& findData = fileArray[i];
+ char* fileFullPath = MergePathStrings(dir, findData.cFileName);
+
+ // Is it zero length? If so, skip it.
+ if ((findData.nFileSizeLow == 0) && (findData.nFileSizeHigh == 0))
+ {
+ LogInfo("Skipping zero-length file '%s'", fileFullPath);
+ }
+ else
+ {
+ result = AppendFile(hFileOut, fileFullPath, buffer, bufferSize);
+ if (result != 0)
+ {
+ // Error was already logged.
+ delete[] fileFullPath;
+ goto CLEAN_UP;
+ }
+ }
+
+ delete[] fileFullPath;
+ totalSize += ((LONGLONG)findData.nFileSizeHigh << 32) + (LONGLONG)findData.nFileSizeLow;
+ }
+
+ // If we need to recurse, then search the directory again for directories, and recursively search each one.
+ if (recursive)
+ {
+ delete[] searchPattern;
+ delete[] fileArray;
+
+ searchPattern = MergePathStrings(dir, "*");
+ fileArray = nullptr;
+ elemCount = 0;
+ result = FilterDirectory(searchPattern, DirectoryFilterDirectories, &fileArray, &elemCount);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+
+ LONGLONG dirSize = 0;
+ for (int i = 0; i < elemCount; i++)
+ {
+ const WIN32_FIND_DATAA& findData = fileArray[i];
+
+ char* fileFullPath = MergePathStrings(dir, findData.cFileName);
+ result = AppendAllInDir(hFileOut, fileFullPath, file, buffer, bufferSize, recursive, &dirSize);
+ delete[] fileFullPath;
+ if (result != 0)
+ {
+ // Error was already logged.
+ goto CLEAN_UP;
+ }
+
+ totalSize += dirSize;
+ }
+ }
+
+CLEAN_UP:
+
+ delete[] searchPattern;
+ delete[] fileArray;
+
+ if (result == 0)
+ {
+ *size = totalSize;
+ }
+
+ return result;
+}
+
+// Merge a set of .MC files into an output .MCH file. The .MC files to merge are given as a pattern, one of:
+// 1. *.mc -- simple pattern. Assumes current directory.
+// 2. foo\bar\*.mc -- simple pattern with relative directory.
+// 3. c:\foo\bar\baz\*.mc -- simple pattern with full path.
+// If no pattern is given, then the last component of the path is expected to be a directory name, and the pattern is assumed to be "*" (that is, all files).
+//
+// If "recursive" is true, then the pattern is searched for in the specified directory (or implicit current directory) and
+// all sub-directories, recursively.
+//
+// static
+int verbMerge::DoWork(const char* nameOfOutputFile, const char* pattern, bool recursive)
+{
+ int result = 0; // default to zero == success
+ SimpleTimer st1;
+
+ LogInfo("Merging files matching '%s' into '%s'", pattern, nameOfOutputFile);
+
+ HANDLE hFileOut = CreateFileA(nameOfOutputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", nameOfOutputFile, GetLastError());
+ return -1;
+ }
+
+ // Create a buffer we can use for all the copies.
+ unsigned char* buffer = new unsigned char[BUFFER_SIZE];
+ char* dir = nullptr;
+ const char* file = nullptr;
+
+ dir = _strdup(pattern);
+ char* lastSlash = strrchr(dir, DIRECTORY_SEPARATOR_CHAR_A);
+ if (lastSlash == NULL)
+ {
+ // The user may have passed a relative path without a slash, or the current directory.
+ // If there is a wildcard, we use it as the file pattern. If there isn't, we assume it's a relative directory name
+ // and use it as a directory, with "*" as the file pattern.
+ const char* wildcard = strchr(dir, '*');
+ if (wildcard == NULL)
+ {
+ file = "*";
+ }
+ else
+ {
+ file = dir;
+ dir = _strdup(".");
+ }
+ }
+ else
+ {
+ const char* wildcard = strchr(lastSlash, '*');
+ if (wildcard == NULL)
+ {
+ file = "*";
+
+ // Minor canonicalization: if there is a trailing last slash, strip it (probably should do this in a loop...)
+ if (*(lastSlash + 1) == '\0')
+ {
+ *lastSlash = '\0';
+ }
+ }
+ else
+ {
+ // ok, we found a wildcard after the last slash, so assume there is a pattern. Strip it at the last slash.
+ *lastSlash = '\0';
+ file = lastSlash + 1;
+ }
+ }
+
+ LONGLONG totalSize = 0;
+ LONGLONG dirSize = 0;
+
+ st1.Start();
+
+ result = AppendAllInDir(hFileOut, dir, file, buffer, BUFFER_SIZE, recursive, &dirSize);
+ if (result != 0)
+ {
+ goto CLEAN_UP;
+ }
+ totalSize += dirSize;
+
+ st1.Stop();
+
+ LogInfo("Read/Wrote %lld MB @ %4.2f MB/s.",
+ totalSize/(1000*1000),
+ (((double)totalSize)/(1000*1000))/st1.GetSeconds()); //yes yes.. http://en.wikipedia.org/wiki/Megabyte_per_second#Megabyte_per_second
+
+CLEAN_UP:
+
+ free((void*)dir);
+ delete[] buffer;
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ result = -1;
+ }
+
+ if (result != 0)
+ {
+ // There was a failure. Delete the output file, to avoid leaving some half-created file.
+ BOOL ok = DeleteFileA(nameOfOutputFile);
+ if (!ok)
+ {
+ LogError("Failed to delete file after MCS /merge failed. GetLastError()=%u", GetLastError());
+ }
+ }
+
+ return result;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbmerge.h b/src/ToolBox/superpmi/mcs/verbmerge.h
new file mode 100644
index 0000000000..1d612426f3
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbmerge.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbMerge.h - verb that merges multiple .MC into one .MCH file
+//----------------------------------------------------------
+#ifndef _verbMerge
+#define _verbMerge
+
+class verbMerge
+{
+public:
+ static int DoWork(const char* nameOfOutputFile, const char* pattern, bool recursive);
+
+private:
+ typedef bool (*DirectoryFilterFunction_t)(WIN32_FIND_DATAA*);
+ static bool DirectoryFilterDirectories(WIN32_FIND_DATAA* findData);
+ static bool DirectoryFilterFile(WIN32_FIND_DATAA* findData);
+ static int __cdecl WIN32_FIND_DATAA_qsort_helper(const void* p1, const void* p2);
+ static int FilterDirectory(const char* searchPattern, DirectoryFilterFunction_t filter, /* out */ WIN32_FIND_DATAA** ppFileArray, int* pElemCount);
+
+ static char* MergePathStrings(const char* dir, const char* file);
+
+ static int AppendFile(HANDLE hFileOut, const char* fileName, unsigned char* buffer, size_t bufferSize);
+ static int AppendAllInDir(HANDLE hFileOut, const char* dir, const char* file, unsigned char* buffer, size_t bufferSize, bool recursive, /* out */ LONGLONG* size);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbremovedup.cpp b/src/ToolBox/superpmi/mcs/verbremovedup.cpp
new file mode 100644
index 0000000000..eca5dc1b09
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbremovedup.cpp
@@ -0,0 +1,145 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbremovedup.h"
+#include "simpletimer.h"
+#include "lightweightmap.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+//We use a hash to limit the number of comparisons we need to do.
+//The first level key to our hash map is ILCodeSize and the second
+//level map key is just an index and the value is an existing MC Hash.
+
+LightWeightMap<int, DenseLightWeightMap<char *> *> *inFile = nullptr;
+
+bool unique(MethodContext *mc)
+{
+ if (inFile == nullptr)
+ inFile = new LightWeightMap<int, DenseLightWeightMap<char *> *>();
+
+ CORINFO_METHOD_INFO newInfo;
+ unsigned newFlags = 0;
+ mc->repCompileMethod(&newInfo, &newFlags);
+
+ char *md5Buff = new char[MD5_HASH_BUFFER_SIZE];
+ mc->dumpMethodMD5HashToBuffer(md5Buff, MD5_HASH_BUFFER_SIZE);
+
+ if (inFile->GetIndex(newInfo.ILCodeSize) == -1)
+ inFile->Add(newInfo.ILCodeSize, new DenseLightWeightMap<char *>());
+
+ DenseLightWeightMap<char *> *ourRank = inFile->Get(newInfo.ILCodeSize);
+
+ for (int i = 0; i < (int)ourRank->GetCount(); i++)
+ {
+ char *md5Buff2 = ourRank->Get(i);
+
+ if (strncmp(md5Buff, md5Buff2, MD5_HASH_BUFFER_SIZE) == 0)
+ {
+ delete[] md5Buff;
+ return false;
+ }
+ }
+
+ ourRank->Append(md5Buff);
+ return true;
+}
+
+LightWeightMap<int, DenseLightWeightMap<MethodContext *> *> *inFileLegacy = nullptr;
+
+bool uniqueLegacy(MethodContext *mc)
+{
+ if (inFileLegacy == nullptr)
+ inFileLegacy = new LightWeightMap<int, DenseLightWeightMap<MethodContext *> *>();
+
+ CORINFO_METHOD_INFO newInfo;
+ unsigned newFlags = 0;
+ mc->repCompileMethod(&newInfo, &newFlags);
+
+ if (inFileLegacy->GetIndex(newInfo.ILCodeSize) == -1)
+ inFileLegacy->Add(newInfo.ILCodeSize, new DenseLightWeightMap<MethodContext *>());
+
+ DenseLightWeightMap<MethodContext *> *ourRank = inFileLegacy->Get(newInfo.ILCodeSize);
+
+ for (int i = 0; i < (int)ourRank->GetCount(); i++)
+ {
+ MethodContext *scratch = ourRank->Get(i);
+ if (mc->Equal(scratch))
+ {
+ return false;
+ }
+ }
+
+ // We store the MethodContext in our map.
+ ourRank->Append(mc);
+ return true;
+}
+
+int verbRemoveDup::DoWork(const char *nameOfInput, const char *nameOfOutput, bool stripCR, bool legacyCompare)
+{
+ LogVerbose("Removing duplicates from '%s', writing to '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.CurrentTakeOwnership();
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ if (legacyCompare)
+ {
+ if (uniqueLegacy(mc))
+ {
+ mc->saveToFile(hFileOut);
+ savedCount++;
+
+ // In this case, for the legacy comparer, it has placed the 'mc' in the 'inFileLegacy' table, so we can't delete it.
+ }
+ else
+ {
+ delete mc; // we no longer need this
+ }
+ }
+ else
+ {
+ if (unique(mc))
+ {
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ }
+ delete mc; // we no longer need this
+ }
+ }
+
+ // We're leaking 'inFile' or 'inFileLegacy', but the process is going away, so it shouldn't matter.
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbremovedup.h b/src/ToolBox/superpmi/mcs/verbremovedup.h
new file mode 100644
index 0000000000..ab0cb98692
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbremovedup.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbRemoveDup.h - verb that attempts to remove dups
+//----------------------------------------------------------
+#ifndef _verbRemoveDup
+#define _verbRemoveDup
+
+class verbRemoveDup
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, bool stripCR, bool legacyCompare);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.cpp b/src/ToolBox/superpmi/mcs/verbsmarty.cpp
new file mode 100644
index 0000000000..9b881cdcbf
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbsmarty.cpp
@@ -0,0 +1,96 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbsmarty.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+//
+// Constructs a new verbSmarty.
+//
+// Arguments:
+// hFile - A handle to the output file that we are writing the Smarty Test IDs
+//
+// Assumptions:
+// hFile refers to an open and writeable file handle.
+//
+verbSmarty::verbSmarty(HANDLE hFile)
+{
+ m_hFile=hFile;
+}
+
+//
+// Dumps the Smarty TestID to file
+//
+// Arguments:
+// testID - Smarty Test ID
+//
+void verbSmarty::DumpTestInfo(int testID)
+{
+ #define bufflen 4096
+ DWORD bytesWritten;
+
+ char buff[bufflen];
+ int buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "%i\r\n", testID);
+ WriteFile(m_hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+}
+
+
+int verbSmarty::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Reading from '%s' reading Smarty ID for the Mc Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ verbSmarty *verbList = new verbSmarty(hFileOut);
+
+ //TODO-Cleanup: look to use toc for this
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ int testID = mc->repGetTestID();
+ if (testID != -1)
+ {
+ //write to the file
+ verbList->DumpTestInfo(testID);
+ }
+ else
+ {
+ LogError("Smarty ID not found for '%s'", mc->cr->repProcessName());
+ }
+ }
+
+ delete verbList;
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.h b/src/ToolBox/superpmi/mcs/verbsmarty.h
new file mode 100644
index 0000000000..994695da79
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbsmarty.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbSmarty.h - verb that outputs Smarty test ID for mc
+//----------------------------------------------------------
+#ifndef _verbSmarty
+#define _verbSmarty
+
+class verbSmarty
+{
+public:
+ verbSmarty(HANDLE hFile);
+ void DumpTestInfo(int testID);
+ static int DoWork(const char *nameOfInput, const char *nameofOutput, int indexCount, const int *indexes);
+
+private:
+ HANDLE m_hFile;
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbstat.cpp b/src/ToolBox/superpmi/mcs/verbstat.cpp
new file mode 100644
index 0000000000..473f452f96
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstat.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbstat.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+#include "errorhandling.h"
+
+int verbStat::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes)
+{
+ LogVerbose("Stat'ing from '%s' and writing output into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(indexCount, indexes, true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ #define bufflen 50000
+ DWORD bytesWritten;
+ char buff[bufflen];
+ int offset = 0;
+ ZeroMemory(&buff[0], bufflen);
+ offset += sprintf_s(buff, bufflen, "Title,MC#,");
+ offset += MethodContext::dumpStatTitleToBuffer(&buff[offset], bufflen - offset);
+ buff[offset++] = 0x0d;
+ buff[offset++] = 0x0a;
+ WriteFile(hFileOut, &buff[0], offset, &bytesWritten, nullptr);
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ offset = 0;
+ ZeroMemory(&buff[0], bufflen);
+ if ((mc->cr->ProcessName != nullptr) && (mc->cr->ProcessName->GetCount() > 0))
+ {
+ const char *procname = mc->cr->repProcessName();
+ strcpy_s(&buff[offset], bufflen, procname);
+ offset += (int)strlen(procname);
+ }
+ buff[offset++] = ',';
+ offset += sprintf_s(&buff[offset], bufflen - offset, "%d,", mci.MethodContextNumber());
+ offset += mc->dumpStatToBuffer(&buff[offset], bufflen - offset);
+ buff[offset++] = 0x0d;
+ buff[offset++] = 0x0a;
+ WriteFile(hFileOut, &buff[0], offset, &bytesWritten, nullptr);
+ savedCount++;
+ }
+
+ if (!CloseHandle(hFileOut))
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ LogInfo("Loaded %d, Stat'd %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbstat.h b/src/ToolBox/superpmi/mcs/verbstat.h
new file mode 100644
index 0000000000..53a3a78d6f
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstat.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbStat.h - verb that
+//----------------------------------------------------------
+#ifndef _verbStat
+#define _verbStat
+
+class verbStat
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbstrip.cpp b/src/ToolBox/superpmi/mcs/verbstrip.cpp
new file mode 100644
index 0000000000..8783b1a767
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstrip.cpp
@@ -0,0 +1,150 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbstrip.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "errorhandling.h"
+#include "methodcontextreader.h"
+#include "methodcontextiterator.h"
+
+// verbStrip::DoWork handles both "-copy" and "-strip". These both copy from input file to output file,
+// but treat the passed-in indexes in opposite ways.
+int verbStrip::DoWork(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool strip, bool stripCR)
+{
+ if (strip)
+ return DoWorkTheOldWay(nameOfInput, nameOfOutput, indexCount, indexes, stripCR);
+ SimpleTimer *st1 = new SimpleTimer();
+
+ LogVerbose("Reading from '%s' removing Mc Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ int loadedCount = 0;
+ MethodContext *mc = nullptr;
+ int savedCount = 0;
+ int index = 0;
+
+ // The method context reader handles skipping any unrequested method contexts
+ // Used in conjunction with an MCI file, it does a lot less work...
+ MethodContextReader *reader = new MethodContextReader(nameOfInput, indexes, indexCount);
+ if (!reader->isValid())
+ {
+ return -1;
+ }
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ if(indexCount == -1)
+ strip = true; //Copy command with no indexes listed should copy all the inputs...
+ while(true)
+ {
+ MethodContextBuffer mcb = reader->GetNextMethodContext();
+ if (mcb.Error())
+ {
+ return -1;
+ }
+ else if (mcb.allDone())
+ {
+ break;
+ }
+
+ loadedCount++;
+ if((loadedCount%500==0)&&(loadedCount>0))
+ {
+ st1->Stop();
+ LogVerbose("%2.1f%% - Loaded %d at %d per second", reader->PercentComplete(), loadedCount, (int)((double)500 / st1->GetSeconds()));
+ st1->Start();
+ }
+
+ if (!MethodContext::Initialize(loadedCount, mcb.buff, mcb.size, &mc))
+ return -1;
+
+ if(stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ delete mc;
+ }
+ if(CloseHandle(hFileOut)==0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ LogInfo("Loaded %d, Saved %d", loadedCount, savedCount);
+
+ return 0;
+}
+
+
+// This is only used for "-strip".
+int verbStrip::DoWorkTheOldWay(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR)
+{
+ LogVerbose("Reading from '%s' removing MC Indexes and writing into '%s'", nameOfInput, nameOfOutput);
+
+ MethodContextIterator mci(true);
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+ bool write;
+ int index = 0; // Can't use MethodContextIterator indexing, since we want the opposite of that.
+
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ write = true; // assume we'll write it
+ if (index < indexCount)
+ {
+ if (indexes[index] == mci.MethodContextNumber())
+ {
+ index++;
+ write = false;
+ }
+ }
+
+ if (write)
+ {
+ if (stripCR)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ }
+ mc->saveToFile(hFileOut);
+ savedCount++;
+ }
+ }
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ if (index < indexCount)
+ LogWarning("Didn't use all of index count input %d < %d (i.e. didn't see MC #%d)", index, indexCount, indexes[index]);
+
+ LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbstrip.h b/src/ToolBox/superpmi/mcs/verbstrip.h
new file mode 100644
index 0000000000..9db77736a8
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbstrip.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbStrip.h - verb that removes a list of mc's from an MCH file
+//----------------------------------------------------------
+#ifndef _verbStrip
+#define _verbStrip
+
+class verbStrip
+{
+public:
+ static int DoWork(const char *nameOfInput1, const char *nameOfOutput, int indexCount, const int *indexes, bool strip, bool stripCR);
+ static int DoWorkTheOldWay(const char *nameOfInput, const char *nameOfOutput, int indexCount, const int *indexes, bool stripCR);
+};
+#endif
diff --git a/src/ToolBox/superpmi/mcs/verbtoc.cpp b/src/ToolBox/superpmi/mcs/verbtoc.cpp
new file mode 100644
index 0000000000..a99fbf0183
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbtoc.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "verbtoc.h"
+#include "methodcontext.h"
+#include "methodcontextreader.h"
+#include "methodcontextiterator.h"
+#include "simpletimer.h"
+
+class TOCElementNode
+{
+public:
+ TOCElementNode *Next;
+ TOCElement tocElement;
+
+ TOCElementNode(int number, __int64 offset)
+ : Next(nullptr)
+ , tocElement(number, offset)
+ {
+ }
+};
+
+int verbTOC::DoWork(const char *nameOfInput)
+{
+ LogVerbose("Indexing from '%s' into '%s.mct'", nameOfInput, nameOfInput);
+
+ MethodContextIterator mci;
+ if (!mci.Initialize(nameOfInput))
+ return -1;
+
+ int savedCount = 0;
+
+ TOCElementNode *head = nullptr;
+ TOCElementNode *curElem = nullptr;
+
+ while (mci.MoveNext())
+ {
+ MethodContext* mc = mci.Current();
+
+ TOCElementNode *nxt = new TOCElementNode(mci.MethodContextNumber(), mci.CurrentPos());
+ mc->dumpMethodMD5HashToBuffer(nxt->tocElement.Hash, MD5_HASH_BUFFER_SIZE);
+
+ if (curElem != nullptr)
+ {
+ curElem->Next = nxt;
+ }
+ else
+ {
+ head = nxt;
+ }
+ curElem = nxt;
+ savedCount++;
+ }
+
+ size_t maxLen = strlen(nameOfInput) + 5;
+ char *nameOfOutput = (char*)_alloca(maxLen);
+ strcpy_s(nameOfOutput, maxLen, nameOfInput);
+ strcat_s(nameOfOutput, maxLen, ".mct");
+ HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
+ return -1;
+ }
+
+ DWORD written;
+ // Write out the signature "INDX" and then the element count
+ LARGE_INTEGER token;
+ token.u.LowPart = *(const int*)"INDX"; // cuz Type Safety is for languages that have good IO facilities
+ token.u.HighPart = savedCount;
+ if (!WriteFile(hFileOut, &token, sizeof(token), &written, nullptr) || written != sizeof(token))
+ {
+ LogError("Failed to write index header. GetLastError()=%u", GetLastError());
+ }
+
+ // Now just dump sizeof(TOCElement) byte chunks into the file.
+ // I could probably do this more efficiently, but I don't think it matters
+ DWORD chunkSize = sizeof(TOCElement);
+ for (curElem = head; curElem != nullptr; curElem = curElem->Next)
+ {
+ if (!WriteFile(hFileOut, &curElem->tocElement, chunkSize, &written, nullptr) || written != chunkSize)
+ {
+ LogError("Failed to write index element '%d'. GetLastError()=%u", curElem->tocElement.Number, GetLastError());
+ return -1;
+ }
+ }
+ // Now write out a final "INDX" to flag the end of the file...
+ if (!WriteFile(hFileOut, &token.u.LowPart, sizeof(token.u.LowPart), &written, nullptr) || (written != sizeof(token.u.LowPart)))
+ {
+ LogError("Failed to write index terminal. GetLastError()=%u", GetLastError());
+ }
+
+ LogInfo("Loaded %d, added %d to Table of Contents", mci.MethodContextNumber(), savedCount);
+
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+
+ if (!mci.Destroy())
+ return -1;
+
+ return 0;
+}
diff --git a/src/ToolBox/superpmi/mcs/verbtoc.h b/src/ToolBox/superpmi/mcs/verbtoc.h
new file mode 100644
index 0000000000..7eea371191
--- /dev/null
+++ b/src/ToolBox/superpmi/mcs/verbtoc.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// verbTOC.h - verb that creates a Table of Context for a MCH file
+//----------------------------------------------------------
+#ifndef _verbTOC
+#define _verbTOC
+
+class verbTOC
+{
+public:
+ static int DoWork(const char *nameOfInput1);
+};
+#endif
diff --git a/src/ToolBox/superpmi/readme.txt b/src/ToolBox/superpmi/readme.txt
new file mode 100644
index 0000000000..8429e4f38e
--- /dev/null
+++ b/src/ToolBox/superpmi/readme.txt
@@ -0,0 +1,86 @@
+OVERVIEW
+========
+
+This directory contains the SuperPMI tool used for testing the .NET
+just-in-time (JIT) compiler.
+
+SuperPMI has two uses:
+1. Verification that a JIT code change doesn't cause any asserts.
+2. Finding test code where two JIT compilers generate different code, or
+verifying that the two compilers generate the same code.
+
+Case #1 is useful for doing quick regression checking when making a source
+code change to the JIT compiler. The process is: (a) make a JIT source code
+change, (b) run that newly built JIT through a SuperPMI run to verify no
+asserts have been introduced.
+
+Case #2 is useful for generating assembly language diffs, to help analyze the
+impact of a JIT code change.
+
+SuperPMI works in two phases: collection and playback. In the collection
+phase, the system is configured to collect SuperPMI data. Then, run any
+set of .NET managed programs. When these managed programs invoke the JIT
+compiler, SuperPMI gathers and captures all information passed between the
+JIT and its .NET host. In the playback phase, SuperPMI loads the JIT directly,
+and causes it to compile all the functions that it previously compiled,
+but using the collected data to provide answers to various questions that
+the JIT needs to ask. The .NET execution engine (EE) is not invoked at all.
+
+
+TOOLS
+==========
+
+There are two native executable tools: superpmi and mcs. There is a .NET Core
+C# program that is built as part of the coreclr repo tests build called
+superpmicollect.exe.
+
+All will show a help screen if passed -?.
+
+
+COLLECTION
+==========
+
+Set the following environment variables:
+
+ SuperPMIShimLogPath=<full path to an empty temporary directory>
+ SuperPMIShimPath=<full path to clrjit.dll, the "standalone" JIT>
+ COMPlus_AltJit=*
+ COMPlus_AltJitName=superpmi-shim-collector.dll
+
+(On Linux, use libclrjit.so and libsuperpmi-shim-collector.so. On Mac,
+use libclrjit.dylib and libsuperpmi-shim-collector.dylib.)
+
+Then, run some managed programs. When done running programs, un-set these
+variables.
+
+Now, you will have a large number of .mc files. Merge these using the mcs
+tool:
+
+ mcs -merge base.mch *.mc
+
+One benefit of SuperPMI is the ability to remove duplicated compilations, so
+on replay only unique functions are compiled. Use the following to create a
+"unique" set of functions:
+
+ mcs -removeDup -thin base.mch unique.mch
+
+Note that -thin is not required. However, it will delete all the compilation
+result collected during the collection phase, which makes the resulting MCH
+file smaller. Those compilation results are not required for playback.
+
+Use the superpmicollect.exe tool to automate and simplify this process.
+
+
+PLAYBACK
+========
+
+Once you have a merged, de-duplicated MCH collection, you can play it back
+using:
+
+ superpmi unique.mch clrjit.dll
+
+You can do this much faster by utilizing all the processors on your machine,
+and replaying in parallel, using:
+
+ superpmi -p unique.mch clrjit.dll
+
diff --git a/src/ToolBox/superpmi/superpmi-shared/.gitmirror b/src/ToolBox/superpmi/superpmi-shared/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/asmdumper.cpp b/src/ToolBox/superpmi/superpmi-shared/asmdumper.cpp
new file mode 100644
index 0000000000..240e228c1e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/asmdumper.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "asmdumper.h"
+
+void ASMDumper::DumpToFile(HANDLE hFile, MethodContext *mc, CompileResult *cr)
+{
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ #define bufflen 4096
+ DWORD bytesWritten;
+ char buff[bufflen];
+
+ int buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, ";;Generated from SuperPMI on original input '%s'",
+ cr->repProcessName());
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "\r\n Method Name \"%s\"",
+ mc->repGetMethodName(info.ftn,nullptr));
+ WriteFile(hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+
+ ULONG hotCodeSize;
+ ULONG coldCodeSize;
+ ULONG roDataSize;
+ ULONG xcptnsCount;
+ CorJitAllocMemFlag flag;
+ unsigned char *hotCodeBlock;
+ unsigned char *coldCodeBlock;
+ unsigned char *roDataBlock;
+ void *orig_hotCodeBlock;
+ void *orig_coldCodeBlock;
+ void *orig_roDataBlock;
+
+ cr->repAllocMem(&hotCodeSize, &coldCodeSize, &roDataSize, &xcptnsCount, &flag,
+ &hotCodeBlock, &coldCodeBlock, &roDataBlock, &orig_hotCodeBlock, &orig_coldCodeBlock, &orig_roDataBlock);
+ cr->applyRelocs(hotCodeBlock, hotCodeSize, orig_hotCodeBlock);
+ cr->applyRelocs(coldCodeBlock, coldCodeSize, orig_coldCodeBlock);
+ cr->applyRelocs(roDataBlock, roDataSize, orig_roDataBlock);
+
+#ifdef USE_MSVCDIS
+
+#ifdef _TARGET_AMD64_
+ DIS *disasm = DIS::PdisNew(DIS::distX8664);
+#elif _TARGET_X86_
+ DIS *disasm = DIS::PdisNew(DIS::distX86);
+#endif
+ size_t offset = 0;
+ while (offset < hotCodeSize)
+ {
+ buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+
+ DIS::INSTRUCTION instr;
+ DIS::OPERAND ops[3];
+
+ size_t instrSize = disasm->CbDisassemble(0, (void *)(hotCodeBlock + offset), 15);
+ if(instrSize==0)
+ {
+ LogWarning("Zero sized instruction");
+ break;
+ }
+ disasm->FDecode(&instr, ops, 3);
+
+ wchar_t instrMnemonic[64]; // I never know how much to allocate...
+ disasm->CchFormatInstr(instrMnemonic, 64);
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "\r\n%p %S", (void*)((size_t)orig_hotCodeBlock+offset), instrMnemonic);
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, " ; ");
+ for(unsigned int i=0;i<instrSize;i++)
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, "%02x ", *((BYTE*)(hotCodeBlock + offset + i) ));
+ WriteFile(hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+ offset += instrSize;
+ }
+
+ delete disasm;
+
+#else // !USE_MSVCDIS
+
+ buff_offset = 0;
+ ZeroMemory(buff, bufflen * sizeof(char));
+ buff_offset+=sprintf_s(&buff[buff_offset], bufflen-buff_offset, ";; No disassembler available");
+ WriteFile(hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
+
+#endif // !USE_MSVCDIS
+
+ FlushFileBuffers(hFile);
+} \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/asmdumper.h b/src/ToolBox/superpmi/superpmi-shared/asmdumper.h
new file mode 100644
index 0000000000..d848d60e20
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/asmdumper.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ASMDumper
+#define _ASMDumper
+
+#include "methodcontext.h"
+#include "compileresult.h"
+
+class ASMDumper
+{
+public:
+ static void DumpToFile(HANDLE hFile, MethodContext *mc, CompileResult *cr);
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/callutils.cpp b/src/ToolBox/superpmi/superpmi-shared/callutils.cpp
new file mode 100644
index 0000000000..027929bec0
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/callutils.cpp
@@ -0,0 +1,416 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CallUtils.cpp - Utility code for analyzing and working with managed calls
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "callutils.h"
+#include "typeutils.h"
+#include "errorhandling.h"
+#include "logging.h"
+
+// String representations of the JIT helper functions
+const char *kHelperName[CORINFO_HELP_COUNT] =
+{
+#define JITHELPER(code, pfnHelper, sig) #code,
+#define DYNAMICJITHELPER(code, pfnHelper,sig) #code,
+#include "jithelpers.h"
+};
+
+//-------------------------------------------------------------------------------------------------
+
+//
+// Provides information about the target of an outgoing call, based on where it was emitted in the
+// generated code stream.
+//
+// Primarily, this returns what the destination of the call is (e.g. a method, a helper function), but this
+// can also provide:
+//
+// - A symbolic name for the call target (i.e. the function name).
+// - For non-helper methods, the method signature of the call target.
+//
+// Arguments:
+// mc - The method context of the method containing this call site.
+// cr - The compile result for the method containing this call site.
+// callInstrOffset - The native offset of the call site in the generated code stream.
+// outSigInfo - [out] The signature of the outgoing call. Optional (pass nullptr if unwanted).
+// outCallTargetSymbol - [out] A string representation of the outgoing call. Optional (pass nullptr if
+// unwanted).
+//
+// Return Value:
+// What type of call the outgoing call is.
+//
+// Notes:
+// - This depends on the JIT having registered the call site with the EE through recordCallSite. If the
+// JIT didn't do this, GetDirectCallSite can obtain most of the same information for direct calls.
+// - If the call site is for a helper method, then outSigInfo will not be changed, since helper calls
+// have no signature information.
+// - If you pass in a valid pointer for outCallTargetSymbol, this function will allocate memory for it
+// if it is able to understand that call (i.e. if it does not return CallType_Unknown). You, the caller,
+// are responsible for freeing the memory (with delete[]).
+//
+CallType CallUtils::GetRecordedCallSiteInfo(MethodContext *mc,
+ CompileResult *cr,
+ unsigned int callInstrOffset,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol)
+{
+ AssertCodeMsg(mc != nullptr, EXCEPTIONCODE_CALLUTILS,
+ "Null method context passed into GetCallTargetInfo for call at offset %x.",
+ callInstrOffset);
+ AssertCodeMsg(cr != nullptr, EXCEPTIONCODE_CALLUTILS,
+ "Null compile result passed into GetCallTargetInfo for call at offset %x.",
+ callInstrOffset);
+
+ CallType targetType = CallType_Unknown;
+
+ CORINFO_SIG_INFO callSig;
+ bool recordedCallSig = cr->fndRecordCallSiteSigInfo(callInstrOffset, &callSig);
+
+ CORINFO_METHOD_HANDLE methodHandle = nullptr;
+ bool recordedMethodHandle = cr->fndRecordCallSiteMethodHandle(callInstrOffset, &methodHandle);
+
+ if (recordedCallSig)
+ {
+ if (outSigInfo != nullptr)
+ *outSigInfo = callSig;
+
+ if (outCallTargetSymbol != nullptr)
+ *outCallTargetSymbol = (char *)GetMethodFullName(mc, methodHandle, callSig);
+
+ targetType = CallType_UserFunction;
+ }
+ else if (recordedMethodHandle)
+ {
+ CorInfoHelpFunc helperNum = CallUtils::GetHelperNum(methodHandle);
+ AssertCodeMsg(helperNum != CORINFO_HELP_UNDEF, EXCEPTIONCODE_CALLUTILS,
+ "Unknown call at offset %x with method handle %016llX.",
+ callInstrOffset, methodHandle);
+
+ size_t length = strlen(kHelperName[helperNum]) + 1;
+ *outCallTargetSymbol = new char[length];
+ strcpy_s(*outCallTargetSymbol, length, kHelperName[helperNum]);
+
+ targetType = CallType_Helper;
+ }
+ else
+ {
+ LogWarning("Call site at offset %x was not recorded via recordCallSite.", callInstrOffset);
+ }
+
+ return targetType;
+}
+
+//
+// Provides information about the target of an outgoing call, based on the outgoing call's target address.
+//
+// Primarily, this returns what the destination of the call is (e.g. a method, a helper function), but this
+// can also provide:
+//
+// - A symbolic name for the call target (i.e. the function name).
+// - For certain types of managed methods, the method signature of the call target.
+//
+// Arguments:
+// mc - The method context of the method containing this outgoing call.
+// callTarget - The target address of the outgoing call.
+// outSigInfo - [out] The signature of the outgoing call. Optional (pass nullptr if unwanted).
+// outCallTargetSymbol - [out] A string representation of the outgoing call. Optional (pass nullptr if
+// unwanted).
+//
+// Return Value:
+// What type of call the outgoing call is.
+//
+// Assumptions:
+// The given method address does not point to a jump stub.
+//
+// Notes:
+// - This only works for direct calls that have a static target address.
+// - If you pass in a valid pointer for outCallTargetSymbol, this function will allocate memory for it
+// if it is able to understand that call (i.e. if it does not return CallType_Unknown). You, the caller,
+// are responsible for freeing the memory (with delete[]).
+//
+
+CallType CallUtils::GetDirectCallSiteInfo(MethodContext *mc,
+ void *callTarget,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol)
+{
+ AssertCodeMsg(mc != nullptr, EXCEPTIONCODE_CALLUTILS,
+ "Null method context passed into GetCallTargetInfo for call to target %016llX.", callTarget);
+
+ CallType targetType = CallType_Unknown;
+ MethodContext::DLD functionEntryPoint;
+ CORINFO_METHOD_HANDLE methodHandle;
+
+ // Try to first obtain a method handle associated with this call target
+ functionEntryPoint.A = (DWORDLONG)callTarget;
+ functionEntryPoint.B = 0; // TODO-Cleanup: we should be more conscious of this...
+
+ if (mc->fndGetFunctionEntryPoint(functionEntryPoint, &methodHandle))
+ {
+ // Now try to obtain the call info associated with this method handle
+
+ struct Param {
+ MethodContext* mc;
+ CORINFO_SIG_INFO* outSigInfo;
+ char** outCallTargetSymbol;
+ CallType* pTargetType;
+ CORINFO_METHOD_HANDLE* pMethodHandle;
+ } param;
+ param.mc = mc;
+ param.outSigInfo = outSigInfo;
+ param.outCallTargetSymbol = outCallTargetSymbol;
+ param.pTargetType = &targetType;
+ param.pMethodHandle = &methodHandle;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ CORINFO_CALL_INFO callInfo;
+
+ pParam->mc->repGetCallInfoFromMethodHandle(*pParam->pMethodHandle, &callInfo);
+
+ if (pParam->outSigInfo != nullptr)
+ *pParam->outSigInfo = callInfo.sig;
+
+ if (pParam->outCallTargetSymbol != nullptr)
+ *pParam->outCallTargetSymbol = (char *)GetMethodFullName(pParam->mc, *pParam->pMethodHandle, callInfo.sig);
+
+ *pParam->pTargetType = CallType_UserFunction;
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
+ {
+ LogWarning("Didn't find call info for method handle %016llX (call target: %016llX)",
+ methodHandle, callTarget);
+ }
+ PAL_ENDTRY
+ }
+ else
+ {
+ // No method handle associated with this target, so check if it's a helper
+ CorInfoHelpFunc helperNum;
+
+ if (mc->fndGetHelperFtn(callTarget, &helperNum))
+ {
+ if (outCallTargetSymbol != nullptr)
+ {
+ size_t length = strlen(kHelperName[helperNum]) + 1;
+ *outCallTargetSymbol = new char[length];
+ strcpy_s(*outCallTargetSymbol, length, kHelperName[helperNum]);
+ }
+
+ targetType = CallType_Helper;
+ }
+ else
+ {
+ LogWarning("Call to target %016llX has no method handle and is not a helper call.",
+ callTarget);
+ }
+ }
+
+ return targetType;
+}
+
+//-------------------------------------------------------------------------------------------------
+// Utilty code that was stolen from various sections of the JIT codebase and tweaked to go through
+// SuperPMI's method context replaying instead of directly making calls into the JIT/EE interface.
+//-------------------------------------------------------------------------------------------------
+
+// Stolen from Compiler::impMethodInfo_hasRetBuffArg (in the importer)
+bool CallUtils::HasRetBuffArg(MethodContext *mc, CORINFO_SIG_INFO args)
+{
+ if (args.retType != CORINFO_TYPE_VALUECLASS &&
+ args.retType != CORINFO_TYPE_REFANY)
+ {
+ return false;
+ }
+
+#if defined(_TARGET_AMD64_)
+ // We don't need a return buffer if:
+ // i) TYP_STRUCT argument that can fit into a single register and
+ // ii) Power of two sized TYP_STRUCT on AMD64.
+ unsigned size = mc->repGetClassSize(args.retTypeClass);
+ return (size > sizeof(void*)) || ((size & (size-1)) != 0);
+#else
+ return true;
+#endif
+}
+
+// Originally from src/jit/ee_il_dll.cpp
+const char *CallUtils::GetMethodName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE method,
+ const char **classNamePtr)
+{
+ if (GetHelperNum(method))
+ {
+ if (classNamePtr != nullptr)
+ *classNamePtr = "HELPER";
+
+ // The JIT version uses the getHelperName JIT/EE interface call, but this is easier for us
+ return kHelperName[GetHelperNum(method)];
+ }
+
+ if (IsNativeMethod(method))
+ {
+ if (classNamePtr != nullptr)
+ *classNamePtr = "NATIVE";
+ method = GetMethodHandleForNative(method);
+ }
+
+ return(mc->repGetMethodName(method, classNamePtr));
+}
+
+// Originally from src/jit/eeinterface.cpp
+const char *CallUtils::GetMethodFullName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE hnd,
+ CORINFO_SIG_INFO sig)
+{
+ const char* returnType = NULL;
+
+ const char* className;
+ const char* methodName = GetMethodName(mc, hnd, &className);
+ if ((GetHelperNum(hnd) != CORINFO_HELP_UNDEF) || IsNativeMethod(hnd))
+ {
+ return methodName;
+ }
+
+ size_t length = 0;
+ unsigned i;
+
+ /* Generating the full signature is a two-pass process. First we have to walk
+ the components in order to assess the total size, then we allocate the buffer
+ and copy the elements into it.
+ */
+
+ /* Right now there is a race-condition in the EE, className can be NULL */
+
+ /* initialize length with length of className and '.' */
+
+ if (className != nullptr)
+ length = strlen(className)+1;
+ else
+ {
+ // Tweaked to avoid using CRT assertions
+ Assert(strlen("<NULL>.") == 7);
+ length = 7;
+ }
+
+ /* add length of methodName and opening bracket */
+ length += strlen(methodName) + 1;
+
+ CORINFO_ARG_LIST_HANDLE argList = sig.args;
+
+ for (i = 0; i < sig.numArgs; i++)
+ {
+ // Tweaked to use EE types instead of JIT-specific types
+ CORINFO_CLASS_HANDLE typeHandle;
+ DWORD exception;
+ CorInfoType type = strip(mc->repGetArgType(&sig, argList, &typeHandle, &exception));
+
+ length += strlen(TypeUtils::GetCorInfoTypeName(type));
+ argList = mc->repGetArgNext(argList);
+ }
+
+ /* add ',' if there is more than one argument */
+
+ if (sig.numArgs > 1)
+ length += (sig.numArgs - 1);
+
+ // Tweaked to use EE types instead of JIT-specific types
+ if (sig.retType != CORINFO_TYPE_VOID)
+ {
+ returnType = TypeUtils::GetCorInfoTypeName(sig.retType);
+ length += strlen(returnType) + 1; // don't forget the delimiter ':'
+ }
+
+ // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first element of the arg type list
+ if (sig.hasThis() && !sig.hasExplicitThis())
+ {
+ // Tweaked to avoid using CRT assertions
+ Assert(strlen(":this") == 5);
+ length += 5;
+ }
+
+ /* add closing bracket and null terminator */
+
+ length += 2;
+
+ char *retName = new char[length]; // Tweaked to use "new" instead of compGetMem
+
+ /* Now generate the full signature string in the allocated buffer */
+
+ if (className)
+ {
+ strcpy_s(retName, length, className);
+ strcat_s(retName, length, ":");
+ }
+ else
+ {
+ strcpy_s(retName, length, "<NULL>.");
+ }
+
+ strcat_s(retName, length, methodName);
+
+ // append the signature
+ strcat_s(retName, length, "(");
+
+ argList = sig.args;
+
+ for (i = 0; i < sig.numArgs; i++)
+ {
+ // Tweaked to use EE types instead of JIT-specific types
+ CORINFO_CLASS_HANDLE typeHandle;
+ DWORD exception;
+ CorInfoType type = strip(mc->repGetArgType(&sig, argList, &typeHandle, &exception));
+ strcat_s(retName, length, TypeUtils::GetCorInfoTypeName(type));
+
+ argList = mc->repGetArgNext(argList);
+ if (i + 1 < sig.numArgs)
+ strcat_s(retName, length, ",");
+ }
+
+ strcat_s(retName, length, ")");
+
+ if (returnType)
+ {
+ strcat_s(retName, length, ":");
+ strcat_s(retName, length, returnType);
+ }
+
+ // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first element of the arg type list
+ if (sig.hasThis() && !sig.hasExplicitThis())
+ {
+ strcat_s(retName, length, ":this");
+ }
+
+ // Tweaked to avoid using CRT assertions
+ Assert(strlen(retName) == (length-1));
+
+ return(retName);
+}
+
+// Originally from jit/compiler.hpp
+inline CorInfoHelpFunc CallUtils::GetHelperNum(CORINFO_METHOD_HANDLE method)
+{
+ // Helpers are marked by the fact that they are odd numbers
+ if (!(((size_t) method) & 1))
+ return(CORINFO_HELP_UNDEF);
+ return((CorInfoHelpFunc) (((size_t) method) >> 2));
+}
+
+// Originally from jit/compiler.hpp
+inline bool CallUtils::IsNativeMethod(CORINFO_METHOD_HANDLE method)
+{
+ return ((((size_t)method) & 0x2) == 0x2);
+}
+
+// Originally from jit/compiler.hpp
+inline CORINFO_METHOD_HANDLE CallUtils::GetMethodHandleForNative(CORINFO_METHOD_HANDLE method)
+{
+ // Tweaked to avoid using CRT assertions
+ Assert((((size_t) method) & 0x3) == 0x2);
+ return (CORINFO_METHOD_HANDLE)(((size_t)method)& ~0x3);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/callutils.h b/src/ToolBox/superpmi/superpmi-shared/callutils.h
new file mode 100644
index 0000000000..f428d77795
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/callutils.h
@@ -0,0 +1,44 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//----------------------------------------------------------
+// CallUtils.h - Utility code for analyzing and working with managed calls
+//----------------------------------------------------------
+#ifndef _CallUtils
+#define _CallUtils
+
+#include "methodcontext.h"
+
+enum CallType
+{
+ CallType_UserFunction = 0,
+ CallType_Helper,
+ CallType_Unknown = -1
+};
+
+class CallUtils
+{
+public:
+ static CallType GetRecordedCallSiteInfo(MethodContext *mc,
+ CompileResult *cr,
+ unsigned int callInstrOffset,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol);
+ static CallType GetDirectCallSiteInfo(MethodContext *mc,
+ void *callTarget,
+ /*out*/ CORINFO_SIG_INFO *outSigInfo,
+ /*out*/ char **outCallTargetSymbol);
+ static bool HasRetBuffArg(MethodContext *mc, CORINFO_SIG_INFO args);
+ static CorInfoHelpFunc GetHelperNum(CORINFO_METHOD_HANDLE method);
+ static bool IsNativeMethod(CORINFO_METHOD_HANDLE method);
+ static CORINFO_METHOD_HANDLE GetMethodHandleForNative(CORINFO_METHOD_HANDLE method);
+ static const char *GetMethodName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE method,
+ const char **classNamePtr);
+ static const char *GetMethodFullName(MethodContext *mc,
+ CORINFO_METHOD_HANDLE hnd,
+ CORINFO_SIG_INFO sig);
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp b/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp
new file mode 100644
index 0000000000..b15c51f28a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp
@@ -0,0 +1,1080 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CompileResult.cpp - CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "compileresult.h"
+#include "methodcontext.h"
+
+CompileResult::CompileResult()
+{
+ #define LWM(map,key,value) map = nullptr;
+ #include "crlwmlist.h"
+
+ //Not persisted to disk. though should it be?
+ CallTargetTypes = new LightWeightMap<DWORDLONG, DWORD>();
+
+ allocMemDets.hotCodeSize = 0;
+ allocMemDets.coldCodeSize = 0;
+ allocMemDets.roDataSize = 0;
+ allocMemDets.xcptnsCount = 0;
+ allocMemDets.flag = (CorJitAllocMemFlag)0;
+ allocMemDets.hotCodeBlock = 0;
+ allocMemDets.coldCodeBlock = 0;
+ allocMemDets.roDataBlock = 0;
+
+ allocGCInfoDets.retval = 0;
+ allocGCInfoDets.size = 0;
+
+ codeHeap = nullptr;
+}
+
+CompileResult::~CompileResult()
+{
+ #define LWM(map,key,value) if (map != nullptr) delete map;
+ #include "crlwmlist.h"
+
+ if (CallTargetTypes != nullptr) delete CallTargetTypes;
+
+#ifndef FEATURE_PAL // PAL doesn't have HeapDestroy()
+ if(codeHeap != nullptr)
+ ::HeapDestroy(codeHeap);
+#endif // !FEATURE_PAL
+}
+
+// Is the CompileResult empty? Define this as whether all the maps that store information given by the JIT are empty.
+// This is useful when determining if a function won't apply after a "mcs -removeDump -thin" operation has been run.
+bool CompileResult::IsEmpty()
+{
+ bool isEmpty = true;
+
+ #define LWM(map,key,value) if (map != nullptr) isEmpty = false;
+ #include "crlwmlist.h"
+
+ return isEmpty;
+}
+
+HANDLE CompileResult::getCodeHeap()
+{
+ if(codeHeap == nullptr)
+ codeHeap = ::HeapCreate(0,0,0);
+ if(codeHeap == nullptr)
+ {
+ LogError("CompileResult::codeHeap() failed to acquire a heap.");
+ __debugbreak();
+ }
+ return codeHeap;
+}
+
+void CompileResult::recAssert(const char *assertText)
+{
+ if(AssertLog == nullptr)
+ AssertLog = new DenseLightWeightMap<DWORD>();
+
+ AssertLog->Append(AssertLog->AddBuffer((const unsigned char*)assertText, (DWORD)strlen(assertText)+1));
+}
+void CompileResult::dmpAssertLog(DWORD key, DWORD value)
+{
+ const char *assert = (const char *)AssertLog->GetBuffer(value);
+ printf("AssertLog key %u, value '%s'", key, assert);
+ AssertLog->Unlock();
+}
+const char *CompileResult::repAssert()
+{
+ if((AssertLog==nullptr)||(AssertLog->GetCount()==0))
+ return nullptr;
+ return (const char *)AssertLog->GetBuffer(AssertLog->Get((DWORD)0));
+}
+
+void CompileResult::AddCall(const char *name)
+{
+ if(CallLog == nullptr)
+ CallLog = new DenseLightWeightMap<DWORD>();
+ //if(name[0] != '+')
+ //CallLog->Append(CallLog->AddBuffer((const unsigned char *)name, (DWORD)strlen(name)+1));
+}
+unsigned int CompileResult::CallLog_GetCount()
+{
+ return CallLog->GetCount();
+}
+
+bool CompileResult::CallLog_Contains(const char *str)
+{
+ return (CallLog->Contains((unsigned char *)str, (unsigned int)strlen(str))>0);
+}
+void CompileResult::dmpCallLog(DWORD key, DWORD value)
+{
+ const char* temp = (const char*)CallLog->GetBuffer(value);
+ printf("CallLog %u '%s'", key, temp);
+ CallLog->Unlock();
+}
+
+void CompileResult::dumpToConsole()
+{
+ printf("***************************************** CompileResult\n");
+
+ #define LWM(map,key,value) dumpLWM(this,map)
+ #define DENSELWM(map,value) dumpLWMDense(this,map)
+ #include "crlwmlist.h"
+
+ printf("-----------------------------------------\n");
+}
+
+//Note - EE allocates these blocks (and the exception blocks) in a single linear region.
+//Note - EE assures that RoBlock is 8 byte aligned
+void CompileResult::recAllocMem(ULONG hotCodeSize, ULONG coldCodeSize, ULONG roDataSize, ULONG xcptnsCount, CorJitAllocMemFlag flag,
+ void **hotCodeBlock, void **coldCodeBlock, void **roDataBlock)
+{
+ //Grab the values, so we can scrape the real answers in the capture method
+ allocMemDets.hotCodeSize = hotCodeSize;
+ allocMemDets.coldCodeSize = coldCodeSize;
+ allocMemDets.roDataSize = roDataSize;
+ allocMemDets.xcptnsCount = xcptnsCount;
+ allocMemDets.flag = flag;
+ allocMemDets.hotCodeBlock = *hotCodeBlock;
+ allocMemDets.coldCodeBlock = *coldCodeBlock;
+ allocMemDets.roDataBlock = *roDataBlock;
+}
+void CompileResult::recAllocMemCapture()
+{
+ if(AllocMem == nullptr)
+ AllocMem = new LightWeightMap<DWORD, Agnostic_AllocMemDetails>();
+
+ Agnostic_AllocMemDetails value;
+
+ value.hotCodeSize = (DWORD)allocMemDets.hotCodeSize;
+ value.coldCodeSize = (DWORD)allocMemDets.coldCodeSize;
+ value.roDataSize = (DWORD)allocMemDets.roDataSize;
+ value.xcptnsCount = (DWORD)allocMemDets.xcptnsCount;
+ value.flag = (DWORD)allocMemDets.flag;
+ value.hotCodeBlock_offset = (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.hotCodeBlock, allocMemDets.hotCodeSize);
+ value.coldCodeBlock_offset = (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.coldCodeBlock, allocMemDets.coldCodeSize);
+ value.roDataBlock_offset = (DWORD)AllocMem->AddBuffer((const unsigned char*)allocMemDets.roDataBlock, allocMemDets.roDataSize);
+ value.hotCodeBlock = (DWORDLONG)allocMemDets.hotCodeBlock;
+ value.coldCodeBlock = (DWORDLONG)allocMemDets.coldCodeBlock;
+ value.roDataBlock = (DWORDLONG)allocMemDets.roDataBlock;
+
+ AllocMem->Add(0, value);
+}
+void CompileResult::dmpAllocMem(DWORD key, const Agnostic_AllocMemDetails& value)
+{
+ printf("AllocMem key 0, value hotCodeSize-%u coldCodeSize-%u roDataSize-%u xcptnsCount-%u flag-%08X hotCodeBlock_offset-%u coldCodeBlock_offset-%u roDataBlock_offset-%u hotCodeBlock-%016llX coldCodeBlock-%016llX roDataBlock-%016llX",
+ value.hotCodeSize,
+ value.coldCodeSize,
+ value.roDataSize,
+ value.xcptnsCount,
+ value.flag,
+ value.hotCodeBlock_offset,
+ value.coldCodeBlock_offset,
+ value.roDataBlock_offset,
+ value.hotCodeBlock,
+ value.coldCodeBlock,
+ value.roDataBlock);
+}
+
+// We can't allocate memory in the same place is was during recording, so we pass back code/data block pointers
+// that point into the AllocMem LightWeightMap, but also return what the original addresses were during recording.
+void CompileResult::repAllocMem(
+ ULONG *hotCodeSize,
+ ULONG *coldCodeSize,
+ ULONG *roDataSize,
+ ULONG *xcptnsCount,
+ CorJitAllocMemFlag *flag,
+ unsigned char **hotCodeBlock,
+ unsigned char **coldCodeBlock,
+ unsigned char **roDataBlock,
+ void **orig_hotCodeBlock,
+ void **orig_coldCodeBlock,
+ void **orig_roDataBlock)
+{
+ Agnostic_AllocMemDetails value;
+
+ value = AllocMem->Get(0);
+
+ *hotCodeSize = (ULONG)value.hotCodeSize;
+ *coldCodeSize = (ULONG)value.coldCodeSize;
+ *roDataSize = (ULONG)value.roDataSize;
+ *xcptnsCount = (ULONG)value.xcptnsCount;
+ *flag = (CorJitAllocMemFlag)value.flag;
+
+ if(*hotCodeSize>0)
+ *hotCodeBlock = AllocMem->GetBuffer(value.hotCodeBlock_offset);
+ else
+ *hotCodeBlock = nullptr;
+
+ if(*coldCodeSize>0)
+ *coldCodeBlock = AllocMem->GetBuffer(value.coldCodeBlock_offset);
+ else
+ *coldCodeBlock = nullptr;
+
+ if(*roDataSize>0)
+ *roDataBlock = AllocMem->GetBuffer(value.roDataBlock_offset);
+ else
+ *roDataBlock = nullptr;
+
+ *orig_hotCodeBlock = (void *)value.hotCodeBlock;
+ *orig_coldCodeBlock = (void *)value.coldCodeBlock;
+ *orig_roDataBlock = (void *)value.roDataBlock;
+}
+
+//Note - Ownership of pMap is transfered with this call. In replay icorjitinfo we should free it.
+void CompileResult::recSetBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap)
+{
+ if(SetBoundaries == nullptr)
+ SetBoundaries = new LightWeightMap<DWORD, Agnostic_SetBoundaries>();
+
+ Agnostic_SetBoundaries value;
+
+ value.ftn = (DWORDLONG)ftn;
+ value.cMap = (DWORD)cMap;
+ value.pMap_offset = (DWORD)SetBoundaries->AddBuffer((const unsigned char*)pMap, sizeof(ICorDebugInfo::OffsetMapping)*cMap);
+
+ SetBoundaries->Add(0, value);
+}
+void CompileResult::dmpSetBoundaries(DWORD key, const Agnostic_SetBoundaries& value)
+{
+ ICorDebugInfo::OffsetMapping *om = (ICorDebugInfo::OffsetMapping *)SetBoundaries->GetBuffer(value.pMap_offset);
+ printf("SetBoundaries key 0, value ftn-%016llX cMap-%u %u{",
+ value.ftn,
+ value.cMap,
+ value.pMap_offset);
+ for(unsigned int i=0;i<value.cMap;i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("%u %u %u", om[i].ilOffset, om[i].nativeOffset, om[i].source);
+ }
+ printf("}");
+ SetBoundaries->Unlock();
+}
+bool CompileResult::repSetBoundaries(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cMap, ICorDebugInfo::OffsetMapping **pMap)
+{
+ if((SetBoundaries == nullptr)||(SetBoundaries->GetCount()==0))
+ {
+ *ftn = (CORINFO_METHOD_HANDLE)-1;
+ *cMap = -1;
+ *pMap = nullptr;
+ return false;
+ }
+ Agnostic_SetBoundaries value;
+
+ value = SetBoundaries->Get(0);
+
+ *ftn = (CORINFO_METHOD_HANDLE)value.ftn;
+ *cMap = (ULONG32)value.cMap;
+ *pMap = (ICorDebugInfo::OffsetMapping *)SetBoundaries->GetBuffer(value.pMap_offset);
+ return true;
+}
+
+//Note - Ownership of vars is transfered with this call. In replay icorjitinfo we should free it.
+void CompileResult::recSetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars)
+{
+ if(SetVars == nullptr)
+ SetVars = new LightWeightMap<DWORD, Agnostic_SetVars>();
+
+ Agnostic_SetVars value;
+
+ value.ftn = (DWORDLONG)ftn;
+ value.cVars = (DWORD)cVars;
+ value.vars_offset = (DWORD)SetVars->AddBuffer((const unsigned char*)vars, sizeof(ICorDebugInfo::NativeVarInfo)*cVars); //not deep enough.. vlt memory is pointer sized.
+
+ SetVars->Add(0, value);
+}
+void CompileResult::dmpSetVars(DWORD key, const Agnostic_SetVars& value)
+{
+ ICorDebugInfo::NativeVarInfo *om = (ICorDebugInfo::NativeVarInfo *)SetVars->GetBuffer(value.vars_offset);
+ printf("SetVars key %u, value ftn-%016llX cVars-%u %u{",
+ key,
+ value.ftn,
+ value.cVars,
+ value.vars_offset);
+ for (unsigned int i = 0; i < value.cVars; i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("so-%u eo-%u var-%u", om[i].startOffset, om[i].endOffset, om[i].varNumber);
+ }
+ printf("}");
+ SetVars->Unlock();
+}
+bool CompileResult::repSetVars(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cVars, ICorDebugInfo::NativeVarInfo **vars)
+{
+ if((SetVars == nullptr)||(SetVars->GetCount()==0))
+ {
+ *ftn = (CORINFO_METHOD_HANDLE)-1;
+ *cVars = -1;
+ *vars = nullptr;
+ return false;
+ }
+
+ Agnostic_SetVars value;
+
+ value = SetVars->Get(0);
+
+ *ftn = (CORINFO_METHOD_HANDLE)value.ftn;
+ *cVars = (ULONG32)value.cVars;
+ *vars = (ICorDebugInfo::NativeVarInfo*)SetVars->GetBuffer(value.vars_offset);
+
+ return true;
+}
+
+void CompileResult::recAllocGCInfo(size_t size, void* retval)
+{
+ allocGCInfoDets.size = size;
+ allocGCInfoDets.retval = retval;
+}
+void CompileResult::recAllocGCInfoCapture()
+{
+ if(AllocGCInfo == nullptr)
+ AllocGCInfo = new LightWeightMap<DWORD, Agnostic_AllocGCInfo>();
+
+ Agnostic_AllocGCInfo value;
+
+ value.size = allocGCInfoDets.size;
+ value.retval_offset = (DWORD)AllocGCInfo->AddBuffer((const unsigned char *)allocGCInfoDets.retval, (DWORD)allocGCInfoDets.size);
+
+ AllocGCInfo->Add(0, value);
+}
+void CompileResult::dmpAllocGCInfo(DWORD key, const Agnostic_AllocGCInfo& value)
+{
+ const unsigned char *buff = AllocGCInfo->GetBuffer(value.retval_offset);
+ printf("AllocGCInfo key 0, ");
+ printf("sz-%llu %p{ ", value.size, buff);
+ for(unsigned int i=0; i<value.size; i++)
+ printf("%02X ", *(buff+i));
+ printf("}");
+ AllocGCInfo->Unlock();
+}
+void CompileResult::repAllocGCInfo(size_t *size, void **retval)
+{
+ Agnostic_AllocGCInfo value;
+
+ value = AllocGCInfo->Get(0);
+
+ *size = (size_t)value.size;
+ if(*size>0)
+ *retval = (void *)AllocGCInfo->GetBuffer(value.retval_offset);
+}
+
+void CompileResult::recCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult result)
+{
+ if(CompileMethod == nullptr)
+ CompileMethod = new LightWeightMap<DWORD, Agnostic_CompileMethodResults>();
+
+ Agnostic_CompileMethodResults value;
+ value.nativeEntry = (DWORDLONG)*nativeEntry;
+ value.nativeSizeOfCode = (DWORD)*nativeSizeOfCode;
+ value.CorJitResult = (DWORD)result;
+
+ CompileMethod->Add(0, value);
+}
+void CompileResult::dmpCompileMethod(DWORD key, const Agnostic_CompileMethodResults& value)
+{
+ printf("CompileMethod key %u, value nativeEntry-%016llX nativeSizeOfCode-%u CorJitResult-%u",
+ key, value.nativeEntry, value.nativeSizeOfCode, value.CorJitResult);
+}
+void CompileResult::repCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult *result)
+{
+ Agnostic_CompileMethodResults value;
+ value = CompileMethod->Get(0);
+ *nativeEntry = (BYTE *)value.nativeEntry;
+ *nativeSizeOfCode = (ULONG)value.nativeSizeOfCode;
+ *result = (CorJitResult)value.CorJitResult;
+}
+
+void CompileResult::recMessageLog(const char* fmt, ...)
+{
+ // TODO-Cleanup: ???
+ return;
+ if(MessageLog == nullptr)
+ MessageLog = new DenseLightWeightMap<DWORD>();
+
+ va_list args;
+
+ // retrieve the variable arguments
+ va_start( args, fmt );
+
+ size_t len = _vscprintf(fmt, args) + 1; //space for the terminator
+
+ unsigned char *messageLogBuffer = new unsigned char[len];
+ vsprintf_s((char*)messageLogBuffer, len, fmt, args);
+ messageLogBuffer[len-1] = 0;
+ MessageLog->Append(MessageLog->AddBuffer(messageLogBuffer, (DWORD)len));
+ delete []messageLogBuffer;
+}
+void CompileResult::dmpMessageLog(DWORD key, DWORD value)
+{
+ printf("MessageLog NYI");
+}
+
+void CompileResult::recClassMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE cls)
+{
+ if(ClassMustBeLoadedBeforeCodeIsRun == nullptr)
+ ClassMustBeLoadedBeforeCodeIsRun = new DenseLightWeightMap<DWORDLONG>();
+
+ ClassMustBeLoadedBeforeCodeIsRun->Append((DWORDLONG)cls);
+}
+void CompileResult::dmpClassMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value)
+{
+ printf("ClassMustBeLoadedBeforeCodeIsRun key %u, value cls-%016llX",
+ key, value);
+}
+
+void CompileResult::recReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult, const char * reason)
+{
+ if(ReportInliningDecision == nullptr)
+ ReportInliningDecision = new DenseLightWeightMap<Agnostic_ReportInliningDecision>();
+
+ Agnostic_ReportInliningDecision value;
+
+ value.inlinerHnd = (DWORDLONG)inlinerHnd;
+ value.inlineeHnd = (DWORDLONG)inlineeHnd;
+ value.inlineResult = (DWORD)inlineResult;
+ if(reason!=nullptr)
+ value.reason_offset = (DWORD)ReportInliningDecision->AddBuffer((unsigned char*)reason, (DWORD)strlen(reason)+1);
+ else
+ value.reason_offset = -1;
+
+ ReportInliningDecision->Append(value);
+}
+void CompileResult::dmpReportInliningDecision(DWORD key, const Agnostic_ReportInliningDecision& value)
+{
+ const char *reason = (const char*)ReportInliningDecision->GetBuffer(value.reason_offset);
+ printf("ReportInliningDecision key %u, value inliner-%016llX inlinee-%016llX res-%u reason-'%s'",
+ key,
+ value.inlinerHnd,
+ value.inlineeHnd,
+ value.inlineResult,
+ reason);
+ ReportInliningDecision->Unlock();
+}
+CorInfoInline CompileResult::repReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd)
+{
+ CorInfoInline result = INLINE_FAIL;
+ if(ReportInliningDecision!=nullptr)
+ {
+ Agnostic_ReportInliningDecision *items = ReportInliningDecision->GetRawItems();
+ unsigned int cnt = ReportInliningDecision->GetCount();
+ for(unsigned int i=0;i<cnt;i++)
+ {
+ if((items[i].inlinerHnd == (DWORDLONG)inlinerHnd)&&(items[i].inlineeHnd == (DWORDLONG)inlineeHnd)&&
+ (items[i].inlineResult == INLINE_PASS))
+ result = INLINE_PASS;
+ }
+ }
+ return result;
+}
+
+void CompileResult::recSetEHcount(unsigned cEH)
+{
+ if(SetEHcount == nullptr)
+ SetEHcount = new LightWeightMap<DWORD, DWORD>();
+
+ SetEHcount->Add((DWORD)0, (DWORD)cEH);
+}
+void CompileResult::dmpSetEHcount(DWORD key, DWORD value)
+{
+ printf("SetEHcount key %u, value %u", key, value);
+}
+ULONG CompileResult::repSetEHcount()
+{
+ if(SetEHcount==nullptr)
+ SetEHcount = new LightWeightMap<DWORD, DWORD>();
+
+ ULONG ehCount;
+ int index = SetEHcount->GetIndex(0);
+ if(index < 0)
+ ehCount = 0;
+ else
+ ehCount = (ULONG)SetEHcount->Get(index);
+ return ehCount;
+}
+
+void CompileResult::recSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE *clause)
+{
+ if(SetEHinfo == nullptr)
+ SetEHinfo = new LightWeightMap<DWORD, Agnostic_CORINFO_EH_CLAUSE2>();
+
+ Agnostic_CORINFO_EH_CLAUSE2 value;
+ value.Flags = (DWORD)clause->Flags;
+ value.TryOffset = (DWORD)clause->TryOffset;
+ value.TryLength = (DWORD)clause->TryLength;
+ value.HandlerOffset = (DWORD)clause->HandlerOffset;
+ value.HandlerLength = (DWORD)clause->HandlerLength;
+ value.ClassToken = (DWORD)clause->ClassToken;
+
+ SetEHinfo->Add((DWORD)EHnumber, value);
+}
+void CompileResult::dmpSetEHinfo(DWORD key, const Agnostic_CORINFO_EH_CLAUSE2& value)
+{
+ printf("SetEHinfo key %u, value flg-%u to-%u tl-%u ho-%u hl-%u",
+ key,
+ value.Flags,
+ value.TryOffset,
+ value.TryLength,
+ value.HandlerOffset,
+ value.HandlerLength);
+ if ((CORINFO_EH_CLAUSE_FLAGS)value.Flags == CORINFO_EH_CLAUSE_FILTER)
+ {
+ printf(" fo-%u", value.ClassToken); // FilterOffset
+ }
+ else if ((CORINFO_EH_CLAUSE_FLAGS)value.Flags == CORINFO_EH_CLAUSE_NONE)
+ {
+ printf(" cls-%08X", value.ClassToken);
+ }
+ // else, no need to print for finally/fault handlers
+}
+void CompileResult::repSetEHinfo(unsigned EHnumber, ULONG *flags, ULONG *tryOffset, ULONG *tryLength, ULONG *handlerOffset, ULONG *handlerLength, ULONG *classToken)
+{
+ Agnostic_CORINFO_EH_CLAUSE2 value;
+ value = SetEHinfo->Get(EHnumber);
+
+ *flags=(ULONG)value.Flags;
+ *tryOffset=(ULONG)value.TryOffset;
+ *tryLength=(ULONG)value.TryLength;
+ *handlerOffset=(ULONG)value.HandlerOffset;
+ *handlerLength=(ULONG)value.HandlerLength;
+ *classToken=(ULONG)value.ClassToken;
+}
+
+void CompileResult::recSetMethodAttribs(CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs)
+{
+ if(SetMethodAttribs == nullptr)
+ SetMethodAttribs = new LightWeightMap<DWORDLONG, DWORD>();
+
+ SetMethodAttribs->Add((DWORDLONG)ftn, (DWORD)attribs);
+}
+void CompileResult::dmpSetMethodAttribs(DWORDLONG key, DWORD value)
+{
+ printf("SetMethodAttribs key ftn-%016llX, value attr-%08X", key, value);
+}
+CorInfoMethodRuntimeFlags CompileResult::repSetMethodAttribs (CORINFO_METHOD_HANDLE ftn)
+{
+ if((SetMethodAttribs==nullptr)||(SetMethodAttribs->GetIndex((DWORDLONG)ftn)==-1))
+ return (CorInfoMethodRuntimeFlags)0;
+ CorInfoMethodRuntimeFlags result = (CorInfoMethodRuntimeFlags)SetMethodAttribs->Get((DWORDLONG)ftn);
+ return result;
+}
+
+void CompileResult::recMethodMustBeLoadedBeforeCodeIsRun(CORINFO_METHOD_HANDLE method)
+{
+ if(MethodMustBeLoadedBeforeCodeIsRun == nullptr)
+ MethodMustBeLoadedBeforeCodeIsRun = new DenseLightWeightMap<DWORDLONG>();
+
+ MethodMustBeLoadedBeforeCodeIsRun->Append((DWORDLONG)method);
+}
+void CompileResult::dmpMethodMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value)
+{
+ printf("MethodMustBeLoadedBeforeCodeIsRun key %u, value ftn-%016llX", key, value);
+}
+
+void CompileResult::recReportTailCallDecision(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult, const char * reason)
+{
+ if(ReportTailCallDecision == nullptr)
+ ReportTailCallDecision = new DenseLightWeightMap<Agnostic_ReportTailCallDecision>();
+
+ Agnostic_ReportTailCallDecision value;
+
+ value.callerHnd = (DWORDLONG)callerHnd;
+ value.calleeHnd = (DWORDLONG)calleeHnd;
+ value.fIsTailPrefix = (DWORD)fIsTailPrefix;
+ value.tailCallResult = (DWORD)tailCallResult;
+ if(reason!=nullptr) //protect strlen
+ value.reason_index = (DWORD)ReportTailCallDecision->AddBuffer((unsigned char*)reason, (DWORD)strlen(reason)+1);
+ else
+ value.reason_index = (DWORD)-1;
+
+ ReportTailCallDecision->Append(value);
+}
+void CompileResult::dmpReportTailCallDecision(DWORD key, const Agnostic_ReportTailCallDecision& value)
+{
+ const char *reason = (const char*)ReportTailCallDecision->GetBuffer(value.reason_index);
+ printf("ReportTailCallDecision key-%u, value cr-%016llX ce-%016llX tail-%u call-%u -%s",
+ key, value.callerHnd, value.calleeHnd, value.tailCallResult, value.tailCallResult, reason);
+ ReportTailCallDecision->Unlock();
+}
+
+void CompileResult::recReportFatalError(CorJitResult result)
+{
+ if(ReportFatalError == nullptr)
+ ReportFatalError = new DenseLightWeightMap<DWORD>();
+
+ ReportFatalError->Append((DWORD)result);
+}
+void CompileResult::dmpReportFatalError(DWORD key, DWORD value)
+{
+ printf("ReportFatalError key Count-%u, value result-%08X", key, value);
+}
+
+void CompileResult::recRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta)
+{
+ repRecordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+const char* relocationTypeToString(WORD fRelocType)
+{
+ switch (fRelocType)
+ {
+ // From winnt.h
+ case IMAGE_REL_BASED_ABSOLUTE : return "absolute";
+ case IMAGE_REL_BASED_HIGH : return "high";
+ case IMAGE_REL_BASED_LOW : return "low";
+ case IMAGE_REL_BASED_HIGHLOW : return "highlow";
+ case IMAGE_REL_BASED_HIGHADJ : return "highadj";
+ case IMAGE_REL_BASED_DIR64 : return "dir64";
+
+ // From corinfo.h
+ case IMAGE_REL_BASED_REL32 : return "rel32";
+ case IMAGE_REL_BASED_THUMB_BRANCH24 : return "thumb_branch24";
+ default : return "UNKNOWN";
+ }
+}
+void CompileResult::dmpRecordRelocation(DWORD key, const Agnostic_RecordRelocation& value)
+{
+ printf("RecordRelocation key %u, value loc-%016llX tgt-%016llX fRelocType-%u(%s) slotNum-%u addlDelta-%d",
+ key,
+ value.location,
+ value.target,
+ value.fRelocType,
+ relocationTypeToString((WORD)value.fRelocType),
+ value.slotNum,
+ (INT32)value.addlDelta);
+}
+void CompileResult::repRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta)
+{
+ if(RecordRelocation == nullptr)
+ RecordRelocation = new DenseLightWeightMap<Agnostic_RecordRelocation>();
+
+ Agnostic_RecordRelocation value;
+
+ value.location = (DWORDLONG)location;
+ value.target = (DWORDLONG)target;
+ value.fRelocType = (DWORD)fRelocType;
+ value.slotNum = (DWORD)slotNum;
+ value.addlDelta = (DWORD)addlDelta;
+
+ RecordRelocation->Append(value);
+}
+
+// When we do a replay, we replace the CompileResult that was originally recorded. In most cases,
+// though, as part of our collection process we do a "thinning" operation, using "mcs -removeDup -thin",
+// which removes the CompileResult from the stored file. Because of this, we lose access to the original
+// addresses.
+//
+// Unfortunately, we may have generated code based on the original relative addresses. For example,
+// we might have generated a relocation based on the results of a call to GetFunctionEntryPoint, and
+// the delta from the generated code to that address fit in a rel32 reloc. Or, it didn't fit, but the
+// VM constructed a jump stub in the JIT code section that would fit, and returned that.
+//
+// Ideally, we would just keep all the original addresses and relocations, even if we delete the CompileResult.
+// If a new relocation has the same target as an original relocation, then simply substitute the original
+// delta and use that instead of the newly computed delta.
+//
+// For now, for rel32 relocations, if it doesn't fit, then simply pick an address immediately after the
+// current section (using originalAddr), assuming we needed a jump stub. We'll let multiple calls to potentially
+// different functions use the same address because even if they used different ones, and diffs were generated,
+// no textual diffs would appear because most of the textual call names are "hackishMethodName".
+void CompileResult::applyRelocs(unsigned char *block1, ULONG blocksize1, void *originalAddr)
+{
+ if(RecordRelocation == nullptr)
+ return;
+ if(blocksize1 == 0)
+ return;
+
+ size_t section_begin = (size_t)block1;
+ size_t section_end = (size_t)block1 + (size_t)blocksize1; // address is exclusive
+
+ LogDebug("applyRelocs block [%p,%p) block size %u, orig addr %p",
+ block1,
+ block1 + blocksize1,
+ blocksize1,
+ originalAddr);
+
+ for(unsigned int i=0;i<RecordRelocation->GetCount();i++)
+ {
+ Agnostic_RecordRelocation tmp = RecordRelocation->GetRawItems()[i];
+
+ if (Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG))
+ {
+ printf(" ");
+ dmpRecordRelocation(i, tmp);
+ printf("\n");
+ }
+
+ switch (tmp.fRelocType)
+ {
+ #if defined(_TARGET_X86_)
+ case IMAGE_REL_BASED_HIGHLOW:
+ {
+ DWORDLONG fixupLocation = tmp.location;
+
+ size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
+ if ( (section_begin <= address) && (address < section_end) ) //A reloc for our section?
+ {
+ LogDebug(" fixupLoc-%016llX (@%p) : %08X => %08X", fixupLocation, address, *(DWORD*)address, (DWORD)tmp.target);
+ *(DWORD*)address = (DWORD)tmp.target;
+ }
+ if (tmp.addlDelta != 0)
+ __debugbreak();
+ if (tmp.slotNum != 0)
+ __debugbreak();
+ }
+ break;
+ #endif // _TARGET_X86_
+
+ #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_)
+ case IMAGE_REL_BASED_REL32:
+ {
+ DWORDLONG target = tmp.target + tmp.addlDelta;
+ DWORDLONG fixupLocation = tmp.location + tmp.slotNum;
+ DWORDLONG baseAddr = fixupLocation + sizeof(INT32);
+ INT64 delta = (INT64)((BYTE *)target - baseAddr);
+
+ #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ if (delta != (INT64)(int)delta)
+ {
+ // This isn't going to fit in a signed 32-bit address. Use something that will fit,
+ // since we assume that original compilation fit fine. This is only an issue for
+ // 32-bit offsets on 64-bit targets.
+ target = (DWORDLONG)originalAddr + (DWORDLONG)blocksize1;
+ INT64 newdelta = (INT64)((BYTE *)target - baseAddr);
+
+ LogDebug(" REL32 overflow. Mapping target to %016llX. Mapping delta: %016llX => %016llX", target, delta, newdelta);
+
+ delta = newdelta;
+ }
+ #endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
+ if (delta != (INT64)(int)delta)
+ {
+ #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ LogError("REL32 relocation overflows field! delta=0x%016llX", delta);
+ #else
+ LogError("REL32 relocation overflows field! delta=0x%08X", delta);
+ #endif
+ }
+
+ // Write 32-bits into location
+ size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
+ if ( (section_begin <= address) && (address < section_end) ) //A reloc for our section?
+ {
+ LogDebug(" fixupLoc-%016llX (@%p) : %08X => %08X", fixupLocation, address, *(DWORD*)address, delta);
+ *(DWORD*)address = (DWORD)delta;
+ }
+ }
+ break;
+ #endif // defined(_TARGET_X86_) || defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_)
+
+ #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ case IMAGE_REL_BASED_DIR64:
+ {
+ DWORDLONG fixupLocation = tmp.location + tmp.slotNum;
+
+ // Write 64-bits into location
+ size_t address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
+ if ( (section_begin <= address) && (address < section_end) ) //A reloc for our section?
+ {
+ LogDebug(" fixupLoc-%016llX (@%p) %016llX => %016llX", fixupLocation, address, *(DWORDLONG*)address, tmp.target);
+ *(DWORDLONG*)address = tmp.target;
+ }
+ }
+ break;
+ #endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
+ #ifdef _TARGET_ARM64_
+ case IMAGE_REL_ARM64_BRANCH26: // 26 bit offset << 2 & sign ext, for B and BL
+ case IMAGE_REL_ARM64_PAGEBASE_REL21:
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A:
+ LogError("Unimplemented reloc type %u", tmp.fRelocType);
+ break;
+ #endif // _TARGET_ARM64_
+
+ default:
+ LogError("Unknown reloc type %u", tmp.fRelocType);
+ break;
+ }
+ }
+}
+
+void CompileResult::recProcessName(const char* name)
+{
+ if(ProcessName == nullptr)
+ ProcessName = new DenseLightWeightMap<DWORD>();
+
+ DWORD index = (DWORD)-1;
+ if(name != nullptr)
+ index = (DWORD)ProcessName->AddBuffer((unsigned char*)name, (DWORD)strlen(name)+1);
+
+ ProcessName->Append(index);
+}
+void CompileResult::dmpProcessName(DWORD key, DWORD value)
+{
+ const char *procName = (const char *)ProcessName->GetBuffer(value);
+ printf("ProcessName key %u, value '%s'", key, procName);
+ ProcessName->Unlock();
+}
+const char *CompileResult::repProcessName()
+{
+ if(ProcessName == nullptr)
+ return "hackishProcessName";
+
+ if(ProcessName->GetCount()>0)
+ {
+ return (const char*)ProcessName->GetBuffer(ProcessName->Get((DWORD)0));
+ }
+ return nullptr;
+}
+
+void CompileResult::recAddressMap(void *originalAddress, void *replayAddress, unsigned int size)
+{
+ if(AddressMap == nullptr)
+ AddressMap = new LightWeightMap<DWORDLONG, Agnostic_AddressMap>();
+
+ Agnostic_AddressMap value;
+
+ value.Address = (DWORDLONG)originalAddress;
+ value.size = (DWORD)size;
+
+ AddressMap->Add((DWORDLONG)replayAddress, value);
+}
+void CompileResult::dmpAddressMap(DWORDLONG key, const Agnostic_AddressMap& value)
+{
+ printf("AddressMap key %016llX, value addr-%016llX, size-%u",
+ key,
+ value.Address,
+ value.size);
+}
+void* CompileResult::repAddressMap(void *replayAddress)
+{
+ if (AddressMap == nullptr)
+ return nullptr;
+ Agnostic_AddressMap value;
+ value = AddressMap->Get((DWORDLONG)replayAddress);
+ return (void*)value.Address;
+}
+void *CompileResult::searchAddressMap(void *newAddress)
+{
+ if(AddressMap==nullptr)
+ return (void*)-1;
+ for(unsigned int i=0;i<AddressMap->GetCount();i++)
+ {
+ DWORDLONG replayAddress = AddressMap->GetRawKeys()[i];
+ Agnostic_AddressMap value = AddressMap->Get(replayAddress);
+ if((replayAddress<=(DWORDLONG)newAddress)&&((DWORDLONG)newAddress<(replayAddress+value.size)))
+ return (void *)(value.Address+((DWORDLONG)newAddress-replayAddress));
+ }
+ return (void*)-1;
+}
+
+void CompileResult::recReserveUnwindInfo(BOOL isFunclet, BOOL isColdCode, ULONG unwindSize)
+{
+ if(ReserveUnwindInfo == nullptr)
+ ReserveUnwindInfo = new DenseLightWeightMap<Agnostic_ReserveUnwindInfo>();
+
+ Agnostic_ReserveUnwindInfo value;
+
+ value.isFunclet = (DWORD)isFunclet;
+ value.isColdCode = (DWORD)isColdCode;
+ value.unwindSize = (DWORD)unwindSize;
+
+ ReserveUnwindInfo->Append(value);
+}
+void CompileResult::dmpReserveUnwindInfo(DWORD key, const Agnostic_ReserveUnwindInfo& value)
+{
+ printf("ReserveUnwindInfo key %u, value isFun-%u isCold-%u usz-%u",
+ key, value.isFunclet, value.isColdCode, value.unwindSize);
+}
+
+void CompileResult::recAllocUnwindInfo(BYTE *pHotCode, BYTE *pColdCode, ULONG startOffset, ULONG endOffset, ULONG unwindSize, BYTE *pUnwindBlock,
+ CorJitFuncKind funcKind)
+{
+ if(AllocUnwindInfo == nullptr)
+ AllocUnwindInfo = new DenseLightWeightMap<Agnostic_AllocUnwindInfo>();
+
+ Agnostic_AllocUnwindInfo value;
+ value.pHotCode = (DWORDLONG)pHotCode;
+ value.pColdCode = (DWORDLONG)pColdCode;
+ value.startOffset = (DWORD)startOffset;
+ value.endOffset = (DWORD)endOffset;
+ value.unwindSize = (DWORD)unwindSize;
+ value.pUnwindBlock_index = AllocUnwindInfo->AddBuffer((unsigned char*)pUnwindBlock, unwindSize);
+ value.funcKind = funcKind;
+
+ AllocUnwindInfo->Append(value);
+}
+void CompileResult::dmpAllocUnwindInfo(DWORD key, const Agnostic_AllocUnwindInfo& value)
+{
+ printf("AllocUnwindInfo key %u, value pHot-%016llX pCold-%016llX startOff-%u endOff-%u unwindSz-%u blki-%u funcKind-%u",
+ key,
+ value.pHotCode,
+ value.pColdCode,
+ value.startOffset,
+ value.endOffset,
+ value.unwindSize,
+ value.pUnwindBlock_index,
+ value.funcKind);
+}
+
+void CompileResult::recAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer, HRESULT result)
+{
+ if(AllocBBProfileBuffer == nullptr)
+ AllocBBProfileBuffer=new LightWeightMap<DWORD, Agnostic_AllocBBProfileBuffer>();
+
+ Agnostic_AllocBBProfileBuffer value;
+
+ value.count = (DWORD)count;
+ value.result = (DWORD)result;
+ value.profileBuffer_index = AllocBBProfileBuffer->AddBuffer((unsigned char*)*profileBuffer, count * sizeof(ICorJitInfo::ProfileBuffer));
+
+ AllocBBProfileBuffer->Add((DWORD)0, value);
+}
+void CompileResult::dmpAllocBBProfileBuffer(DWORD key, const Agnostic_AllocBBProfileBuffer& value)
+{
+ printf("AllocBBProfileBuffer key %u, value cnt-%u ind-%u res-%08X",
+ key,
+ value.count,
+ value.profileBuffer_index,
+ value.result);
+}
+HRESULT CompileResult::repAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer)
+{
+ Agnostic_AllocBBProfileBuffer value;
+ value = AllocBBProfileBuffer->Get((DWORD)0);
+
+ if(count != value.count)
+ __debugbreak();
+
+ HRESULT result = (HRESULT)value.result;
+ *profileBuffer = (ICorJitInfo::ProfileBuffer *)AllocBBProfileBuffer->GetBuffer(value.profileBuffer_index);
+ recAddressMap((void*)0x4242, (void*)*profileBuffer, count *(sizeof(ICorJitInfo::ProfileBuffer)));
+ return result;
+}
+
+void CompileResult::recRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle)
+{
+ repRecordCallSite(instrOffset, callSig, methodHandle);
+}
+
+void CompileResult::dmpRecordCallSite(DWORD key, const Agnostic_RecordCallSite& value)
+{
+ printf("RecordCallSite key %u, callSig{cc-%u rtc-%016llX rts-%016llX rt-%u flg-%u na-%u cc-%u ci-%u mc-%u mi-%u sig-%u pSig-%u scp-%016llX tok-%08X} ftn-%016llX",
+ key,
+ value.callSig.callConv,
+ value.callSig.retTypeClass,
+ value.callSig.retTypeSigClass,
+ value.callSig.retType,
+ value.callSig.flags,
+ value.callSig.numArgs,
+ value.callSig.sigInst_classInstCount,
+ value.callSig.sigInst_classInst_Index,
+ value.callSig.sigInst_methInstCount,
+ value.callSig.sigInst_methInst_Index,
+ value.callSig.cbSig,
+ value.callSig.pSig,
+ value.callSig.scope,
+ value.callSig.token,
+ value.methodHandle);
+}
+
+void CompileResult::repRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle)
+{
+ if (RecordCallSite == nullptr)
+ RecordCallSite = new LightWeightMap<DWORD, Agnostic_RecordCallSite>();
+
+ Agnostic_RecordCallSite value;
+ ZeroMemory(&value, sizeof(Agnostic_RecordCallSite));
+
+ if (callSig != nullptr)
+ {
+ value.callSig.callConv = (DWORD)callSig->callConv;
+ value.callSig.retTypeClass = (DWORDLONG)callSig->retTypeClass;
+ value.callSig.retTypeSigClass = (DWORDLONG)callSig->retTypeSigClass;
+ value.callSig.retType = (DWORD)callSig->retType;
+ value.callSig.flags = (DWORD)callSig->flags;
+ value.callSig.numArgs = (DWORD)callSig->numArgs;
+ value.callSig.sigInst_classInstCount = (DWORD)callSig->sigInst.classInstCount;
+ value.callSig.sigInst_classInst_Index = RecordCallSite->AddBuffer((unsigned char*)callSig->sigInst.classInst, callSig->sigInst.classInstCount*8); // porting issue
+ value.callSig.sigInst_methInstCount = (DWORD)callSig->sigInst.methInstCount;
+ value.callSig.sigInst_methInst_Index = RecordCallSite->AddBuffer((unsigned char*)callSig->sigInst.methInst, callSig->sigInst.methInstCount*8); // porting issue
+ value.callSig.args = (DWORDLONG)callSig->args;
+ value.callSig.cbSig = (DWORD)callSig->cbSig;
+ value.callSig.pSig = (DWORD)RecordCallSite->AddBuffer((unsigned char *)callSig->pSig, callSig->cbSig);
+ value.callSig.scope = (DWORDLONG)callSig->scope;
+ value.callSig.token = (DWORD)callSig->token;
+ }
+ else
+ {
+ value.callSig.callConv = (DWORD)-1;
+ value.callSig.retTypeClass = (DWORDLONG)-1;
+ value.callSig.retTypeSigClass = (DWORDLONG)-1;
+ value.callSig.retType = (DWORD)-1;
+ value.callSig.flags = (DWORD)-1;
+ value.callSig.numArgs = (DWORD)-1;
+ value.callSig.sigInst_classInstCount = (DWORD)-1;
+ value.callSig.sigInst_classInst_Index = (DWORD)-1;
+ value.callSig.sigInst_methInstCount = (DWORD)-1;
+ value.callSig.sigInst_methInst_Index = (DWORD)-1;
+ value.callSig.args = (DWORDLONG)-1;
+ value.callSig.cbSig = (DWORD)-1;
+ value.callSig.pSig = (DWORD)-1;
+ value.callSig.scope = (DWORDLONG)-1;
+ value.callSig.token = (DWORD)-1;
+ }
+
+ value.methodHandle = (DWORDLONG)methodHandle;
+
+ RecordCallSite->Add(instrOffset, value);
+}
+
+bool CompileResult::fndRecordCallSiteSigInfo(ULONG instrOffset, CORINFO_SIG_INFO *pCallSig)
+{
+ if (RecordCallSite == nullptr)
+ return false;
+
+ if (RecordCallSite->GetIndex(instrOffset) == -1)
+ return false;
+
+ Agnostic_RecordCallSite value = RecordCallSite->Get(instrOffset);
+
+ if (value.callSig.callConv == -1)
+ return false;
+
+ pCallSig->callConv = (CorInfoCallConv)value.callSig.callConv;
+ pCallSig->retTypeClass = (CORINFO_CLASS_HANDLE)value.callSig.retTypeClass;
+ pCallSig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.callSig.retTypeSigClass;
+ pCallSig->retType = (CorInfoType)value.callSig.retType;
+ pCallSig->flags = (unsigned)value.callSig.flags;
+ pCallSig->numArgs = (unsigned)value.callSig.numArgs;
+ pCallSig->sigInst.classInstCount = (unsigned)value.callSig.sigInst_classInstCount;
+ pCallSig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)RecordCallSite->GetBuffer(value.callSig.sigInst_classInst_Index);
+ pCallSig->sigInst.methInstCount = (unsigned)value.callSig.sigInst_methInstCount;
+ pCallSig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)RecordCallSite->GetBuffer(value.callSig.sigInst_methInst_Index);
+ pCallSig->args = (CORINFO_ARG_LIST_HANDLE)value.callSig.args;
+ pCallSig->cbSig = (unsigned int)value.callSig.cbSig;
+ pCallSig->pSig = (PCCOR_SIGNATURE)RecordCallSite->GetBuffer(value.callSig.pSig);
+ pCallSig->scope = (CORINFO_MODULE_HANDLE)value.callSig.scope;
+ pCallSig->token = (mdToken)value.callSig.token;
+
+ return true;
+}
+
+bool CompileResult::fndRecordCallSiteMethodHandle(ULONG instrOffset, CORINFO_METHOD_HANDLE *pMethodHandle)
+{
+ if (RecordCallSite == nullptr)
+ return false;
+
+ if (RecordCallSite->GetIndex(instrOffset) == -1)
+ return false;
+
+ Agnostic_RecordCallSite value = RecordCallSite->Get(instrOffset);
+ *pMethodHandle = (CORINFO_METHOD_HANDLE)value.methodHandle;
+
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/compileresult.h b/src/ToolBox/superpmi/superpmi-shared/compileresult.h
new file mode 100644
index 0000000000..8fc3f7a352
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/compileresult.h
@@ -0,0 +1,276 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//----------------------------------------------------------
+// CompileResult.h - CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+#ifndef _CompileResult
+#define _CompileResult
+
+#include "runtimedetails.h"
+#include "lightweightmap.h"
+
+class CompileResult
+{
+public:
+#pragma pack(push, 1)
+ struct Agnostic_RecordRelocation
+ {
+ DWORDLONG location;
+ DWORDLONG target;
+ DWORD fRelocType;
+ DWORD slotNum;
+ DWORD addlDelta;
+ };
+ struct Capture_AllocMemDetails
+ {
+ ULONG hotCodeSize;
+ ULONG coldCodeSize;
+ ULONG roDataSize;
+ ULONG xcptnsCount;
+ CorJitAllocMemFlag flag;
+ void *hotCodeBlock;
+ void *coldCodeBlock;
+ void *roDataBlock;
+ };
+ struct allocGCInfoDetails
+ {
+ size_t size;
+ void* retval;
+ };
+ struct Agnostic_AddressMap
+ {
+ DWORDLONG Address;
+ DWORD size;
+ };
+ struct Agnostic_AllocGCInfo
+ {
+ DWORDLONG size;
+ DWORD retval_offset;
+ };
+ struct Agnostic_AllocMemDetails
+ {
+ DWORD hotCodeSize;
+ DWORD coldCodeSize;
+ DWORD roDataSize;
+ DWORD xcptnsCount;
+ DWORD flag;
+ DWORD hotCodeBlock_offset;
+ DWORD coldCodeBlock_offset;
+ DWORD roDataBlock_offset;
+ DWORDLONG hotCodeBlock;
+ DWORDLONG coldCodeBlock;
+ DWORDLONG roDataBlock;
+ };
+ struct Agnostic_AllocUnwindInfo
+ {
+ DWORDLONG pHotCode;
+ DWORDLONG pColdCode;
+ DWORD startOffset;
+ DWORD endOffset;
+ DWORD unwindSize;
+ DWORD pUnwindBlock_index;
+ DWORD funcKind;
+ };
+ struct Agnostic_CompileMethodResults
+ {
+ DWORDLONG nativeEntry;
+ DWORD nativeSizeOfCode;
+ DWORD CorJitResult;
+ };
+ struct Agnostic_ReportInliningDecision
+ {
+ DWORDLONG inlinerHnd;
+ DWORDLONG inlineeHnd;
+ DWORD inlineResult;
+ DWORD reason_offset;
+ };
+ struct Agnostic_ReportTailCallDecision
+ {
+ DWORDLONG callerHnd;
+ DWORDLONG calleeHnd;
+ DWORD fIsTailPrefix;
+ DWORD tailCallResult;
+ DWORD reason_index;
+ };
+ struct Agnostic_ReserveUnwindInfo
+ {
+ DWORD isFunclet;
+ DWORD isColdCode;
+ DWORD unwindSize;
+ };
+ struct Agnostic_SetBoundaries
+ {
+ DWORDLONG ftn;
+ DWORD cMap;
+ DWORD pMap_offset;
+ };
+ struct Agnostic_SetVars
+ {
+ DWORDLONG ftn;
+ DWORD cVars;
+ DWORD vars_offset;
+ };
+ struct Agnostic_CORINFO_EH_CLAUSE2
+ {
+ DWORD Flags;
+ DWORD TryOffset;
+ DWORD TryLength;
+ DWORD HandlerOffset;
+ DWORD HandlerLength;
+ DWORD ClassToken; //one view of symetric union
+ };
+ struct Agnostic_AllocBBProfileBuffer
+ {
+ DWORD count;
+ DWORD profileBuffer_index;
+ DWORD result;
+ };
+ struct Agnostic_CORINFO_SIG_INFO2
+ {
+ DWORD callConv;
+ DWORDLONG retTypeClass;
+ DWORDLONG retTypeSigClass;
+ DWORD retType;
+ DWORD flags;
+ DWORD numArgs;
+ DWORD sigInst_classInstCount;
+ DWORD sigInst_classInst_Index;
+ DWORD sigInst_methInstCount;
+ DWORD sigInst_methInst_Index;
+ DWORDLONG args;
+ DWORD pSig;
+ DWORD cbSig;
+ DWORDLONG scope;
+ DWORD token;
+ };
+ struct Agnostic_RecordCallSite
+ {
+ Agnostic_CORINFO_SIG_INFO2 callSig;
+ DWORDLONG methodHandle;
+ };
+#pragma pack(pop)
+
+ CompileResult();
+ ~CompileResult();
+
+ bool IsEmpty();
+
+ void AddCall(const char *name);
+ unsigned int CallLog_GetCount();
+ bool CallLog_Contains(const char *str);
+ void dmpCallLog(DWORD key, DWORD value);
+
+ void dumpToConsole();
+
+ HANDLE getCodeHeap();
+
+ void recAssert(const char *buff);
+ void dmpAssertLog(DWORD key, DWORD value);
+ const char *repAssert();
+
+ void recAllocMem(ULONG hotCodeSize, ULONG coldCodeSize, ULONG roDataSize, ULONG xcptnsCount, CorJitAllocMemFlag flag,
+ void **hotCodeBlock, void **coldCodeBlock, void **roDataBlock);
+ void recAllocMemCapture();
+ void dmpAllocMem(DWORD key, const Agnostic_AllocMemDetails& value);
+ void repAllocMem(ULONG *hotCodeSize, ULONG *coldCodeSize, ULONG *roDataSize, ULONG *xcptnsCount, CorJitAllocMemFlag *flag,
+ unsigned char **hotCodeBlock, unsigned char **coldCodeBlock, unsigned char **roDataBlock,
+ void **orig_hotCodeBlock, void **orig_coldCodeBlock, void **orig_roDataBlock);
+
+ void recSetBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap);
+ void dmpSetBoundaries(DWORD key, const Agnostic_SetBoundaries& value);
+ bool repSetBoundaries(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cMap, ICorDebugInfo::OffsetMapping **pMap);
+
+ void recSetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars);
+ void dmpSetVars(DWORD key, const Agnostic_SetVars& value);
+ bool repSetVars(CORINFO_METHOD_HANDLE *ftn, ULONG32 *cVars, ICorDebugInfo::NativeVarInfo **vars);
+
+ void recAllocGCInfo(size_t size, void *retval);
+ void recAllocGCInfoCapture();
+ void dmpAllocGCInfo(DWORD key, const Agnostic_AllocGCInfo& value);
+ void repAllocGCInfo(size_t *size, void **retval);
+
+ void recCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult result);
+ void dmpCompileMethod(DWORD key, const Agnostic_CompileMethodResults& value);
+ void repCompileMethod(BYTE **nativeEntry, ULONG *nativeSizeOfCode, CorJitResult *result);
+
+ void recMessageLog(const char* fmt, ...);
+ void dmpMessageLog(DWORD key, DWORD value);
+
+ void recClassMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE cls);
+ void dmpClassMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value);
+
+ void recReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd, CorInfoInline inlineResult, const char * reason);
+ void dmpReportInliningDecision(DWORD key, const Agnostic_ReportInliningDecision& value);
+ CorInfoInline CompileResult::repReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd);
+
+ void recSetEHcount(unsigned cEH);
+ void dmpSetEHcount(DWORD key, DWORD value);
+ ULONG repSetEHcount();
+
+ void recSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE *clause);
+ void dmpSetEHinfo(DWORD key, const Agnostic_CORINFO_EH_CLAUSE2& value);
+ void repSetEHinfo(unsigned EHnumber, ULONG *flags, ULONG *tryOffset, ULONG *tryLength, ULONG *handlerOffset, ULONG *handlerLength, ULONG *classToken);
+
+ void recSetMethodAttribs (CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs);
+ void dmpSetMethodAttribs(DWORDLONG key, DWORD value);
+ CorInfoMethodRuntimeFlags repSetMethodAttribs (CORINFO_METHOD_HANDLE ftn);
+
+ void recMethodMustBeLoadedBeforeCodeIsRun(CORINFO_METHOD_HANDLE method);
+ void dmpMethodMustBeLoadedBeforeCodeIsRun(DWORD key, DWORDLONG value);
+
+ void recReportTailCallDecision(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult, const char * reason);
+ void dmpReportTailCallDecision(DWORD key, const Agnostic_ReportTailCallDecision& value);
+
+ void recReportFatalError(CorJitResult result);
+ void dmpReportFatalError(DWORD key, DWORD value);
+
+ void recRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta);
+ void dmpRecordRelocation(DWORD key, const Agnostic_RecordRelocation& value);
+ void repRecordRelocation(void *location, void *target, WORD fRelocType, WORD slotNum, INT32 addlDelta);
+ void applyRelocs(unsigned char *block1, ULONG blocksize1, void *originalAddr);
+
+ void recProcessName(const char* name);
+ void dmpProcessName(DWORD key, DWORD value);
+ const char *repProcessName();
+
+ void recAddressMap(void *original_address, void *replay_address, unsigned int size);
+ void dmpAddressMap(DWORDLONG key, const Agnostic_AddressMap& value);
+ void* repAddressMap(void *replay_address);
+ void *searchAddressMap(void *replay_address);
+
+ void recReserveUnwindInfo(BOOL isFunclet, BOOL isColdCode, ULONG unwindSize);
+ void dmpReserveUnwindInfo(DWORD key, const Agnostic_ReserveUnwindInfo& value);
+
+ void recAllocUnwindInfo(BYTE *pHotCode, BYTE *pColdCode, ULONG startOffset, ULONG endOffset, ULONG unwindSize, BYTE *pUnwindBlock,
+ CorJitFuncKind funcKind);
+ void dmpAllocUnwindInfo(DWORD key, const Agnostic_AllocUnwindInfo& value);
+
+ void recAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer, HRESULT result);
+ void dmpAllocBBProfileBuffer(DWORD key, const Agnostic_AllocBBProfileBuffer& value);
+ HRESULT repAllocBBProfileBuffer(ULONG count, ICorJitInfo::ProfileBuffer **profileBuffer);
+
+ void recRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle);
+ void dmpRecordCallSite(DWORD key, const Agnostic_RecordCallSite& value);
+ void repRecordCallSite(ULONG instrOffset, CORINFO_SIG_INFO *callSig, CORINFO_METHOD_HANDLE methodHandle);
+ bool fndRecordCallSiteSigInfo(ULONG instrOffset, CORINFO_SIG_INFO *pCallSig);
+ bool fndRecordCallSiteMethodHandle(ULONG instrOffset, CORINFO_METHOD_HANDLE *pMethodHandle);
+
+ DOUBLE secondsToCompile;
+ ULONGLONG clockCyclesToCompile;
+
+ #define LWM(map,key,value) LightWeightMap<key, value>* map;
+ #define DENSELWM(map,value) DenseLightWeightMap<value>* map;
+ #include "crlwmlist.h"
+
+//not persisted to disk.
+public:
+ LightWeightMap<DWORDLONG, DWORD> *CallTargetTypes;
+private:
+ HANDLE codeHeap;
+ Capture_AllocMemDetails allocMemDets;
+ allocGCInfoDetails allocGCInfoDets;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h b/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h
new file mode 100644
index 0000000000..84bf13e553
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h
@@ -0,0 +1,46 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// crlwmlist.h - List of all LightWeightMap in CompileResult.
+// To use, #define LWM(map, key, value) to something.
+// If you need to distinguish DenseLightWeightMap, #define DENSELWM(map, value) as well.
+//----------------------------------------------------------
+
+#ifndef LWM
+#error Define LWM before including this file.
+#endif
+
+// If the key is needed, then DENSELWM must be defined.
+#ifndef DENSELWM
+#define DENSELWM(map, value) LWM(map, this_is_an_error, value)
+#endif
+
+LWM(AddressMap, DWORDLONG, CompileResult::Agnostic_AddressMap)
+LWM(AllocBBProfileBuffer, DWORD, CompileResult::Agnostic_AllocBBProfileBuffer)
+LWM(AllocGCInfo, DWORD, CompileResult::Agnostic_AllocGCInfo)
+LWM(AllocMem, DWORD, CompileResult::Agnostic_AllocMemDetails)
+DENSELWM(AllocUnwindInfo, CompileResult::Agnostic_AllocUnwindInfo)
+DENSELWM(AssertLog, DWORD)
+DENSELWM(CallLog, DWORD)
+DENSELWM(ClassMustBeLoadedBeforeCodeIsRun, DWORDLONG)
+LWM(CompileMethod, DWORD, CompileResult::Agnostic_CompileMethodResults)
+DENSELWM(MessageLog, DWORD)
+DENSELWM(MethodMustBeLoadedBeforeCodeIsRun, DWORDLONG)
+DENSELWM(ProcessName, DWORD)
+LWM(RecordCallSite, DWORD, CompileResult::Agnostic_RecordCallSite)
+DENSELWM(RecordRelocation, CompileResult::Agnostic_RecordRelocation)
+DENSELWM(ReportFatalError, DWORD)
+DENSELWM(ReportInliningDecision, CompileResult::Agnostic_ReportInliningDecision)
+DENSELWM(ReportTailCallDecision, CompileResult::Agnostic_ReportTailCallDecision)
+DENSELWM(ReserveUnwindInfo, CompileResult::Agnostic_ReserveUnwindInfo)
+LWM(SetBoundaries, DWORD, CompileResult::Agnostic_SetBoundaries)
+LWM(SetEHcount, DWORD, DWORD)
+LWM(SetEHinfo, DWORD, CompileResult::Agnostic_CORINFO_EH_CLAUSE2)
+LWM(SetMethodAttribs, DWORDLONG, DWORD)
+LWM(SetVars, DWORD, CompileResult::Agnostic_SetVars)
+
+#undef LWM
+#undef DENSELWM
diff --git a/src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp b/src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp
new file mode 100644
index 0000000000..ad871db4ce
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/errorhandling.cpp
@@ -0,0 +1,151 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "runtimedetails.h"
+
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode)
+{
+ RaiseException(exceptionCode, 0, 0, nullptr);
+}
+
+// Allocating memory here seems moderately dangerous: we'll probably leak like a sieve...
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode, va_list args, const char *message)
+{
+ char *buffer = new char[8192];
+ ULONG_PTR *ptr = new ULONG_PTR();
+ *ptr = (ULONG_PTR)buffer;
+ _vsnprintf_s(buffer, 8192, 8191, message, args);
+
+ RaiseException(exceptionCode, 0, 1, ptr);
+}
+
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode, const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ ThrowException(exceptionCode, ap, msg);
+}
+
+SpmiException::SpmiException(PEXCEPTION_POINTERS exp)
+ : exCode(exp->ExceptionRecord->ExceptionCode)
+{
+ exMessage = (exp->ExceptionRecord->NumberParameters != 1) ? nullptr : (char *) exp->ExceptionRecord->ExceptionInformation[0];
+}
+
+SpmiException::SpmiException(DWORD exceptionCode, char* exceptionMessage)
+ : exCode(exceptionCode)
+ , exMessage(exceptionMessage)
+{
+}
+
+#if 0
+SpmiException::~SpmiException()
+{
+ delete[] exMessage;
+ exMessage = nullptr;
+}
+#endif
+
+char *SpmiException::GetExceptionMessage()
+{
+ return exMessage;
+}
+
+void SpmiException::ShowAndDeleteMessage()
+{
+ if (exMessage != nullptr)
+ {
+ LogError("Exception thrown: %s", exMessage);
+ delete[] exMessage;
+ exMessage = nullptr;
+ }
+}
+
+void SpmiException::DeleteMessage()
+{
+ delete[] exMessage;
+ exMessage = nullptr;
+}
+
+DWORD SpmiException::GetCode()
+{
+ return exCode;
+}
+
+// This filter function executes the handler only for EXCEPTIONCODE_MC, otherwise it continues the handler search.
+LONG FilterSuperPMIExceptions_CatchMC(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ return (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTIONCODE_MC) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
+}
+
+// This filter function captures the exception pointers and continues searching.
+LONG FilterSuperPMIExceptions_CaptureExceptionAndContinue(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ FilterSuperPMIExceptionsParam_CaptureException* pSPMIEParam = (FilterSuperPMIExceptionsParam_CaptureException *)lpvParam;
+ pSPMIEParam->exceptionPointers = *pExceptionPointers; // Capture the exception pointers for use later
+ pSPMIEParam->exceptionCode = pSPMIEParam->exceptionPointers.ExceptionRecord->ExceptionCode;
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+LONG FilterSuperPMIExceptions_CaptureExceptionAndStop(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ FilterSuperPMIExceptionsParam_CaptureException* pSPMIEParam = (FilterSuperPMIExceptionsParam_CaptureException *)lpvParam;
+ pSPMIEParam->exceptionPointers = *pExceptionPointers; // Capture the exception pointers for use later
+ pSPMIEParam->exceptionCode = pSPMIEParam->exceptionPointers.ExceptionRecord->ExceptionCode;
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+bool IsSuperPMIException(unsigned code)
+{
+ switch (code)
+ {
+ case EXCEPTIONCODE_MC:
+ case EXCEPTIONCODE_LWM:
+ case EXCEPTIONCODE_CALLUTILS:
+ case EXCEPTIONCODE_TYPEUTILS:
+ case EXCEPTIONCODE_ASSERT:
+ return true;
+ default:
+ if ((EXCEPTIONCODE_DebugBreakorAV <= code) && (code < EXCEPTIONCODE_DebugBreakorAV_MAX))
+ {
+ return true;
+ }
+ return false;
+ }
+}
+
+// This filter function executes the handler only for non-SuperPMI generated exceptions, otherwise it continues the handler search.
+// This allows for SuperPMI-thrown exceptions to pass through the JIT and be caught by the outer SuperPMI handler.
+LONG FilterSuperPMIExceptions_CatchNonSuperPMIException(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ return !IsSuperPMIException(pExceptionPointers->ExceptionRecord->ExceptionCode);
+}
+
+bool RunWithErrorTrap(void (*function)(void*), void* param)
+{
+ bool success = true;
+
+ struct TrapParam {
+ void (*function)(void*);
+ void *param;
+ } trapParam;
+ trapParam.function = function;
+ trapParam.param = param;
+
+ PAL_TRY(TrapParam*, pTrapParam, &trapParam)
+ {
+ pTrapParam->function(pTrapParam->param);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchNonSuperPMIException)
+ {
+ success = false;
+ }
+ PAL_ENDTRY
+
+ return success;
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/errorhandling.h b/src/ToolBox/superpmi/superpmi-shared/errorhandling.h
new file mode 100644
index 0000000000..412bf97ba6
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/errorhandling.h
@@ -0,0 +1,88 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// ErrorHandling.h - Helpers & whatnot for using SEH for errors
+//----------------------------------------------------------
+#ifndef _ErrorHandling
+#define _ErrorHandling
+
+#include "logging.h"
+
+// EXCEPTIONCODE_DebugBreakorAV is just the base exception number; calls to DebugBreakorAV()
+// pass a unique number to add to this. EXCEPTIONCODE_DebugBreakorAV_MAX is the maximum number
+// of this exception range.
+#define EXCEPTIONCODE_DebugBreakorAV 0xe0421000
+#define EXCEPTIONCODE_DebugBreakorAV_MAX 0xe0422000
+
+#define EXCEPTIONCODE_MC 0xe0422000
+#define EXCEPTIONCODE_LWM 0xe0423000
+#define EXCEPTIONCODE_CALLUTILS 0xe0426000
+#define EXCEPTIONCODE_TYPEUTILS 0xe0427000
+#define EXCEPTIONCODE_ASSERT 0xe0440000
+
+// RaiseException wrappers
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode);
+void MSC_ONLY(__declspec(noreturn)) ThrowException(DWORD exceptionCode, const char *message, ...);
+
+// Assert stuff
+#define AssertCodeMsg(expr, exCode, msg, ...) \
+ do { \
+ if (!(expr)) LogException(exCode, "SuperPMI assertion '%s' failed (" #msg ")", #expr, ##__VA_ARGS__); \
+ } while (0)
+
+#define AssertCode(expr, exCode) \
+ do { \
+ if (!(expr)) LogException(exCode, "SuperPMI assertion '%s' failed", #expr); \
+ } while (0)
+
+#define AssertMsg(expr, msg, ...) AssertCodeMsg(expr, EXCEPTIONCODE_ASSERT, msg, ##__VA_ARGS__)
+#define Assert(expr) AssertCode(expr, EXCEPTIONCODE_ASSERT)
+
+class SpmiException
+{
+private:
+ DWORD exCode;
+ char* exMessage;
+
+public:
+ SpmiException(PEXCEPTION_POINTERS exp);
+ SpmiException(DWORD exceptionCode, char* exceptionMessage);
+#if 0
+ ~SpmiException();
+#endif
+
+ char *GetExceptionMessage();
+ DWORD GetCode();
+
+ void ShowAndDeleteMessage();
+ void DeleteMessage();
+};
+
+//
+// Functions and types used by PAL_TRY-related macros.
+//
+
+extern LONG FilterSuperPMIExceptions_CatchMC(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam);
+
+struct FilterSuperPMIExceptionsParam_CaptureException
+{
+ EXCEPTION_POINTERS exceptionPointers;
+ DWORD exceptionCode;
+
+ FilterSuperPMIExceptionsParam_CaptureException()
+ : exceptionCode(0)
+ {
+ exceptionPointers.ExceptionRecord = nullptr;
+ exceptionPointers.ContextRecord = nullptr;
+ }
+};
+
+extern LONG FilterSuperPMIExceptions_CaptureExceptionAndContinue(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam);
+extern LONG FilterSuperPMIExceptions_CaptureExceptionAndStop(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam);
+
+extern bool RunWithErrorTrap(void (*function)(void*), void* param);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h
new file mode 100644
index 0000000000..671b45b392
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h
@@ -0,0 +1,70 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitCompilerImpl
+#define _ICorJitCompilerImpl
+
+// ICorJitCompilerImpl: declare for implementation all the members of the ICorJitCompiler interface (which are
+// specified as pure virtual methods). This is done once, here, and all implementations share it,
+// to avoid duplicated declarations. This file is #include'd within all the ICorJitCompiler implementation
+// classes.
+//
+// NOTE: this file is in exactly the same order, with exactly the same whitespace, as the ICorJitCompiler
+// interface declaration (with the "virtual" and "= 0" syntax removed). This is to make it easy to compare
+// against the interface declaration.
+
+public:
+ // compileMethod is the main routine to ask the JIT Compiler to create native code for a method. The
+ // method to be compiled is passed in the 'info' parameter, and the code:ICorJitInfo is used to allow the
+ // JIT to resolve tokens, and make any other callbacks needed to create the code. nativeEntry, and
+ // nativeSizeOfCode are just for convenience because the JIT asks the EE for the memory to emit code into
+ // (see code:ICorJitInfo.allocMem), so really the EE already knows where the method starts and how big
+ // it is (in fact, it could be in more than one chunk).
+ //
+ // * In the 32 bit jit this is implemented by code:CILJit.compileMethod
+ // * For the 64 bit jit this is implemented by code:PreJit.compileMethod
+ //
+ // Note: Obfuscators that are hacking the JIT depend on this method having __stdcall calling convention
+ CorJitResult __stdcall compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ );
+
+ // Some JIT compilers (most notably Phoenix), cache information about EE structures from one invocation
+ // of the compiler to the next. This can be a problem when appdomains are unloaded, as some of this
+ // cached information becomes stale. The code:ICorJitCompiler.isCacheCleanupRequired is called by the EE
+ // early first to see if jit needs these notifications, and if so, the EE will call ClearCache is called
+ // whenever the compiler should abandon its cache (eg on appdomain unload)
+ void clearCache();
+ BOOL isCacheCleanupRequired();
+
+ // Do any appropriate work at process shutdown. Default impl is to do nothing.
+ void ProcessShutdownWork(ICorStaticInfo* info); /* {}; */
+
+ // The EE asks the JIT for a "version identifier". This represents the version of the JIT/EE interface.
+ // If the JIT doesn't implement the same JIT/EE interface expected by the EE (because the JIT doesn't
+ // return the version identifier that the EE expects), then the EE fails to load the JIT.
+ //
+ void getVersionIdentifier(
+ GUID* versionIdentifier /* OUT */
+ );
+
+ // When the EE loads the System.Numerics.Vectors assembly, it asks the JIT what length (in bytes) of
+ // SIMD vector it supports as an intrinsic type. Zero means that the JIT does not support SIMD
+ // intrinsics, so the EE should use the default size (i.e. the size of the IL implementation).
+ unsigned getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags); /* { return 0; } */
+
+ // IL obfuscators sometimes interpose on the EE-JIT interface. This function allows the VM to
+ // tell the JIT to use a particular ICorJitCompiler to implement the methods of this interface,
+ // and not to implement those methods itself. The JIT must not return this method when getJit()
+ // is called. Instead, it must pass along all calls to this interface from within its own
+ // ICorJitCompiler implementation. If 'realJitCompiler' is nullptr, then the JIT should resume
+ // executing all the functions itself.
+ void setRealJit(ICorJitCompiler* realJitCompiler); /* { } */
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h
new file mode 100644
index 0000000000..8bd09e5331
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjithostimpl.h
@@ -0,0 +1,55 @@
+// 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 _ICorJitHostImpl
+#define _ICorJitHostImpl
+
+// ICorJitHost
+//
+// ICorJitHost provides the interface that the JIT uses to access some functionality that
+// would normally be provided by the operating system. This is intended to allow for
+// host-specific policies re: memory allocation, configuration value access, etc. It is
+// expected that the `ICorJitHost` value provided to `jitStartup` lives at least as
+// long as the JIT itself.
+
+// ICorJitHostImpl: declare for implementation all the members of the ICorJitHost interface (which are
+// specified as pure virtual methods). This is done once, here, and all implementations share it,
+// to avoid duplicated declarations. This file is #include'd within all the ICorJitHost implementation
+// classes.
+//
+// NOTE: this file is in exactly the same order, with exactly the same whitespace, as the ICorJitHost
+// interface declaration (with the "virtual" and "= 0" syntax removed). This is to make it easy to compare
+// against the interface declaration.
+
+public:
+ // Allocate memory of the given size in bytes. All bytes of the returned block
+ // must be initialized to zero. If `usePageAllocator` is true, the implementation
+ // should use an allocator that deals in OS pages if one exists.
+ void* allocateMemory(size_t size, bool usePageAllocator = false);
+
+ // Frees memory previous obtained by a call to `ICorJitHost::allocateMemory`. The
+ // value of the `usePageAllocator` parameter must match the value that was
+ // provided to the call to used to allocate the memory.
+ void freeMemory(void* block, bool usePageAllocator = false);
+
+ // Return an integer config value for the given key, if any exists.
+ int getIntConfigValue(
+ const wchar_t* name,
+ int defaultValue
+ );
+
+ // Return a string config value for the given key, if any exists.
+ const wchar_t* getStringConfigValue(
+ const wchar_t* name
+ );
+
+ // Free a string ConfigValue returned by the runtime.
+ // JITs using the getStringConfigValue query are required
+ // to return the string values to the runtime for deletion.
+ // This avoids leaking the memory in the JIT.
+ void freeStringConfigValue(
+ const wchar_t* value
+ );
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
new file mode 100644
index 0000000000..6eb862c8b8
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
@@ -0,0 +1,1316 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitInfoImpl
+#define _ICorJitInfoImpl
+
+// ICorJitInfoImpl: declare for implementation all the members of the ICorJitInfo interface (which are
+// specified as pure virtual methods). This is done once, here, and all implementations share it,
+// to avoid duplicated declarations. This file is #include'd within all the ICorJitInfo implementation
+// classes.
+//
+// NOTE: this file is in exactly the same order, with exactly the same whitespace, as the ICorJitInfo
+// interface declaration (with the "virtual" and "= 0" syntax removed). This is to make it easy to compare
+// against the interface declaration.
+
+public:
+ /**********************************************************************************/
+ //
+ // ICorMethodInfo
+ //
+ /**********************************************************************************/
+
+ // return flags (defined above, CORINFO_FLG_PUBLIC ...)
+ DWORD getMethodAttribs (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ );
+
+ // sets private JIT flags, which can be, retrieved using getAttrib.
+ void setMethodAttribs (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CorInfoMethodRuntimeFlags attribs /* IN */
+ );
+
+ // Given a method descriptor ftnHnd, extract signature information into sigInfo
+ //
+ // 'memberParent' is typically only set when verifying. It should be the
+ // result of calling getMemberParent.
+ void getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent = NULL /* IN */
+ );
+
+ /*********************************************************************
+ * Note the following methods can only be used on functions known
+ * to be IL. This includes the method being compiled and any method
+ * that 'getMethodInfo' returns true for
+ *********************************************************************/
+
+ // return information about a method private to the implementation
+ // returns false if method is not IL, or is otherwise unavailable.
+ // This method is used to fetch data needed to inline functions
+ bool getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ );
+
+ // Decides if you have any limitations for inlining. If everything's OK, it will return
+ // INLINE_PASS and will fill out pRestrictions with a mask of restrictions the caller of this
+ // function must respect. If caller passes pRestrictions = NULL, if there are any restrictions
+ // INLINE_FAIL will be returned
+ //
+ // The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+ //
+ // The inlined method need not be verified
+
+ CorInfoInline canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ );
+
+ // Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all
+ // inlining results when it returns INLINE_FAIL and INLINE_NEVER. All other results are reported by the
+ // JIT.
+ void reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason);
+
+
+ // Returns false if the call is across security boundaries thus we cannot tailcall
+ //
+ // The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+ bool canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ );
+
+ // Reports whether or not a method can be tail called, and why.
+ // canTailCall is responsible for reporting all results when it returns
+ // false. All other results are reported by the JIT.
+ void reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason);
+
+ // get individual exception handler
+ void getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ );
+
+ // return class it belongs to
+ CORINFO_CLASS_HANDLE getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // return module it belongs to
+ CORINFO_MODULE_HANDLE getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // This function returns the offset of the specified method in the
+ // vtable of it's owning class or interface.
+ void getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ );
+
+ // If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+ // getIntrinsicID() returns the intrinsic ID.
+ // *pMustExpand tells whether or not JIT must expand the intrinsic.
+#if COR_JIT_EE_VERSION > 460
+ CorInfoIntrinsics getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand = NULL /* OUT */
+ );
+#else
+ CorInfoIntrinsics getIntrinsicID(
+ CORINFO_METHOD_HANDLE method
+ );
+#endif
+
+ // Is the given module the System.Numerics.Vectors module?
+ // This defaults to false.
+ bool isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ ); /* { return false; } */
+
+ // return the unmanaged calling convention for a PInvoke
+ CorInfoUnmanagedCallConv getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // return if any marshaling is required for PInvoke methods. Note that
+ // method == 0 => calli. The call site sig is only needed for the varargs or calli case
+ BOOL pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ );
+
+ // Check constraints on method type arguments (only).
+ // The parent class should be checked separately using satisfiesClassConstraints(parent).
+ BOOL satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // Given a delegate target class, a target method parent class, a target method,
+ // a delegate class, check if the method signature is compatible with the Invoke method of the delegate
+ // (under the typical instantiation of any free type variables in the memberref signatures).
+ BOOL isCompatibleDelegate(
+ CORINFO_CLASS_HANDLE objCls, /* type of the delegate target, if any */
+ CORINFO_CLASS_HANDLE methodParentCls, /* exact parent of the target method, if any */
+ CORINFO_METHOD_HANDLE method, /* (representative) target method, if any */
+ CORINFO_CLASS_HANDLE delegateCls, /* exact type of the delegate */
+ BOOL *pfIsOpenDelegate /* is the delegate open */
+ );
+
+ // Determines whether the delegate creation obeys security transparency rules
+ BOOL isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ );
+
+
+ // Indicates if the method is an instance of the generic
+ // method that passes (or has passed) verification
+ CorInfoInstantiationVerification isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ );
+
+ // Loads the constraints on a typical method definition, detecting cycles;
+ // for use in verification.
+ void initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ );
+
+ // Returns enum whether the method does not require verification
+ // Also see ICorModuleInfo::canSkipVerification
+ CorInfoCanSkipVerificationResult canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ );
+
+ // load and restore the method
+ void methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ );
+
+ CORINFO_METHOD_HANDLE mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ );
+
+ // Returns the global cookie for the /GS unsafe buffer checks
+ // The cookie might be a constant value (JIT), or a handle to memory location (Ngen)
+ void getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ );
+
+ /**********************************************************************************/
+ //
+ // ICorModuleInfo
+ //
+ /**********************************************************************************/
+
+ // Resolve metadata token into runtime method handles. This function may not
+ // return normally (e.g. it may throw) if it encounters invalid metadata or other
+ // failures during token resolution.
+ void resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken);
+
+#if COR_JIT_EE_VERSION > 460
+ // Attempt to resolve a metadata token into a runtime method handle. Returns true
+ // if resolution succeeded and false otherwise (e.g. if it encounters invalid metadata
+ // during token reoslution). This method should be used instead of `resolveToken` in
+ // situations that need to be resilient to invalid metadata.
+ bool tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken);
+#endif
+
+ // Signature information about the call sig
+ void findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ );
+
+ // for Varargs, the signature at the call site may differ from
+ // the signature at the definition. Thus we need a way of
+ // fetching the call site information
+ void findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ );
+
+ CORINFO_CLASS_HANDLE getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */);
+
+ // Returns true if the module does not require verification
+ //
+ // If fQuickCheckOnlyWithoutCommit=TRUE, the function only checks that the
+ // module does not currently require verification in the current AppDomain.
+ // This decision could change in the future, and so should not be cached.
+ // If it is cached, it should only be used as a hint.
+ // This is only used by ngen for calculating certain hints.
+ //
+
+ // Returns enum whether the module does not require verification
+ // Also see ICorMethodInfo::canSkipMethodVerification();
+ CorInfoCanSkipVerificationResult canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ );
+
+ // Checks if the given metadata token is valid
+ BOOL isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ );
+
+ // Checks if the given metadata token is valid StringRef
+ BOOL isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ );
+
+ BOOL shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ );
+
+ /**********************************************************************************/
+ //
+ // ICorClassInfo
+ //
+ /**********************************************************************************/
+
+ // If the value class 'cls' is isomorphic to a primitive type it will
+ // return that type, otherwise it will return CORINFO_TYPE_VALUECLASS
+ CorInfoType asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // for completeness
+ const char* getClassName (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+
+ // Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
+ // If fNamespace=TRUE, include the namespace/enclosing classes
+ // If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
+ // If fAssembly=TRUE, suffix with a comma and the full assembly qualification
+ // return size of representation
+ int appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ );
+
+ // Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+ BOOL isValueClass(CORINFO_CLASS_HANDLE cls);
+
+ // If this method returns true, JIT will do optimization to inline the check for
+ // GetTypeFromHandle(handle) == obj.GetType()
+ BOOL canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls);
+
+ // return flags (defined above, CORINFO_FLG_PUBLIC ...)
+ DWORD getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Returns "TRUE" iff "cls" is a struct type such that return buffers used for returning a value
+ // of this type must be stack-allocated. This will generally be true only if the struct
+ // contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
+ // an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
+ // returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
+ // buffers do not require GC write barriers.
+ BOOL isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls);
+
+ CORINFO_MODULE_HANDLE getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Returns the assembly that contains the module "mod".
+ CORINFO_ASSEMBLY_HANDLE getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ );
+
+ // Returns the name of the assembly "assem".
+ const char* getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ );
+
+ // Allocate and delete process-lifetime objects. Should only be
+ // referred to from static fields, lest a leak occur.
+ // Note that "LongLifetimeFree" does not execute destructors, if "obj"
+ // is an array of a struct type with a destructor.
+ void* LongLifetimeMalloc(size_t sz);
+ void LongLifetimeFree(void* obj);
+
+ size_t getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ );
+
+ // return the number of bytes needed by an instance of the class
+ unsigned getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ unsigned getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint = FALSE
+ );
+
+ // This is only called for Value classes. It returns a boolean array
+ // in representing of 'cls' from a GC perspective. The class is
+ // assumed to be an array of machine words
+ // (of length // getClassSize(cls) / sizeof(void*)),
+ // 'gcPtrs' is a poitner to an array of BYTEs of this length.
+ // getClassGClayout fills in this array so that gcPtrs[i] is set
+ // to one of the CorInfoGCType values which is the GC type of
+ // the i-th machine word of an object of type 'cls'
+ // returns the number of GC pointers in the array
+ unsigned getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ );
+
+ // returns the number of instance fields in a class
+ unsigned getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ );
+
+ CORINFO_FIELD_HANDLE getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ );
+
+ BOOL checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ );
+
+ // returns the "NEW" helper optimized for "newCls."
+ CorInfoHelpFunc getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ );
+
+ // returns the newArr (1-Dim array) helper optimized for "arrayCls."
+ CorInfoHelpFunc getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ );
+
+ // returns the optimized "IsInstanceOf" or "ChkCast" helper
+ CorInfoHelpFunc getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ );
+
+ // returns helper to trigger static constructor
+ CorInfoHelpFunc getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ );
+
+ CorInfoHelpFunc getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ );
+
+ // This is not pretty. Boxing nullable<T> actually returns
+ // a boxed<T> not a boxed Nullable<T>. This call allows the verifier
+ // to call back to the EE on the 'box' instruction and get the transformed
+ // type to use for verification.
+ CORINFO_CLASS_HANDLE getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // returns the correct box helper for a particular class. Note
+ // that if this returns CORINFO_HELP_BOX, the JIT can assume
+ // 'standard' boxing (allocate object and copy), and optimize
+ CorInfoHelpFunc getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // returns the unbox helper. If 'helperCopies' points to a true
+ // value it means the JIT is requesting a helper that unboxes the
+ // value into a particular location and thus has the signature
+ // void unboxHelper(void* dest, CORINFO_CLASS_HANDLE cls, Object* obj)
+ // Otherwise (it is null or points at a FALSE value) it is requesting
+ // a helper that returns a poitner to the unboxed data
+ // void* unboxHelper(CORINFO_CLASS_HANDLE cls, Object* obj)
+ // The EE has the option of NOT returning the copy style helper
+ // (But must be able to always honor the non-copy style helper)
+ // The EE set 'helperCopies' on return to indicate what kind of
+ // helper has been created.
+
+ CorInfoHelpFunc getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+#if COR_JIT_EE_VERSION > 460
+ bool getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ );
+
+ void getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ );
+#else
+ void getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ );
+#endif
+
+
+ const char* getHelperName(
+ CorInfoHelpFunc
+ );
+
+ // This function tries to initialize the class (run the class constructor).
+ // this function returns whether the JIT must insert helper calls before
+ // accessing static field or method.
+ //
+ // See code:ICorClassInfo#ClassConstruction.
+ CorInfoInitClassResult initClass(
+ CORINFO_FIELD_HANDLE field, // Non-NULL - inquire about cctor trigger before static field access
+ // NULL - inquire about cctor trigger in method prolog
+ CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
+ CORINFO_CONTEXT_HANDLE context, // Exact context of method
+ BOOL speculative = FALSE // TRUE means don't actually run it
+ );
+
+ // This used to be called "loadClass". This records the fact
+ // that the class must be loaded (including restored if necessary) before we execute the
+ // code that we are currently generating. When jitting code
+ // the function loads the class immediately. When zapping code
+ // the zapper will if necessary use the call to record the fact that we have
+ // to do a fixup/restore before running the method currently being generated.
+ //
+ // This is typically used to ensure value types are loaded before zapped
+ // code that manipulates them is executed, so that the GC can access information
+ // about those value types.
+ void classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // returns the class handle for the special builtin classes
+ CORINFO_CLASS_HANDLE getBuiltinClass (
+ CorInfoClassId classId
+ );
+
+ // "System.Int32" ==> CORINFO_TYPE_INT..
+ CorInfoType getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // TRUE if child is a subtype of parent
+ // if parent is an interface, then does child implement / extend parent
+ BOOL canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ );
+
+ // TRUE if cls1 and cls2 are considered equivalent types.
+ BOOL areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ );
+
+ // returns is the intersection of cls1 and cls2.
+ CORINFO_CLASS_HANDLE mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ );
+
+ // Given a class handle, returns the Parent type.
+ // For COMObjectType, it returns Class Handle of System.Object.
+ // Returns 0 if System.Object is passed in.
+ CORINFO_CLASS_HANDLE getParentType (
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Returns the CorInfoType of the "child type". If the child type is
+ // not a primitive type, *clsRet will be set.
+ // Given an Array of Type Foo, returns Foo.
+ // Given BYREF Foo, returns Foo
+ CorInfoType getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ );
+
+ // Check constraints on type arguments of this class and parent classes
+ BOOL satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Check if this is a single dimensional array type
+ BOOL isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Get the numbmer of dimensions in an array
+ unsigned getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ );
+
+ // Get static field data for an array
+ void * getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ );
+
+ // Check Visibility rules.
+ CorInfoIsAccessAllowedResult canAccessClass(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper /* If canAccessMethod returns something other
+ than ALLOWED, then this is filled in. */
+ );
+
+ /**********************************************************************************/
+ //
+ // ICorFieldInfo
+ //
+ /**********************************************************************************/
+
+ // this function is for debugging only. It returns the field name
+ // and if 'moduleName' is non-null, it sets it to something that will
+ // says which method (a class name, or a module name)
+ const char* getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ );
+
+ // return class it belongs to
+ CORINFO_CLASS_HANDLE getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ );
+
+ // Return the field's type, if it is CORINFO_TYPE_VALUECLASS 'structType' is set
+ // the field's value class (if 'structType' == 0, then don't bother
+ // the structure info).
+ //
+ // 'memberParent' is typically only set when verifying. It should be the
+ // result of calling getMemberParent.
+ CorInfoType getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent = NULL /* IN */
+ );
+
+ // return the data member's instance offset
+ unsigned getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ );
+
+ // TODO: jit64 should be switched to the same plan as the i386 jits - use
+ // getClassGClayout to figure out the need for writebarrier helper, and inline the copying.
+ // The interpretted value class copy is slow. Once this happens, USE_WRITE_BARRIER_HELPERS
+ bool isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field);
+
+ void getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ );
+
+ // Returns true iff "fldHnd" represents a static field.
+ bool isFieldStatic(CORINFO_FIELD_HANDLE fldHnd);
+
+ /*********************************************************************************/
+ //
+ // ICorDebugInfo
+ //
+ /*********************************************************************************/
+
+ // Query the EE to find out where interesting break points
+ // in the code are. The native compiler will ensure that these places
+ // have a corresponding break point in native code.
+ //
+ // Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+ // be used only as a hint and the native compiler should not change its
+ // code generation.
+ void getBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ unsigned int *cILOffsets, // [OUT] size of pILOffsets
+ DWORD **pILOffsets, // [OUT] IL offsets of interest
+ // jit MUST free with freeArray!
+ ICorDebugInfo::BoundaryTypes *implictBoundaries // [OUT] tell jit, all boundries of this type
+ );
+
+ // Report back the mapping from IL to native code,
+ // this map should include all boundaries that 'getBoundaries'
+ // reported as interesting to the debugger.
+
+ // Note that debugger (and profiler) is assuming that all of the
+ // offsets form a contiguous block of memory, and that the
+ // OffsetMapping is sorted in order of increasing native offset.
+ void setBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cMap, // [IN] size of pMap
+ ICorDebugInfo::OffsetMapping *pMap // [IN] map including all points of interest.
+ // jit allocated with allocateArray, EE frees
+ );
+
+ // Query the EE to find out the scope of local varables.
+ // normally the JIT would trash variables after last use, but
+ // under debugging, the JIT needs to keep them live over their
+ // entire scope so that they can be inspected.
+ //
+ // Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+ // be used only as a hint and the native compiler should not change its
+ // code generation.
+ void getVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 *cVars, // [OUT] size of 'vars'
+ ICorDebugInfo::ILVarInfo **vars, // [OUT] scopes of variables of interest
+ // jit MUST free with freeArray!
+ bool *extendOthers // [OUT] it TRUE, then assume the scope
+ // of unmentioned vars is entire method
+ );
+
+ // Report back to the EE the location of every variable.
+ // note that the JIT might split lifetimes into different
+ // locations etc.
+
+ void setVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cVars, // [IN] size of 'vars'
+ ICorDebugInfo::NativeVarInfo *vars // [IN] map telling where local vars are stored at what points
+ // jit allocated with allocateArray, EE frees
+ );
+
+ /*-------------------------- Misc ---------------------------------------*/
+
+ // Used to allocate memory that needs to handed to the EE.
+ // For eg, use this to allocated memory for reporting debug info,
+ // which will be handed to the EE by setVars() and setBoundaries()
+ void * allocateArray(
+ ULONG cBytes
+ );
+
+ // JitCompiler will free arrays passed by the EE using this
+ // For eg, The EE returns memory in getVars() and getBoundaries()
+ // to the JitCompiler, which the JitCompiler should release using
+ // freeArray()
+ void freeArray(
+ void *array
+ );
+
+ /*********************************************************************************/
+ //
+ // ICorArgInfo
+ //
+ /*********************************************************************************/
+
+ // advance the pointer to the argument list.
+ // a ptr of 0, is special and always means the first argument
+ CORINFO_ARG_LIST_HANDLE getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ );
+
+ // Get the type of a particular argument
+ // CORINFO_TYPE_UNDEF is returned when there are no more arguments
+ // If the type returned is a primitive type (or an enum) *vcTypeRet set to NULL
+ // otherwise it is set to the TypeHandle associted with the type
+ // Enumerations will always look their underlying type (probably should fix this)
+ // Otherwise vcTypeRet is the type as would be seen by the IL,
+ // The return value is the type that is used for calling convention purposes
+ // (Thus if the EE wants a value class to be passed like an int, then it will
+ // return CORINFO_TYPE_INT
+ CorInfoTypeWithMod getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ );
+
+ // If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+ CORINFO_CLASS_HANDLE getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ );
+
+ // Returns type of HFA for valuetype
+ CorInfoType getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ );
+
+ /*****************************************************************************
+ * ICorErrorInfo contains methods to deal with SEH exceptions being thrown
+ * from the corinfo interface. These methods may be called when an exception
+ * with code EXCEPTION_COMPLUS is caught.
+ *****************************************************************************/
+
+ // Returns the HRESULT of the current exception
+ HRESULT GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ );
+
+ // Fetches the message of the current exception
+ // Returns the size of the message (including terminating null). This can be
+ // greater than bufferLength if the buffer is insufficient.
+ ULONG GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ );
+
+ // returns EXCEPTION_EXECUTE_HANDLER if it is OK for the compile to handle the
+ // exception, abort some work (like the inlining) and continue compilation
+ // returns EXCEPTION_CONTINUE_SEARCH if exception must always be handled by the EE
+ // things like ThreadStoppedException ...
+ // returns EXCEPTION_CONTINUE_EXECUTION if exception is fixed up by the EE
+
+ int FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ );
+
+ // Cleans up internal EE tracking when an exception is caught.
+ void HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ );
+
+ void ThrowExceptionForJitResult(
+ HRESULT result);
+
+ //Throws an exception defined by the given throw helper.
+ void ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper);
+
+#if COR_JIT_EE_VERSION > 460
+ // Runs the given function under an error trap. This allows the JIT to make calls
+ // to interface functions that may throw exceptions without needing to be aware of
+ // the EH ABI, exception types, etc. Returns true if the given function completed
+ // successfully and false otherwise.
+ bool runWithErrorTrap(
+ void (*function)(void*), // The function to run
+ void* parameter // The context parameter that will be passed to the function and the handler
+ );
+#endif
+
+/*****************************************************************************
+ * ICorStaticInfo contains EE interface methods which return values that are
+ * constant from invocation to invocation. Thus they may be embedded in
+ * persisted information like statically generated code. (This is of course
+ * assuming that all code versions are identical each time.)
+ *****************************************************************************/
+
+ // Return details about EE internal data structures
+ void getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ );
+
+ // Returns name of the JIT timer log
+ LPCWSTR getJitTimeLogFilename();
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+ // this function is for debugging only. Returns method token.
+ // Returns mdMethodDefNil for dynamic methods.
+ mdMethodDef getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ );
+
+ // this function is for debugging only. It returns the method name
+ // and if 'moduleName' is non-null, it sets it to something that will
+ // says which method (a class name, or a module name)
+ const char* getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ );
+
+ // this function is for debugging only. It returns a value that
+ // is will always be the same for a given method. It is used
+ // to implement the 'jitRange' functionality
+ unsigned getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ );
+
+ // this function is for debugging only.
+ size_t findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ );
+
+#if COR_JIT_EE_VERSION > 460
+
+ // returns whether the struct is enregisterable. Only valid on a System V VM. Returns true on success, false on failure.
+ bool getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ );
+
+#endif // COR_JIT_EE_VERSION
+
+/*****************************************************************************
+ * ICorDynamicInfo contains EE interface methods which return values that may
+ * change from invocation to invocation. They cannot be embedded in persisted
+ * data; they must be requeried each time the EE is run.
+ *****************************************************************************/
+
+ //
+ // These methods return values to the JIT which are not constant
+ // from session to session.
+ //
+ // These methods take an extra parameter : void **ppIndirection.
+ // If a JIT supports generation of prejit code (install-o-jit), it
+ // must pass a non-null value for this parameter, and check the
+ // resulting value. If *ppIndirection is NULL, code should be
+ // generated normally. If non-null, then the value of
+ // *ppIndirection is an address in the cookie table, and the code
+ // generator needs to generate an indirection through the table to
+ // get the resulting value. In this case, the return result of the
+ // function must NOT be directly embedded in the generated code.
+ //
+ // Note that if a JIT does not support prejit code generation, it
+ // may ignore the extra parameter & pass the default of NULL - the
+ // prejit ICorDynamicInfo implementation will see this & generate
+ // an error if the jitter is used in a prejit scenario.
+ //
+
+ // Return details about EE internal data structures
+
+ DWORD getThreadTLSIndex(
+ void **ppIndirection = NULL
+ );
+
+ const void * getInlinedCallFrameVptr(
+ void **ppIndirection = NULL
+ );
+
+ LONG * getAddrOfCaptureThreadGlobal(
+ void **ppIndirection = NULL
+ );
+
+ SIZE_T* getAddrModuleDomainID(CORINFO_MODULE_HANDLE module);
+
+ // return the native entry point to an EE helper (see CorInfoHelpFunc)
+ void* getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection = NULL
+ );
+
+ // return a callable address of the function (native code). This function
+ // may return a different value (depending on whether the method has
+ // been JITed or not.
+ void getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags = CORINFO_ACCESS_ANY);
+
+ // return a directly callable address. This can be used similarly to the
+ // value returned by getFunctionEntryPoint() except that it is
+ // guaranteed to be multi callable entrypoint.
+ void getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult);
+
+ // get the synchronization handle that is passed to monXstatic function
+ void* getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection = NULL
+ );
+
+ // get slow lazy string literal helper to use (CORINFO_HELP_STRCNS*).
+ // Returns CORINFO_HELP_UNDEF if lazy string literal helper cannot be used.
+ CorInfoHelpFunc getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ );
+
+ CORINFO_MODULE_HANDLE embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ CORINFO_CLASS_HANDLE embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ CORINFO_METHOD_HANDLE embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ CORINFO_FIELD_HANDLE embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection = NULL
+ );
+
+ // Given a module scope (module), a method handle (context) and
+ // a metadata token (metaTOK), fetch the handle
+ // (type, field or method) associated with the token.
+ // If this is not possible at compile-time (because the current method's
+ // code is shared and the token contains generic parameters)
+ // then indicate how the handle should be looked up at run-time.
+ //
+ void embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult);
+
+ // Return information used to locate the exact enclosing type of the current method.
+ // Used only to invoke .cctor method from code shared across generic instantiations
+ // !needsRuntimeLookup statically known (enclosing type of method itself)
+ // needsRuntimeLookup:
+ // CORINFO_LOOKUP_THISOBJ use vtable pointer of 'this' param
+ // CORINFO_LOOKUP_CLASSPARAM use vtable hidden param
+ // CORINFO_LOOKUP_METHODPARAM use enclosing type of method-desc hidden param
+ CORINFO_LOOKUP_KIND getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ );
+
+ // NOTE: the two methods below--getPInvokeUnmanagedTarget and getAddressOfPInvokeFixup--are
+ // deprecated. New code (i.e. anything that can depend on COR_JIT_EE_VERSION being
+ // greater than 460) should instead use getAddressOfPInvokeTarget, which subsumes the
+ // functionality of these methods.
+
+ // return the unmanaged target *if method has already been prelinked.*
+ void* getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection = NULL
+ );
+
+ // return address of fixup area for late-bound PInvoke calls.
+ void* getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection = NULL
+ );
+
+#if COR_JIT_EE_VERSION > 460
+ // return the address of the PInvoke target. May be a fixup area in the
+ // case of late-bound PInvoke calls.
+ void getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ );
+#endif
+
+ // Generate a cookie based on the signature that would needs to be passed
+ // to CORINFO_HELP_PINVOKE_CALLI
+ LPVOID GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection = NULL
+ );
+
+ // returns true if a VM cookie can be generated for it (might be false due to cross-module
+ // inlining, in which case the inlining should be aborted)
+ bool canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ );
+
+ // Gets a handle that is checked to see if the current method is
+ // included in "JustMyCode"
+ CORINFO_JUST_MY_CODE_HANDLE getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection = NULL
+ );
+
+ // Gets a method handle that can be used to correlate profiling data.
+ // This is the IP of a native method, or the address of the descriptor struct
+ // for IL. Always guaranteed to be unique per process, and not to move. */
+ void GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ );
+
+ // Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+ void getCallInfo(
+ // Token info
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+
+ //Generics info
+ CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
+
+ //Security info
+ CORINFO_METHOD_HANDLE callerHandle,
+
+ //Jit info
+ CORINFO_CALLINFO_FLAGS flags,
+
+ //out params
+ CORINFO_CALL_INFO *pResult
+ );
+
+ BOOL canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType);
+
+ // Returns TRUE if the Class Domain ID is the RID of the class (currently true for every class
+ // except reflection emitted classes and generics)
+ BOOL isRIDClassDomainID(CORINFO_CLASS_HANDLE cls);
+
+ // returns the class's domain ID for accessing shared statics
+ unsigned getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection = NULL
+ );
+
+
+ // return the data's address (for static fields only)
+ void* getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection = NULL
+ );
+
+ // registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+ CORINFO_VARARGS_HANDLE getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection = NULL
+ );
+
+ // returns true if a VM cookie can be generated for it (might be false due to cross-module
+ // inlining, in which case the inlining should be aborted)
+ bool canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ );
+
+ // Allocate a string literal on the heap and return a handle to it
+ InfoAccessType constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ );
+
+ InfoAccessType emptyStringLiteral(
+ void **ppValue
+ );
+
+ // (static fields only) given that 'field' refers to thread local store,
+ // return the ID (TLS index), which is used to find the begining of the
+ // TLS data area for the particular DLL 'field' is associated with.
+ DWORD getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection = NULL
+ );
+
+ // Sets another object to intercept calls to "self" and current method being compiled
+ void setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ );
+
+ // Adds an active dependency from the context method's module to the given module
+ // This is internal callback for the EE. JIT should not call it directly.
+ void addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ );
+
+ CORINFO_METHOD_HANDLE GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ );
+
+ void MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ );
+
+ // return a thunk that will copy the arguments for the given signature.
+ void* getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ );
+
+ // return memory manager that the JIT can use to allocate a regular memory
+ IEEMemoryManager* getMemoryManager();
+
+ // get a block of memory for the code, readonly data, and read-write data
+ void allocMem (
+ ULONG hotCodeSize, /* IN */
+ ULONG coldCodeSize, /* IN */
+ ULONG roDataSize, /* IN */
+ ULONG xcptnsCount, /* IN */
+ CorJitAllocMemFlag flag, /* IN */
+ void ** hotCodeBlock, /* OUT */
+ void ** coldCodeBlock, /* OUT */
+ void ** roDataBlock /* OUT */
+ );
+
+ // Reserve memory for the method/funclet's unwind information.
+ // Note that this must be called before allocMem. It should be
+ // called once for the main method, once for every funclet, and
+ // once for every block of cold code for which allocUnwindInfo
+ // will be called.
+ //
+ // This is necessary because jitted code must allocate all the
+ // memory needed for the unwindInfo at the allocMem call.
+ // For prejitted code we split up the unwinding information into
+ // separate sections .rdata and .pdata.
+ //
+ void reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ );
+
+ // Allocate and initialize the .rdata and .pdata for this method or
+ // funclet, and get the block of memory needed for the machine-specific
+ // unwind information (the info for crawling the stack frame).
+ // Note that allocMem must be called first.
+ //
+ // Parameters:
+ //
+ // pHotCode main method code buffer, always filled in
+ // pColdCode cold code buffer, only filled in if this is cold code,
+ // null otherwise
+ // startOffset start of code block, relative to appropriate code buffer
+ // (e.g. pColdCode if cold, pHotCode if hot).
+ // endOffset end of code block, relative to appropriate code buffer
+ // unwindSize size of unwind info pointed to by pUnwindBlock
+ // pUnwindBlock pointer to unwind info
+ // funcKind type of funclet (main method code, handler, filter)
+ //
+ void allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ );
+
+ // Get a block of memory needed for the code manager information,
+ // (the info for enumerating the GC pointers while crawling the
+ // stack frame).
+ // Note that allocMem must be called first
+ void * allocGCInfo (
+ size_t size /* IN */
+ );
+
+ void yieldExecution();
+
+ // Indicate how many exception handler blocks are to be returned.
+ // This is guaranteed to be called before any 'setEHinfo' call.
+ // Note that allocMem must be called before this method can be called.
+ void setEHcount (
+ unsigned cEH /* IN */
+ );
+
+ // Set the values for one particular exception handler block.
+ //
+ // Handler regions should be lexically contiguous.
+ // This is because FinallyIsUnwinding() uses lexicality to
+ // determine if a "finally" clause is executing.
+ void setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ );
+
+ // Level -> fatalError, Level 2 -> Error, Level 3 -> Warning
+ // Level 4 means happens 10 times in a run, level 5 means 100, level 6 means 1000 ...
+ // returns non-zero if the logging succeeded
+ BOOL logMsg(unsigned level, const char* fmt, va_list args);
+
+ // do an assert. will return true if the code should retry (DebugBreak)
+ // returns false, if the assert should be igored.
+ int doAssert(const char* szFile, int iLine, const char* szExpr);
+
+ void reportFatalError(CorJitResult result);
+
+ /*
+ struct ProfileBuffer // Also defined here: code:CORBBTPROF_BLOCK_DATA
+ {
+ ULONG ILOffset;
+ ULONG ExecutionCount;
+ };
+ */
+
+ // allocate a basic block profile buffer where execution counts will be stored
+ // for jitted basic blocks.
+ HRESULT allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer
+ );
+
+ // get profile information to be used for optimizing the current method. The format
+ // of the buffer is the same as the format the JIT passes to allocBBProfileBuffer.
+ HRESULT getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * numRuns
+ );
+
+ // Associates a native call site, identified by its offset in the native code stream, with
+ // the signature information and method handle the JIT used to lay out the call site. If
+ // the call site has no signature information (e.g. a helper call) or has no method handle
+ // (e.g. a CALLI P/Invoke), then null should be passed instead.
+ void recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ );
+
+ // A relocation is recorded if we are pre-jitting.
+ // A jump thunk may be inserted if we are jitting
+ void recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ );
+
+ WORD getRelocTypeHint(void * target);
+
+ // A callback to identify the range of address known to point to
+ // compiler-generated native entry points that call back into
+ // MSIL.
+ void getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ );
+
+ // For what machine does the VM expect the JIT to generate code? The VM
+ // returns one of the IMAGE_FILE_MACHINE_* values. Note that if the VM
+ // is cross-compiling (such as the case for crossgen), it will return a
+ // different value than if it was compiling for the host architecture.
+ //
+ DWORD getExpectedTargetArchitecture();
+
+#if COR_JIT_EE_VERSION > 460
+ // Fetches extended flags for a particular compilation instance. Returns
+ // the number of bytes written to the provided buffer.
+ DWORD getJitFlags(
+ CORJIT_FLAGS* flags, /* IN: Points to a buffer that will hold the extended flags. */
+ DWORD sizeInBytes /* IN: The size of the buffer. Note that this is effectively a
+ version number for the CORJIT_FLAGS value. */
+ );
+#endif
+
+#endif // _ICorJitInfoImpl
diff --git a/src/ToolBox/superpmi/superpmi-shared/lightweightmap.h b/src/ToolBox/superpmi/superpmi-shared/lightweightmap.h
new file mode 100644
index 0000000000..860f545fac
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/lightweightmap.h
@@ -0,0 +1,726 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// LightWeightMap.h -
+// Notes:
+// improvements:
+// 1. we could pack the size down a bit with various encoding tricks (don't use 4 bytes for the numItems etc)
+// 2. Add() could find the right place to insert via binary search... though the list is normally very small
+// 3. Buffer encoding could easily be more compact
+//----------------------------------------------------------
+#ifndef _LightWeightMap
+#define _LightWeightMap
+
+#include "errorhandling.h"
+
+//#define DEBUG_LWM
+
+// Common base class that implements the raw buffer functionality.
+class LightWeightMapBuffer
+{
+public:
+ LightWeightMapBuffer()
+ {
+ InitialClear();
+ }
+
+ LightWeightMapBuffer(const LightWeightMapBuffer &lwm)
+ {
+ InitialClear();
+ bufferLength = lwm.bufferLength;
+
+ if((lwm.buffer!=nullptr)&&(lwm.bufferLength>0))
+ {
+ buffer = new unsigned char[lwm.bufferLength];
+ memcpy(buffer, lwm.buffer, lwm.bufferLength);
+ }
+ }
+
+ ~LightWeightMapBuffer()
+ {
+ delete []buffer;
+ }
+
+ unsigned int AddBuffer(const unsigned char *buff, unsigned int len)
+ {
+ return AddBuffer(buff, len, false);
+ }
+
+ unsigned int AddBuffer(const unsigned char *buff, unsigned int len, bool forceUnique)
+ {
+ if(len == 0)
+ return -1;
+ if(buff == nullptr)
+ return -1;
+ int index = Contains(buff, len); //See if there is already a copy of this data in our buffer
+ if((index != -1)&&(!forceUnique))
+ return index;
+ if(locked)
+ {
+ LogError("Added item that extended the buffer after it was locked by a call to GetBuffer()");
+ __debugbreak();
+ }
+
+ unsigned int newbuffsize = bufferLength + sizeof(unsigned int) + len;
+ unsigned char *newbuffer = new unsigned char[newbuffsize];
+ unsigned int newOffset = bufferLength;
+ if(bufferLength>0)
+ memcpy(newbuffer, buffer, bufferLength);
+ memcpy(newbuffer + bufferLength + sizeof(unsigned int), buff, len);
+ *((unsigned int *)(newbuffer + bufferLength)) = len;
+ bufferLength += sizeof(unsigned int) + len;
+ if(buffer!=nullptr)
+ delete []buffer;
+ buffer = newbuffer;
+ return newOffset + sizeof(unsigned int);
+ }
+
+ unsigned char *GetBuffer(unsigned int offset)
+ {
+ if(offset == (unsigned int)-1)
+ return nullptr;
+ AssertCodeMsg(offset < bufferLength, EXCEPTIONCODE_LWM,
+ "Hit offset bigger than bufferLength %u >= %u", offset, bufferLength);
+ locked = true;
+ // LogDebug("Address given %p", &buffer[offset]);
+ return &buffer[offset];
+ }
+
+ int Contains(const unsigned char *buff, unsigned int len)
+ {
+#ifdef DEBUG_LWM
+ LogDebug("New call to Contains %d {", len);
+ for(int i=0;i<len;i++)
+ LogDebug("0x%02x ", buff[len]);
+ LogDebug("}");
+#endif
+ if(len == 0)
+ return -1;
+ if(bufferLength == 0)
+ return -1;
+ unsigned int offset = 0;
+ while((offset+sizeof(unsigned int)+len) <= bufferLength)
+ {
+ unsigned int buffChunkLen = *(unsigned int*)(&buffer[offset]);
+#ifdef DEBUG_LWM
+ LogDebug("Investigating len %d @ %d", buffChunkLen, offset);
+#endif
+ if(buffChunkLen == len)
+ {
+#ifdef DEBUG_LWM
+ LogDebug("peering into {");
+ for(int i=0;i<len;i++)
+ LogDebug("0x%02x ", buff[len]);
+ LogDebug("}");
+#endif
+ if(memcmp(&buffer[offset+sizeof(unsigned int)], buff, len)==0)
+ {
+#ifdef DEBUG_LWM
+ LogDebug("Found!");
+#endif
+ return offset+sizeof(unsigned int);
+ }
+ }
+ offset += sizeof(unsigned int) + buffChunkLen;
+ }
+#ifdef DEBUG_LWM
+ LogDebug("NOT Found!");
+#endif
+ return -1;
+ }
+
+ void Unlock() //did you really mean to use this?
+ {
+ locked = false;
+ }
+
+protected:
+
+ void InitialClear()
+ {
+ buffer = nullptr;
+ bufferLength = 0;
+ locked = false;
+ }
+
+ unsigned char* buffer; // TODO-Cleanup: this should really be a linked list; we reallocate it with every call to AddBuffer().
+ unsigned int bufferLength;
+ bool locked;
+};
+
+template<typename _Key, typename _Item>
+class LightWeightMap : public LightWeightMapBuffer
+{
+public:
+ LightWeightMap()
+ {
+ InitialClear();
+ }
+
+ LightWeightMap(const LightWeightMap &lwm)
+ {
+ InitialClear();
+ numItems = lwm.numItems;
+ strideSize = lwm.strideSize;
+ bufferLength = lwm.bufferLength;
+ locked = false;
+
+ pKeys = nullptr;
+ pItems = nullptr;
+
+ if(lwm.pKeys!=nullptr)
+ {
+ pKeys = new _Key[numItems];
+ memcpy(pKeys, lwm.pKeys, numItems * sizeof(_Key));
+ }
+ if(lwm.pItems!=nullptr)
+ {
+ pItems = new _Item[numItems];
+ memcpy(pItems, lwm.pItems, numItems * sizeof(_Item));
+ }
+ if((lwm.buffer!=nullptr)&&(lwm.bufferLength>0))
+ {
+ buffer = new unsigned char[lwm.bufferLength];
+ memcpy(buffer, lwm.buffer, lwm.bufferLength);
+ }
+ }
+
+ ~LightWeightMap()
+ {
+ if(pKeys!=nullptr)
+ delete []pKeys;
+ if(pItems!=nullptr)
+ delete []pItems;
+ }
+
+ void ReadFromArray(const unsigned char *rawData, unsigned int size)
+ {
+ unsigned int sizeOfKey = sizeof(_Key);
+ unsigned int sizeOfItem = sizeof(_Item);
+ const unsigned char *ptr = rawData;
+
+ // The tag is optional, to roll forward previous formats which don't have
+ // the tag, but which also have the same format.
+ if (0 == memcmp(ptr, "LWM1", 4))
+ {
+ ptr += 4;
+ }
+
+ memcpy(&numItems, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+ strideSize = numItems;
+
+ if(numItems > 0)
+ {
+ //Read the buffersize
+ memcpy(&bufferLength, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ AssertCodeMsg(pKeys == nullptr, EXCEPTIONCODE_LWM, "Found existing pKeys");
+ pKeys = new _Key[numItems];
+ //Set the Keys
+ memcpy(pKeys, ptr, sizeOfKey * numItems);
+ ptr += sizeOfKey * numItems;
+
+ AssertCodeMsg(pItems == nullptr, EXCEPTIONCODE_LWM, "Found existing pItems");
+ pItems = new _Item[numItems];
+ //Set the Items
+ memcpy(pItems, ptr, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ AssertCodeMsg(buffer == nullptr, EXCEPTIONCODE_LWM, "Found existing buffer");
+ buffer = new unsigned char[bufferLength];
+ //Read the buffer
+ memcpy(buffer, ptr, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ // If we have RTTI, we can make this assert report the correct type. No RTTI, though, when
+ // built with .NET Core, especially when built against the PAL.
+ AssertCodeMsg((ptr - rawData) == size, EXCEPTIONCODE_LWM,
+ "%s - Ended with unexpected sizes %Ix != %x",
+ "Unknown type" /*typeid(_Item).name()*/, ptr - rawData, size);
+ }
+
+ unsigned int CalculateArraySize()
+ {
+ int size = 4 /* tag */ + sizeof(unsigned int) /* numItems */;
+ if(numItems >0)
+ {
+ size += sizeof(unsigned int); //size of bufferLength
+ size += sizeof(_Key) * numItems; //size of keyset
+ size += sizeof(_Item) * numItems; //size of itemset
+ size += sizeof(unsigned char) * bufferLength; //bulk size of raw buffer
+ }
+ return size;
+ }
+
+ unsigned int DumpToArray(unsigned char *bytes)
+ {
+ unsigned char *ptr = bytes;
+ unsigned int size = CalculateArraySize();
+
+ //Write the tag
+ memcpy(ptr, "LWM1", 4);
+ ptr += 4;
+
+ //Write the header
+ memcpy(ptr, &numItems, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ if(numItems > 0)
+ {
+ unsigned int sizeOfKey = sizeof(_Key);
+ unsigned int sizeOfItem = sizeof(_Item);
+
+ //Write the buffersize
+ memcpy(ptr, &bufferLength, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ //Write the Keys
+ memcpy(ptr, pKeys, sizeOfKey * numItems);
+ ptr += sizeOfKey * numItems;
+
+ //Write the Items
+ memcpy(ptr, pItems, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ //Write the buffer
+ memcpy(ptr, buffer, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ // If we have RTTI, we can make this assert report the correct type. No RTTI, though, when
+ // built with .NET Core, especially when built against the PAL.
+ AssertCodeMsg((ptr - bytes) == size, EXCEPTIONCODE_LWM,
+ "%s - Ended with unexpected sizes %p != %x",
+ "Unknown type" /*typeid(_Item).name()*/, (void*)(ptr - bytes), size);
+ return size;
+ }
+
+ //its worth noting that the acutal order of insert here doesnt meet what you migth expect. Its using memcmp, so
+ // since we are on a little endian machine we'd use the lowest 8 bits as the first part of the key. This is
+ // a side effect of using the same code for large structs and DWORDS etc...
+ bool Add(_Key key, _Item item)
+ {
+ //Make sure we have space left, expand if needed
+ if(numItems == strideSize)
+ {
+ _Key *tKeys = pKeys;
+ _Item *tItems = pItems;
+ pKeys = new _Key[(strideSize * 2) + 4];
+ memcpy(pKeys, tKeys, strideSize * sizeof(_Key));
+ pItems = new _Item[(strideSize * 2) + 4];
+ memcpy(pItems, tItems, strideSize * sizeof(_Item));
+ strideSize = (strideSize * 2) + 4;
+ delete []tKeys;
+ delete []tItems;
+ }
+ unsigned int insert = 0;
+ //Find the right place to insert O(n) version
+/* for(;insert < numItems; insert++)
+ {
+ int res = memcmp(&pKeys[insert], &key, sizeof(_Key));
+ if(res == 0)
+ return false;
+ if(res>0)
+ break;
+ }
+*/
+ //O(log n) version
+ int first = 0;
+ int mid = 0;
+ int last = numItems-1;
+ while (first <= last)
+ {
+ mid = (first + last) / 2; // compute mid point.
+ int res = memcmp(&pKeys[mid], &key, sizeof(_Key));
+
+ if (res < 0)
+ first = mid + 1; // repeat search in top half.
+ else if (res > 0)
+ last = mid - 1; // repeat search in bottom half.
+ else
+ return false; // found it. return position /////
+ }
+ insert = first;
+ if(insert!=first)
+ {
+ LogDebug("index = %u f %u mid = %u l %u***************************", insert, first, mid, last);
+ __debugbreak();
+ }
+
+ if(numItems>0)
+ {
+ for(unsigned int i=numItems; i>insert; i--)
+ {
+ pKeys[i] = pKeys[i-1];
+ pItems[i] = pItems[i-1];
+ }
+ }
+
+ pKeys[insert] = key;
+ pItems[insert] = item;
+ numItems++;
+ return true;
+ }
+
+ int GetIndex(_Key key)
+ {
+ if(numItems == 0)
+ return -1;
+
+ //O(log n) version
+ int first = 0;
+ int mid = 0;
+ int last = numItems;
+ while (first <= last)
+ {
+ mid = (first + last) / 2; // compute mid point.
+ int res = memcmp(&pKeys[mid], &key, sizeof(_Key));
+
+ if (res < 0)
+ first = mid + 1; // repeat search in top half.
+ else if (res > 0)
+ last = mid - 1; // repeat search in bottom half.
+ else
+ return mid; // found it. return position /////
+ }
+ return -1; // Didn't find key
+ }
+
+ _Item GetItem(int index)
+ {
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_LWM, "Didn't find Key");
+ return pItems[index]; // found it. return position /////
+ }
+
+ _Key GetKey(int index)
+ {
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_LWM, "Didn't find Key (in GetKey)");
+ return pKeys[index];
+ }
+
+ _Item Get(_Key key)
+ {
+ int index = GetIndex(key);
+ return GetItem(index);
+ }
+
+ _Item *GetRawItems()
+ {
+ return pItems;
+ }
+
+ _Key *GetRawKeys()
+ {
+ return pKeys;
+ }
+
+ unsigned int GetCount()
+ {
+ return numItems;
+ }
+
+private:
+
+ void InitialClear()
+ {
+ numItems = 0;
+ strideSize = 0;
+ pKeys = nullptr;
+ pItems = nullptr;
+ }
+
+ unsigned int numItems; // Number of active items in the pKeys and pItems arrays.
+ unsigned int strideSize; // Allocated count of items in the pKeys and pItems arrays.
+ _Key *pKeys;
+ _Item *pItems;
+};
+
+
+// Second implementation of LightWeightMap where the Key type is an unsigned int in the range [0 .. numItems - 1] (where
+// numItems is the number of items stored in the map). Keys are not stored, since the index into the pItems array is
+// the key. Appending to the end of the map is O(1), since we don't have to search for it, and we don't have to move anything down.
+
+template<typename _Item>
+class DenseLightWeightMap : public LightWeightMapBuffer
+{
+public:
+ DenseLightWeightMap()
+ {
+ InitialClear();
+ }
+
+ DenseLightWeightMap(const DenseLightWeightMap &lwm)
+ {
+ InitialClear();
+ numItems = lwm.numItems;
+ strideSize = lwm.strideSize;
+ bufferLength = lwm.bufferLength;
+
+ if(lwm.pItems!=nullptr)
+ {
+ pItems = new _Item[numItems];
+ memcpy(pItems, lwm.pItems, numItems * sizeof(_Item));
+ }
+ if((lwm.buffer!=nullptr)&&(lwm.bufferLength>0))
+ {
+ buffer = new unsigned char[lwm.bufferLength];
+ memcpy(buffer, lwm.buffer, lwm.bufferLength);
+ }
+ }
+
+ ~DenseLightWeightMap()
+ {
+ if(pItems!=nullptr)
+ delete []pItems;
+ }
+
+ void ReadFromArray(const unsigned char *rawData, unsigned int size)
+ {
+ unsigned int sizeOfItem = sizeof(_Item);
+ const unsigned char *ptr = rawData;
+
+ // Check tag; if this is a v1 LWM, convert it to a DenseLightWeightMap in memory
+ if (0 != memcmp(ptr, "DWM1", 4))
+ {
+ ReadFromArrayAndConvertLWM1(rawData, size);
+ return;
+ }
+ ptr += 4;
+
+ memcpy(&numItems, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+ strideSize = numItems;
+
+ if(numItems > 0)
+ {
+ //Read the buffersize
+ memcpy(&bufferLength, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ AssertCodeMsg(pItems == nullptr, EXCEPTIONCODE_LWM, "Found existing pItems");
+ pItems = new _Item[numItems];
+ //Set the Items
+ memcpy(pItems, ptr, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ AssertCodeMsg(buffer == nullptr, EXCEPTIONCODE_LWM, "Found existing buffer");
+ buffer = new unsigned char[bufferLength];
+ //Read the buffer
+ memcpy(buffer, ptr, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ AssertCodeMsg((ptr - rawData) == size, EXCEPTIONCODE_LWM, "Ended with unexpected sizes %Ix != %x", ptr - rawData, size);
+ }
+
+private:
+
+ void ReadFromArrayAndConvertLWM1(const unsigned char *rawData, unsigned int size)
+ {
+ unsigned int sizeOfKey = sizeof(DWORD);
+ unsigned int sizeOfItem = sizeof(_Item);
+ const unsigned char *ptr = rawData;
+
+ memcpy(&numItems, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+ strideSize = numItems;
+
+ if(numItems > 0)
+ {
+ //Read the buffersize
+ memcpy(&bufferLength, ptr, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ DWORD* tKeys = new DWORD[numItems];
+ //Set the Keys
+ memcpy(tKeys, ptr, sizeOfKey * numItems);
+ ptr += sizeOfKey * numItems;
+
+ _Item* tItems = new _Item[numItems];
+ //Set the Items
+ memcpy(tItems, ptr, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ AssertCodeMsg(buffer == nullptr, EXCEPTIONCODE_LWM, "Found existing buffer");
+ buffer = new unsigned char[bufferLength];
+ //Read the buffer
+ memcpy(buffer, ptr, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+
+ // Convert to new format
+ AssertCodeMsg(pItems == nullptr, EXCEPTIONCODE_LWM, "Found existing pItems");
+ bool* tKeySeen = new bool[numItems]; // Used for assert, below: keys must be unique.
+ for (unsigned int i = 0; i < numItems; i++)
+ {
+ tKeySeen[i] = false;
+ }
+ pItems = new _Item[numItems];
+ for (unsigned int index = 0; index < numItems; index++)
+ {
+ unsigned int key = tKeys[index];
+ AssertCodeMsg(key < numItems, EXCEPTIONCODE_LWM, "Illegal key %d, numItems == %d", key, numItems);
+ AssertCodeMsg(!tKeySeen[key], EXCEPTIONCODE_LWM, "Duplicate key %d", key);
+ tKeySeen[key] = true;
+ pItems[key] = tItems[index];
+ }
+
+ // Note that if we get here, we've seen every key [0 .. numItems - 1].
+ delete[] tKeySeen;
+ delete[] tKeys;
+ delete[] tItems;
+ }
+
+ AssertCodeMsg((ptr - rawData) == size, EXCEPTIONCODE_LWM, "Ended with unexpected sizes %Ix != %x", ptr - rawData, size);
+ }
+
+public:
+
+ unsigned int CalculateArraySize()
+ {
+ int size = 4 /* tag */ + sizeof(unsigned int) /* numItems */;
+ if(numItems >0)
+ {
+ size += sizeof(unsigned int); //size of bufferLength
+ size += sizeof(_Item) * numItems; //size of itemset
+ size += sizeof(unsigned char) * bufferLength; //bulk size of raw buffer
+ }
+ return size;
+ }
+
+ unsigned int DumpToArray(unsigned char *bytes)
+ {
+ unsigned char *ptr = bytes;
+ unsigned int size = CalculateArraySize();
+
+ //Write the tag
+ memcpy(ptr, "DWM1", 4);
+ ptr += 4;
+
+ //Write the header
+ memcpy(ptr, &numItems, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ if(numItems > 0)
+ {
+ unsigned int sizeOfItem = sizeof(_Item);
+
+ //Write the buffersize
+ memcpy(ptr, &bufferLength, sizeof(unsigned int));
+ ptr += sizeof(unsigned int);
+
+ //Write the Items
+ memcpy(ptr, pItems, sizeOfItem * numItems);
+ ptr += sizeOfItem * numItems;
+
+ //Write the buffer
+ memcpy(ptr, buffer, bufferLength * sizeof(unsigned char));
+ ptr += bufferLength * sizeof(unsigned char);
+ }
+
+ AssertCodeMsg((ptr - bytes) == size, EXCEPTIONCODE_LWM, "Ended with unexpected sizes %Ix != %x", ptr - bytes, size);
+ return size;
+ }
+
+ bool Append(_Item item)
+ {
+ //Make sure we have space left, expand if needed
+ if (numItems == strideSize)
+ {
+ // NOTE: if this is the first allocation, we'll just allocate 4 items. ok?
+ _Item *tItems = pItems;
+ pItems = new _Item[(strideSize * 2) + 4];
+ memcpy(pItems, tItems, strideSize * sizeof(_Item));
+ strideSize = (strideSize * 2) + 4;
+ delete []tItems;
+ }
+
+ pItems[numItems] = item;
+ numItems++;
+ return true;
+ }
+
+ int GetIndex(unsigned int key)
+ {
+ if (key >= numItems)
+ return -1;
+
+ return (int)key;
+ }
+
+ _Item GetItem(int index)
+ {
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_LWM, "Didn't find Key");
+ return pItems[index]; // found it. return position /////
+ }
+
+ _Item Get(unsigned int key)
+ {
+ int index = GetIndex(key);
+ return GetItem(index);
+ }
+
+ _Item *GetRawItems()
+ {
+ return pItems;
+ }
+
+ unsigned int GetCount()
+ {
+ return numItems;
+ }
+
+private:
+
+ void InitialClear()
+ {
+ numItems = 0;
+ strideSize = 0;
+ pItems = nullptr;
+ }
+
+ static int CompareKeys(unsigned int key1, unsigned int key2)
+ {
+ if (key1 < key2)
+ return -1;
+ else if (key1 > key2)
+ return 1;
+ else
+ return 0; // equal
+ }
+
+ unsigned int numItems; // Number of active items in the pKeys and pItems arrays.
+ unsigned int strideSize; // Allocated count of items in the pKeys and pItems arrays.
+ _Item *pItems;
+};
+
+#define dumpLWM(ptr,mapName) \
+ if (ptr->mapName != nullptr) \
+ { \
+ printf("%s - %u\n", #mapName, ptr->mapName->GetCount()); \
+ for (unsigned int i = 0; i < ptr->mapName->GetCount(); i++) \
+ { \
+ printf("%u-", i); \
+ ptr->dmp##mapName(ptr->mapName->GetRawKeys()[i], ptr->mapName->GetRawItems()[i]); \
+ printf("\n"); \
+ } \
+ }
+
+#define dumpLWMDense(ptr,mapName) \
+ if (ptr->mapName != nullptr) \
+ { \
+ printf("%s - %u\n", #mapName, ptr->mapName->GetCount()); \
+ for (unsigned int i = 0; i < ptr->mapName->GetCount(); i++) \
+ { \
+ printf("%u-", i); \
+ ptr->dmp##mapName(i, ptr->mapName->GetRawItems()[i]); \
+ printf("\n"); \
+ } \
+ }
+
+#endif // _LightWeightMap
diff --git a/src/ToolBox/superpmi/superpmi-shared/logging.cpp b/src/ToolBox/superpmi/superpmi-shared/logging.cpp
new file mode 100644
index 0000000000..5f7aa48a4f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/logging.cpp
@@ -0,0 +1,342 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// Logging.cpp - Common logging and console output infrastructure
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "logging.h"
+#include "errorhandling.h"
+#include <time.h>
+
+//
+// NOTE: Since the logging system is at the core of the error handling infrastructure, any errors
+// that occur while logging will print a message to the console. Fatal errors trigger a debugbreak.
+//
+
+bool Logger::s_initialized = false;
+UINT32 Logger::s_logLevel = LOGMASK_DEFAULT;
+HANDLE Logger::s_logFile = INVALID_HANDLE_VALUE;
+char *Logger::s_logFilePath = nullptr;
+CRITICAL_SECTION Logger::s_critSec;
+
+//
+// Initializes the logging subsystem. This must be called before invoking any of the logging functionality.
+//
+/* static */
+void Logger::Initialize()
+{
+ if (!s_initialized)
+ {
+ InitializeCriticalSection(&s_critSec);
+ s_initialized = true;
+ }
+}
+
+//
+// Shuts down the logging subsystem, freeing resources, closing handles, and such.
+//
+/* static */
+void Logger::Shutdown()
+{
+ if (s_initialized)
+ {
+ DeleteCriticalSection(&s_critSec);
+ CloseLogFile();
+ s_initialized = false;
+ }
+}
+
+//
+// Opens a log file at the given path and enables file-based logging, if the given path is valid.
+//
+/* static */
+void Logger::OpenLogFile(char *logFilePath)
+{
+ if (s_logFile == INVALID_HANDLE_VALUE && logFilePath != nullptr)
+ {
+ s_logFile = CreateFileA(logFilePath,
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_DELETE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+
+ if (s_logFile != INVALID_HANDLE_VALUE)
+ {
+ // We may need the file path later in order to delete the log file
+ s_logFilePath = _strdup(logFilePath);
+ }
+ else
+ {
+ fprintf(stderr, "WARNING: [Logger::OpenLogFile] Failed to open log file '%s'. GetLastError()=%u\n", logFilePath, GetLastError());
+ }
+ }
+}
+
+//
+// Closes the currently open log file, if one is open.
+//
+/* static */
+void Logger::CloseLogFile()
+{
+ if (s_logFile != INVALID_HANDLE_VALUE)
+ {
+ // Avoid polluting the file system with empty log files
+ if (GetFileSize(s_logFile, nullptr) == 0 && s_logFilePath != nullptr)
+ {
+ // We can call this before closing the handle because DeleteFile just marks the file
+ // for deletion, i.e. it does not actually get deleted until its last handle is closed.
+ if (!DeleteFileA(s_logFilePath))
+ fprintf(stderr, "WARNING: [Logger::CloseLogFile] DeleteFile failed. GetLastError()=%u\n", GetLastError());
+ }
+
+ if (!CloseHandle(s_logFile))
+ fprintf(stderr, "WARNING: [Logger::CloseLogFile] CloseHandle failed. GetLastError()=%u\n", GetLastError());
+
+ s_logFile = INVALID_HANDLE_VALUE;
+
+ free(s_logFilePath);
+ s_logFilePath = nullptr;
+ }
+}
+
+//
+// Returns a bitmask representing the logging levels that are specified by the given string. The string
+// format is described explicitly in the command-line usage documentation for SuperPMI and MCS.
+//
+// In essence, each log level has a corresponding character representing it, and the presence of that
+// character in the specifier string indicates that the log mask should include that log level. The
+// "quiet" level will override any other level specified: that is, if a "q" is present in the string,
+// all other levels specified will be disregarded.
+//
+// If "q" is not specified, and "a" is specified, then all log levels are enabled. This is a shorthand
+// to avoid looking up all the log levels and enabling them all by specifying all the individual characters.
+//
+/* static */
+UINT32 Logger::ParseLogLevelString(const char *specifierStr)
+{
+ UINT32 logLevelMask = LOGMASK_NONE;
+
+ if (strchr(specifierStr, 'q') == nullptr) // "Quiet" overrides all other specifiers
+ {
+ if (strchr(specifierStr, 'a') != nullptr) // "All" overrides the other specifiers
+ {
+ logLevelMask |= LOGMASK_ALL;
+ }
+ else
+ {
+ if (strchr(specifierStr, 'e') != nullptr)
+ logLevelMask |= LOGLEVEL_ERROR;
+
+ if (strchr(specifierStr, 'w') != nullptr)
+ logLevelMask |= LOGLEVEL_WARNING;
+
+ if (strchr(specifierStr, 'm') != nullptr)
+ logLevelMask |= LOGLEVEL_MISSING;
+
+ if (strchr(specifierStr, 'i') != nullptr)
+ logLevelMask |= LOGLEVEL_ISSUE;
+
+ if (strchr(specifierStr, 'n') != nullptr)
+ logLevelMask |= LOGLEVEL_INFO;
+
+ if (strchr(specifierStr, 'v') != nullptr)
+ logLevelMask |= LOGLEVEL_VERBOSE;
+
+ if (strchr(specifierStr, 'd') != nullptr)
+ logLevelMask |= LOGLEVEL_DEBUG;
+ }
+ }
+
+ return logLevelMask;
+}
+
+/* static */
+void Logger::LogPrintf(const char *function, const char *file, int line,
+ LogLevel level, const char *msg, ...)
+{
+ va_list argList;
+ va_start(argList, msg);
+ LogVprintf(function, file, line, level, argList, msg);
+}
+
+//
+// Logs a message, if the given log level is enabled, to both the console and the log file. This is the
+// main logging function that all other logging functions eventually funnel into.
+//
+/* static */
+void Logger::LogVprintf(const char *function, const char *file, int line,
+ LogLevel level, va_list argList, const char *msg)
+{
+ if (!s_initialized)
+ {
+ fprintf(stderr, "ERROR: [Logger::LogVprintf] Invoked the logging system before initializing it.\n");
+ __debugbreak();
+ }
+
+ // Capture this first to make the timestamp more accurately reflect the actual time of logging
+ time_t timestamp = time(nullptr);
+
+ int fullMsgLen = _vscprintf(msg, argList) + 1; // This doesn't count the null terminator
+ char *fullMsg = new char[fullMsgLen];
+
+ _vsnprintf_s(fullMsg, fullMsgLen, fullMsgLen, msg, argList);
+ va_end(argList);
+
+ const char *logLevelStr = "INVALID_LOGLEVEL";
+ switch (level)
+ {
+ case LOGLEVEL_ERROR:
+ logLevelStr = "ERROR";
+ break;
+
+ case LOGLEVEL_WARNING:
+ logLevelStr = "WARNING";
+ break;
+
+ case LOGLEVEL_MISSING:
+ logLevelStr = "MISSING";
+ break;
+
+ case LOGLEVEL_ISSUE:
+ logLevelStr = "ISSUE";
+ break;
+
+ case LOGLEVEL_INFO:
+ logLevelStr = "INFO";
+ break;
+
+ case LOGLEVEL_VERBOSE:
+ logLevelStr = "VERBOSE";
+ break;
+
+ case LOGLEVEL_DEBUG:
+ logLevelStr = "DEBUG";
+ break;
+ }
+
+ // NOTE: This implementation doesn't guarantee that log messages will be written in chronological
+ // order, since Windows doesn't guarantee FIFO behavior when a thread relinquishes a lock. If
+ // maintaining chronological order is crucial, then we can implement a priority queueing system
+ // for log messages.
+
+ EnterCriticalSection(&s_critSec);
+
+ if (level & GetLogLevel())
+ {
+ // Sends error messages to stderr instead out stdout
+ FILE *dest = (level <= LOGLEVEL_WARNING) ? stderr : stdout;
+
+ if (level < LOGLEVEL_INFO)
+ fprintf(dest, "%s: ", logLevelStr);
+
+ fprintf(dest, "%s\n", fullMsg);
+
+ if (s_logFile != INVALID_HANDLE_VALUE)
+ {
+#ifndef FEATURE_PAL // TODO: no localtime_s() or strftime() in PAL
+ tm timeInfo;
+ errno_t err = localtime_s(&timeInfo, &timestamp);
+ if (err != 0)
+ {
+ fprintf(stderr, "WARNING: [Logger::LogVprintf] localtime failed with error %d.\n", err);
+ goto CleanUp;
+ }
+
+ size_t timeStrBuffSize = 20 * sizeof(char);
+ char *timeStr = (char *)malloc(timeStrBuffSize); // Use malloc so we can realloc if necessary
+
+ // This particular format string should always generate strings of the same size, but
+ // for the sake of robustness, we shouldn't rely on that assumption.
+ while (strftime(timeStr, timeStrBuffSize, "%Y-%m-%d %H:%M:%S", &timeInfo) == 0)
+ {
+ timeStrBuffSize *= 2;
+ timeStr = (char *)realloc(timeStr, timeStrBuffSize);
+ }
+#else // FEATURE_PAL
+ const char *timeStr = "";
+#endif // FEATURE_PAL
+
+ const char *logEntryFmtStr = "%s - %s [%s:%d] - %s - %s\r\n";
+ size_t logEntryBuffSize = _snprintf(nullptr, 0, logEntryFmtStr,
+ timeStr, function, file, line, logLevelStr, fullMsg) + 1;
+
+ char *logEntry = new char[logEntryBuffSize];
+ sprintf_s(logEntry, logEntryBuffSize, logEntryFmtStr,
+ timeStr, function, file, line, logLevelStr, fullMsg);
+
+ DWORD bytesWritten;
+
+ if (!WriteFile(s_logFile, logEntry, (DWORD)logEntryBuffSize - 1, &bytesWritten, nullptr))
+ fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to write to log file. GetLastError()=%u\n", GetLastError());
+
+ if (!FlushFileBuffers(s_logFile))
+ fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to flush log file. GetLastError()=%u\n", GetLastError());
+
+ delete[] logEntry;
+
+#ifndef FEATURE_PAL
+ free((void*)timeStr);
+#endif // !FEATURE_PAL
+ }
+ }
+
+#ifndef FEATURE_PAL
+CleanUp:
+#endif // !FEATURE_PAL
+
+ LeaveCriticalSection(&s_critSec);
+ delete[] fullMsg;
+}
+
+//
+// Special helper for logging exceptions. This logs the exception message given as a debug message.
+//
+/* static */
+void Logger::LogExceptionMessage(const char *function, const char *file, int line,
+ DWORD exceptionCode, const char *msg, ...)
+{
+ std::string fullMsg = "Exception thrown: ";
+ fullMsg += msg;
+
+ va_list argList;
+ va_start(argList, msg);
+ LogVprintf(function, file, line, LOGLEVEL_DEBUG, argList, fullMsg.c_str());
+}
+
+//
+// Logger for JIT issues. Identifies the issue type and logs the given message normally.
+//
+/* static */
+void IssueLogger::LogIssueHelper(const char *function, const char *file, int line,
+ IssueType issue, const char *msg, ...)
+{
+ std::string fullMsg;
+
+ switch (issue)
+ {
+ case ISSUE_ASSERT:
+ fullMsg += "<ASSERT>";
+ break;
+
+ case ISSUE_ASM_DIFF:
+ fullMsg += "<ASM_DIFF>";
+ break;
+
+ default:
+ fullMsg += "<UNKNOWN_ISSUE_TYPE>";
+ break;
+ }
+
+ fullMsg += " ";
+ fullMsg += msg;
+
+ va_list argList;
+ va_start(argList, msg);
+ Logger::LogVprintf(function, file, line, LOGLEVEL_ISSUE, argList, fullMsg.c_str());
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/logging.h b/src/ToolBox/superpmi/superpmi-shared/logging.h
new file mode 100644
index 0000000000..a2c388ee3e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/logging.h
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// Logging.h - Common logging and console output infrastructure
+//----------------------------------------------------------
+#ifndef _Logging
+#define _Logging
+
+//
+// General purpose logging macros
+//
+
+#define LogMessage(level, ...) \
+ Logger::LogPrintf(__func__, __FILE__, __LINE__, level, __VA_ARGS__)
+
+#define LogError(...) LogMessage(LOGLEVEL_ERROR, __VA_ARGS__)
+#define LogWarning(...) LogMessage(LOGLEVEL_WARNING, __VA_ARGS__)
+#define LogMissing(...) LogMessage(LOGLEVEL_MISSING, __VA_ARGS__)
+#define LogInfo(...) LogMessage(LOGLEVEL_INFO, __VA_ARGS__)
+#define LogVerbose(...) LogMessage(LOGLEVEL_VERBOSE, __VA_ARGS__)
+#define LogDebug(...) LogMessage(LOGLEVEL_DEBUG, __VA_ARGS__)
+
+#define LogIssue(issue, msg, ...) \
+ IssueLogger::LogIssueHelper(__FUNCTION__, __FILE__, __LINE__, issue, msg, __VA_ARGS__)
+
+// Captures the exception message before throwing so we can log it at the point of occurrence
+#define LogException(exCode, msg, ...) \
+ do { \
+ Logger::LogExceptionMessage(__FUNCTION__, __FILE__, __LINE__, exCode, msg, __VA_ARGS__); \
+ ThrowException(exCode, msg, __VA_ARGS__); \
+ } while (0)
+
+// These are specified as flags so subsets of the logging functionality can be enabled/disabled at once
+enum LogLevel : UINT32
+{
+ LOGLEVEL_ERROR = 0x00000001, // Internal fatal errors that are non-recoverable
+ LOGLEVEL_WARNING = 0x00000002, // Internal conditions that are unusual, but not serious
+ LOGLEVEL_MISSING = 0x00000004, // Failures to due to missing JIT-EE details
+ LOGLEVEL_ISSUE = 0x00000008, // Issues found with the JIT, e.g. asm diffs, asserts
+ LOGLEVEL_INFO = 0x00000010, // Notifications/summaries, e.g. 'Loaded 5 Jitted 4 FailedCompile 1'
+ LOGLEVEL_VERBOSE = 0x00000020, // Status messages, e.g. 'Jit startup took 151.12ms'
+ LOGLEVEL_DEBUG = 0x00000040 // Detailed output that's only useful for SuperPMI debugging
+};
+
+// Preset log level combinations
+enum LogLevelMask : UINT32
+{
+ LOGMASK_NONE = 0x00000000,
+ LOGMASK_DEFAULT = (LOGLEVEL_DEBUG - 1), // Default is essentially "enable everything except debug"
+ LOGMASK_ALL = 0xffffffff
+};
+
+//
+// Manages the SuperPMI logging subsystem, including both file-based logging and logging to the console.
+//
+class Logger
+{
+private:
+ static bool s_initialized;
+ static UINT32 s_logLevel;
+ static HANDLE s_logFile;
+ static char *s_logFilePath;
+ static CRITICAL_SECTION s_critSec;
+
+public:
+ static void Initialize();
+ static void Shutdown();
+
+ static void OpenLogFile(char *logFilePath);
+ static void CloseLogFile();
+
+ static UINT32 ParseLogLevelString(const char *specifierStr);
+ static void SetLogLevel(UINT32 logLevelMask) { s_logLevel = logLevelMask; }
+ static UINT32 GetLogLevel() { return s_logLevel; }
+
+ // Return true if all specified log levels are enabled.
+ static bool IsLogLevelEnabled(UINT32 logLevelMask) { return (logLevelMask & GetLogLevel()) == logLevelMask; }
+
+ static void LogPrintf(const char *function, const char *file, int line,
+ LogLevel level, const char *msg, ...);
+ static void LogVprintf(const char *function, const char *file, int line,
+ LogLevel level, va_list argList, const char *msg);
+ static void LogExceptionMessage(const char *function, const char *file, int line,
+ DWORD exceptionCode, const char *msg, ...);
+};
+
+enum IssueType
+{
+ ISSUE_ASSERT,
+ ISSUE_ASM_DIFF
+};
+
+//
+// JIT issues have more granularity than other types of log messages. The logging of issues is abstracted
+// from the normal logger to reflect this. It also will enable us to track things specific to JIT issues,
+// like statistics on issues that were found during a run.
+//
+class IssueLogger
+{
+public:
+ static void LogIssueHelper(const char *function, const char *file, int line,
+ IssueType issue, const char *msg, ...);
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
new file mode 100644
index 0000000000..774e732620
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
@@ -0,0 +1,149 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// lwmlist.h - List of all LightWeightMap in MethodContext.
+// To use, #define LWM(map, key, value) to something.
+// If you need to distinguish DenseLightWeightMap, #define DENSELWM(map, value) as well.
+//----------------------------------------------------------
+
+#ifndef LWM
+#error Define LWM before including this file.
+#endif
+
+// If the key is needed, then DENSELWM must be defined.
+#ifndef DENSELWM
+#define DENSELWM(map, value) LWM(map, this_is_an_error, value)
+#endif
+
+LWM(AppendClassName, Agnostic_AppendClassName, DWORD)
+LWM(AreTypesEquivalent, DLDL, DWORD)
+LWM(AsCorInfoType, DWORDLONG, DWORD)
+LWM(CanAccessClass, Agnostic_CanAccessClassIn, Agnostic_CanAccessClassOut)
+LWM(CanAccessFamily, DLDL, DWORD)
+LWM(CanCast, DLDL, DWORD)
+LWM(CanGetCookieForPInvokeCalliSig, Agnostic_CORINFO_SIG_INFO, DWORD)
+LWM(CanGetVarArgsHandle, Agnostic_CORINFO_SIG_INFO, DWORD)
+LWM(CanInline, DLDL, Agnostic_CanInline)
+LWM(CanInlineTypeCheckWithObjectVTable, DWORDLONG, DWORD)
+LWM(CanSkipMethodVerification, DLD, DWORD)
+LWM(CanTailCall, Agnostic_CanTailCall, DWORD)
+LWM(CheckMethodModifier, Agnostic_CheckMethodModifier, DWORD)
+LWM(CompileMethod, DWORD, Agnostic_CompileMethod)
+LWM(ConstructStringLiteral, DLD, DLD)
+LWM(EmbedClassHandle, DWORDLONG, DLDL)
+LWM(EmbedFieldHandle, DWORDLONG, DLDL)
+LWM(EmbedGenericHandle, Agnostic_EmbedGenericHandle, Agnostic_CORINFO_GENERICHANDLE_RESULT)
+LWM(EmbedMethodHandle, DWORDLONG, DLDL)
+LWM(EmbedModuleHandle, DWORDLONG, DLDL)
+DENSELWM(EmptyStringLiteral, DLD)
+DENSELWM(Environment, Agnostic_Environment)
+DENSELWM(ErrorList, DWORD)
+LWM(FilterException, DWORD, DWORD)
+LWM(FindCallSiteSig, Agnostic_FindCallSiteSig, Agnostic_CORINFO_SIG_INFO)
+LWM(FindNameOfToken, DLD, DLD)
+LWM(FindSig, Agnostic_FindSig, Agnostic_CORINFO_SIG_INFO)
+LWM(GetAddressOfPInvokeFixup, DWORDLONG, DLDL)
+LWM(GetAddressOfPInvokeTarget, DWORDLONG, DLD)
+LWM(GetAddrOfCaptureThreadGlobal, DWORD, DLDL)
+LWM(GetArgClass, Agnostic_GetArgClass, Agnostic_GetArgClass_Value)
+LWM(GetArgNext, DWORDLONG, DWORDLONG)
+LWM(GetArgType, Agnostic_GetArgType, Agnostic_GetArgType_Value)
+LWM(GetArrayInitializationData, DLD, DWORDLONG)
+LWM(GetArrayRank, DWORDLONG, DWORD)
+LWM(GetBBProfileData, DWORDLONG, Agnostic_GetBBProfileData)
+LWM(GetBoundaries, DWORDLONG, Agnostic_GetBoundaries)
+LWM(GetBoxHelper, DWORDLONG, DWORD)
+LWM(GetBuiltinClass, DWORD, DWORDLONG)
+LWM(GetCallInfo, Agnostic_GetCallInfo, Agnostic_CORINFO_CALL_INFO)
+LWM(GetCastingHelper, Agnostic_GetCastingHelper, DWORD)
+LWM(GetChildType, DWORDLONG, DLD)
+LWM(GetClassAlignmentRequirement, DLD, DWORD)
+LWM(GetClassAttribs, DWORDLONG, DWORD)
+LWM(GetClassDomainID, DWORDLONG, DLD)
+LWM(GetClassGClayout, DWORDLONG, Agnostic_GetClassGClayout)
+LWM(GetClassModuleIdForStatics, DWORDLONG, Agnostic_GetClassModuleIdForStatics)
+LWM(GetClassName, DWORDLONG, DWORD)
+LWM(GetClassNumInstanceFields, DWORDLONG, DWORD)
+LWM(GetClassSize, DWORDLONG, DWORD)
+LWM(GetCookieForPInvokeCalliSig, Agnostic_CORINFO_SIG_INFO, DLDL)
+LWM(GetDelegateCtor, Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut)
+LWM(GetEEInfo, DWORD, Agnostic_CORINFO_EE_INFO)
+LWM(GetEHinfo, DLD, Agnostic_CORINFO_EH_CLAUSE)
+LWM(GetFieldAddress, DWORDLONG, Agnostic_GetFieldAddress)
+LWM(GetFieldClass, DWORDLONG, DWORDLONG)
+LWM(GetFieldInClass, DLD, DWORDLONG)
+LWM(GetFieldInfo, Agnostic_GetFieldInfo, Agnostic_CORINFO_FIELD_INFO)
+LWM(GetFieldName, DWORDLONG, DD)
+LWM(GetFieldOffset, DWORDLONG, DWORD)
+LWM(GetFieldThreadLocalStoreID, DWORDLONG, DLD)
+LWM(GetFieldType, DLDL, DLD)
+LWM(GetFunctionEntryPoint, DLD, DLD)
+LWM(GetFunctionFixedEntryPoint, DWORDLONG, Agnostic_CORINFO_CONST_LOOKUP)
+LWM(GetGSCookie, DWORD, DLDL)
+LWM(GetHelperFtn, DWORD, DLDL)
+LWM(GetHelperName, DWORD, DWORD)
+LWM(GetInlinedCallFrameVptr, DWORD, DLDL)
+LWM(GetIntConfigValue, Agnostic_ConfigIntInfo, DWORD)
+LWM(GetIntrinsicID, DWORDLONG, DD)
+LWM(GetJitFlags, DWORD, DD)
+LWM(GetJitTimeLogFilename, DWORD, DWORD)
+LWM(GetJustMyCodeHandle, DWORDLONG, DLDL)
+LWM(GetLazyStringLiteralHelper, DWORDLONG, DWORD)
+LWM(GetLocationOfThisType, DWORDLONG, Agnostic_CORINFO_LOOKUP_KIND)
+LWM(GetMethodAttribs, DWORDLONG, DWORD)
+LWM(GetMethodClass, DWORDLONG, DWORDLONG)
+LWM(GetMethodDefFromMethod, DWORDLONG, DWORD)
+LWM(GetMethodHash, DWORDLONG, DWORD)
+LWM(GetMethodInfo, DWORDLONG, Agnostic_GetMethodInfo)
+LWM(GetMethodName, DLD, DD)
+LWM(GetMethodSig, DLDL, Agnostic_CORINFO_SIG_INFO)
+LWM(GetMethodSync, DWORDLONG, DLDL)
+LWM(GetMethodVTableOffset, DWORDLONG, DD)
+LWM(GetNewArrHelper, DWORDLONG, DWORD)
+LWM(GetNewHelper, Agnostic_GetNewHelper, DWORD)
+LWM(GetParentType, DWORDLONG, DWORDLONG)
+LWM(GetPInvokeUnmanagedTarget, DWORDLONG, DLDL)
+LWM(GetProfilingHandle, DWORD, Agnostic_GetProfilingHandle)
+LWM(GetReadyToRunHelper, DWORDLONG, DWORD)
+LWM(GetReadyToRunDelegateCtorHelper, DWORDLONG, DWORD)
+LWM(GetRelocTypeHint, DWORDLONG, DWORD)
+LWM(GetSecurityPrologHelper, DWORDLONG, DWORD)
+LWM(GetSharedCCtorHelper, DWORDLONG, DWORD)
+LWM(GetStringConfigValue, DWORD, DWORD)
+LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor)
+LWM(GetTailCallCopyArgsThunk, Agnostic_GetTailCallCopyArgsThunk, DWORDLONG)
+LWM(GetThreadTLSIndex, DWORD, DLD)
+LWM(GetTokenTypeAsHandle, Agnostic_CORINFO_RESOLVED_TOKEN, DWORDLONG)
+LWM(GetTypeForBox, DWORDLONG, DWORDLONG)
+LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD)
+LWM(GetUnBoxHelper, DWORDLONG, DWORD)
+LWM(GetUnmanagedCallConv, DWORDLONG, DWORD)
+LWM(GetVarArgsHandle, Agnostic_CORINFO_SIG_INFO, DLDL)
+LWM(GetVars, DWORDLONG, Agnostic_GetVars)
+DENSELWM(HandleException, DWORD)
+LWM(InitClass, Agnostic_InitClass, DWORD)
+LWM(InitConstraintsForVerification, DWORDLONG, DD)
+LWM(IsCompatibleDelegate, Agnostic_IsCompatibleDelegate, DD)
+LWM(IsDelegateCreationAllowed, DLDL, DWORD)
+LWM(IsFieldStatic, DWORDLONG, DWORD)
+LWM(IsInSIMDModule, DWORDLONG, DWORD)
+LWM(IsInstantiationOfVerifiedGeneric, DWORDLONG, DWORD)
+LWM(IsSDArray, DWORDLONG, DWORD)
+LWM(IsStructRequiringStackAllocRetBuf, DWORDLONG, DWORD)
+LWM(IsValidStringRef, DLD, DWORD)
+LWM(IsValidToken, DLD, DWORD)
+LWM(IsValueClass, DWORDLONG, DWORD)
+LWM(IsWriteBarrierHelperRequired, DWORDLONG, DWORD)
+LWM(MergeClasses, DLDL, DWORDLONG)
+LWM(PInvokeMarshalingRequired, Agnostic_PInvokeMarshalingRequired, DWORD)
+LWM(ResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
+LWM(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout)
+LWM(SatisfiesClassConstraints, DWORDLONG, DWORD)
+LWM(SatisfiesMethodConstraints, DLDL, DWORD)
+LWM(ShouldEnforceCallvirtRestriction, DWORDLONG, DWORD)
+
+#undef LWM
+#undef DENSELWM
diff --git a/src/ToolBox/superpmi/superpmi-shared/mclist.cpp b/src/ToolBox/superpmi/superpmi-shared/mclist.cpp
new file mode 100644
index 0000000000..6a6f8701bf
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/mclist.cpp
@@ -0,0 +1,258 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MCList.h - MethodContext List utility class
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "mclist.h"
+#include "logging.h"
+
+bool MCList::processArgAsMCL(char *input, int *count, int **list)
+{
+ // If it contains only '0-9', '-', ',' try to see it as a range list, else try to load as a file
+ bool isRangeList = true;
+
+ size_t len = strlen(input);
+
+ for (unsigned int i=0; (i < len) && isRangeList; i++)
+ {
+ if ((input[i] != '-') && (input[i] != ',') && (!isdigit((unsigned char)input[i])))
+ isRangeList = false;
+ }
+
+ if (isRangeList)
+ {
+ //Count items
+ *count = 0;
+ unsigned rangeStart = 0;
+ bool inRange = false;
+ unsigned scratch = 0;
+ bool foundDigit = false;
+
+ char *tail = input+len;
+
+ for(char* head = input; head <= tail; head++)
+ {
+ scratch = 0;
+ foundDigit = false;
+ while((head<=tail)&&(isdigit((unsigned char)*head)))
+ {
+ scratch = (scratch*10)+((*head)-'0');
+ foundDigit = true;
+ head++;
+ }
+ if(foundDigit)
+ {
+ if(inRange)
+ {
+ inRange = false;
+ if(rangeStart>=scratch)
+ {
+ LogError("Invalid range in '%s'", input);
+ return false;
+ }
+ (*count) += scratch-rangeStart;
+ }
+ else
+ {
+ rangeStart = scratch;
+ (*count)++;
+ }
+ }
+ if(*head == '-')
+ inRange = true;
+ }
+
+ if (*count == 0)
+ {
+ LogError("Didn't find a list!");
+ return false;
+ }
+
+ inRange = false;
+ rangeStart = 0;
+
+ int *ll = new int[*count];
+ *list = ll;
+ int index = 0;
+ ll[index] = 0;
+
+ for(char* head = input; head <= tail; head++)
+ {
+ scratch = 0;
+ foundDigit = false;
+ while((head<=tail)&&(isdigit((unsigned char)*head)))
+ {
+ scratch = (scratch*10)+((*head)-'0');
+ foundDigit = true;
+ head++;
+ }
+ if(foundDigit)
+ {
+ if(inRange)
+ {
+ inRange = false;
+ for(unsigned int i=rangeStart+1;i<=scratch;i++)
+ ll[index++] = i;
+ }
+ else
+ {
+ rangeStart = scratch;
+ ll[index++] = scratch;
+ }
+ }
+ if(*head == '-')
+ inRange = true;
+ }
+ if(inRange)
+ {
+ LogError("Found invalid external range in '%s'", input);
+ return false;
+ }
+ goto checkMCL;
+ }
+ else
+ {
+ char *lastdot = strrchr(input,'.');
+ if(lastdot != nullptr && _stricmp(lastdot, ".mcl")==0)
+ {
+ //Read MCLFile
+ if (!getLineData(input, count, list))
+ return false;
+ if (*count >= 0)
+ goto checkMCL;
+ }
+ return false;
+ }
+
+checkMCL: //check that mcl list is increasing only
+ int *ll = (*list);
+ if (ll[0] == 0)
+ {
+ LogError("MCL list needs to start from 1!");
+ return false;
+ }
+ for (int i = 1; i < *count; i++)
+ {
+ if (ll[i-1] >= ll[i])
+ {
+ LogError("MCL list must be increasing.. found %d -> %d", ll[i - 1], ll[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+// Returns true on success, false on failure.
+// On success, sets *pIndexCount to the number of indices read, and *pIndexes to a new array with all the indices read. The caller must
+// free the memory with delete[].
+/* static */
+bool MCList::getLineData(const char *nameOfInput, /* OUT */ int *pIndexCount, /* OUT */ int **pIndexes)
+{
+ HANDLE hFile = CreateFileA(nameOfInput, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Unable to open '%s'. GetLastError()=%u", nameOfInput, GetLastError());
+ return false;
+ }
+ LARGE_INTEGER DataTemp;
+ if (!GetFileSizeEx(hFile, &DataTemp))
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ return false;
+ }
+
+ if (DataTemp.QuadPart > MAXMCLFILESIZE)
+ {
+ LogError("Size %d exceeds max size of %d", DataTemp.QuadPart, MAXMCLFILESIZE);
+ return false;
+ }
+
+ int sz = DataTemp.u.LowPart;
+ char* buff = new char[sz];
+ DWORD bytesRead;
+ if (ReadFile(hFile, buff, sz, &bytesRead, nullptr) == 0)
+ {
+ LogError("ReadFile failed. GetLastError()=%u", GetLastError());
+ delete[] buff;
+ return false;
+ }
+ if (!CloseHandle(hFile))
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ delete[] buff;
+ return false;
+ }
+
+ // Count the lines. Note that the last line better be terminated by a newline.
+ int lineCount = 0;
+ for (int i = 0; i < sz; i++)
+ {
+ if (buff[i] == '\n')
+ {
+ lineCount++;
+ }
+ }
+
+ int* indexes = new int[lineCount];
+ int indexCount = 0;
+ int i = 0;
+ while (i < sz)
+ {
+ //seek the first number on the line. This will skip empty lines and lines with no digits.
+ while (!isdigit((unsigned char)buff[i]))
+ i++;
+ //read in the number
+ indexes[indexCount++] = atoi(&buff[i]);
+ //seek to the start of next line
+ while ((i < sz) && (buff[i] != '\n'))
+ i++;
+ i++;
+ }
+ delete[] buff;
+
+ *pIndexCount = indexCount;
+ *pIndexes = indexes;
+ return true;
+}
+
+void MCList::InitializeMCL(char *filename)
+{
+ hMCLFile = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hMCLFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", filename, GetLastError());
+ }
+}
+
+void MCList::AddMethodToMCL(int methodIndex)
+{
+ if (hMCLFile != INVALID_HANDLE_VALUE)
+ {
+ char strMethodIndex[12];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ charCount = sprintf(strMethodIndex, "%d\r\n", methodIndex);
+
+ if (!WriteFile(hMCLFile, strMethodIndex, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
+ {
+ LogError("Failed to write method index '%d'. GetLastError()=%u", strMethodIndex, GetLastError());
+ }
+ }
+}
+
+void MCList::CloseMCL()
+{
+ if (hMCLFile != INVALID_HANDLE_VALUE)
+ {
+ if (CloseHandle(hMCLFile) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ }
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/mclist.h b/src/ToolBox/superpmi/superpmi-shared/mclist.h
new file mode 100644
index 0000000000..93b2879569
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/mclist.h
@@ -0,0 +1,35 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MCList.h - MethodContext List utility class
+//----------------------------------------------------------
+#ifndef _MCList
+#define _MCList
+#define MAXMCLFILESIZE 0xFFFFFF
+
+class MCList
+{
+public:
+ static bool processArgAsMCL(char *input, int *count, int **list);
+
+ MCList()
+ {
+ //Initialize the static file handle
+ hMCLFile = INVALID_HANDLE_VALUE;
+ }
+
+ //Methods to create an MCL file
+ void InitializeMCL(char *filename);
+ void AddMethodToMCL(int methodIndex);
+ void CloseMCL();
+
+private:
+ static bool getLineData(const char *nameOfInput, /* OUT */ int *pIndexCount, /* OUT */ int **pIndexes);
+
+ //File handle for MCL file
+ HANDLE hMCLFile;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
new file mode 100644
index 0000000000..2c46065b48
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
@@ -0,0 +1,6509 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContext.cpp - Primary structure to store all the EE-JIT details required to replay creation of a method
+// CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "md5.h"
+#include "methodcontext.h"
+#include "compileresult.h"
+#include "lightweightmap.h"
+#include "callutils.h"
+
+struct { int packetID; const char *message; } retiredPackets[] =
+{
+ { 6, "CanEmbedModuleHandleForHelper id 6 superseded by GetLazyStringLiteralHelper id 147 on 12/20/2013" },
+ { 13, "CheckMethodModifier id 13 superseded by id 142 on 2013/07/04. Re-record input with newer shim." },
+ { 14, "CompileMethod id 14 superseded by id 141 on 2013/07/03. Re-record input with newer shim." },
+ { 24, "FindNameOfToken id 24 superseded by id 145 on 2013/07/19. Re-record input with newer shim. Adjusted members to be proper." },
+ { 28, "GetArgClass id 28 superseded by id 139 on 2013/07/03. Re-record input with newer shim." },
+ { 30, "GetArgType id 30 superseded by id 140 on 2013/07/03. Re-record input with newer shim." },
+ { 93, "GetUnBoxHelper2 id 93 unused. 2016/02/19. Re-record input with newer shim." },
+ { 104, "IsValidToken id 104 superseded by id 144 on 2013/07/19. Re-record input with newer shim. Adjusted members to be proper." },
+ { 141, "CompileMethod id 141 superseded by id 142 on 2013/07/09. Re-record input with newer shim. We basically reset lots of other stuff too. :-)" },
+};
+int retiredPacketCount = 7;
+
+#define sparseMC //Support filling in details where guesses are okay and will still generate good code. (i.e. helper function addresses)
+
+#if 0
+// Enable these to get verbose logging during record or playback.
+#define DEBUG_REC(x) \
+ printf("rec"); \
+ x; \
+ printf("\n");
+
+#define DEBUG_REP(x) \
+ printf("rep"); \
+ x; \
+ printf("\n");
+#else
+#define DEBUG_REC(x)
+#define DEBUG_REP(x)
+#endif
+
+MethodContext::MethodContext()
+{
+ methodSize = 0;
+
+ #define LWM(map,key,value) map = nullptr;
+ #include "lwmlist.h"
+
+ cr = new CompileResult();
+ index = -1;
+}
+
+MethodContext::~MethodContext()
+{
+ Destroy();
+}
+
+void MethodContext::Destroy()
+{
+ #define LWM(map,key,value) if (map != nullptr) delete map;
+ #include "lwmlist.h"
+
+ delete cr;
+}
+
+#define sparseAddLen(target) \
+ if (target != nullptr) \
+ { \
+ if (target->GetCount() != 0) \
+ totalLen += target->CalculateArraySize() + 6; /* packet canary from lightweightmap + packet marker */ \
+ }
+
+#define sparseWriteFile(target) \
+ if (target != nullptr) \
+ { \
+ if (target->GetCount() != 0) \
+ { \
+ buff2[buffIndex++] = (unsigned char) Packet_##target; \
+ unsigned int loc = target->DumpToArray(&buff2[buffIndex + 4]); \
+ memcpy(&buff2[buffIndex], &loc, sizeof(unsigned int)); \
+ buffIndex += 4 + loc; \
+ buff2[buffIndex++] = 0x42; \
+ } \
+ }
+
+#define sparseWriteFileCR(target) \
+ if (cr != nullptr) \
+ { \
+ if (cr->target != nullptr) \
+ { \
+ if (cr->target->GetCount() != 0) \
+ { \
+ buff2[buffIndex++] = (unsigned char) PacketCR_##target; \
+ unsigned int loc = cr->target->DumpToArray(&buff2[buffIndex + 4]); \
+ memcpy(&buff2[buffIndex], &loc, sizeof(unsigned int)); \
+ buffIndex += 4 + loc; \
+ buff2[buffIndex++] = 0x42; \
+ } \
+ } \
+ }
+
+#define sparseReadFile(target, key, value) \
+ case Packet_##target: \
+ { \
+ target = new LightWeightMap<key, value>(); \
+ target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+#define sparseReadFileCR(target, key, value) \
+ case PacketCR_##target: \
+ { \
+ cr->target = new LightWeightMap<key, value>(); \
+ cr->target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+#define sparseReadFileDense(target, value) \
+ case Packet_##target: \
+ { \
+ target = new DenseLightWeightMap<value>(); \
+ target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+#define sparseReadFileCRDense(target, value) \
+ case PacketCR_##target: \
+ { \
+ cr->target = new DenseLightWeightMap<value>(); \
+ cr->target->ReadFromArray(&buff2[buffIndex], localsize); \
+ break; \
+ }
+
+unsigned int MethodContext::calculateFileSize()
+{
+ //Calculate file size
+ unsigned int totalLen = 0;
+
+ #define LWM(map,key,value) sparseAddLen(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ if (cr != nullptr)
+ {
+ #define LWM(map,key,value) sparseAddLen(cr->map);
+ #include "crlwmlist.h"
+ }
+
+ return totalLen;
+}
+
+unsigned int MethodContext::calculateRawFileSize()
+{
+ return 2 /* leading magic cookie 'm', 'c' */ + 4 /* 4-byte data length */ + calculateFileSize() + 2 /* end canary '4', '2' */;
+}
+
+unsigned int MethodContext::saveToFile(HANDLE hFile)
+{
+ unsigned int totalLen = calculateFileSize();
+ unsigned int totalFileSize = 2 /* leading magic cookie 'm', 'c' */ + 4 /* 4-byte data length */ + totalLen + 2 /* end canary '4', '2' */;
+
+ DWORD bytesWritten = 0;
+ unsigned int buffIndex = 0;
+ unsigned char *buff2 = new unsigned char[totalFileSize];
+ buff2[buffIndex++] = 'm';
+ buff2[buffIndex++] = 'c';
+ memcpy(&buff2[buffIndex], &totalLen, sizeof(unsigned int));
+ buffIndex += 4;
+
+ #define LWM(map,key,value) sparseWriteFile(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) sparseWriteFileCR(map);
+ #include "crlwmlist.h"
+
+ // Write the end canary
+ buff2[buffIndex++] = '4';
+ buff2[buffIndex++] = '2';
+
+ Assert(buffIndex == totalFileSize);
+
+ WriteFile(hFile, buff2, totalFileSize, &bytesWritten, NULL);
+ delete[]buff2;
+ return bytesWritten;
+}
+
+// This code can't exist in a function with C++ objects needing destruction. Returns true on success
+// (and sets *ppmc with new MethodContext), false on failure.
+//
+// static
+bool MethodContext::Initialize(int loadedCount, unsigned char* buff, DWORD size, /* OUT */ MethodContext** ppmc)
+{
+ MethodContext* mc = new MethodContext();
+ mc->index = loadedCount;
+ *ppmc = mc;
+ return mc->Initialize(loadedCount, buff, size);
+}
+
+// static
+bool MethodContext::Initialize(int loadedCount, HANDLE hFile, /* OUT */ MethodContext** ppmc)
+{
+ MethodContext* mc = new MethodContext();
+ mc->index = loadedCount;
+ *ppmc = mc;
+ return mc->Initialize(loadedCount, hFile);
+}
+
+bool MethodContext::Initialize(int loadedCount, unsigned char* buff, DWORD size)
+{
+ bool result = true;
+
+ struct Param
+ {
+ unsigned char* buff;
+ DWORD size;
+ MethodContext* pThis;
+ } param;
+ param.buff = buff;
+ param.size = size;
+ param.pThis = this;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->pThis->MethodInitHelper(pParam->buff, pParam->size);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
+ {
+ LogError("Method %d is of low integrity.", loadedCount);
+ result = false;
+ }
+ PAL_ENDTRY
+
+ return result;
+}
+
+bool MethodContext::Initialize(int loadedCount, HANDLE hFile)
+{
+ bool result = true;
+
+ struct Param
+ {
+ HANDLE hFile;
+ MethodContext* pThis;
+ } param;
+ param.hFile = hFile;
+ param.pThis = this;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->pThis->MethodInitHelperFile(pParam->hFile);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
+ {
+ LogError("Method %d is of low integrity.", loadedCount);
+ result = false;
+ }
+ PAL_ENDTRY
+
+ return result;
+}
+
+void MethodContext::MethodInitHelperFile(HANDLE hFile)
+{
+ DWORD bytesRead;
+ char buff[512];
+ unsigned int totalLen = 0;
+
+ AssertCode(ReadFile(hFile, buff, 2 + sizeof(unsigned int), &bytesRead, NULL) == TRUE, EXCEPTIONCODE_MC); //Read Magic number and totalLen
+ AssertCodeMsg((buff[0] == 'm') && (buff[1] == 'c'), EXCEPTIONCODE_MC, "Didn't find magic number");
+ memcpy(&totalLen, &buff[2], sizeof(unsigned int));
+ unsigned char *buff2 = new unsigned char[totalLen + 2]; //total + End Canary
+ AssertCode(ReadFile(hFile, buff2, totalLen + 2, &bytesRead, NULL) == TRUE, EXCEPTIONCODE_MC);
+ AssertCodeMsg((buff2[totalLen] == '4') && (buff2[totalLen + 1] == '2'), EXCEPTIONCODE_MC, "Didn't find end canary");
+ MethodInitHelper(buff2, totalLen);
+}
+
+void MethodContext::MethodInitHelper(unsigned char *buff2, unsigned int totalLen)
+{
+ MethodContext::MethodContext();
+
+ unsigned int buffIndex = 0;
+ unsigned int localsize = 0;
+ unsigned char canary = 0xff;
+ unsigned char *buff3 = nullptr;
+
+ while (buffIndex < totalLen)
+ {
+ unsigned char packetType = buff2[buffIndex++];
+ memcpy(&localsize, &buff2[buffIndex], sizeof(unsigned int));
+ buffIndex += 4;
+
+ switch (packetType)
+ {
+ #define LWM(map,key,value) sparseReadFile(map,key,value)
+ #define DENSELWM(map,value) sparseReadFileDense(map,value)
+ #include "lwmlist.h"
+
+ #define LWM(map,key,value) sparseReadFileCR(map,key,value)
+ #define DENSELWM(map,value) sparseReadFileCRDense(map,value)
+ #include "crlwmlist.h"
+
+ default:
+ for (int i = 0; i < retiredPacketCount; i++)
+ {
+ AssertCodeMsg(retiredPackets[i].packetID != packetType, EXCEPTIONCODE_MC, "Ran into retired packet %u '%s'", packetType, retiredPackets[i].message);
+ }
+ LogException(EXCEPTIONCODE_MC, "Read ran into unknown packet type %u. Are you using a newer recorder?", packetType);
+ // break;
+ }
+ buffIndex += localsize;
+ canary = buff2[buffIndex++];
+ AssertCodeMsg(canary == 0x42, EXCEPTIONCODE_MC, "Didn't find trailing canary for map");
+ }
+ AssertCodeMsg((buff2[buffIndex++] == '4') && (buff2[buffIndex++] == '2'), EXCEPTIONCODE_MC, "Didn't find trailing canary for map");
+ delete[]buff2;
+}
+
+#define dumpStat(target) \
+ if (target != nullptr) \
+ { \
+ if (target->GetCount() > 0) \
+ { \
+ int t = sprintf_s(buff, len, "%u", target->GetCount()); \
+ buff += t; \
+ len -= t; \
+ } \
+ } \
+ { \
+ *buff++ = ','; \
+ len--; \
+ } \
+ if (target != nullptr) \
+ { \
+ int t = sprintf_s(buff, len, "%u", target->CalculateArraySize()); \
+ buff += t; \
+ len -= t; \
+ } \
+ { \
+ *buff++ = ','; \
+ len--; \
+ }
+
+// Dump statistics about each LightWeightMap to the buffer: count of elements, and total size in bytes of map.
+int MethodContext::dumpStatToBuffer(char *buff, int len)
+{
+ char *obuff = buff;
+ //assumption of enough buffer.. :-|
+
+ #define LWM(map,key,value) dumpStat(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) dumpStat(cr->map);
+ #include "crlwmlist.h"
+
+ return (int)(buff - obuff);
+}
+int MethodContext::dumpStatTitleToBuffer(char *buff, int len)
+{
+ const char *title =
+
+ #define LWM(map,key,value) #map "," #map " SZ,"
+ #include "lwmlist.h"
+
+ #define LWM(map,key,value) "CR_" #map ",CR_" #map " SZ,"
+ #include "crlwmlist.h"
+
+ ;
+
+ int titleLen = (int)strlen(title);
+ if ((titleLen + 1) > len)
+ {
+ LogError("titleLen is larger than given len");
+ return 0;
+ }
+ strcpy_s(buff, len, title);
+ return titleLen;
+}
+
+#define softMapEqual(a) \
+ if (a != nullptr) \
+ { \
+ if (other->a == nullptr) return false; \
+ if (a->GetCount() != other->a->GetCount()) return false; \
+ } \
+ else if (other->a != nullptr) return false;
+
+
+bool MethodContext::Equal(MethodContext *other)
+{
+ //returns true if equal. Note this is permissive, that is to say that we may reason that too many things are equal. Adding more detailed checks would cause us to reason more things as unique;
+
+ //Compare MethodInfo's first.
+ CORINFO_METHOD_INFO otherInfo;
+ unsigned otherFlags = 0;
+ other->repCompileMethod(&otherInfo, &otherFlags);
+
+ CORINFO_METHOD_INFO ourInfo;
+ unsigned ourFlags = 0;
+ repCompileMethod(&ourInfo, &ourFlags);
+
+ if (otherInfo.ILCodeSize != ourInfo.ILCodeSize)
+ return false;
+ if (otherInfo.args.numArgs != ourInfo.args.numArgs)
+ return false;
+ if (otherInfo.args.retType != ourInfo.args.retType)
+ return false;
+ if (otherInfo.locals.numArgs != ourInfo.locals.numArgs)
+ return false;
+ if (otherInfo.EHcount != ourInfo.EHcount)
+ return false;
+ if (otherInfo.options != ourInfo.options)
+ return false;
+ for (unsigned int j = 0; j < otherInfo.ILCodeSize; j++)
+ if (otherInfo.ILCode[j] != ourInfo.ILCode[j])
+ return false;
+ if (otherInfo.maxStack != ourInfo.maxStack)
+ return false;
+ if (otherInfo.regionKind != ourInfo.regionKind)
+ return false;
+ if (otherInfo.args.callConv != ourInfo.args.callConv)
+ return false;
+ if (otherInfo.args.cbSig != ourInfo.args.cbSig)
+ return false;
+ if (otherInfo.args.flags != ourInfo.args.flags)
+ return false;
+ if (otherInfo.locals.cbSig != ourInfo.locals.cbSig)
+ return false;
+ if (otherFlags != ourFlags)
+ return false;
+
+ //Now compare the other maps to "estimate" equality.
+
+ #define LWM(map,key,value) softMapEqual(map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) softMapEqual(cr->map)
+ #include "crlwmlist.h"
+
+ //Base case is we "match"
+ return true;
+}
+
+//------------------------------------------------------------------------------
+// MethodContext::recGlobalContext
+// This method copies any relevant global (i.e. per-JIT-instance) data from
+// the given method context. Currently this is limited to configuiration
+// values, but may grow to encompass other information in the future (e.g.
+// any information that is exposed by the ICorJitHost interface and is
+// therefore accessible outside the context of a call to
+// `ICJI::compileMethod`).
+//
+// This method is intended to be called as part of initializing a method
+// during collection, similar to `methodContext::recEnvironment`.
+void MethodContext::recGlobalContext(const MethodContext& other)
+{
+ Assert(GetIntConfigValue == nullptr);
+ Assert(GetStringConfigValue == nullptr);
+
+ if (other.GetIntConfigValue != nullptr)
+ {
+ GetIntConfigValue = new LightWeightMap<Agnostic_ConfigIntInfo, DWORD>(*other.GetIntConfigValue);
+ }
+
+ if (other.GetStringConfigValue != nullptr)
+ {
+ GetStringConfigValue = new LightWeightMap<DWORD, DWORD>(*other.GetStringConfigValue);
+ }
+}
+
+void MethodContext::recEnvironment()
+{
+ if (Environment == nullptr)
+ Environment = new DenseLightWeightMap<Agnostic_Environment>();
+
+ char *l_EnvStr;
+ char *l_val;
+
+#ifdef FEATURE_PAL
+ l_EnvStr = GetEnvironmentStringsA();
+#else // !FEATURE_PAL
+ l_EnvStr = GetEnvironmentStrings();
+#endif // !FEATURE_PAL
+
+ l_val = l_EnvStr;
+
+ char* l_str = l_EnvStr;
+
+ int count = 0;
+ while (true)
+ {
+ if (*l_str == 0) break;
+ while (*l_str != 0) l_str++;
+ l_str++;
+ count++;
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ if ((_strnicmp(l_EnvStr, "complus_", 8) == 0) || (_strnicmp(l_EnvStr, "dbflag", 6) == 0) || (_strnicmp(l_EnvStr, "BVT_TEST_ID", 11) == 0))
+ {
+ char *val = l_EnvStr;
+ while (*val != '=')
+ val++;
+ *val++ = 0;
+ int nameind = Environment->AddBuffer((unsigned char*)l_EnvStr, (int)strlen(l_EnvStr) + 1);
+ int valind = Environment->AddBuffer((unsigned char*)val, (int)strlen(val) + 1);
+ Agnostic_Environment value;
+ value.name_index = nameind;
+ value.val_index = valind;
+ DWORD key = (DWORD)Environment->GetCount();
+ Environment->Append(value);
+ DEBUG_REC(dmpEnvironment(key, value));
+ l_EnvStr = val;
+ }
+ while (*l_EnvStr != '\0')
+ l_EnvStr++;
+ l_EnvStr++;
+ }
+ FreeEnvironmentStringsA(l_val);
+}
+void MethodContext::dmpEnvironment(DWORD key, const Agnostic_Environment& value)
+{
+ printf("Environment key %u, value '%s' '%s'", key, (LPCSTR)Environment->GetBuffer(value.name_index),
+ (LPCSTR)Environment->GetBuffer(value.val_index));
+ Environment->Unlock();
+}
+void MethodContext::repEnvironmentSet()
+{
+ if (Environment == nullptr)
+ return;
+ Agnostic_Environment val;
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ DEBUG_REP(dmpEnvironment(i, val));
+
+ SetEnvironmentVariableA((LPCSTR)Environment->GetBuffer(val.name_index), (LPCSTR)Environment->GetBuffer(val.val_index));
+ }
+}
+
+int MethodContext::repGetTestID()
+{
+ //CLR Test asset only - we capture the testID via smarty-environnent (BVT_TEST_ID) during record time
+ //This procedure returns the test id if found and -1 otherwise
+
+ if (Environment == nullptr)
+ return -1;
+ Agnostic_Environment val;
+ LPCSTR key;
+ int value = -1;
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ key = (LPCSTR)Environment->GetBuffer(val.name_index);
+
+ if (_strnicmp(key, "BVT_TEST_ID", 11) == 0)
+ {
+ value = atoi((LPCSTR)Environment->GetBuffer(val.val_index));
+ break;
+ }
+ }
+
+ if (value == -1)
+ {
+ LogError("Couldn't find Smarty test ID");
+ }
+
+ return value;
+}
+
+void MethodContext::repEnvironmentUnset()
+{
+ if (Environment == nullptr)
+ return;
+ Agnostic_Environment val;
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ SetEnvironmentVariableA((LPCSTR)Environment->GetBuffer(val.name_index), nullptr);
+ }
+}
+int MethodContext::repEnvironmentGetCount()
+{
+ int result = 0;
+ if (Environment != nullptr)
+ result = Environment->GetCount();
+ return result;
+}
+
+void MethodContext::dumpToConsole(int mcNumber)
+{
+ printf("*****************************************");
+ if (mcNumber != -1)
+ {
+ printf(" method context #%d", mcNumber);
+ }
+ printf("\n");
+
+ #define LWM(map,key,value) dumpLWM(this,map)
+ #define DENSELWM(map,value) dumpLWMDense(this,map)
+ #include "lwmlist.h"
+
+ //Compile Result members
+ #define LWM(map,key,value) dumpLWM(this->cr,map)
+ #define DENSELWM(map,value) dumpLWMDense(this->cr,map)
+ #include "crlwmlist.h"
+}
+
+const char* toString(CorInfoType cit)
+{
+ switch (cit)
+ {
+ case CORINFO_TYPE_UNDEF : return "undef";
+ case CORINFO_TYPE_VOID : return "void";
+ case CORINFO_TYPE_BOOL : return "bool";
+ case CORINFO_TYPE_CHAR : return "char";
+ case CORINFO_TYPE_BYTE : return "byte";
+ case CORINFO_TYPE_UBYTE : return "ubyte";
+ case CORINFO_TYPE_SHORT : return "short";
+ case CORINFO_TYPE_USHORT : return "ushort";
+ case CORINFO_TYPE_INT : return "int";
+ case CORINFO_TYPE_UINT : return "uint";
+ case CORINFO_TYPE_LONG : return "long";
+ case CORINFO_TYPE_ULONG : return "ulong";
+ case CORINFO_TYPE_NATIVEINT : return "nativeint";
+ case CORINFO_TYPE_NATIVEUINT : return "nativeuint";
+ case CORINFO_TYPE_FLOAT : return "float";
+ case CORINFO_TYPE_DOUBLE : return "double";
+ case CORINFO_TYPE_STRING : return "string";
+ case CORINFO_TYPE_PTR : return "ptr";
+ case CORINFO_TYPE_BYREF : return "byref";
+ case CORINFO_TYPE_VALUECLASS : return "valueclass";
+ case CORINFO_TYPE_CLASS : return "class";
+ case CORINFO_TYPE_REFANY : return "refany";
+ case CORINFO_TYPE_VAR : return "var";
+ default : return "UNKNOWN";
+ }
+}
+
+unsigned int toCorInfoSize(CorInfoType cit)
+{
+ switch (cit)
+ {
+ case CORINFO_TYPE_BOOL:
+ case CORINFO_TYPE_BYTE:
+ case CORINFO_TYPE_UBYTE:
+ return 1;
+
+ case CORINFO_TYPE_CHAR:
+ case CORINFO_TYPE_SHORT:
+ case CORINFO_TYPE_USHORT:
+ return 2;
+
+ case CORINFO_TYPE_FLOAT:
+ case CORINFO_TYPE_INT:
+ case CORINFO_TYPE_UINT:
+ return 4;
+
+ case CORINFO_TYPE_DOUBLE:
+ case CORINFO_TYPE_LONG:
+ case CORINFO_TYPE_ULONG:
+ return 8;
+
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ case CORINFO_TYPE_PTR:
+ case CORINFO_TYPE_BYREF:
+ case CORINFO_TYPE_CLASS:
+ return sizeof(void *);
+
+ case CORINFO_TYPE_STRING:
+ case CORINFO_TYPE_VALUECLASS:
+ case CORINFO_TYPE_REFANY:
+ case CORINFO_TYPE_UNDEF:
+ case CORINFO_TYPE_VOID:
+ default:
+ __debugbreak();
+ return 0;
+ }
+ return -1;
+}
+
+void MethodContext::recCompileMethod(CORINFO_METHOD_INFO *info, unsigned flags)
+{
+ if (CompileMethod == nullptr)
+ CompileMethod = new LightWeightMap<DWORD, Agnostic_CompileMethod>();
+
+ Agnostic_CompileMethod value;
+
+ value.info.ftn = (DWORDLONG)info->ftn;
+ value.info.scope = (DWORDLONG)info->scope;
+ value.info.ILCode_offset = (DWORD)CompileMethod->AddBuffer(info->ILCode, info->ILCodeSize);
+ value.info.ILCodeSize = (DWORD)info->ILCodeSize;
+ value.info.maxStack = (DWORD)info->maxStack;
+ value.info.EHcount = (DWORD)info->EHcount;
+ value.info.options = (DWORD)info->options;
+ value.info.regionKind = (DWORD)info->regionKind;
+ value.info.args.callConv = (DWORD)info->args.callConv;
+ value.info.args.retTypeClass = (DWORDLONG)info->args.retTypeClass;
+ value.info.args.retTypeSigClass = (DWORDLONG)info->args.retTypeSigClass;
+ value.info.args.retType = (DWORD)info->args.retType;
+ value.info.args.flags = (DWORD)info->args.flags;
+ value.info.args.numArgs = (DWORD)info->args.numArgs;
+ value.info.args.sigInst_classInstCount = (DWORD)info->args.sigInst.classInstCount;
+ value.info.args.sigInst_classInst_Index = CompileMethod->AddBuffer((unsigned char*)info->args.sigInst.classInst, info->args.sigInst.classInstCount * 8); // porting issue
+ value.info.args.sigInst_methInstCount = (DWORD)info->args.sigInst.methInstCount;
+ value.info.args.sigInst_methInst_Index = CompileMethod->AddBuffer((unsigned char*)info->args.sigInst.methInst, info->args.sigInst.methInstCount * 8); // porting issue
+ value.info.args.args = (DWORDLONG)info->args.args;
+ value.info.args.cbSig = (DWORD)info->args.cbSig;
+ value.info.args.pSig = (DWORD)CompileMethod->AddBuffer((unsigned char *)info->args.pSig, info->args.cbSig);
+ value.info.args.scope = (DWORDLONG)info->args.scope;
+ value.info.args.token = (DWORD)info->args.token;
+ value.info.locals.callConv = (DWORD)info->locals.callConv;
+ value.info.locals.retTypeClass = (DWORDLONG)info->locals.retTypeClass;
+ value.info.locals.retTypeSigClass = (DWORDLONG)info->locals.retTypeSigClass;
+ value.info.locals.retType = (DWORD)info->locals.retType;
+ value.info.locals.flags = (DWORD)info->locals.flags;
+ value.info.locals.numArgs = (DWORD)info->locals.numArgs;
+ value.info.locals.sigInst_classInstCount = (DWORD)info->locals.sigInst.classInstCount;
+ value.info.locals.sigInst_classInst_Index = CompileMethod->AddBuffer((unsigned char*)info->locals.sigInst.classInst, info->locals.sigInst.classInstCount * 8); // porting issue
+ value.info.locals.sigInst_methInstCount = (DWORD)info->locals.sigInst.methInstCount;
+ value.info.locals.sigInst_methInst_Index = CompileMethod->AddBuffer((unsigned char*)info->locals.sigInst.methInst, info->locals.sigInst.methInstCount * 8); // porting issue
+ value.info.locals.args = (DWORDLONG)info->locals.args;
+ value.info.locals.cbSig = (DWORD)info->locals.cbSig;
+ value.info.locals.pSig = (DWORD)CompileMethod->AddBuffer((unsigned char *)info->locals.pSig, info->locals.cbSig);
+ value.info.locals.scope = (DWORDLONG)info->locals.scope;
+ value.info.locals.token = (DWORD)info->locals.token;
+ value.flags = (DWORD)flags;
+
+ CompileMethod->Add(0, value);
+ DEBUG_REC(dmpCompileMethod(0, value));
+}
+void MethodContext::dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& value)
+{
+ printf("CompiledMethod key %u, value ftn-%016llX scp-%016llX ilo-%u ils-%u ms-%u ehc-%u opt-%u rk-%u "
+ "args{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "locals{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "flg-%08X",
+ key,
+ value.info.ftn,
+ value.info.scope,
+ value.info.ILCode_offset,
+ value.info.ILCodeSize,
+ value.info.maxStack,
+ value.info.EHcount,
+ value.info.options,
+ value.info.regionKind,
+ value.info.args.callConv,
+ value.info.args.retTypeClass,
+ value.info.args.retTypeSigClass,
+ value.info.args.retType,
+ toString((CorInfoType)value.info.args.retType),
+ value.info.args.flags,
+ value.info.args.numArgs,
+ value.info.args.sigInst_classInstCount,
+ value.info.args.sigInst_classInst_Index,
+ value.info.args.sigInst_methInstCount,
+ value.info.args.sigInst_methInst_Index,
+ value.info.args.args,
+ value.info.args.cbSig,
+ value.info.args.pSig,
+ value.info.args.scope,
+ value.info.args.token,
+ value.info.locals.callConv,
+ value.info.locals.retTypeClass,
+ value.info.locals.retTypeSigClass,
+ value.info.locals.retType,
+ toString((CorInfoType)value.info.locals.retType),
+ value.info.locals.flags,
+ value.info.locals.numArgs,
+ value.info.locals.sigInst_classInstCount,
+ value.info.locals.sigInst_classInst_Index,
+ value.info.locals.sigInst_methInstCount,
+ value.info.locals.sigInst_methInst_Index,
+ value.info.locals.args,
+ value.info.locals.cbSig,
+ value.info.locals.pSig,
+ value.info.locals.scope,
+ value.info.locals.token,
+ value.flags);
+}
+void MethodContext::repCompileMethod(CORINFO_METHOD_INFO *info, unsigned *flags)
+{
+ Agnostic_CompileMethod value;
+
+ value = CompileMethod->Get((DWORD)0); //The only item in this set is a single group of inputs to CompileMethod
+
+ info->ftn = (CORINFO_METHOD_HANDLE)value.info.ftn;
+ info->scope = (CORINFO_MODULE_HANDLE)value.info.scope;
+ info->ILCode = CompileMethod->GetBuffer(value.info.ILCode_offset);
+ info->ILCodeSize = (unsigned)value.info.ILCodeSize;
+ methodSize = info->ILCodeSize;
+ info->maxStack = (unsigned)value.info.maxStack;
+ info->EHcount = (unsigned)value.info.EHcount;
+ info->options = (CorInfoOptions)value.info.options;
+ info->regionKind = (CorInfoRegionKind)value.info.regionKind;
+ info->args.callConv = (CorInfoCallConv)value.info.args.callConv;
+ info->args.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeClass;
+ info->args.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeSigClass;
+ info->args.retType = (CorInfoType)value.info.args.retType;
+ info->args.flags = (unsigned)value.info.args.flags;
+ info->args.numArgs = (unsigned)value.info.args.numArgs;
+ info->args.sigInst.classInstCount = (unsigned)value.info.args.sigInst_classInstCount;
+ info->args.sigInst.classInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.args.sigInst_classInst_Index);
+ info->args.sigInst.methInstCount = (unsigned)value.info.args.sigInst_methInstCount;
+ info->args.sigInst.methInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.args.sigInst_methInst_Index);
+ info->args.args = (CORINFO_ARG_LIST_HANDLE)value.info.args.args;
+ info->args.cbSig = (unsigned int)value.info.args.cbSig;
+ info->args.pSig = (PCCOR_SIGNATURE)CompileMethod->GetBuffer(value.info.args.pSig);
+ info->args.scope = (CORINFO_MODULE_HANDLE)value.info.args.scope;
+ info->args.token = (mdToken)value.info.args.token;
+ info->locals.callConv = (CorInfoCallConv)value.info.locals.callConv;
+ info->locals.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeClass;
+ info->locals.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeSigClass;
+ info->locals.retType = (CorInfoType)value.info.locals.retType;
+ info->locals.flags = (unsigned)value.info.locals.flags;
+ info->locals.numArgs = (unsigned)value.info.locals.numArgs;
+ info->locals.sigInst.classInstCount = (unsigned)value.info.locals.sigInst_classInstCount;
+ info->locals.sigInst.classInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.locals.sigInst_classInst_Index);
+ info->locals.sigInst.methInstCount = (unsigned)value.info.locals.sigInst_methInstCount;
+ info->locals.sigInst.methInst = (CORINFO_CLASS_HANDLE*)CompileMethod->GetBuffer(value.info.locals.sigInst_methInst_Index);
+ info->locals.args = (CORINFO_ARG_LIST_HANDLE)value.info.locals.args;
+ info->locals.cbSig = (unsigned int)value.info.locals.cbSig;
+ info->locals.pSig = (PCCOR_SIGNATURE)CompileMethod->GetBuffer(value.info.locals.pSig);
+ info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope;
+ info->locals.token = (mdToken)value.info.locals.token;
+ *flags = (unsigned)value.flags;
+ DEBUG_REP(dmpCompileMethod(0, value));
+}
+
+void MethodContext::recGetMethodClass(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CLASS_HANDLE classHandle)
+{
+ if (GetMethodClass == nullptr)
+ GetMethodClass = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetMethodClass->Add((DWORDLONG)methodHandle, (DWORDLONG)classHandle);
+ DEBUG_REC(dmpGetMethodClass((DWORDLONG)methodHandle, (DWORDLONG)classHandle));
+}
+void MethodContext::dmpGetMethodClass(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetMethodClass key %016llX, value %016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetMethodClass(CORINFO_METHOD_HANDLE methodHandle)
+{
+ AssertCodeMsg(GetMethodClass != nullptr, EXCEPTIONCODE_MC, "Found a null GetMethodClass. Probably missing a fatTrigger for %016llX.", (DWORDLONG)methodHandle);
+ int index = GetMethodClass->GetIndex((DWORDLONG)methodHandle);
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_MC, "Didn't find %016llX. Probably missing a fatTrigger", (DWORDLONG)methodHandle);
+ CORINFO_CLASS_HANDLE value = (CORINFO_CLASS_HANDLE)GetMethodClass->Get((DWORDLONG)methodHandle);
+ DEBUG_REP(dmpGetMethodClass((DWORDLONG)methodHandle, (DWORDLONG)value));
+ return value;
+}
+
+void MethodContext::recGetClassAttribs(CORINFO_CLASS_HANDLE classHandle, DWORD attribs)
+{
+ if (GetClassAttribs == nullptr)
+ GetClassAttribs = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetClassAttribs->Add((DWORDLONG)classHandle, (DWORD)attribs);
+ DEBUG_REC(dmpGetClassAttribs((DWORDLONG)classHandle, attribs));
+}
+void MethodContext::dmpGetClassAttribs(DWORDLONG key, DWORD value)
+{
+ printf("GetClassAttribs key %016llX, value %u", key, value);
+}
+DWORD MethodContext::repGetClassAttribs(CORINFO_CLASS_HANDLE classHandle)
+{
+ AssertCodeMsg(GetClassAttribs != nullptr, EXCEPTIONCODE_MC, "Found a null GetMethodClass. Probably missing a fatTrigger for %016llX.", (DWORDLONG)classHandle);
+ int index = GetClassAttribs->GetIndex((DWORDLONG)classHandle);
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_MC, "Didn't find %016llX. Probably missing a fatTrigger", (DWORDLONG)classHandle);
+ DWORD value = (DWORD)GetClassAttribs->Get((DWORDLONG)classHandle);
+ DEBUG_REP(dmpGetClassAttribs((DWORDLONG)classHandle, value));
+ return value;
+}
+
+void MethodContext::recGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle, DWORD attribs)
+{
+ if (GetMethodAttribs == nullptr)
+ GetMethodAttribs = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetMethodAttribs->Add((DWORDLONG)methodHandle, attribs);
+ DEBUG_REC(dmpGetMethodAttribs((DWORDLONG)methodHandle, attribs));
+}
+void MethodContext::dmpGetMethodAttribs(DWORDLONG key, DWORD value)
+{
+ printf("GetMethodAttribs key %016llX, value %u", key, value);
+}
+DWORD MethodContext::repGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle)
+{
+ AssertCodeMsg(GetMethodAttribs != nullptr, EXCEPTIONCODE_MC, "Found a null GetMethodAttribs. Probably missing a fatTrigger for %016llX.", (DWORDLONG)methodHandle);
+ int index = GetMethodAttribs->GetIndex((DWORDLONG)methodHandle);
+ AssertCodeMsg(index != -1, EXCEPTIONCODE_MC, "Didn't find %016llX. Probably missing a fatTrigger", (DWORDLONG)methodHandle);
+ DWORD value = (DWORD)GetMethodAttribs->Get((DWORDLONG)methodHandle);
+ DEBUG_REP(dmpGetMethodAttribs((DWORDLONG)methodHandle, value));
+ if (cr->repSetMethodAttribs(methodHandle) == CORINFO_FLG_BAD_INLINEE)
+ value ^= CORINFO_FLG_DONT_INLINE;
+ return value;
+}
+
+//Note - the jit will call freearray on the array we give back....
+void MethodContext::recGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars_in, bool *extendOthers)
+{
+ if (GetVars == nullptr)
+ GetVars = new LightWeightMap<DWORDLONG, Agnostic_GetVars>();
+
+ Agnostic_GetVars value;
+
+ value.cVars = (DWORD)*cVars;
+ value.vars_offset = (DWORD)GetVars->AddBuffer((unsigned char*)*vars_in, sizeof(ICorDebugInfo::ILVarInfo) * (*cVars));
+
+ value.extendOthers = (DWORD)*extendOthers;
+ GetVars->Add((DWORDLONG)ftn, value);
+ DEBUG_REC(dmpGetVars((DWORDLONG)ftn, value));
+}
+void MethodContext::dmpGetVars(DWORDLONG key, const Agnostic_GetVars& value)
+{
+ ICorDebugInfo::ILVarInfo *vars = (ICorDebugInfo::ILVarInfo *)GetVars->GetBuffer(value.vars_offset);
+ printf("GetVars key ftn-%016llX, value cVars-%u extendOthers-%u (", key, value.cVars, value.extendOthers);
+ for (unsigned int i = 0; i < value.cVars; i++)
+ printf("(%u %u %u %u)", i, vars[i].startOffset, vars[i].endOffset, vars[i].varNumber);
+ printf(")");
+ GetVars->Unlock();
+}
+void MethodContext::repGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars_in, bool *extendOthers)
+{
+ Agnostic_GetVars value;
+ if (GetVars == nullptr)
+ {
+ *cVars = 0;
+ return;
+ }
+ value = GetVars->Get((DWORDLONG)ftn);
+ *cVars = (ULONG32)value.cVars;
+ if (*cVars > 0)
+ *vars_in = (ICorDebugInfo::ILVarInfo *)GetVars->GetBuffer(value.vars_offset);
+ *extendOthers = value.extendOthers != 0;
+ DEBUG_REP(dmpGetVars((DWORDLONG)ftn, value));
+}
+
+//Note - the jit will call freearray on the array we give back....
+void MethodContext::recGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries)
+{
+ if (GetBoundaries == nullptr)
+ GetBoundaries = new LightWeightMap<DWORDLONG, Agnostic_GetBoundaries>();
+
+ Agnostic_GetBoundaries value;
+
+ value.cILOffsets = (DWORD)*cILOffsets;
+ value.pILOffset_offset = (DWORD)GetBoundaries->AddBuffer((unsigned char*)*pILOffsets, sizeof(DWORD)*(*cILOffsets));
+ value.implicitBoundaries = *implictBoundaries;
+
+ GetBoundaries->Add((DWORDLONG)ftn, value);
+ DEBUG_REC(dmpGetBoundaries((DWORDLONG)ftn, value));
+}
+void MethodContext::dmpGetBoundaries(DWORDLONG key, const Agnostic_GetBoundaries& value)
+{
+ printf("GetBoundaries key ftn-%016llX, value cnt-%u imp-%u{", key, value.cILOffsets, value.implicitBoundaries);
+ DWORD *bnd = (DWORD *)GetBoundaries->GetBuffer(value.pILOffset_offset);
+ for (unsigned int i = 0; i < value.cILOffsets; i++)
+ {
+ printf("%u", bnd[i]);
+ if (i < (value.cILOffsets + 1))
+ printf(",");
+ }
+ GetBoundaries->Unlock();
+ printf("}");
+}
+void MethodContext::repGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries)
+{
+ Agnostic_GetBoundaries value;
+
+ value = GetBoundaries->Get((DWORDLONG)ftn);
+
+ *cILOffsets = (unsigned int)value.cILOffsets;
+ if (*cILOffsets > 0)
+ *pILOffsets = (DWORD *)GetBoundaries->GetBuffer(value.pILOffset_offset);
+ *implictBoundaries = (ICorDebugInfo::BoundaryTypes)value.implicitBoundaries;
+
+ DEBUG_REP(dmpGetBoundaries((DWORDLONG)ftn, value));
+}
+
+void MethodContext::recInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative, CorInfoInitClassResult result)
+{
+ if (InitClass == nullptr)
+ InitClass = new LightWeightMap<Agnostic_InitClass, DWORD>();
+
+ Agnostic_InitClass key;
+ ZeroMemory(&key, sizeof(Agnostic_InitClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.field = (DWORDLONG)field;
+ key.method = (DWORDLONG)method;
+ key.context = (DWORDLONG)context;
+ key.speculative = (DWORD)speculative;
+
+ InitClass->Add(key, (DWORD)result);
+ DEBUG_REC(dmpInitClass(key, (DWORD)result));
+}
+void MethodContext::dmpInitClass(const Agnostic_InitClass& key, DWORD value)
+{
+ printf("InitClass key fld-%016llX meth-%016llX con-%016llX spec-%u, value res-%u", key.field, key.method, key.context, key.speculative, value);
+}
+CorInfoInitClassResult MethodContext::repInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative)
+{
+ Agnostic_InitClass key;
+ ZeroMemory(&key, sizeof(Agnostic_InitClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.field = (DWORDLONG)field;
+ key.method = (DWORDLONG)method;
+ key.context = (DWORDLONG)context;
+ key.speculative = (DWORD)speculative;
+
+ AssertCodeMsg(InitClass != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.method);
+ AssertCodeMsg(InitClass->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.method);
+ CorInfoInitClassResult result = (CorInfoInitClassResult)InitClass->Get(key);
+
+ DEBUG_REP(dmpInitClass(key, result));
+ return result;
+}
+
+void MethodContext::recGetMethodName(CORINFO_METHOD_HANDLE ftn, char *methodname, const char **moduleName)
+{
+ if (GetMethodName == nullptr)
+ GetMethodName = new LightWeightMap<DLD, DD>();
+ DD value;
+ DLD key;
+ key.A = (DWORDLONG)ftn;
+ key.B = (moduleName != nullptr);
+
+ if (methodname != nullptr)
+ value.A = GetMethodName->AddBuffer((unsigned char *)methodname, (DWORD)strlen(methodname) + 1);
+ else
+ value.A = (DWORD)-1;
+
+ if (moduleName != nullptr)
+ value.B = GetMethodName->AddBuffer((unsigned char *)*moduleName, (DWORD)strlen(*moduleName) + 1);
+ else
+ value.B = (DWORD)-1;
+
+ GetMethodName->Add(key, value);
+ DEBUG_REC(dmpGetMethodName(key, value));
+}
+void MethodContext::dmpGetMethodName(DLD key, DD value)
+{
+ unsigned char *methodName = (unsigned char *)GetMethodName->GetBuffer(value.A);
+ unsigned char *moduleName = (unsigned char *)GetMethodName->GetBuffer(value.B);
+ printf("GetMethodName key - ftn-%016llX modNonNull-%u, value meth-'%s', mod-'%s'", key.A, key.B, methodName, moduleName);
+ GetMethodName->Unlock();
+}
+const char *MethodContext::repGetMethodName(CORINFO_METHOD_HANDLE ftn, const char **moduleName)
+{
+ const char* result = "hackishMethodName";
+ DD value;
+ DLD key;
+ key.A = (DWORDLONG)ftn;
+ key.B = (moduleName != nullptr);
+
+ int itemIndex = -1;
+ if (GetMethodName != nullptr)
+ itemIndex = GetMethodName->GetIndex(key);
+ if (itemIndex < 0)
+ {
+ if (moduleName != nullptr)
+ *moduleName = "hackishModuleName";
+ }
+ else
+ {
+ value = GetMethodName->Get(key);
+ if (moduleName != nullptr)
+ *moduleName = (const char *)GetMethodName->GetBuffer(value.B);
+ result = (const char *)GetMethodName->GetBuffer(value.A);
+ }
+ DEBUG_REP(dmpGetMethodName(key, value));
+ return result;
+}
+
+void MethodContext::recGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes, DWORD result)
+{
+ if (GetJitFlags == nullptr)
+ GetJitFlags = new LightWeightMap<DWORD, DD>();
+
+ DD value;
+ value.A = (DWORD)GetJitFlags->AddBuffer((unsigned char *)jitFlags, sizeInBytes);
+ value.B = result;
+
+ // NOTE: getJitFlags() is expected to be idempotent per method, so the mapping key is always
+ // zero.
+ GetJitFlags->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetJitFlags((DWORD)0, value));
+}
+void MethodContext::dmpGetJitFlags(DWORD key, DD value)
+{
+ CORJIT_FLAGS *flags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A);
+ printf("GetJitFlags key %u sizeInBytes-%u corJitFlags-%08X corJitFlags2-%08X", key, value.B, flags->corJitFlags, flags->corJitFlags2);
+ GetJitFlags->Unlock();
+}
+DWORD MethodContext::repGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes)
+{
+ DD value = GetJitFlags->Get((DWORD)0);
+ CORJIT_FLAGS *resultFlags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A);
+ memcpy(jitFlags, resultFlags, value.B);
+ DEBUG_REP(dmpGetJitFlags((DWORD)0, value));
+ return value.B;
+}
+
+void MethodContext::recGetJitTimeLogFilename(LPCWSTR tempFileName)
+{
+ if (GetJitTimeLogFilename == nullptr)
+ GetJitTimeLogFilename = new LightWeightMap<DWORD, DWORD>();
+
+ DWORD name_index = -1;
+ if (tempFileName != nullptr)
+ {
+ name_index = (DWORD)GetJitTimeLogFilename->AddBuffer((unsigned char *)tempFileName, (DWORD)wcslen(tempFileName) + 2);
+ }
+ GetJitTimeLogFilename->Add((DWORD)0, name_index);
+ DEBUG_REC(dmpGetJitTimeLogFilename((DWORD)0, name_index));
+}
+void MethodContext::dmpGetJitTimeLogFilename(DWORD key, DWORD value)
+{
+ unsigned char *fileName = nullptr;
+ if (value != 0)
+ fileName = (unsigned char *)GetJitTimeLogFilename->GetBuffer(value);
+ printf("GetJitTimeLogFilename key %u, value '%s'", key, fileName);
+ GetJitTimeLogFilename->Unlock();
+}
+LPCWSTR MethodContext::repGetJitTimeLogFilename()
+{
+ DWORD offset = GetJitTimeLogFilename->Get((DWORD)0);
+ LPCWSTR value = nullptr;
+ if (offset != 0)
+ value = (LPCWSTR)GetJitTimeLogFilename->GetBuffer(offset);
+ DEBUG_REP(dmpGetJitTimeLogFilename((DWORD)0, offset));
+ return value;
+}
+
+void MethodContext::recCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD *pRestrictions, CorInfoInline response, DWORD exceptionCode)
+{
+ if (CanInline == nullptr)
+ CanInline = new LightWeightMap<DLDL, Agnostic_CanInline>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CanInline value;
+
+ key.A = (DWORDLONG)callerHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ if (pRestrictions != nullptr)
+ value.Restrictions = (DWORD)*pRestrictions;
+ else
+ value.Restrictions = (DWORD)0;
+ value.result = (DWORD)response;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ CanInline->Add(key, value);
+ DEBUG_REC(dmpCanInline(key, value));
+}
+void MethodContext::dmpCanInline(DLDL key, const Agnostic_CanInline& value)
+{
+ printf("CanInline key - callerHnd-%016llX calleeHnd-%016llX, value pRestrictions-%u result-%u exceptionCode-%08X", key.A, key.B, value.Restrictions, value.result, value.exceptionCode);
+}
+CorInfoInline MethodContext::repCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD* pRestrictions, DWORD *exceptionCode)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CanInline value;
+
+ key.A = (DWORDLONG)callerHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ if ((CanInline == nullptr) || (CanInline->GetIndex(key) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repCanInline saying INLINE_FAIL");
+ return INLINE_FAIL; //if we have no info, its pretty safe to say we can't inline it.
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in canInline", key.A, key.B);
+#endif
+ }
+
+ value = CanInline->Get(key);
+
+ *exceptionCode = value.exceptionCode;
+
+ if (pRestrictions != nullptr)
+ *pRestrictions = (DWORD)value.Restrictions;
+ CorInfoInline response = (CorInfoInline)value.result;
+ DEBUG_REP(dmpCanInline(key, value));
+ return response;
+}
+
+void MethodContext::recResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD exceptionCode)
+{
+ if (ResolveToken == nullptr)
+ ResolveToken = new LightWeightMap<Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout>();
+
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ value.hClass = (DWORDLONG)pResolvedToken->hClass;
+ value.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ value.hField = (DWORDLONG)pResolvedToken->hField;
+ value.pTypeSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ value.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ value.pMethodSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ value.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ ResolveToken->Add(key, value);
+ DEBUG_REC(dmpResolveToken(key, value));
+}
+void MethodContext::dmpResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value)
+{
+ printf("ResolveToken key tc-%016llX ts-%016llX tok-%08X tt-%u",
+ key.tokenContext,
+ key.tokenScope,
+ key.token,
+ key.tokenType);
+ printf(", value cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u excp-%08X",
+ value.hClass,
+ value.hMethod,
+ value.hField,
+ value.pTypeSpec_Index,
+ value.cbTypeSpec,
+ value.pMethodSpec_Index,
+ value.cbMethodSpec,
+ value.exceptionCode);
+}
+void MethodContext::repResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD *exceptionCode)
+{
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ AssertCodeMsg(ResolveToken->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+
+ value = ResolveToken->Get(key);
+
+ pResolvedToken->hClass = (CORINFO_CLASS_HANDLE)value.hClass;
+ pResolvedToken->hMethod = (CORINFO_METHOD_HANDLE)value.hMethod;
+ pResolvedToken->hField = (CORINFO_FIELD_HANDLE)value.hField;
+ pResolvedToken->pTypeSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pTypeSpec_Index);
+ pResolvedToken->cbTypeSpec = (ULONG)value.cbTypeSpec;
+ pResolvedToken->pMethodSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pMethodSpec_Index);
+ pResolvedToken->cbMethodSpec = (ULONG)value.cbMethodSpec;
+ *exceptionCode = (DWORD)value.exceptionCode;
+
+ DEBUG_REP(dmpResolveToken(key, value));
+}
+
+void MethodContext::recTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool success)
+{
+ if (TryResolveToken == nullptr)
+ TryResolveToken = new LightWeightMap<Agnostic_CORINFO_RESOLVED_TOKENin, Agnostic_CORINFO_RESOLVED_TOKENout>();
+
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ value.hClass = (DWORDLONG)pResolvedToken->hClass;
+ value.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ value.hField = (DWORDLONG)pResolvedToken->hField;
+ value.pTypeSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ value.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ value.pMethodSpec_Index = (DWORD)ResolveToken->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ value.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ value.exceptionCode = success ? 0 : 1;
+
+ TryResolveToken->Add(key, value);
+ DEBUG_REC(dmpTryResolveToken(key, value));
+}
+void MethodContext::dmpTryResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value)
+{
+ printf("TryResolveToken key tc-%016llX ts-%016llX tok-%08X tt-%u",
+ key.tokenContext,
+ key.tokenScope,
+ key.token,
+ key.tokenType);
+ printf(", value cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u failed-%u",
+ value.hClass,
+ value.hMethod,
+ value.hField,
+ value.pTypeSpec_Index,
+ value.cbTypeSpec,
+ value.pMethodSpec_Index,
+ value.cbMethodSpec,
+ value.exceptionCode);
+}
+bool MethodContext::repTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ Agnostic_CORINFO_RESOLVED_TOKENin key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKENin)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_RESOLVED_TOKENout value;
+
+ key.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.token = (DWORD)pResolvedToken->token;
+ key.tokenType = (DWORD)pResolvedToken->tokenType;
+
+ // Best-effort: if the `tryResolveToken` map is missing or the key is not found therein,
+ // fall back to the `resolveToken` map.
+ if (TryResolveToken != nullptr && TryResolveToken->GetIndex(key) != -1)
+ {
+ value = TryResolveToken->Get(key);
+ }
+ else
+ {
+ AssertCodeMsg(ResolveToken->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+ value = ResolveToken->Get(key);
+ }
+
+ pResolvedToken->hClass = (CORINFO_CLASS_HANDLE)value.hClass;
+ pResolvedToken->hMethod = (CORINFO_METHOD_HANDLE)value.hMethod;
+ pResolvedToken->hField = (CORINFO_FIELD_HANDLE)value.hField;
+ pResolvedToken->pTypeSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pTypeSpec_Index);
+ pResolvedToken->cbTypeSpec = (ULONG)value.cbTypeSpec;
+ pResolvedToken->pMethodSpec = (PCCOR_SIGNATURE)ResolveToken->GetBuffer(value.pMethodSpec_Index);
+ pResolvedToken->cbMethodSpec = (ULONG)value.cbMethodSpec;
+
+ DEBUG_REP(dmpTryResolveToken(key, value));
+ return (DWORD)value.exceptionCode == 0;
+}
+
+void MethodContext::recGetCallInfo(
+ CORINFO_RESOLVED_TOKEN *pResolvedToken,
+ CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_CALLINFO_FLAGS flags,
+ CORINFO_CALL_INFO *pResult,
+ DWORD exceptionCode)
+{
+ if (GetCallInfo == nullptr)
+ GetCallInfo = new LightWeightMap<Agnostic_GetCallInfo, Agnostic_CORINFO_CALL_INFO>();
+
+ Agnostic_GetCallInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCallInfo)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_CALL_INFO value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ if (pConstrainedResolvedToken != nullptr)
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)pConstrainedResolvedToken->tokenContext;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)pConstrainedResolvedToken->tokenScope;
+ key.ConstrainedResolvedToken.token = (DWORD)pConstrainedResolvedToken->token;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)pConstrainedResolvedToken->tokenType;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)pConstrainedResolvedToken->hClass;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)pConstrainedResolvedToken->hMethod;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)pConstrainedResolvedToken->hField;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pConstrainedResolvedToken->pTypeSpec, pConstrainedResolvedToken->cbTypeSpec);
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)pConstrainedResolvedToken->cbTypeSpec;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->AddBuffer((unsigned char*)pConstrainedResolvedToken->pMethodSpec, pConstrainedResolvedToken->cbMethodSpec);
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)pConstrainedResolvedToken->cbMethodSpec;
+ }
+ else
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.token = (DWORD)0;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)0;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)0;
+ }
+
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ if (exceptionCode == 0)
+ {
+ value.hMethod = (DWORDLONG)pResult->hMethod;
+ value.methodFlags = (DWORD)pResult->methodFlags;
+ value.classFlags = (DWORD)pResult->classFlags;
+ value.sig.callConv = (DWORD)pResult->sig.callConv;
+ value.sig.retTypeClass = (DWORDLONG)pResult->sig.retTypeClass;
+ value.sig.retTypeSigClass = (DWORDLONG)pResult->sig.retTypeSigClass;
+ value.sig.retType = (DWORD)pResult->sig.retType;
+ value.sig.flags = (DWORD)pResult->sig.flags;
+ value.sig.numArgs = (DWORD)pResult->sig.numArgs;
+ value.sig.sigInst_classInstCount = (DWORD)pResult->sig.sigInst.classInstCount;
+ value.sig.sigInst_classInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->sig.sigInst.classInst, pResult->sig.sigInst.classInstCount * 8); //porting issue
+ value.sig.sigInst_methInstCount = (DWORD)pResult->sig.sigInst.methInstCount;
+ value.sig.sigInst_methInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->sig.sigInst.methInst, pResult->sig.sigInst.methInstCount * 8); //porting issue
+ value.sig.args = (DWORDLONG)pResult->sig.args;
+ value.sig.cbSig = (DWORD)pResult->sig.cbSig;
+ value.sig.pSig = (DWORD)GetCallInfo->AddBuffer((unsigned char *)pResult->sig.pSig, pResult->sig.cbSig);
+ value.sig.scope = (DWORDLONG)pResult->sig.scope;
+ value.sig.token = (DWORD)pResult->sig.token;
+ if (flags & CORINFO_CALLINFO_VERIFICATION)
+ {
+ value.verMethodFlags = (DWORD)pResult->verMethodFlags;
+ value.verSig.callConv = (DWORD)pResult->verSig.callConv;
+ value.verSig.retTypeClass = (DWORDLONG)pResult->verSig.retTypeClass;
+ value.verSig.retTypeSigClass = (DWORDLONG)pResult->verSig.retTypeSigClass;
+ value.verSig.retType = (DWORD)pResult->verSig.retType;
+ value.verSig.flags = (DWORD)pResult->verSig.flags;
+ value.verSig.numArgs = (DWORD)pResult->verSig.numArgs;
+ value.verSig.sigInst_classInstCount = (DWORD)pResult->verSig.sigInst.classInstCount;
+ value.verSig.sigInst_classInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->verSig.sigInst.classInst, pResult->verSig.sigInst.classInstCount * 8); //porting issue
+ value.verSig.sigInst_methInstCount = (DWORD)pResult->verSig.sigInst.methInstCount;
+ value.verSig.sigInst_methInst_Index = GetCallInfo->AddBuffer((unsigned char*)pResult->verSig.sigInst.methInst, pResult->verSig.sigInst.methInstCount * 8); //porting issue
+ value.verSig.args = (DWORDLONG)pResult->verSig.args;
+ value.verSig.cbSig = (DWORD)pResult->verSig.cbSig;
+ value.verSig.pSig = (DWORD)GetCallInfo->AddBuffer((unsigned char *)pResult->verSig.pSig, pResult->verSig.cbSig);
+ value.verSig.scope = (DWORDLONG)pResult->verSig.scope;
+ value.verSig.token = (DWORD)pResult->verSig.token;
+ }
+ else
+ {
+ value.verMethodFlags = (DWORD)0;
+ value.verSig.callConv = (DWORD)0;
+ value.verSig.retTypeClass = (DWORDLONG)0;
+ value.verSig.retTypeSigClass = (DWORDLONG)0;
+ value.verSig.retType = (DWORD)0;
+ value.verSig.flags = (DWORD)0;
+ value.verSig.numArgs = (DWORD)0;
+ value.verSig.sigInst_classInstCount = (DWORD)0;
+ value.verSig.sigInst_classInst_Index = (DWORD)0;
+ value.verSig.sigInst_methInstCount = (DWORD)0;
+ value.verSig.sigInst_methInst_Index = (DWORD)0;
+ value.verSig.args = (DWORDLONG)0;
+ value.verSig.cbSig = (DWORD)0;
+ value.verSig.pSig = (DWORD)0;
+ value.verSig.scope = (DWORDLONG)0;
+ value.verSig.token = (DWORD)0;
+ }
+
+ value.accessAllowed = (DWORD)pResult->accessAllowed;
+ value.callsiteCalloutHelper.helperNum = (DWORD)pResult->callsiteCalloutHelper.helperNum;
+ value.callsiteCalloutHelper.numArgs = (DWORD)pResult->callsiteCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ value.callsiteCalloutHelper.args[i].constant = (DWORDLONG)pResult->callsiteCalloutHelper.args[i].constant;
+ value.callsiteCalloutHelper.args[i].argType = (DWORD)pResult->callsiteCalloutHelper.args[i].argType;
+ }
+ value.thisTransform = (DWORD)pResult->thisTransform;
+
+ value.kind = (DWORD)pResult->kind;
+ value.nullInstanceCheck = (DWORD)pResult->nullInstanceCheck;
+ value.contextHandle = (DWORDLONG)pResult->contextHandle;
+ value.exactContextNeedsRuntimeLookup = (DWORD)pResult->exactContextNeedsRuntimeLookup;
+ value.stubLookup.lookupKind.needsRuntimeLookup = (DWORD)pResult->stubLookup.lookupKind.needsRuntimeLookup;
+ value.stubLookup.lookupKind.runtimeLookupKind = (DWORD)pResult->stubLookup.lookupKind.runtimeLookupKind;
+ if (pResult->stubLookup.lookupKind.needsRuntimeLookup)
+ {
+ value.stubLookup.constLookup.accessType = (DWORD)0;
+ value.stubLookup.constLookup.handle = (DWORDLONG)0;
+
+ value.stubLookup.runtimeLookup.signature = (DWORDLONG)pResult->stubLookup.runtimeLookup.signature; // needs to be a more flexible copy based on value
+ value.stubLookup.runtimeLookup.helper = (DWORD)pResult->stubLookup.runtimeLookup.helper;
+ value.stubLookup.runtimeLookup.indirections = (DWORD)pResult->stubLookup.runtimeLookup.indirections;
+ value.stubLookup.runtimeLookup.testForNull = (DWORD)pResult->stubLookup.runtimeLookup.testForNull;
+ value.stubLookup.runtimeLookup.testForFixup = (DWORD)pResult->stubLookup.runtimeLookup.testForFixup;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.stubLookup.runtimeLookup.offsets[i] = (DWORDLONG)pResult->stubLookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ value.stubLookup.runtimeLookup.signature = (DWORDLONG)0;
+ value.stubLookup.runtimeLookup.helper = (DWORD)0;
+ value.stubLookup.runtimeLookup.indirections = (DWORD)0;
+ value.stubLookup.runtimeLookup.testForNull = (DWORD)0;
+ value.stubLookup.runtimeLookup.testForFixup = (DWORD)0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.stubLookup.runtimeLookup.offsets[i] = (DWORDLONG)0;
+
+ value.stubLookup.constLookup.accessType = (DWORD)pResult->stubLookup.constLookup.accessType;
+ value.stubLookup.constLookup.handle = (DWORDLONG)pResult->stubLookup.constLookup.handle;
+ }
+
+ value.instParamLookup.accessType = (DWORD)pResult->instParamLookup.accessType;
+ value.instParamLookup.handle = (DWORDLONG)pResult->instParamLookup.handle;
+ value.secureDelegateInvoke = (DWORD)pResult->secureDelegateInvoke;
+ }
+ else
+ ZeroMemory(&value, sizeof(Agnostic_CORINFO_CALL_INFO));
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ GetCallInfo->Add(key, value);
+ DEBUG_REC(dmpGetCallInfo(key, value));
+}
+void MethodContext::dmpGetCallInfo(const Agnostic_GetCallInfo& key, const Agnostic_CORINFO_CALL_INFO& value)
+{
+ printf("GetCallInfo key"
+ " rt{tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u}"
+ " crt{tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u}"
+ " ch-%016llX flg-%08X",
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec,
+ key.ConstrainedResolvedToken.tokenContext,
+ key.ConstrainedResolvedToken.tokenScope,
+ key.ConstrainedResolvedToken.token,
+ key.ConstrainedResolvedToken.tokenType,
+ key.ConstrainedResolvedToken.hClass,
+ key.ConstrainedResolvedToken.hMethod,
+ key.ConstrainedResolvedToken.hField,
+ key.ConstrainedResolvedToken.typeSpec_Index,
+ key.ConstrainedResolvedToken.cbTypeSpec,
+ key.ConstrainedResolvedToken.methodSpec_Index,
+ key.ConstrainedResolvedToken.cbMethodSpec,
+ key.callerHandle,
+ key.flags);
+ printf(", value mth-%016llX, mf-%08X cf-%08X"
+ " sig{flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u args-%016llX scp-%016llX tok-%08X}"
+ " vsig{flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u args-%016llX scp-%016llX tok-%08X}"
+ " ipl{at-%08X hnd-%016llX}"
+ " sdi-%08X"
+ " excp-%08X",
+ value.hMethod,
+ value.methodFlags,
+ value.classFlags,
+ value.sig.flags,
+ value.sig.numArgs,
+ value.sig.sigInst_classInstCount,
+ value.sig.sigInst_classInst_Index,
+ value.sig.sigInst_methInstCount,
+ value.sig.sigInst_methInst_Index,
+ value.sig.args,
+ value.sig.scope,
+ value.sig.token,
+ value.verSig.flags,
+ value.verSig.numArgs,
+ value.verSig.sigInst_classInstCount,
+ value.verSig.sigInst_classInst_Index,
+ value.verSig.sigInst_methInstCount,
+ value.verSig.sigInst_methInst_Index,
+ value.verSig.args,
+ value.verSig.scope,
+ value.verSig.token,
+ value.instParamLookup.accessType,
+ value.instParamLookup.handle,
+ value.secureDelegateInvoke,
+ value.exceptionCode);
+}
+void MethodContext::repGetCallInfo(
+ CORINFO_RESOLVED_TOKEN *pResolvedToken,
+ CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_CALLINFO_FLAGS flags,
+ CORINFO_CALL_INFO *pResult,
+ DWORD *exceptionCode)
+{
+ Agnostic_GetCallInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCallInfo)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_CALL_INFO value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ if (pConstrainedResolvedToken != nullptr)
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)pConstrainedResolvedToken->tokenContext;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)pConstrainedResolvedToken->tokenScope;
+ key.ConstrainedResolvedToken.token = (DWORD)pConstrainedResolvedToken->token;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)pConstrainedResolvedToken->tokenType;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)pConstrainedResolvedToken->hClass;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)pConstrainedResolvedToken->hMethod;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)pConstrainedResolvedToken->hField;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pConstrainedResolvedToken->pTypeSpec, pConstrainedResolvedToken->cbTypeSpec);
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)pConstrainedResolvedToken->cbTypeSpec;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)GetCallInfo->Contains((unsigned char *)pConstrainedResolvedToken->pMethodSpec, pConstrainedResolvedToken->cbMethodSpec);
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)pConstrainedResolvedToken->cbMethodSpec;
+ }
+ else
+ {
+ key.ConstrainedResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.token = (DWORD)0;
+ key.ConstrainedResolvedToken.tokenType = (DWORD)0;
+ key.ConstrainedResolvedToken.hClass = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hMethod = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.hField = (DWORDLONG)0;
+ key.ConstrainedResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ConstrainedResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ConstrainedResolvedToken.cbMethodSpec = (DWORD)0;
+ }
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ AssertCodeMsg(GetCallInfo->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %08x, %016llx. Probably a missing exception in GetCallInfo", key.ResolvedToken.token, key.ResolvedToken.hClass);
+ value = GetCallInfo->Get(key);
+
+ pResult->hMethod = (CORINFO_METHOD_HANDLE)value.hMethod;
+ pResult->methodFlags = (unsigned)value.methodFlags;
+ pResult->classFlags = (unsigned)value.classFlags;
+ pResult->sig.callConv = (CorInfoCallConv)value.sig.callConv;
+ pResult->sig.retTypeClass = (CORINFO_CLASS_HANDLE)value.sig.retTypeClass;
+ pResult->sig.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.sig.retTypeSigClass;
+ pResult->sig.retType = (CorInfoType)value.sig.retType;
+ pResult->sig.flags = (unsigned)value.sig.flags;
+ pResult->sig.numArgs = (unsigned)value.sig.numArgs;
+ pResult->sig.sigInst.classInstCount = (unsigned)value.sig.sigInst_classInstCount;
+ pResult->sig.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.sig.sigInst_classInst_Index);
+ pResult->sig.sigInst.methInstCount = (unsigned)value.sig.sigInst_methInstCount;
+ pResult->sig.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.sig.sigInst_methInst_Index);
+ pResult->sig.args = (CORINFO_ARG_LIST_HANDLE)value.sig.args;
+ pResult->sig.cbSig = (unsigned int)value.sig.cbSig;
+ pResult->sig.pSig = (PCCOR_SIGNATURE)GetCallInfo->GetBuffer(value.sig.pSig);
+ pResult->sig.scope = (CORINFO_MODULE_HANDLE)value.sig.scope;
+ pResult->sig.token = (mdToken)value.sig.token;
+ if (flags & CORINFO_CALLINFO_VERIFICATION)
+ {
+ pResult->verMethodFlags = (unsigned)value.verMethodFlags;
+ pResult->verSig.callConv = (CorInfoCallConv)value.verSig.callConv;
+ pResult->verSig.retTypeClass = (CORINFO_CLASS_HANDLE)value.verSig.retTypeClass;
+ pResult->verSig.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.verSig.retTypeSigClass;
+ pResult->verSig.retType = (CorInfoType)value.verSig.retType;
+ pResult->verSig.flags = (unsigned)value.verSig.flags;
+ pResult->verSig.numArgs = (unsigned)value.verSig.numArgs;
+ pResult->verSig.sigInst.classInstCount = (unsigned)value.verSig.sigInst_classInstCount;
+ pResult->verSig.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.verSig.sigInst_classInst_Index);
+ pResult->verSig.sigInst.methInstCount = (unsigned)value.verSig.sigInst_methInstCount;
+ pResult->verSig.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetCallInfo->GetBuffer(value.verSig.sigInst_methInst_Index);
+ pResult->verSig.args = (CORINFO_ARG_LIST_HANDLE)value.verSig.args;
+ pResult->verSig.cbSig = (unsigned int)value.verSig.cbSig;
+ pResult->verSig.pSig = (PCCOR_SIGNATURE)GetCallInfo->GetBuffer(value.verSig.pSig);
+ pResult->verSig.scope = (CORINFO_MODULE_HANDLE)value.verSig.scope;
+ pResult->verSig.token = (mdToken)value.verSig.token;
+ }
+ pResult->accessAllowed = (CorInfoIsAccessAllowedResult)value.accessAllowed;
+ pResult->callsiteCalloutHelper.helperNum = (CorInfoHelpFunc)value.callsiteCalloutHelper.helperNum;
+ pResult->callsiteCalloutHelper.numArgs = (unsigned)value.callsiteCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ pResult->callsiteCalloutHelper.args[i].constant = (size_t)value.callsiteCalloutHelper.args[i].constant;
+ pResult->callsiteCalloutHelper.args[i].argType = (CorInfoAccessAllowedHelperArgType)value.callsiteCalloutHelper.args[i].argType;
+ }
+ pResult->thisTransform = (CORINFO_THIS_TRANSFORM)value.thisTransform;
+ pResult->kind = (CORINFO_CALL_KIND)value.kind;
+ pResult->nullInstanceCheck = (BOOL)value.nullInstanceCheck;
+ pResult->contextHandle = (CORINFO_CONTEXT_HANDLE)value.contextHandle;
+ pResult->exactContextNeedsRuntimeLookup = (BOOL)value.exactContextNeedsRuntimeLookup;
+ pResult->stubLookup.lookupKind.needsRuntimeLookup = value.stubLookup.lookupKind.needsRuntimeLookup != 0;
+ pResult->stubLookup.lookupKind.runtimeLookupKind = (CORINFO_RUNTIME_LOOKUP_KIND)value.stubLookup.lookupKind.runtimeLookupKind;
+ if (pResult->stubLookup.lookupKind.needsRuntimeLookup)
+ {
+ pResult->stubLookup.runtimeLookup.signature = (LPVOID)value.stubLookup.runtimeLookup.signature; // needs to be a more flexible copy based on valuevalue.stubLookup.runtimeLookup.signature;
+ pResult->stubLookup.runtimeLookup.helper = (CorInfoHelpFunc)value.stubLookup.runtimeLookup.helper;
+ pResult->stubLookup.runtimeLookup.indirections = (WORD)value.stubLookup.runtimeLookup.indirections;
+ pResult->stubLookup.runtimeLookup.testForNull = value.stubLookup.runtimeLookup.testForNull != 0;
+ pResult->stubLookup.runtimeLookup.testForFixup = value.stubLookup.runtimeLookup.testForFixup != 0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ pResult->stubLookup.runtimeLookup.offsets[i] = (SIZE_T)value.stubLookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ pResult->stubLookup.constLookup.accessType = (InfoAccessType)value.stubLookup.constLookup.accessType;
+ pResult->stubLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)value.stubLookup.constLookup.handle;
+ }
+ if (pResult->kind == CORINFO_VIRTUALCALL_STUB)
+ {
+ cr->CallTargetTypes->Add((DWORDLONG)pResult->codePointerLookup.constLookup.addr, (DWORD)CORINFO_VIRTUALCALL_STUB);
+ }
+ pResult->instParamLookup.accessType = (InfoAccessType)value.instParamLookup.accessType;
+ pResult->instParamLookup.handle = (CORINFO_GENERIC_HANDLE)value.instParamLookup.handle;
+ pResult->secureDelegateInvoke = (BOOL)value.secureDelegateInvoke;
+ *exceptionCode = (DWORD)value.exceptionCode;
+
+ DEBUG_REP(dmpGetCallInfo(key, value));
+}
+
+//
+// Variant of repGetCallInfo that only requires a method handle, i.e. it performs a reverse lookup to find the
+// resolved token info that, along with the given method handle, was passed into getCallInfo.
+//
+// Arguments:
+// methodHandle - The method handle to find call info for.
+// pResult - [out] The call info for the given method.
+//
+// Notes:
+// If this fails to find a recorded call to getCallInfo with the given method handle, this will throw an
+// exception.
+//
+void MethodContext::repGetCallInfoFromMethodHandle(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CALL_INFO *pResult)
+{
+ if (GetCallInfo != nullptr)
+ {
+ for (unsigned int i = 0; i < GetCallInfo->GetCount(); i++)
+ {
+ Agnostic_GetCallInfo key = GetCallInfo->GetKey(i);
+ Agnostic_CORINFO_CALL_INFO val = GetCallInfo->GetItem(i);
+
+ if ((CORINFO_METHOD_HANDLE)val.hMethod == methodHandle)
+ {
+ CORINFO_RESOLVED_TOKEN resolvedToken;
+ DWORD exceptionCode;
+
+ resolvedToken.tokenContext = (CORINFO_CONTEXT_HANDLE)key.ResolvedToken.tokenContext;
+ resolvedToken.tokenScope = (CORINFO_MODULE_HANDLE)key.ResolvedToken.tokenScope;
+ resolvedToken.token = (mdToken)key.ResolvedToken.token;
+ resolvedToken.tokenType = (CorInfoTokenKind)key.ResolvedToken.tokenType;
+
+ repResolveToken(&resolvedToken, &exceptionCode);
+
+ // If the original call to getCallInfo passed in a null constrainedResolvedToken pointer,
+ // then we won't be able to replay it. In that case, we'll need to pass a null pointer into
+ // repGetCallInfo for constrainedResolvedToken, instead of just passing the address of our
+ // local (but meaningless) constrainedResolvedToken struct.
+ CORINFO_RESOLVED_TOKEN constrainedResolvedToken;
+ CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken = nullptr;
+
+ if (key.ConstrainedResolvedToken.tokenContext != 0 &&
+ key.ConstrainedResolvedToken.tokenScope != 0)
+ {
+ constrainedResolvedToken.tokenContext = (CORINFO_CONTEXT_HANDLE)key.ConstrainedResolvedToken.tokenContext;
+ constrainedResolvedToken.tokenScope = (CORINFO_MODULE_HANDLE)key.ConstrainedResolvedToken.tokenScope;
+ constrainedResolvedToken.token = (mdToken)key.ConstrainedResolvedToken.token;
+ constrainedResolvedToken.tokenType = (CorInfoTokenKind)key.ConstrainedResolvedToken.tokenType;
+ pConstrainedResolvedToken = &constrainedResolvedToken;
+
+ repResolveToken(pConstrainedResolvedToken, &exceptionCode);
+ }
+
+ repGetCallInfo(&resolvedToken,
+ pConstrainedResolvedToken,
+ (CORINFO_METHOD_HANDLE)key.callerHandle,
+ (CORINFO_CALLINFO_FLAGS)key.flags,
+ pResult,
+ &exceptionCode);
+ return;
+ }
+ }
+ }
+
+ // If we reached here, we didn't find a key associated with the given method handle
+ LogException(EXCEPTIONCODE_MC, "Didn't find key %016llX.", methodHandle);
+}
+
+
+void MethodContext::recGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand, CorInfoIntrinsics result)
+{
+ if (GetIntrinsicID == nullptr)
+ GetIntrinsicID = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+ value.A = (pMustExpand != nullptr) ? (DWORD) (*pMustExpand ? 1 : 0) : (DWORD)0;
+ value.B = (DWORD)result;
+
+ GetIntrinsicID->Add((DWORDLONG)method, value);
+ DEBUG_REC(dmpGetIntrinsicID((DWORDLONG)method, value));
+}
+void MethodContext::dmpGetIntrinsicID(DWORDLONG key, DD value)
+{
+ printf("GetIntrinsicID key mth-%016llX, mustExpand-%u, value intr-%u", key, value.A, value.B);
+}
+CorInfoIntrinsics MethodContext::repGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand)
+{
+ AssertCodeMsg(GetIntrinsicID != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)method);
+ AssertCodeMsg(GetIntrinsicID->GetIndex((DWORDLONG)method) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)method);
+
+ DD value;
+ value = GetIntrinsicID->Get((DWORDLONG)method);
+ if (pMustExpand != nullptr)
+ {
+ *pMustExpand = (value.A == 0) ? false : true;
+ }
+ CorInfoIntrinsics result = (CorInfoIntrinsics)value.B;
+
+ DEBUG_REP(dmpGetIntrinsicID((DWORDLONG)method, value));
+ return result;
+}
+
+void MethodContext::recIsInSIMDModule(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsInSIMDModule == nullptr)
+ IsInSIMDModule = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsInSIMDModule->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpIsInSIMDModule((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsInSIMDModule(DWORDLONG key, DWORD value)
+{
+ printf("IsInSIMDModule key mth-%016llX, value intr-%u", key, value);
+}
+BOOL MethodContext::repIsInSIMDModule(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(IsInSIMDModule != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(IsInSIMDModule->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ BOOL result = (BOOL)IsInSIMDModule->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsInSIMDModule((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CorInfoUnmanagedCallConv result)
+{
+ if (GetUnmanagedCallConv == nullptr)
+ GetUnmanagedCallConv = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetUnmanagedCallConv->Add((DWORDLONG)method, result);
+ DEBUG_REC(dmpGetUnmanagedCallConv((DWORDLONG)method, (DWORD)result));
+}
+void MethodContext::dmpGetUnmanagedCallConv(DWORDLONG key, DWORD result)
+{
+ printf("GetUnmanagedCallConv key ftn-%016llX, value res-%u", key, result);
+}
+CorInfoUnmanagedCallConv MethodContext::repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method)
+{
+ if ((GetUnmanagedCallConv == nullptr) || (GetUnmanagedCallConv->GetIndex((DWORDLONG)method) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetUnmanagedCallConv returning CORINFO_UNMANAGED_CALLCONV_STDCALL");
+ return CORINFO_UNMANAGED_CALLCONV_STDCALL;
+#else
+ LogException(EXCEPTIONCODE_MC, "Found a null GetUnmanagedCallConv. Probably missing a fatTrigger for %016llX.", (DWORDLONG)method);
+#endif
+ }
+ CorInfoUnmanagedCallConv result = (CorInfoUnmanagedCallConv)GetUnmanagedCallConv->Get((DWORDLONG)method);
+ DEBUG_REP(dmpGetUnmanagedCallConv((DWORDLONG)method, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method, CorInfoInstantiationVerification result)
+{
+ if (IsInstantiationOfVerifiedGeneric == nullptr)
+ IsInstantiationOfVerifiedGeneric = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsInstantiationOfVerifiedGeneric->Add((DWORDLONG)method, result);
+ DEBUG_REC(dmpIsInstantiationOfVerifiedGeneric((DWORDLONG)method, (DWORD)result));
+}
+void MethodContext::dmpIsInstantiationOfVerifiedGeneric(DWORDLONG key, DWORD value)
+{
+ printf("IsInstantiationOfVerifiedGeneric key ftn-%016llX, value res-%u", key, value);
+}
+CorInfoInstantiationVerification MethodContext::repIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method)
+{
+ CorInfoInstantiationVerification result = (CorInfoInstantiationVerification)IsInstantiationOfVerifiedGeneric->Get((DWORDLONG)method);
+ DEBUG_REP(dmpIsInstantiationOfVerifiedGeneric((DWORDLONG)method, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recAsCorInfoType(CORINFO_CLASS_HANDLE cls, CorInfoType result)
+{
+ if (AsCorInfoType == nullptr)
+ AsCorInfoType = new LightWeightMap<DWORDLONG, DWORD>();
+
+ AsCorInfoType->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpAsCorInfoType((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpAsCorInfoType(DWORDLONG key, DWORD value)
+{
+ printf("AsCorInfoType key cls-%016llX, value cit-%u(%s)", key, value, toString((CorInfoType)value));
+}
+CorInfoType MethodContext::repAsCorInfoType(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg((AsCorInfoType != nullptr) && (AsCorInfoType->GetIndex((DWORDLONG)cls) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX. Probable cached value in JIT issue", (DWORDLONG)cls);
+ CorInfoType result = (CorInfoType)AsCorInfoType->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpAsCorInfoType((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recIsValueClass(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsValueClass == nullptr)
+ IsValueClass = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsValueClass->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpIsValueClass((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsValueClass(DWORDLONG key, DWORD value)
+{
+ printf("IsValueClass key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repIsValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg((IsValueClass != nullptr) && (IsValueClass->GetIndex((DWORDLONG)cls) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+
+ BOOL result = (BOOL)IsValueClass->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsValueClass((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsStructRequiringStackAllocRetBuf == nullptr)
+ IsStructRequiringStackAllocRetBuf = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsStructRequiringStackAllocRetBuf->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpIsStructRequiringStackAllocRetBuf((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsStructRequiringStackAllocRetBuf(DWORDLONG key, DWORD value)
+{
+ printf("IsStructRequiringStackAllocRetBuf key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(IsStructRequiringStackAllocRetBuf != nullptr, EXCEPTIONCODE_MC, "Found a null IsStructRequiringStackAllocRetBuf. Probably missing a fatTrigger for %016llX.", (DWORDLONG)cls);
+ AssertCodeMsg(IsStructRequiringStackAllocRetBuf->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ BOOL result = (BOOL)IsStructRequiringStackAllocRetBuf->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsStructRequiringStackAllocRetBuf((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetClassSize(CORINFO_CLASS_HANDLE cls, unsigned result)
+{
+ if (GetClassSize == nullptr)
+ GetClassSize = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetClassSize->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpGetClassSize((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetClassSize(DWORDLONG key, DWORD val)
+{
+ printf("GetClassSize key %016llX, value %u", key, val);
+}
+unsigned MethodContext::repGetClassSize(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(GetClassSize != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassSize->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ unsigned result = (unsigned)GetClassSize->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetClassSize((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls, unsigned result)
+{
+ if (GetClassNumInstanceFields == nullptr)
+ GetClassNumInstanceFields = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetClassNumInstanceFields->Add((DWORDLONG)cls, (DWORD)result);
+ DEBUG_REC(dmpGetClassNumInstanceFields((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetClassNumInstanceFields(DWORDLONG key, DWORD value)
+{
+ printf("GetClassNumInstanceFields key cls-%016llX, value res-%u", key, value);
+}
+unsigned MethodContext::repGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(GetClassNumInstanceFields != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassNumInstanceFields->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+
+ unsigned result = (unsigned)GetClassNumInstanceFields->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetClassNumInstanceFields((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls, CorInfoHelpFunc result)
+{
+ if (GetNewArrHelper == nullptr)
+ GetNewArrHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetNewArrHelper->Add((DWORDLONG)arrayCls, result);
+ DEBUG_REC(dmpGetNewArrHelper((DWORDLONG)arrayCls, (DWORD)result));
+}
+void MethodContext::dmpGetNewArrHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetNewArrHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetNewArrHelper->Get((DWORDLONG)arrayCls);
+ DEBUG_REP(dmpGetNewArrHelper((DWORDLONG)arrayCls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd, CorInfoHelpFunc result)
+{
+ if (GetSharedCCtorHelper == nullptr)
+ GetSharedCCtorHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetSharedCCtorHelper->Add((DWORDLONG)clsHnd, result);
+ DEBUG_REC(dmpGetSharedCCtorHelper((DWORDLONG)clsHnd, (DWORD)result));
+}
+void MethodContext::dmpGetSharedCCtorHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetSharedCCtorHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetSharedCCtorHelper->Get((DWORDLONG)clsHnd);
+ DEBUG_REP(dmpGetSharedCCtorHelper((DWORDLONG)clsHnd, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn, CorInfoHelpFunc result)
+{
+ if (GetSecurityPrologHelper == nullptr)
+ GetSecurityPrologHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetSecurityPrologHelper->Add((DWORDLONG)ftn, result);
+ DEBUG_REC(dmpGetSecurityPrologHelper((DWORDLONG)ftn, (DWORD)result));
+}
+void MethodContext::dmpGetSecurityPrologHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetSecurityPrologHelper key ftn-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetSecurityPrologHelper->Get((DWORDLONG)ftn);
+ DEBUG_REP(dmpGetSecurityPrologHelper((DWORDLONG)ftn, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetTypeForBox(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
+{
+ if (GetTypeForBox == nullptr)
+ GetTypeForBox = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetTypeForBox->Add((DWORDLONG)cls, (DWORDLONG)result);
+ DEBUG_REC(dmpGetTypeForBox((DWORDLONG)cls, (DWORDLONG)result));
+}
+void MethodContext::dmpGetTypeForBox(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetTypeForBox key cls-%016llX, value res-%016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetTypeForBox(CORINFO_CLASS_HANDLE cls)
+{
+ CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)GetTypeForBox->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetTypeForBox((DWORDLONG)cls, (DWORDLONG)result));
+ return result;
+}
+
+void MethodContext::recGetBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result)
+{
+ if (GetBoxHelper == nullptr)
+ GetBoxHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetBoxHelper->Add((DWORDLONG)cls, result);
+ DEBUG_REC(dmpGetBoxHelper((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetBoxHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetBoxHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetBoxHelper(CORINFO_CLASS_HANDLE cls)
+{
+ CorInfoHelpFunc result = (CorInfoHelpFunc)GetBoxHelper->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetBoxHelper((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetBuiltinClass(CorInfoClassId classId, CORINFO_CLASS_HANDLE result)
+{
+ if (GetBuiltinClass == nullptr)
+ GetBuiltinClass = new LightWeightMap<DWORD, DWORDLONG>();
+
+ GetBuiltinClass->Add((DWORD)classId, (DWORDLONG)result);
+ DEBUG_REC(dmpGetBuiltinClass((DWORDLONG)classId, (DWORDLONG)result));
+}
+void MethodContext::dmpGetBuiltinClass(DWORD key, DWORDLONG value)
+{
+ printf("GetBuiltinClass key cls-%08X, value cls-%016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetBuiltinClass(CorInfoClassId classId)
+{
+ CORINFO_CLASS_HANDLE value = (CORINFO_CLASS_HANDLE)GetBuiltinClass->Get((DWORD)classId);
+ DEBUG_REP(dmpGetBuiltinClass((DWORDLONG)classId, (DWORDLONG)value));
+ return value;
+}
+
+void MethodContext::recGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls, CorInfoType result)
+{
+ if (GetTypeForPrimitiveValueClass == nullptr)
+ GetTypeForPrimitiveValueClass = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetTypeForPrimitiveValueClass->Add((DWORDLONG)cls, result);
+ DEBUG_REC(dmpGetTypeForPrimitiveValueClass((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpGetTypeForPrimitiveValueClass(DWORDLONG key, DWORD value)
+{
+ printf("GetTypeForPrimitiveValueClass key cls-%016llX, value cit-%u(%s)", key, value, toString((CorInfoType)value));
+}
+CorInfoType MethodContext::repGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(GetTypeForPrimitiveValueClass != nullptr, EXCEPTIONCODE_MC, "Encountered an empty LWM while looking for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetTypeForPrimitiveValueClass->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ CorInfoType result = (CorInfoType)GetTypeForPrimitiveValueClass->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpGetTypeForPrimitiveValueClass((DWORDLONG)cls, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetParentType(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
+{
+ if (GetParentType == nullptr)
+ GetParentType = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetParentType->Add((DWORDLONG)cls, (DWORDLONG)result);
+
+}
+void MethodContext::dmpGetParentType(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetParentType key cls-%016llX, value cls-%016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetParentType(CORINFO_CLASS_HANDLE cls)
+{
+ CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)GetParentType->Get((DWORDLONG)cls);
+ return result;
+}
+
+void MethodContext::recIsSDArray(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (IsSDArray == nullptr)
+ IsSDArray = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsSDArray->Add((DWORDLONG)cls, result);
+ DEBUG_REC(dmpIsSDArray((DWORDLONG)cls, (DWORD)result));
+}
+void MethodContext::dmpIsSDArray(DWORDLONG key, DWORD value)
+{
+ printf("IsSDArray key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repIsSDArray(CORINFO_CLASS_HANDLE cls)
+{
+ AssertCodeMsg(IsSDArray != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(IsSDArray->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ BOOL temp = (BOOL)IsSDArray->Get((DWORDLONG)cls);
+ DEBUG_REP(dmpIsSDArray((DWORDLONG)cls, (DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recGetFieldClass(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE result)
+{
+ if (GetFieldClass == nullptr)
+ GetFieldClass = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetFieldClass->Add((DWORDLONG)field, (DWORDLONG)result);
+ DEBUG_REC(dmpGetFieldClass((DWORDLONG)field, (DWORDLONG)result));
+}
+void MethodContext::dmpGetFieldClass(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetFieldClass key %016llX, value %016llX", key, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetFieldClass(CORINFO_FIELD_HANDLE field)
+{
+ AssertCodeMsg(GetFieldClass != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)field);
+ AssertCodeMsg(GetFieldClass->GetIndex((DWORDLONG)field) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)field);
+ CORINFO_CLASS_HANDLE temp = (CORINFO_CLASS_HANDLE)GetFieldClass->Get((DWORDLONG)field);
+ DEBUG_REP(dmpGetFieldClass((DWORDLONG)field, (DWORDLONG)temp));
+ return temp;
+}
+
+void MethodContext::recGetFieldOffset(CORINFO_FIELD_HANDLE field, unsigned result)
+{
+ if (GetFieldOffset == nullptr)
+ GetFieldOffset = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetFieldOffset->Add((DWORDLONG)field, result);
+ DEBUG_REC(dmpGetFieldOffset((DWORDLONG)field, (DWORD)result));
+}
+void MethodContext::dmpGetFieldOffset(DWORDLONG key, DWORD value)
+{
+ printf("GetFieldOffset key FLD-%016llX, value %08X", key, value);
+}
+unsigned MethodContext::repGetFieldOffset(CORINFO_FIELD_HANDLE field)
+{
+ AssertCodeMsg((GetFieldOffset != nullptr) && (GetFieldOffset->GetIndex((DWORDLONG)field) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)field);
+
+ unsigned temp = (unsigned)GetFieldOffset->Get((DWORDLONG)field);
+ DEBUG_REP(dmpGetFieldOffset((DWORDLONG)field, (DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle, CorInfoHelpFunc result)
+{
+ if (GetLazyStringLiteralHelper == nullptr)
+ GetLazyStringLiteralHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetLazyStringLiteralHelper->Add((DWORDLONG)handle, result);
+ DEBUG_REC(dmpGetLazyStringLiteralHelper((DWORDLONG)handle, result));
+}
+
+void MethodContext::dmpGetLazyStringLiteralHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetLazyStringLiteralHelper key mod-%016llX, value res-%u", key, value);
+}
+
+CorInfoHelpFunc MethodContext::repGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle)
+{
+ AssertCodeMsg(GetLazyStringLiteralHelper != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)handle);
+ AssertCodeMsg(GetLazyStringLiteralHelper->GetIndex((DWORDLONG)handle) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)handle);
+ CorInfoHelpFunc temp = (CorInfoHelpFunc)GetLazyStringLiteralHelper->Get((DWORDLONG)handle);
+ DEBUG_REP(dmpGetLazyStringLiteralHelper((DWORDLONG)handle, temp));
+ return temp;
+}
+
+void MethodContext::recGetUnBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result)
+{
+ if (GetUnBoxHelper == nullptr)
+ GetUnBoxHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetUnBoxHelper->Add((DWORDLONG)cls, result);
+}
+void MethodContext::dmpGetUnBoxHelper(DWORDLONG key, DWORD value)
+{
+ printf("GetUnBoxHelper key cls-%016llX, value res-%u", key, value);
+}
+CorInfoHelpFunc MethodContext::repGetUnBoxHelper(CORINFO_CLASS_HANDLE cls)
+{
+ CorInfoHelpFunc temp = (CorInfoHelpFunc)GetUnBoxHelper->Get((DWORDLONG)cls);
+ return temp;
+}
+
+void MethodContext::recGetReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup,
+ bool result
+ )
+{
+ LogError("getReadyToRunHelper NYI");
+
+ // TODO: we need a more sophisticated mapping
+
+ if (GetReadyToRunHelper == nullptr)
+ GetReadyToRunHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ //GetReadyToRunHelper->Add((DWORDLONG)cls, result);
+}
+void MethodContext::dmpGetReadyToRunHelper(DWORDLONG key, DWORD value)
+{
+ LogError("getReadyToRunHelper NYI");
+}
+bool MethodContext::repGetReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ LogError("getReadyToRunHelper NYI");
+ return false;
+}
+
+void MethodContext::recGetReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ LogError("GetReadyToRunDelegateCtorHelper NYI");
+
+ // TODO: we need a more sophisticated mapping
+
+ if (GetReadyToRunDelegateCtorHelper == nullptr)
+ GetReadyToRunDelegateCtorHelper = new LightWeightMap<DWORDLONG, DWORD>();
+
+ //GetReadyToRunDelegateCtorHelper->Add((DWORDLONG)delegateType, result);
+}
+void MethodContext::dmpGetReadyToRunDelegateCtorHelper(DWORDLONG key, DWORD value)
+{
+ LogError("getReadyToRunDelegateCtorHelper NYI");
+}
+void MethodContext::repGetReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ LogError("getReadyToRunDelegateCtorHelper NYI");
+}
+
+void MethodContext::recGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection, void *result)
+{
+ if (GetHelperFtn == nullptr)
+ GetHelperFtn = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+ value.A = (DWORDLONG)*ppIndirection;
+ value.B = (DWORDLONG)result;
+
+ GetHelperFtn->Add((DWORD)ftnNum, value);
+ DEBUG_REC(dmpGetHelperFtn((DWORD)ftnNum, value));
+}
+void MethodContext::dmpGetHelperFtn(DWORD key, DLDL value)
+{
+ printf("GetHelperFtn key ftn-%u, value ppi-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection)
+{
+ if ((GetHelperFtn == nullptr) || (GetHelperFtn->GetIndex((DWORD)ftnNum) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetHelperFtn returning 0xCAFE0002 and 0XCAFE0003");
+ *ppIndirection = (void*)(size_t)0xCAFE0002;
+ return (void*)(size_t)0xCAFE0003;
+#else
+ LogException(EXCEPTIONCODE_MC, "Encountered an empty LWM while looking for %08X", (DWORD)ftnNum);
+#endif
+ }
+
+ DLDL value = (DLDL)GetHelperFtn->Get((DWORD)ftnNum);
+ *ppIndirection = (void *)value.A;
+ DEBUG_REP(dmpGetHelperFtn((DWORD)ftnNum, value));
+ return (void *)value.B;
+}
+
+//
+// Finds the identifier (i.e. the CorInfoHelpFunc enum) of a helper function, based on the address where it
+// is located in memory.
+//
+// Arguments:
+// functionAddress - The starting address of the helper function in memory.
+// pResult - [out] Pointer to write out the identifier of the helper function located at the given
+// address.
+//
+// Return Value:
+// True if there is a helper function associated with the given target address; false otherwise.
+//
+// Assumptions:
+// Only the lower 32 bits of the method address are necessary to identify the method.
+//
+// Notes:
+// - See notes for fndGetFunctionEntryPoint for a more in-depth discussion of why we only match on the
+// lower 32 bits of the target address.
+// - This might not work correctly with method contexts recorded via NGen compilation.
+//
+bool MethodContext::fndGetHelperFtn(void *functionAddress, CorInfoHelpFunc *pResult)
+{
+ if (GetHelperFtn != nullptr)
+ {
+ for (unsigned int i = 0; i < GetHelperFtn->GetCount(); i++)
+ {
+ DWORD key = GetHelperFtn->GetKey(i);
+ DLDL val = GetHelperFtn->GetItem(i);
+
+ // TODO-Cleanup: this only compares the function addresses, and doesn't account for
+ // ppIndirection, which will break if the helper is a dynamic helper function.
+ if (val.B == (DWORDLONG)functionAddress)
+ {
+ *pResult = (CorInfoHelpFunc)key;
+ return true;
+ }
+ }
+ }
+
+ LogDebug("fndGetHelperFtn - didn't find value %p", functionAddress);
+ return false;
+}
+
+void MethodContext::recGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection, CORINFO_JUST_MY_CODE_HANDLE result)
+{
+ if (GetJustMyCodeHandle == nullptr)
+ GetJustMyCodeHandle = new LightWeightMap<DWORDLONG, DLDL>();
+ DLDL temp;
+ temp.A = (DWORDLONG)*ppIndirection;
+ temp.B = (DWORDLONG)result;
+ GetJustMyCodeHandle->Add((DWORDLONG)method, temp);
+ DEBUG_REC(dmpGetJustMyCodeHandle((DWORDLONG)method, temp));
+}
+void MethodContext::dmpGetJustMyCodeHandle(DWORDLONG key, DLDL value)
+{
+ printf("GetJustMyCodeHandle key ftn-%016llX, value pp-%016llX, res-%016llX", key, value.A, value.B);
+}
+CORINFO_JUST_MY_CODE_HANDLE MethodContext::repGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection)
+{
+ DLDL temp = (DLDL)GetJustMyCodeHandle->Get((DWORDLONG)method);
+ *ppIndirection = (CORINFO_JUST_MY_CODE_HANDLE *)temp.A;
+ CORINFO_JUST_MY_CODE_HANDLE result = (CORINFO_JUST_MY_CODE_HANDLE)temp.B;
+ DEBUG_REP(dmpGetJustMyCodeHandle((DWORDLONG)method, temp));
+ return result;
+}
+
+void MethodContext::recGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags)
+{
+ if (GetFunctionEntryPoint == nullptr)
+ GetFunctionEntryPoint = new LightWeightMap<DLD, DLD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)accessFlags;
+ value.A = (DWORDLONG)pResult->addr; //First union member
+ value.B = (DWORD)pResult->accessType;
+ GetFunctionEntryPoint->Add(key, value);
+ DEBUG_REC(dmpGetFunctionEntryPoint(key, value));
+}
+void MethodContext::dmpGetFunctionEntryPoint(DLD key, DLD value)
+{
+ printf("GetFunctionEntryPoint key ftn-%016llX af-%08X, value add-%016llX at-%u", key.A, key.B, value.A, value.B);
+}
+void MethodContext::repGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)accessFlags;
+
+ if (GetFunctionEntryPoint == nullptr)
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetFunctionEntryPoint fabricated result for request.");
+ pResult->accessType = (InfoAccessType)IAT_PVALUE;
+ pResult->addr = (void *)((DWORDLONG)ftn + 0x1c);
+ return;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX, %8x", (DWORDLONG)ftn, accessFlags);
+#endif
+ }
+ if (GetFunctionEntryPoint->GetIndex(key) == -1)
+ {
+#ifdef sparseMC
+ key.B ^= (DWORD)CORINFO_ACCESS_NONNULL;
+ if (GetFunctionEntryPoint->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFunctionEntryPoint found result with inverted CORINFO_ACCESS_NONNULL");
+ }
+ else
+ {
+ LogDebug("Sparse - repGetFunctionEntryPoint fabricated result for request.");
+ pResult->accessType = (InfoAccessType)IAT_PVALUE;
+ pResult->addr = (void *)((DWORDLONG)ftn + 0x1c);
+ return;
+ }
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX, %8x", (DWORDLONG)ftn, accessFlags);
+#endif
+ }
+ value = GetFunctionEntryPoint->Get(key);
+
+ pResult->accessType = (InfoAccessType)value.B;
+ pResult->addr = (void *)value.A;
+ DEBUG_REP(dmpGetFunctionEntryPoint(key, value));
+}
+
+//
+// Finds the method handle associated with a method, based on the address where its generated code is located
+// in memory.
+//
+// Arguments:
+// methodAddress - The starting address of the generated code for the method.
+// pResult - [out] Pointer to a method handle to write into. If this successfully finds a method
+// handle associated with the given target address, it will be written to here.
+//
+// Return Value:
+// True if there is a helper function associated with the given target address; false otherwise.
+//
+// Assumptions:
+// - The given method address does not point to a jump stub.
+// - The given method is not a generic method.
+// - Only the lower 32 bits of the method address are necessary to identify the method.
+//
+// Notes:
+// On 64-bit platforms, this only checks if the lower 32 bits of the method address match a recorded
+// function entry point because, on AMD64, this only supports reverse lookups for near calls, which
+// encode their target address as a 32-bit PC-relative displacement.
+//
+// Practically speaking, there are two concrete reasons why we ignore the upper 64 bits. First, on
+// AMD64, when the JIT emits a near call, it records the displacement it encodes into the call with
+// the EE as a 32-bit relative "relocation". Consequently, this leads to the second reason why we
+// ignore the upper 64 bits: when SuperPMI is replaying method compilation, it patches up addresses
+// based on the "relocations" the JIT had previously recorded with the EE. Since these relocations
+// are only 32-bit deltas, what you'll usually end up seeing is that, after SuperPMI has applied
+// these fixups, the lower 32 bits of method addresses will match, but the upper 32 bits will differ.
+//
+bool MethodContext::fndGetFunctionEntryPoint(DLD value, CORINFO_METHOD_HANDLE *pResult)
+{
+ if (GetFunctionEntryPoint != nullptr)
+ {
+ for (unsigned int i = 0; i < GetFunctionEntryPoint->GetCount(); i++)
+ {
+ DLD key = GetFunctionEntryPoint->GetKey(i);
+ DLD val = GetFunctionEntryPoint->GetItem(i);
+
+ // TODO-Cleanup: we should be more conscious of the rest of the information in CORINFO_CONST_LOOKUP
+ if ((DWORD)val.A == (DWORD)value.A)
+ {
+ *pResult = (CORINFO_METHOD_HANDLE)key.A;
+ return true;
+ }
+ }
+ }
+
+ LogDebug("fndGetFunctionEntryPoint - didn't find value %016llX", value.A);
+ return false;
+}
+
+void MethodContext::recConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void *pValue, InfoAccessType result)
+{
+ if (ConstructStringLiteral == nullptr)
+ ConstructStringLiteral = new LightWeightMap<DLD, DLD>();
+ DLD temp;
+ ZeroMemory(&temp, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD temp2;
+ temp.A = (DWORDLONG)module;
+ temp.B = (DWORD)metaTok;
+ temp2.A = (DWORDLONG)pValue;
+ temp2.B = (DWORD)result;
+
+ ConstructStringLiteral->Add(temp, temp2);
+ DEBUG_REC(dmpConstructStringLiteral(temp, temp2));
+}
+void MethodContext::dmpConstructStringLiteral(DLD key, DLD value)
+{
+ printf("ConstructStringLiteral key mod-%016llX tok-%08X, value pp-%016llX iat-%u", key.A, key.B, value.A, value.B);
+}
+InfoAccessType MethodContext::repConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void **ppValue)
+{
+ DLD temp;
+ ZeroMemory(&temp, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD temp2;
+ temp.A = (DWORDLONG)module;
+ temp.B = (DWORD)metaTok;
+ AssertCodeMsg(ConstructStringLiteral != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)module);
+ AssertCodeMsg(ConstructStringLiteral->GetIndex(temp) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)module);
+ temp2 = ConstructStringLiteral->Get(temp);
+ *ppValue = (void *)temp2.A;
+ DEBUG_REP(dmpConstructStringLiteral(temp, temp2));
+ return (InfoAccessType)temp2.B;
+}
+
+void MethodContext::recEmptyStringLiteral(void **pValue, InfoAccessType result)
+{
+ if (EmptyStringLiteral == nullptr)
+ EmptyStringLiteral = new DenseLightWeightMap<DLD>();
+ DLD temp2;
+ temp2.A = (DWORDLONG)*pValue;
+ temp2.B = (DWORD)result;
+
+ EmptyStringLiteral->Append(temp2);
+}
+void MethodContext::dmpEmptyStringLiteral(DWORD key, DLD value)
+{
+ printf("EmptyStringLiteral key %u, value pVal-%016llX res-%u", key, value.A, value.B);
+}
+InfoAccessType MethodContext::repEmptyStringLiteral(void **ppValue)
+{
+ // TODO-Cleanup: sketchy if someone calls this twice
+ DLD temp2;
+ temp2 = EmptyStringLiteral->Get((DWORD)0);
+ *ppValue = (void *)temp2.A;
+ return (InfoAccessType)temp2.B;
+}
+
+void MethodContext::recGetArgType(CORINFO_SIG_INFO *sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, CorInfoTypeWithMod result, DWORD exceptionCode)
+{
+ if (GetArgType == nullptr)
+ GetArgType = new LightWeightMap<Agnostic_GetArgType, Agnostic_GetArgType_Value>();
+
+ Agnostic_GetArgType key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgType)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetArgType_Value value;
+
+ //Only setting values for things the EE seems to pay attention to... this is necessary since some of the values
+ //are unset and fail our precise comparisions...
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)sig->flags;
+ key.sig.numArgs = (DWORD)sig->numArgs;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgType->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgType->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ value.vcTypeRet = (DWORDLONG)*vcTypeRet;
+ value.result = (DWORD)result;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ GetArgType->Add(key, value);
+ DEBUG_REC(dmpGetArgType(key, value));
+}
+void MethodContext::dmpGetArgType(const Agnostic_GetArgType& key, const Agnostic_GetArgType_Value& value)
+{
+ printf("GetArgType key flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u scp-%016llX arg-%016llX",
+ key.sig.flags,
+ key.sig.numArgs,
+ key.sig.sigInst_classInstCount,
+ key.sig.sigInst_classInst_Index,
+ key.sig.sigInst_methInstCount,
+ key.sig.sigInst_methInst_Index,
+ key.sig.scope,
+ key.args);
+ printf(", value rt-%016llX ci-%u excp-%08X", value.vcTypeRet, value.result, value.exceptionCode);
+}
+CorInfoTypeWithMod MethodContext::repGetArgType(CORINFO_SIG_INFO *sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, DWORD *exceptionCode)
+{
+ Agnostic_GetArgType key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgType)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetArgType_Value value;
+
+ AssertCodeMsg(GetArgType != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgType", key.sig.scope, key.args);
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)sig->flags;
+ key.sig.numArgs = (DWORD)sig->numArgs;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgType->Contains((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgType->Contains((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ AssertCodeMsg(GetArgType->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgType", key.sig.scope, key.args);
+
+ value = GetArgType->Get(key);
+ *vcTypeRet = (CORINFO_CLASS_HANDLE)value.vcTypeRet;
+ CorInfoTypeWithMod temp = (CorInfoTypeWithMod)value.result;
+ *exceptionCode = (DWORD)value.exceptionCode;
+
+ DEBUG_REP(dmpGetArgType(key, value));
+ return temp;
+}
+
+void MethodContext::recGetArgNext(CORINFO_ARG_LIST_HANDLE args, CORINFO_ARG_LIST_HANDLE result)
+{
+ if (GetArgNext == nullptr)
+ GetArgNext = new LightWeightMap<DWORDLONG, DWORDLONG>();
+
+ GetArgNext->Add((DWORDLONG)args, (DWORDLONG)result);
+ DEBUG_REC(dmpGetArgNext((DWORDLONG)args, (DWORDLONG)result));
+}
+void MethodContext::dmpGetArgNext(DWORDLONG key, DWORDLONG value)
+{
+ printf("GetArgNext key %016llX, value %016llX", key, value);
+}
+CORINFO_ARG_LIST_HANDLE MethodContext::repGetArgNext(CORINFO_ARG_LIST_HANDLE args)
+{
+ CORINFO_ARG_LIST_HANDLE temp = (CORINFO_ARG_LIST_HANDLE)GetArgNext->Get((DWORDLONG)args);
+ DEBUG_REP(dmpGetArgNext((DWORDLONG)args, (DWORDLONG)temp));
+ return temp;
+}
+void MethodContext::recGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent)
+{
+ if (GetMethodSig == nullptr)
+ GetMethodSig = new LightWeightMap<DLDL, Agnostic_CORINFO_SIG_INFO>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORDLONG)memberParent;
+
+ value.callConv = (DWORD)sig->callConv;
+ value.retTypeClass = (DWORDLONG)sig->retTypeClass;
+ value.retTypeSigClass = (DWORDLONG)sig->retTypeSigClass;
+ value.retType = (DWORD)sig->retType;
+ value.flags = (DWORD)sig->flags;
+ value.numArgs = (DWORD)sig->numArgs;
+ value.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ value.sigInst_classInst_Index = GetMethodSig->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8); //porting issue
+ value.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ value.sigInst_methInst_Index = GetMethodSig->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8); //porting issue
+ value.args = (DWORDLONG)sig->args;
+ value.cbSig = (DWORD)sig->cbSig;
+ value.pSig = (DWORD)GetMethodSig->AddBuffer((unsigned char *)sig->pSig, sig->cbSig);
+ value.scope = (DWORDLONG)sig->scope;
+ value.token = (DWORD)sig->token;
+
+ GetMethodSig->Add(key, value);
+ DEBUG_REC(dmpGetMethodSig(key, value));
+}
+void MethodContext::dmpGetMethodSig(DLDL key, const Agnostic_CORINFO_SIG_INFO& value)
+{
+ printf("GetMethodSig key ftn-%016llX prt-%016llX, value cc-%u rtc-%016llX rts-%016llX rt-%u(%s) flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u args-%016llX sig-%u pSig-%u scp-%016llX tok-%08X",
+ key.A, key.B,
+ value.callConv,
+ value.retTypeClass,
+ value.retTypeSigClass,
+ value.retType,
+ toString((CorInfoType)value.retType),
+ value.flags,
+ value.numArgs,
+ value.sigInst_classInstCount,
+ value.sigInst_classInst_Index,
+ value.sigInst_methInstCount,
+ value.sigInst_methInst_Index,
+ value.args,
+ value.cbSig,
+ value.pSig,
+ value.scope,
+ value.token);
+}
+void MethodContext::repGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORDLONG)memberParent;
+
+ value = GetMethodSig->Get(key);
+
+ sig->callConv = (CorInfoCallConv)value.callConv;
+ sig->retTypeClass = (CORINFO_CLASS_HANDLE)value.retTypeClass;
+ sig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.retTypeSigClass;
+ sig->retType = (CorInfoType)value.retType;
+ sig->flags = (unsigned)value.flags;
+ sig->numArgs = (unsigned)value.numArgs;
+ sig->sigInst.classInstCount = (unsigned)value.sigInst_classInstCount;
+ sig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetMethodSig->GetBuffer(value.sigInst_classInst_Index);
+ sig->sigInst.methInstCount = (unsigned)value.sigInst_methInstCount;
+ sig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetMethodSig->GetBuffer(value.sigInst_methInst_Index);
+ sig->args = (CORINFO_ARG_LIST_HANDLE)value.args;
+ sig->cbSig = (unsigned int)value.cbSig;
+ sig->pSig = (PCCOR_SIGNATURE)GetMethodSig->GetBuffer(value.pSig);
+ sig->scope = (CORINFO_MODULE_HANDLE)value.scope;
+ sig->token = (mdToken)value.token;
+
+ DEBUG_REP(dmpGetMethodSig(key, value));
+}
+
+void MethodContext::recGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE result, DWORD exceptionCode)
+{
+ if (GetArgClass == nullptr)
+ GetArgClass = new LightWeightMap<Agnostic_GetArgClass, Agnostic_GetArgClass_Value>();
+
+ Agnostic_GetArgClass key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetArgClass_Value value;
+
+ //Only setting values for things the EE seems to pay attention to... this is necessary since some of the values
+ //are unset and fail our precise comparisions...
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)0;
+ key.sig.numArgs = (DWORD)0;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgClass->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgClass->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ value.result = (DWORDLONG)result;
+ value.exceptionCode = exceptionCode;
+
+ GetArgClass->Add(key, value);
+ DEBUG_REC(dmpGetArgClass(key, value));
+}
+void MethodContext::dmpGetArgClass(const Agnostic_GetArgClass& key, const Agnostic_GetArgClass_Value& value)
+{
+ printf("GetArgClass key cc-%u ci-%u mc-%u mi-%u scp-%016llX args-%016llX",
+ key.sig.sigInst_classInstCount,
+ key.sig.sigInst_classInst_Index,
+ key.sig.sigInst_methInstCount,
+ key.sig.sigInst_methInst_Index,
+ key.sig.scope,
+ key.args);
+ printf(", value %016llX excp-%08X", value.result, value.exceptionCode);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, DWORD *exceptionCode)
+{
+ Agnostic_GetArgClass key;
+ ZeroMemory(&key, sizeof(Agnostic_GetArgClass)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ AssertCodeMsg(GetArgClass != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgClass", key.sig.scope, key.args);
+ key.sig.callConv = (DWORD)0;
+ key.sig.retTypeClass = (DWORDLONG)0;
+ key.sig.retTypeSigClass = (DWORDLONG)0;
+ key.sig.retType = (DWORD)0;
+ key.sig.flags = (DWORD)0;
+ key.sig.numArgs = (DWORD)0;
+ key.sig.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ key.sig.sigInst_classInst_Index = (DWORD)GetArgClass->Contains((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8);
+ key.sig.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ key.sig.sigInst_methInst_Index = (DWORD)GetArgClass->Contains((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8);
+ key.sig.args = (DWORDLONG)0;
+ key.sig.cbSig = (DWORD)0;
+ key.sig.pSig = -1;
+ key.sig.scope = (DWORDLONG)sig->scope;
+ key.sig.token = (DWORD)0;
+ key.args = (DWORDLONG)args;
+
+ AssertCodeMsg(GetArgClass->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx, %016llx. probably a missing exception in getArgClass", key.sig.scope, key.args);
+
+ Agnostic_GetArgClass_Value value = GetArgClass->Get(key);
+ *exceptionCode = value.exceptionCode;
+ DEBUG_REP(dmpGetArgClass(key, value));
+
+ return (CORINFO_CLASS_HANDLE)value.result;
+}
+
+void MethodContext::recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, bool result, DWORD exceptionCode)
+{
+ if (GetMethodInfo == nullptr)
+ GetMethodInfo = new LightWeightMap<DWORDLONG, Agnostic_GetMethodInfo>();
+
+ Agnostic_GetMethodInfo value;
+ ZeroMemory(&value, sizeof(Agnostic_GetMethodInfo));
+
+ if (result)
+ {
+ value.info.ftn = (DWORDLONG)info->ftn;
+ value.info.scope = (DWORDLONG)info->scope;
+ value.info.ILCode_offset = (DWORD)GetMethodInfo->AddBuffer(info->ILCode, info->ILCodeSize);
+ value.info.ILCodeSize = (DWORD)info->ILCodeSize;
+ value.info.maxStack = (DWORD)info->maxStack;
+ value.info.EHcount = (DWORD)info->EHcount;
+ value.info.options = (DWORD)info->options;
+ value.info.regionKind = (DWORD)info->regionKind;
+ value.info.args.callConv = (DWORD)info->args.callConv;
+ value.info.args.retTypeClass = (DWORDLONG)info->args.retTypeClass;
+ value.info.args.retTypeSigClass = (DWORDLONG)info->args.retTypeSigClass;
+ value.info.args.retType = (DWORD)info->args.retType;
+ value.info.args.flags = (DWORD)info->args.flags;
+ value.info.args.numArgs = (DWORD)info->args.numArgs;
+ value.info.args.sigInst_classInstCount = (DWORD)info->args.sigInst.classInstCount;
+ value.info.args.sigInst_classInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->args.sigInst.classInst, info->args.sigInst.classInstCount * 8); //porting issue
+ value.info.args.sigInst_methInstCount = (DWORD)info->args.sigInst.methInstCount;
+ value.info.args.sigInst_methInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->args.sigInst.methInst, info->args.sigInst.methInstCount * 8); //porting issue
+ value.info.args.args = (DWORDLONG)info->args.args;
+ value.info.args.cbSig = (DWORD)info->args.cbSig;
+ value.info.args.pSig = (DWORD)GetMethodInfo->AddBuffer((unsigned char *)info->args.pSig, info->args.cbSig);
+ value.info.args.scope = (DWORDLONG)info->args.scope;
+ value.info.args.token = (DWORD)info->args.token;
+ value.info.locals.callConv = (DWORD)info->locals.callConv;
+ value.info.locals.retTypeClass = (DWORDLONG)info->locals.retTypeClass;
+ value.info.locals.retTypeSigClass = (DWORDLONG)info->locals.retTypeSigClass;
+ value.info.locals.retType = (DWORD)info->locals.retType;
+ value.info.locals.flags = (DWORD)info->locals.flags;
+ value.info.locals.numArgs = (DWORD)info->locals.numArgs;
+ value.info.locals.sigInst_classInstCount = (DWORD)info->locals.sigInst.classInstCount;
+ value.info.locals.sigInst_classInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->locals.sigInst.classInst, info->locals.sigInst.classInstCount * 8); //porting issue
+ value.info.locals.sigInst_methInstCount = (DWORD)info->locals.sigInst.methInstCount;
+ value.info.locals.sigInst_methInst_Index = (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->locals.sigInst.methInst, info->locals.sigInst.methInstCount * 8); //porting issue
+ value.info.locals.args = (DWORDLONG)info->locals.args;
+ value.info.locals.cbSig = (DWORD)info->locals.cbSig;
+ value.info.locals.pSig = (DWORD)GetMethodInfo->AddBuffer((unsigned char *)info->locals.pSig, info->locals.cbSig);
+ value.info.locals.scope = (DWORDLONG)info->locals.scope;
+ value.info.locals.token = (DWORD)info->locals.token;
+ }
+ value.result = result;
+ value.exceptionCode = (DWORD)exceptionCode;
+
+ GetMethodInfo->Add((DWORDLONG)ftn, value);
+ DEBUG_REC(dmpGetMethodInfo((DWORDLONG)ftn, value));
+}
+void MethodContext::dmpGetMethodInfo(DWORDLONG key, const Agnostic_GetMethodInfo& value)
+{
+ printf("GetMethodInfo key ftn-%016llX", key);
+ printf(", value res-%u ftn-%016llX scp-%016llX ilo-%u ils-%u ms-%u ehc-%u opt-%08X rk-%u "
+ "args{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "locals{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} "
+ "excp-%08X",
+ value.result,
+ value.info.ftn,
+ value.info.scope,
+ value.info.ILCode_offset,
+ value.info.ILCodeSize,
+ value.info.maxStack,
+ value.info.EHcount,
+ value.info.options,
+ value.info.regionKind,
+ value.info.args.callConv,
+ value.info.args.retTypeClass,
+ value.info.args.retTypeSigClass,
+ value.info.args.retType,
+ toString((CorInfoType)value.info.args.retType),
+ value.info.args.flags,
+ value.info.args.numArgs,
+ value.info.args.sigInst_classInstCount,
+ value.info.args.sigInst_classInst_Index,
+ value.info.args.sigInst_methInstCount,
+ value.info.args.sigInst_methInst_Index,
+ value.info.args.args,
+ value.info.args.cbSig,
+ value.info.args.pSig,
+ value.info.args.scope,
+ value.info.args.token,
+ value.info.locals.callConv,
+ value.info.locals.retTypeClass,
+ value.info.locals.retTypeSigClass,
+ value.info.locals.retType,
+ toString((CorInfoType)value.info.locals.retType),
+ value.info.locals.flags,
+ value.info.locals.numArgs,
+ value.info.locals.sigInst_classInstCount,
+ value.info.locals.sigInst_classInst_Index,
+ value.info.locals.sigInst_methInstCount,
+ value.info.locals.sigInst_methInst_Index,
+ value.info.locals.args,
+ value.info.locals.cbSig,
+ value.info.locals.pSig,
+ value.info.locals.scope,
+ value.info.locals.token,
+ value.exceptionCode);
+}
+bool MethodContext::repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, DWORD *exceptionCode)
+{
+ Agnostic_GetMethodInfo value;
+ AssertCodeMsg(GetMethodInfo != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx. probably a missing exception in getMethodInfo", (DWORDLONG)ftn);
+ AssertCodeMsg(GetMethodInfo->GetIndex((DWORDLONG)ftn) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx. probably a missing exception in getMethodInfo", (DWORDLONG)ftn);
+
+ value = GetMethodInfo->Get((DWORDLONG)ftn);
+ if (value.result)
+ {
+ info->ftn = (CORINFO_METHOD_HANDLE)value.info.ftn;
+ info->scope = (CORINFO_MODULE_HANDLE)value.info.scope;
+ info->ILCode = GetMethodInfo->GetBuffer(value.info.ILCode_offset);
+ info->ILCodeSize = (unsigned)value.info.ILCodeSize;
+ info->maxStack = (unsigned)value.info.maxStack;
+ info->EHcount = (unsigned)value.info.EHcount;
+ info->options = (CorInfoOptions)value.info.options;
+ info->regionKind = (CorInfoRegionKind)value.info.regionKind;
+ info->args.callConv = (CorInfoCallConv)value.info.args.callConv;
+ info->args.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeClass;
+ info->args.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.args.retTypeSigClass;
+ info->args.retType = (CorInfoType)value.info.args.retType;
+ info->args.flags = (unsigned)value.info.args.flags;
+ info->args.numArgs = (unsigned)value.info.args.numArgs;
+ info->args.sigInst.classInstCount = (unsigned)value.info.args.sigInst_classInstCount;
+ info->args.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.args.sigInst_classInst_Index);
+ info->args.sigInst.methInstCount = (unsigned)value.info.args.sigInst_methInstCount;
+ info->args.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.args.sigInst_methInst_Index);
+ info->args.args = (CORINFO_ARG_LIST_HANDLE)value.info.args.args;
+ info->args.cbSig = (unsigned int)value.info.args.cbSig;
+ info->args.pSig = (PCCOR_SIGNATURE)GetMethodInfo->GetBuffer(value.info.args.pSig);
+ info->args.scope = (CORINFO_MODULE_HANDLE)value.info.args.scope;
+ info->args.token = (mdToken)value.info.args.token;
+ info->locals.callConv = (CorInfoCallConv)value.info.locals.callConv;
+ info->locals.retTypeClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeClass;
+ info->locals.retTypeSigClass = (CORINFO_CLASS_HANDLE)value.info.locals.retTypeSigClass;
+ info->locals.retType = (CorInfoType)value.info.locals.retType;
+ info->locals.flags = (unsigned)value.info.locals.flags;
+ info->locals.numArgs = (unsigned)value.info.locals.numArgs;
+ info->locals.sigInst.classInstCount = (unsigned)value.info.locals.sigInst_classInstCount;
+ info->locals.sigInst.classInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.locals.sigInst_classInst_Index);
+ info->locals.sigInst.methInstCount = (unsigned)value.info.locals.sigInst_methInstCount;
+ info->locals.sigInst.methInst = (CORINFO_CLASS_HANDLE*)GetMethodInfo->GetBuffer(value.info.locals.sigInst_methInst_Index);
+ info->locals.args = (CORINFO_ARG_LIST_HANDLE)value.info.locals.args;
+ info->locals.cbSig = (unsigned int)value.info.locals.cbSig;
+ info->locals.pSig = (PCCOR_SIGNATURE)GetMethodInfo->GetBuffer(value.info.locals.pSig);
+ info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope;
+ info->locals.token = (mdToken)value.info.locals.token;
+ }
+ bool result = (bool)value.result;
+ *exceptionCode = (DWORD)value.exceptionCode;
+ DEBUG_REP(dmpGetMethodInfo((DWORDLONG)ftn, value));
+ return result;
+}
+
+void MethodContext::recGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CorInfoHelpFunc result)
+{
+ if (GetNewHelper == nullptr)
+ GetNewHelper = new LightWeightMap<Agnostic_GetNewHelper, DWORD>();
+
+ Agnostic_GetNewHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetNewHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ GetNewHelper->Add(key, (DWORD)result);
+ DEBUG_REC(dmpGetNewHelper(key, (DWORD)result));
+}
+void MethodContext::dmpGetNewHelper(const Agnostic_GetNewHelper& key, DWORD value)
+{
+ printf("GetNewHelper key cls-%016llX chan-%016llX, value res-%u", key.ResolvedToken.hClass, key.callerHandle, value);
+}
+CorInfoHelpFunc MethodContext::repGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle)
+{
+ Agnostic_GetNewHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetNewHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ AssertCodeMsg(GetNewHelper != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.ResolvedToken.hClass);
+ AssertCodeMsg(GetNewHelper->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.ResolvedToken.hClass);
+ CorInfoHelpFunc value = (CorInfoHelpFunc)GetNewHelper->Get(key);
+ DEBUG_REP(dmpGetNewHelper(key, value));
+ return value;
+}
+
+void MethodContext::recEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult)
+{
+ if (EmbedGenericHandle == nullptr)
+ EmbedGenericHandle = new LightWeightMap<Agnostic_EmbedGenericHandle, Agnostic_CORINFO_GENERICHANDLE_RESULT>();
+
+ Agnostic_EmbedGenericHandle key;
+ ZeroMemory(&key, sizeof(Agnostic_EmbedGenericHandle)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_GENERICHANDLE_RESULT value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)EmbedGenericHandle->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)EmbedGenericHandle->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.fEmbedParent = (DWORD)fEmbedParent;
+
+ value.lookup.lookupKind.needsRuntimeLookup = (DWORD)pResult->lookup.lookupKind.needsRuntimeLookup;
+ value.lookup.lookupKind.runtimeLookupKind = (DWORD)pResult->lookup.lookupKind.runtimeLookupKind;
+ if (pResult->lookup.lookupKind.needsRuntimeLookup)
+ {
+ value.lookup.constLookup.accessType = (DWORD)0;
+ value.lookup.constLookup.handle = (DWORDLONG)0;
+ //copy the runtimeLookup view of the union
+ value.lookup.runtimeLookup.signature = (DWORDLONG)pResult->lookup.runtimeLookup.signature;
+ value.lookup.runtimeLookup.helper = (DWORD)pResult->lookup.runtimeLookup.helper;
+ value.lookup.runtimeLookup.indirections = (DWORD)pResult->lookup.runtimeLookup.indirections;
+ value.lookup.runtimeLookup.testForNull = (DWORD)pResult->lookup.runtimeLookup.testForNull;
+ value.lookup.runtimeLookup.testForFixup = (DWORD)pResult->lookup.runtimeLookup.testForFixup;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.lookup.runtimeLookup.offsets[i] = (DWORDLONG)pResult->lookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ value.lookup.runtimeLookup.signature = (DWORDLONG)0;
+ value.lookup.runtimeLookup.helper = (DWORD)0;
+ value.lookup.runtimeLookup.indirections = (DWORD)0;
+ value.lookup.runtimeLookup.testForNull = (DWORD)0;
+ value.lookup.runtimeLookup.testForFixup = (DWORD)0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.lookup.runtimeLookup.offsets[i] = (DWORDLONG)0;
+ //copy the constLookup view of the union
+ value.lookup.constLookup.accessType = (DWORD)pResult->lookup.constLookup.accessType;
+ value.lookup.constLookup.handle = (DWORDLONG)pResult->lookup.constLookup.handle;
+ }
+ value.compileTimeHandle = (DWORDLONG)pResult->compileTimeHandle;
+ value.handleType = (DWORD)pResult->handleType;
+
+ EmbedGenericHandle->Add(key, value);
+ DEBUG_REC(dmpEmbedGenericHandle(key, value));
+}
+void MethodContext::dmpEmbedGenericHandle(const Agnostic_EmbedGenericHandle& key, const Agnostic_CORINFO_GENERICHANDLE_RESULT& value)
+{
+ printf("EmbedGenericHandle key rt{tokCon-%016llX tokScp-%016llX tok-%08X tokTyp-%08X cls-%016llX ftn-%016llX fld-%016llX tsi-%u cbts-%u msi-%u cbms-%u} emb-%u",
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec,
+ key.fEmbedParent);
+ printf(", value nrl-%u rlk-%u", value.lookup.lookupKind.needsRuntimeLookup, value.lookup.lookupKind.runtimeLookupKind);
+ if (value.lookup.lookupKind.needsRuntimeLookup)
+ {
+ printf(" sig-%016llX hlp-%u ind-%u tfn-%u tff-%u { ",
+ value.lookup.runtimeLookup.signature,
+ value.lookup.runtimeLookup.helper,
+ value.lookup.runtimeLookup.indirections,
+ value.lookup.runtimeLookup.testForNull,
+ value.lookup.runtimeLookup.testForFixup);
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ printf("%016llX ", value.lookup.runtimeLookup.offsets[i]);
+ printf("}");
+ }
+ else
+ {
+ printf(" at-%u han-%016llX",
+ value.lookup.constLookup.accessType,
+ value.lookup.constLookup.handle);
+ }
+ printf(" cth-%016llX ht-%u", value.compileTimeHandle, value.handleType);
+}
+void MethodContext::repEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult)
+{
+ Agnostic_EmbedGenericHandle key;
+ ZeroMemory(&key, sizeof(Agnostic_EmbedGenericHandle)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_GENERICHANDLE_RESULT value;
+
+ AssertCodeMsg(EmbedGenericHandle != nullptr, EXCEPTIONCODE_MC, "Encountered an empty LWM while looking for ...");
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)EmbedGenericHandle->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)EmbedGenericHandle->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.fEmbedParent = (DWORD)fEmbedParent;
+
+ AssertCodeMsg(EmbedGenericHandle->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find ...");
+ value = EmbedGenericHandle->Get(key);
+
+ pResult->lookup.lookupKind.needsRuntimeLookup = value.lookup.lookupKind.needsRuntimeLookup != 0;
+ pResult->lookup.lookupKind.runtimeLookupKind = (CORINFO_RUNTIME_LOOKUP_KIND)value.lookup.lookupKind.runtimeLookupKind;
+ if (pResult->lookup.lookupKind.needsRuntimeLookup)
+ {
+ //copy the runtimeLookup view of the union
+ pResult->lookup.runtimeLookup.signature = (LPVOID)value.lookup.runtimeLookup.signature;
+ pResult->lookup.runtimeLookup.helper = (CorInfoHelpFunc)value.lookup.runtimeLookup.helper;
+ pResult->lookup.runtimeLookup.indirections = (WORD)value.lookup.runtimeLookup.indirections;
+ pResult->lookup.runtimeLookup.testForNull = value.lookup.runtimeLookup.testForNull != 0;
+ pResult->lookup.runtimeLookup.testForFixup = value.lookup.runtimeLookup.testForFixup != 0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ pResult->lookup.runtimeLookup.offsets[i] = (size_t)value.lookup.runtimeLookup.offsets[i];
+ }
+ else
+ {
+ pResult->lookup.constLookup.accessType = (InfoAccessType)value.lookup.constLookup.accessType;
+ pResult->lookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)value.lookup.constLookup.handle;
+ }
+ pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)value.compileTimeHandle;
+ pResult->handleType = (CorInfoGenericHandleType)value.handleType;
+
+ DEBUG_REP(dmpEmbedGenericHandle(key, value));
+}
+
+void MethodContext::recGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause)
+{
+ if (GetEHinfo == nullptr)
+ GetEHinfo = new LightWeightMap<DLD, Agnostic_CORINFO_EH_CLAUSE>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_EH_CLAUSE value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)EHnumber;
+
+ value.Flags = (DWORD)clause->Flags;
+ value.TryOffset = (DWORD)clause->TryOffset;
+ value.TryLength = (DWORD)clause->TryLength;
+ value.HandlerOffset = (DWORD)clause->HandlerOffset;
+ value.HandlerLength = (DWORD)clause->HandlerLength;
+ value.ClassToken = (DWORD)clause->ClassToken;
+
+ GetEHinfo->Add(key, value);
+ DEBUG_REC(dmpGetEHinfo(key, value));
+}
+void MethodContext::dmpGetEHinfo(DLD key, const Agnostic_CORINFO_EH_CLAUSE& value)
+{
+ printf("GetEHinfo key ftn-%016llX ehn-%u, value flg-%u to-%u tl-%u ho-%u hl-%u ct-%u",
+ key.A, key.B, value.Flags, value.TryOffset, value.TryLength, value.HandlerOffset, value.HandlerLength, value.ClassToken);
+}
+void MethodContext::repGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_EH_CLAUSE value;
+
+ key.A = (DWORDLONG)ftn;
+ key.B = (DWORD)EHnumber;
+
+ value = GetEHinfo->Get(key);
+
+ clause->Flags = (CORINFO_EH_CLAUSE_FLAGS)value.Flags;
+ clause->TryOffset = (DWORD)value.TryOffset;
+ clause->TryLength = (DWORD)value.TryLength;
+ clause->HandlerOffset = (DWORD)value.HandlerOffset;
+ clause->HandlerLength = (DWORD)value.HandlerLength;
+ clause->ClassToken = (DWORD)value.ClassToken;
+ DEBUG_REP(dmpGetEHinfo(key, value));
+}
+
+void MethodContext::recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection)
+{
+ if (GetMethodVTableOffset == nullptr)
+ GetMethodVTableOffset = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+ value.A = (DWORD)*offsetOfIndirection;
+ value.B = (DWORD)*offsetAfterIndirection;
+ GetMethodVTableOffset->Add((DWORDLONG)method, value);
+ DEBUG_REC(dmpGetMethodVTableOffset((DWORDLONG)method, value));
+}
+void MethodContext::dmpGetMethodVTableOffset(DWORDLONG key, DD value)
+{
+ printf("GetMethodVTableOffset key ftn-%016llX, value offi-%u, offa-%u", key, value.A, value.B);
+}
+void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection)
+{
+ DD value;
+
+ AssertCodeMsg(GetMethodVTableOffset != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)method);
+ AssertCodeMsg(GetMethodVTableOffset->GetIndex((DWORDLONG)method) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)method);
+ value = GetMethodVTableOffset->Get((DWORDLONG)method);
+
+ *offsetOfIndirection = (unsigned)value.A;
+ *offsetAfterIndirection = (unsigned)value.B;
+ DEBUG_REP(dmpGetMethodVTableOffset((DWORDLONG)method, value));
+}
+
+void MethodContext::recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result)
+{
+ if (GetTokenTypeAsHandle == nullptr)
+ GetTokenTypeAsHandle = new LightWeightMap<Agnostic_CORINFO_RESOLVED_TOKEN, DWORDLONG>();
+
+ Agnostic_CORINFO_RESOLVED_TOKEN key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKEN)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.tokenContext = (DWORDLONG)0;
+ key.tokenScope = (DWORDLONG)0;
+ key.token = (DWORD)0;
+ key.tokenType = (DWORD)0;
+ key.hClass = (DWORDLONG)0;
+ key.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.hField = (DWORDLONG)pResolvedToken->hField;
+ key.typeSpec_Index = (DWORD)0;
+ key.cbTypeSpec = (DWORD)0;
+ key.methodSpec_Index = (DWORD)0;
+ key.cbMethodSpec = (DWORD)0;
+
+ GetTokenTypeAsHandle->Add(key, (DWORDLONG)result);
+}
+void MethodContext::dmpGetTokenTypeAsHandle(const Agnostic_CORINFO_RESOLVED_TOKEN& key, DWORDLONG value)
+{
+ printf("GetTokenTypeAsHandle key ftn-%016llX fld-%016llX, value cls-%016llX", key.hMethod, key.hField, value);
+}
+CORINFO_CLASS_HANDLE MethodContext::repGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ Agnostic_CORINFO_RESOLVED_TOKEN key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_RESOLVED_TOKEN)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.tokenContext = (DWORDLONG)0;
+ key.tokenScope = (DWORDLONG)0;
+ key.token = (DWORD)0;
+ key.tokenType = (DWORD)0;
+ key.hClass = (DWORDLONG)0;
+ key.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.hField = (DWORDLONG)pResolvedToken->hField;
+ key.typeSpec_Index = (DWORD)0;
+ key.cbTypeSpec = (DWORD)0;
+ key.methodSpec_Index = (DWORD)0;
+ key.cbMethodSpec = (DWORD)0;
+
+ CORINFO_CLASS_HANDLE value = (CORINFO_CLASS_HANDLE)GetTokenTypeAsHandle->Get(key);
+ return value;
+}
+
+void MethodContext::recGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult)
+{
+ if (GetFieldInfo == nullptr)
+ GetFieldInfo = new LightWeightMap<Agnostic_GetFieldInfo, Agnostic_CORINFO_FIELD_INFO>();
+ Agnostic_GetFieldInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetFieldInfo)); //Since dd has nested structs, and we use memcmp to compare, we need to zero out the padding bytes too
+ Agnostic_CORINFO_FIELD_INFO value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetFieldInfo->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetFieldInfo->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ value.fieldAccessor = (DWORD)pResult->fieldAccessor;
+ value.fieldFlags = (DWORD)pResult->fieldFlags;
+ value.helper = (DWORD)pResult->helper;
+ value.offset = (DWORD)pResult->offset;
+ value.fieldType = (DWORD)pResult->fieldType;
+ value.structType = (DWORDLONG)pResult->structType;
+ value.accessAllowed = (DWORD)pResult->accessAllowed;
+ value.accessCalloutHelper.helperNum = (DWORD)pResult->accessCalloutHelper.helperNum;
+ value.accessCalloutHelper.numArgs = (DWORD)pResult->accessCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ value.accessCalloutHelper.args[i].constant = (DWORDLONG)pResult->accessCalloutHelper.args[i].constant;
+ value.accessCalloutHelper.args[i].argType = (DWORD)pResult->accessCalloutHelper.args[i].argType;
+ }
+ GetFieldInfo->Add(key, value);
+ DEBUG_REC(dmpGetFieldInfo(key, value));
+}
+void MethodContext::dmpGetFieldInfo(const Agnostic_GetFieldInfo& key, const Agnostic_CORINFO_FIELD_INFO& value)
+{
+ printf("GetFieldInfo key ch-%016llX flg-%08X rt{tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX tsi-%u cbts-%u msi-%u cbms-%u}",
+ key.callerHandle,
+ key.flags,
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec);
+
+ printf(", value fa-%u fflg-%08X hlp-%u off-%u fT-%u(%s) sT-%016llX aa-%u hnum-%u na-%u {",
+ value.fieldAccessor,
+ value.fieldFlags,
+ value.helper,
+ value.offset,
+ value.fieldType,
+ toString((CorInfoType)value.fieldType),
+ value.structType,
+ value.accessAllowed,
+ value.accessCalloutHelper.helperNum,
+ value.accessCalloutHelper.numArgs);
+
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ switch ((CorInfoAccessAllowedHelperArgType)value.accessCalloutHelper.args[i].argType)
+ {
+ default:
+ printf("{%u: illegal}", i);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Field:
+ printf("{%u: fld-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Method:
+ printf("{%u: mth-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Class:
+ printf("{%u: cls-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Module:
+ printf("{%u: mod-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ case CORINFO_HELPER_ARG_TYPE_Const:
+ printf("{%u: const-%016llX}", i, value.accessCalloutHelper.args[i].constant);
+ break;
+ }
+ }
+ printf("}");
+}
+void MethodContext::repGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult)
+{
+ Agnostic_GetFieldInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_GetFieldInfo)); //Since dd has nested structs, and we use memcmp to compare, we need to zero out the padding bytes too
+ Agnostic_CORINFO_FIELD_INFO value;
+
+ AssertCodeMsg(GetFieldInfo != nullptr, EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)GetFieldInfo->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)GetFieldInfo->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+ key.flags = (DWORD)flags;
+
+ DWORD origFlag = key.flags;
+
+ if (GetFieldInfo->GetIndex(key) == -1)
+ {
+#ifdef sparseMC
+ key.flags = origFlag ^ (DWORD)CORINFO_ACCESS_UNWRAP;
+ if (GetFieldInfo->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_UNWRAP");
+ }
+ else
+ {
+ key.flags = origFlag ^ ((DWORD)CORINFO_ACCESS_THIS | (DWORD)CORINFO_ACCESS_UNWRAP);
+ if (GetFieldInfo->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_UNWRAP|CORINFO_ACCESS_THIS");
+ }
+ else
+ {
+ key.flags = origFlag ^ (DWORD)CORINFO_ACCESS_INLINECHECK;
+ if (GetFieldInfo->GetIndex(key) != -1)
+ {
+ LogDebug("Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_INLINECHECK");
+ }
+ else
+ {
+ LogException(EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+ }
+ }
+ }
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %x", pResolvedToken->token);
+#endif
+ }
+
+ value = GetFieldInfo->Get(key);
+
+ pResult->fieldAccessor = (CORINFO_FIELD_ACCESSOR)value.fieldAccessor;
+ pResult->fieldFlags = (unsigned)value.fieldFlags;
+ pResult->helper = (CorInfoHelpFunc)value.helper;
+ pResult->offset = (DWORD)value.offset;
+ pResult->fieldType = (CorInfoType)value.fieldType;
+ pResult->structType = (CORINFO_CLASS_HANDLE)value.structType;
+ pResult->accessAllowed = (CorInfoIsAccessAllowedResult)value.accessAllowed;
+ pResult->accessCalloutHelper.helperNum = (CorInfoHelpFunc)value.accessCalloutHelper.helperNum;
+ pResult->accessCalloutHelper.numArgs = (unsigned)value.accessCalloutHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ pResult->accessCalloutHelper.args[i].constant = (size_t)value.accessCalloutHelper.args[i].constant;
+ pResult->accessCalloutHelper.args[i].argType = (CorInfoAccessAllowedHelperArgType)value.accessCalloutHelper.args[i].argType;
+ }
+ DEBUG_REP(dmpGetFieldInfo(key, value));
+}
+
+void MethodContext::recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection, CORINFO_METHOD_HANDLE result)
+{
+ if (EmbedMethodHandle == nullptr)
+ EmbedMethodHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection == nullptr)
+ value.A = (DWORDLONG)0;
+ else
+ value.A = (DWORDLONG)*ppIndirection;
+ value.B = (DWORDLONG)result;
+
+ EmbedMethodHandle->Add((DWORDLONG)handle, value);
+ DEBUG_REC(dmpEmbedMethodHandle((DWORDLONG)handle, value));
+}
+void MethodContext::dmpEmbedMethodHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedMethodHandle key ftn-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+CORINFO_METHOD_HANDLE MethodContext::repEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ AssertCodeMsg(EmbedMethodHandle != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)handle);
+ AssertCodeMsg(EmbedMethodHandle->GetIndex((DWORDLONG)handle) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)handle);
+ value = EmbedMethodHandle->Get((DWORDLONG)handle);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+ DEBUG_REP(dmpEmbedMethodHandle((DWORDLONG)handle, value));
+ return (CORINFO_METHOD_HANDLE)value.B;
+}
+
+void MethodContext::recGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection, void *result, CorInfoType cit)
+{
+ if (GetFieldAddress == nullptr)
+ GetFieldAddress = new LightWeightMap<DWORDLONG, Agnostic_GetFieldAddress>();
+
+ Agnostic_GetFieldAddress value;
+ if (ppIndirection == nullptr)
+ value.ppIndirection = (DWORDLONG)0;
+ else
+ value.ppIndirection = (DWORDLONG)*ppIndirection;
+ value.fieldAddress = (DWORDLONG)result;
+
+ value.fieldValue = (DWORD)-1;
+
+ // Make an attempt at stashing a copy of the value
+ if (result > (void*)0xffff) //TODO-Cleanup: sometimes there is a field offset?
+ {
+ DWORDLONG scratch = 0x4242424242424242;
+ switch (cit)
+ {
+ case CORINFO_TYPE_BOOL:
+ case CORINFO_TYPE_BYTE:
+ case CORINFO_TYPE_UBYTE:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(BYTE), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_CHAR:
+ case CORINFO_TYPE_SHORT:
+ case CORINFO_TYPE_USHORT:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(WORD), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_INT:
+ case CORINFO_TYPE_UINT:
+ case CORINFO_TYPE_FLOAT:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(DWORD), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_LONG:
+ case CORINFO_TYPE_ULONG:
+ case CORINFO_TYPE_DOUBLE:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(DWORDLONG), true);//important to not merge two fields into one address
+ break;
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ case CORINFO_TYPE_PTR:
+ value.fieldValue = (DWORD)GetFieldAddress->AddBuffer((unsigned char*)result, sizeof(size_t), true);//important to not merge two fields into one address
+ GetFieldAddress->AddBuffer((unsigned char*)&scratch, sizeof(DWORD)); //Padding out the data so we can read it back "safetly" on x64
+ break;
+ default:
+ break;
+ }
+ }
+ GetFieldAddress->Add((DWORDLONG)field, value);
+ DEBUG_REC(dmpGetFieldAddress((DWORDLONG)field, value));
+}
+void MethodContext::dmpGetFieldAddress(DWORDLONG key, const Agnostic_GetFieldAddress& value)
+{
+ printf("GetFieldAddress key fld-%016llX, value ppi-%016llX addr-%016llX val-%u", key, value.ppIndirection, value.fieldAddress, value.fieldValue);
+}
+void* MethodContext::repGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection)
+{
+ Agnostic_GetFieldAddress value;
+
+ value = GetFieldAddress->Get((DWORDLONG)field);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.ppIndirection;
+ void *temp;
+
+ if (value.fieldValue != (DWORD)-1)
+ {
+ temp = (void*)GetFieldAddress->GetBuffer(value.fieldValue);
+ cr->recAddressMap((void*)value.fieldAddress, temp, toCorInfoSize(repGetFieldType(field, nullptr, nullptr)));
+ }
+ else
+ temp = (void*)value.fieldAddress;
+
+ DEBUG_REP(dmpGetFieldAddress((DWORDLONG)field, value));
+ return temp;
+}
+
+void MethodContext::recGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs, unsigned len, unsigned result)
+{
+ if (GetClassGClayout == nullptr)
+ GetClassGClayout = new LightWeightMap<DWORDLONG, Agnostic_GetClassGClayout>();
+
+ Agnostic_GetClassGClayout value;
+
+ value.gcPtrs_Index = (DWORD)GetClassGClayout->AddBuffer((unsigned char*)gcPtrs, len*sizeof(BYTE));
+ value.len = (DWORD)len;
+ value.valCount = (DWORD)result;
+
+ GetClassGClayout->Add((DWORDLONG)cls, value);
+ DEBUG_REC(dmpGetClassGClayout((DWORDLONG)cls, value));
+}
+void MethodContext::dmpGetClassGClayout(DWORDLONG key, const Agnostic_GetClassGClayout& value)
+{
+ printf("GetClassGCLayout key %016llX, value len %u cnt %u {", key, value.len, value.valCount);
+ if (value.gcPtrs_Index != -1)
+ {
+ BYTE *ptr = (BYTE *)GetClassGClayout->GetBuffer(value.gcPtrs_Index);
+ for (unsigned int i = 0; i < value.len; i++)
+ {
+ printf("0x%02x", ptr[i]);
+ if (i + 1 < value.len)
+ printf(",");
+ }
+ GetClassGClayout->Unlock();
+ }
+ printf("}");
+}
+unsigned MethodContext::repGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs)
+{
+ Agnostic_GetClassGClayout value;
+
+ AssertCodeMsg(GetClassGClayout != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassGClayout->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ value = GetClassGClayout->Get((DWORDLONG)cls);
+
+ unsigned int len = (unsigned int)value.len;
+ unsigned int index = (unsigned int)value.gcPtrs_Index;
+
+ if (index != -1)
+ {
+ BYTE *ptr = (BYTE *)GetClassGClayout->GetBuffer(index);
+ for (unsigned int i = 0; i < len; i++)
+ gcPtrs[i] = ptr[i];
+ }
+ DEBUG_REP(dmpGetClassGClayout((DWORDLONG)cls, value));
+ return (unsigned)value.valCount;
+}
+
+void MethodContext::recGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint, unsigned result)
+{
+ if (GetClassAlignmentRequirement == nullptr)
+ GetClassAlignmentRequirement = new LightWeightMap<DLD, DWORD>();
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls;
+ key.B = (DWORD)fDoubleAlignHint;
+
+ GetClassAlignmentRequirement->Add(key, (DWORD)result);
+ DEBUG_REC(dmpGetClassAlignmentRequirement(key, result));
+}
+void MethodContext::dmpGetClassAlignmentRequirement(DLD key, DWORD value)
+{
+ printf("GetClassAlignmentRequirement key %016llX %u, value %u", key.A, key.B, value);
+}
+unsigned MethodContext::repGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.A = (DWORDLONG)cls;
+ key.B = (DWORD)fDoubleAlignHint;
+
+ unsigned result = (unsigned)GetClassAlignmentRequirement->Get(key);
+ DEBUG_REP(dmpGetClassAlignmentRequirement(key, result));
+ return result;
+}
+
+void MethodContext::recCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper, CorInfoIsAccessAllowedResult result)
+{
+ if (CanAccessClass == nullptr)
+ CanAccessClass = new LightWeightMap<Agnostic_CanAccessClassIn, Agnostic_CanAccessClassOut>();
+
+ Agnostic_CanAccessClassIn key;
+ ZeroMemory(&key, sizeof(Agnostic_CanAccessClassIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ Agnostic_CanAccessClassOut value;
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)CanAccessClass->AddBuffer((unsigned char*)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)CanAccessClass->AddBuffer((unsigned char*)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ value.AccessHelper.helperNum = (DWORD)pAccessHelper->helperNum;
+ value.AccessHelper.numArgs = (DWORD)pAccessHelper->numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ value.AccessHelper.args[i].constant = (DWORDLONG)pAccessHelper->args[i].constant;
+ value.AccessHelper.args[i].argType = (DWORD)pAccessHelper->args[i].argType;
+ }
+ value.result = (DWORD)result;
+
+ CanAccessClass->Add(key, value);
+ DEBUG_REC(dmpCanAccessClass(key, value));
+}
+void MethodContext::dmpCanAccessClass(const Agnostic_CanAccessClassIn& key, const Agnostic_CanAccessClassOut& value)
+{
+ printf("CanAccessClass key tc-%016llX ts-%016llX tok-%08X tt-%u cls-%016llX meth-%016llX fld-%016llX ti-%u ts-%u mi-%u ms-%u",
+ key.ResolvedToken.tokenContext,
+ key.ResolvedToken.tokenScope,
+ key.ResolvedToken.token,
+ key.ResolvedToken.tokenType,
+ key.ResolvedToken.hClass,
+ key.ResolvedToken.hMethod,
+ key.ResolvedToken.hField,
+ key.ResolvedToken.typeSpec_Index,
+ key.ResolvedToken.cbTypeSpec,
+ key.ResolvedToken.methodSpec_Index,
+ key.ResolvedToken.cbMethodSpec);
+ printf(", value hnum-%u na-%u {",
+ value.AccessHelper.helperNum,
+ value.AccessHelper.numArgs);
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ printf("{%016llX %u}",
+ value.AccessHelper.args[i].constant,
+ value.AccessHelper.args[i].argType);
+ }
+ printf("} res-%u", value.result);
+}
+CorInfoIsAccessAllowedResult MethodContext::repCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper)
+{
+ Agnostic_CanAccessClassIn key;
+ ZeroMemory(&key, sizeof(Agnostic_CanAccessClassIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CanAccessClassOut value;
+
+ AssertCodeMsg(CanAccessClass != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)pResolvedToken->hClass);
+ key.ResolvedToken.tokenContext = (DWORDLONG)pResolvedToken->tokenContext;
+ key.ResolvedToken.tokenScope = (DWORDLONG)pResolvedToken->tokenScope;
+ key.ResolvedToken.token = (DWORD)pResolvedToken->token;
+ key.ResolvedToken.tokenType = (DWORD)pResolvedToken->tokenType;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)pResolvedToken->hMethod;
+ key.ResolvedToken.hField = (DWORDLONG)pResolvedToken->hField;
+ key.ResolvedToken.typeSpec_Index = (DWORD)CanAccessClass->Contains((unsigned char *)pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
+ key.ResolvedToken.cbTypeSpec = (DWORD)pResolvedToken->cbTypeSpec;
+ key.ResolvedToken.methodSpec_Index = (DWORD)CanAccessClass->Contains((unsigned char *)pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
+ key.ResolvedToken.cbMethodSpec = (DWORD)pResolvedToken->cbMethodSpec;
+ key.callerHandle = (DWORDLONG)callerHandle;
+
+ AssertCodeMsg(CanAccessClass->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)pResolvedToken->hClass);
+ value = CanAccessClass->Get(key);
+
+ pAccessHelper->helperNum = (CorInfoHelpFunc)value.AccessHelper.helperNum;
+ pAccessHelper->numArgs = (unsigned)value.AccessHelper.numArgs;
+ for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
+ {
+ pAccessHelper->args[i].constant = (size_t)value.AccessHelper.args[i].constant;
+ pAccessHelper->args[i].argType = (CorInfoAccessAllowedHelperArgType)value.AccessHelper.args[i].argType;
+ }
+ CorInfoIsAccessAllowedResult temp = (CorInfoIsAccessAllowedResult)value.result;
+ DEBUG_REP(dmpCanAccessClass(key, value));
+ return temp;
+}
+
+void MethodContext::recGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing, CorInfoHelpFunc result)
+{
+ if (GetCastingHelper == nullptr)
+ GetCastingHelper = new LightWeightMap<Agnostic_GetCastingHelper, DWORD>();
+
+ Agnostic_GetCastingHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCastingHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.fThrowing = (DWORD)fThrowing;
+
+ GetCastingHelper->Add(key, (DWORD)result);
+}
+void MethodContext::dmpGetCastingHelper(const Agnostic_GetCastingHelper& key, DWORD value)
+{
+ printf("GetCastingHelper key cls-%016llX, thw-%u, value res-%u", key.ResolvedToken.hClass, key.fThrowing, value);
+}
+CorInfoHelpFunc MethodContext::repGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing)
+{
+ Agnostic_GetCastingHelper key;
+ ZeroMemory(&key, sizeof(Agnostic_GetCastingHelper)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.ResolvedToken.tokenContext = (DWORDLONG)0;
+ key.ResolvedToken.tokenScope = (DWORDLONG)0;
+ key.ResolvedToken.token = (DWORD)0;
+ key.ResolvedToken.tokenType = (DWORD)0;
+ key.ResolvedToken.hClass = (DWORDLONG)pResolvedToken->hClass;
+ key.ResolvedToken.hMethod = (DWORDLONG)0;
+ key.ResolvedToken.hField = (DWORDLONG)0;
+ key.ResolvedToken.typeSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbTypeSpec = (DWORD)0;
+ key.ResolvedToken.methodSpec_Index = (DWORD)0;
+ key.ResolvedToken.cbMethodSpec = (DWORD)0;
+ key.fThrowing = (DWORD)fThrowing;
+
+ CorInfoHelpFunc value = (CorInfoHelpFunc)GetCastingHelper->Get(key);
+ return value;
+}
+
+void MethodContext::recEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection, CORINFO_MODULE_HANDLE result)
+{
+ if (EmbedModuleHandle == nullptr)
+ EmbedModuleHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ EmbedModuleHandle->Add((DWORDLONG)handle, value);
+}
+void MethodContext::dmpEmbedModuleHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedModuleHandle key mod-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+CORINFO_MODULE_HANDLE MethodContext::repEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ value = EmbedModuleHandle->Get((DWORDLONG)handle);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (CORINFO_MODULE_HANDLE)value.B;
+
+}
+
+void MethodContext::recEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection, CORINFO_CLASS_HANDLE result)
+{
+ if (EmbedClassHandle == nullptr)
+ EmbedClassHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ EmbedClassHandle->Add((DWORDLONG)handle, value);
+ DEBUG_REC(dmpEmbedClassHandle((DWORDLONG)handle, value));
+}
+void MethodContext::dmpEmbedClassHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedClassHandle key cls-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+CORINFO_CLASS_HANDLE MethodContext::repEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ AssertCodeMsg(EmbedClassHandle != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)handle);
+ AssertCodeMsg(EmbedClassHandle->GetIndex((DWORDLONG)handle) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)handle);
+ value = EmbedClassHandle->Get((DWORDLONG)handle);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ DEBUG_REP(dmpEmbedClassHandle((DWORDLONG)handle, value));
+ return (CORINFO_CLASS_HANDLE)value.B;
+}
+
+void MethodContext::recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, BOOL result)
+{
+ if (PInvokeMarshalingRequired == nullptr)
+ PInvokeMarshalingRequired = new LightWeightMap<Agnostic_PInvokeMarshalingRequired, DWORD>();
+
+ Agnostic_PInvokeMarshalingRequired key;
+ ZeroMemory(&key, sizeof(Agnostic_PInvokeMarshalingRequired)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.method = (DWORDLONG)method;
+ key.callSiteSig.callConv = (DWORD)0;
+ key.callSiteSig.retTypeClass = (DWORDLONG)0;
+ key.callSiteSig.retTypeSigClass = (DWORDLONG)0;
+ key.callSiteSig.retType = (DWORD)0;
+ key.callSiteSig.flags = (DWORD)0;
+ key.callSiteSig.numArgs = (DWORD)0;
+ key.callSiteSig.sigInst_classInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_classInst_Index = (DWORD)0;
+ key.callSiteSig.sigInst_methInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_methInst_Index = (DWORD)0;
+ key.callSiteSig.args = (DWORDLONG)0;
+ key.callSiteSig.pSig = (DWORD)PInvokeMarshalingRequired->AddBuffer((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
+ key.callSiteSig.cbSig = (DWORD)callSiteSig->cbSig;
+ key.callSiteSig.scope = (DWORDLONG)callSiteSig->scope;
+ key.callSiteSig.token = (DWORD)0;
+
+ PInvokeMarshalingRequired->Add(key, (DWORD)result);
+ DEBUG_REC(dmpPInvokeMarshalingRequired(key, (DWORD)result));
+}
+void MethodContext::dmpPInvokeMarshalingRequired(const Agnostic_PInvokeMarshalingRequired& key, DWORD value)
+{
+ printf("PInvokeMarshalingRequired key mth-%016llX scp-%016llX sig-%u, value res-%u",
+ key.method,
+ key.callSiteSig.scope,
+ key.callSiteSig.pSig,
+ value);
+}
+//Note the jit interface implementation seems to only care about scope and pSig from callSiteSig
+BOOL MethodContext::repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig)
+{
+ if (PInvokeMarshalingRequired == nullptr)//so when we replay checked on free, we throw from lwm
+ return TRUE; // TODO-Cleanup: hackish...
+
+ Agnostic_PInvokeMarshalingRequired key;
+ ZeroMemory(&key, sizeof(Agnostic_PInvokeMarshalingRequired)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.method = (DWORDLONG)method;
+ key.callSiteSig.callConv = (DWORD)0;
+ key.callSiteSig.retTypeClass = (DWORDLONG)0;
+ key.callSiteSig.retTypeSigClass = (DWORDLONG)0;
+ key.callSiteSig.retType = (DWORD)0;
+ key.callSiteSig.flags = (DWORD)0;
+ key.callSiteSig.numArgs = (DWORD)0;
+ key.callSiteSig.sigInst_classInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_classInst_Index = (DWORD)0;
+ key.callSiteSig.sigInst_methInstCount = (DWORD)0;
+ key.callSiteSig.sigInst_methInst_Index = (DWORD)0;
+ key.callSiteSig.args = (DWORDLONG)0;
+ key.callSiteSig.pSig = (DWORD)PInvokeMarshalingRequired->Contains((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
+ key.callSiteSig.cbSig = (DWORD)callSiteSig->cbSig;
+ key.callSiteSig.scope = (DWORDLONG)callSiteSig->scope;
+ key.callSiteSig.token = (DWORD)0;
+
+ DWORD value = PInvokeMarshalingRequired->Get(key);
+ DEBUG_REP(dmpPInvokeMarshalingRequired(key, value));
+ return value;
+}
+
+void MethodContext::recFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ if (FindSig == nullptr)
+ FindSig = new LightWeightMap<Agnostic_FindSig, Agnostic_CORINFO_SIG_INFO>();
+
+ Agnostic_FindSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.sigTOK = (DWORD)sigTOK;
+ key.context = (DWORDLONG)context;
+
+ value.callConv = (DWORD)sig->callConv;
+ value.retTypeClass = (DWORDLONG)sig->retTypeClass;
+ value.retTypeSigClass = (DWORDLONG)sig->retTypeSigClass;
+ value.retType = (DWORD)sig->retType;
+ value.flags = (DWORD)sig->flags;
+ value.numArgs = (DWORD)sig->numArgs;
+ value.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ value.sigInst_classInst_Index = FindSig->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8); //porting issue
+ value.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ value.sigInst_methInst_Index = FindSig->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8); //porting issue
+ value.args = (DWORDLONG)sig->args;
+ value.cbSig = (DWORD)sig->cbSig;
+ value.pSig = (DWORD)FindSig->AddBuffer((unsigned char *)sig->pSig, sig->cbSig);
+ value.scope = (DWORDLONG)sig->scope;
+ value.token = (DWORD)sig->token;
+
+ FindSig->Add(key, value);
+ DEBUG_REC(dmpFindSig(key, value));
+}
+void MethodContext::dmpFindSig(const Agnostic_FindSig& key, const Agnostic_CORINFO_SIG_INFO& value)
+{
+ printf("FindSig key module-%016llX sigTOK-%08X context-%016llX", key.module, key.sigTOK, key.context);
+ printf(", value callConv-%08X retTypeClass-%016llX retTypeSigClass-%016llX retType-%u(%s) flags-%08X numArgs-%08X classInstCount-%08X classInd-%08X "
+ "methInstCount-%08X methInd-%08X args-%016llX cbSig-%08X pSig-%08X scope-%016llX token-%08X",
+ value.callConv,
+ value.retTypeClass,
+ value.retTypeSigClass,
+ value.retType,
+ toString((CorInfoType)value.retType),
+ value.flags,
+ value.numArgs,
+ value.sigInst_classInstCount,
+ value.sigInst_classInst_Index,
+ value.sigInst_methInstCount,
+ value.sigInst_methInst_Index,
+ value.args,
+ value.cbSig,
+ value.pSig,
+ value.scope,
+ value.token);
+}
+void MethodContext::repFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ Agnostic_FindSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.sigTOK = (DWORD)sigTOK;
+ key.context = (DWORDLONG)context;
+
+ value = FindSig->Get(key);
+
+ sig->callConv = (CorInfoCallConv)value.callConv;
+ sig->retTypeClass = (CORINFO_CLASS_HANDLE)value.retTypeClass;
+ sig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.retTypeSigClass;
+ sig->retType = (CorInfoType)value.retType;
+ sig->flags = (unsigned)value.flags;
+ sig->numArgs = (unsigned)value.numArgs;
+ sig->sigInst.classInstCount = (unsigned)value.sigInst_classInstCount;
+ sig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)FindSig->GetBuffer(value.sigInst_classInst_Index);
+ sig->sigInst.methInstCount = (unsigned)value.sigInst_methInstCount;
+ sig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)FindSig->GetBuffer(value.sigInst_methInst_Index);
+ sig->args = (CORINFO_ARG_LIST_HANDLE)value.args;
+ sig->cbSig = (unsigned int)value.cbSig;
+ sig->pSig = (PCCOR_SIGNATURE)FindSig->GetBuffer(value.pSig);
+ sig->scope = (CORINFO_MODULE_HANDLE)value.scope;
+ sig->token = (mdToken)value.token;
+ DEBUG_REP(dmpFindSig(key, value));
+}
+
+void MethodContext::recGetEEInfo(CORINFO_EE_INFO *pEEInfoOut)
+{
+ if (GetEEInfo == nullptr)
+ GetEEInfo = new LightWeightMap<DWORD, Agnostic_CORINFO_EE_INFO>();
+
+ Agnostic_CORINFO_EE_INFO value;
+
+ value.inlinedCallFrameInfo.size = (DWORD)pEEInfoOut->inlinedCallFrameInfo.size;
+ value.inlinedCallFrameInfo.offsetOfGSCookie = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie;
+ value.inlinedCallFrameInfo.offsetOfFrameVptr = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr;
+ value.inlinedCallFrameInfo.offsetOfFrameLink = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink;
+ value.inlinedCallFrameInfo.offsetOfCallSiteSP = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP;
+ value.inlinedCallFrameInfo.offsetOfCalleeSavedFP = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP;
+ value.inlinedCallFrameInfo.offsetOfCallTarget = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget;
+ value.inlinedCallFrameInfo.offsetOfReturnAddress = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress;
+ value.offsetOfThreadFrame = (DWORD)pEEInfoOut->offsetOfThreadFrame;
+ value.offsetOfGCState = (DWORD)pEEInfoOut->offsetOfGCState;
+ value.offsetOfDelegateInstance = (DWORD)pEEInfoOut->offsetOfDelegateInstance;
+ value.offsetOfDelegateFirstTarget = (DWORD)pEEInfoOut->offsetOfDelegateFirstTarget;
+ value.offsetOfSecureDelegateIndirectCell = (DWORD)pEEInfoOut->offsetOfSecureDelegateIndirectCell;
+ value.offsetOfTransparentProxyRP = (DWORD)pEEInfoOut->offsetOfTransparentProxyRP;
+ value.offsetOfRealProxyServer = (DWORD)pEEInfoOut->offsetOfRealProxyServer;
+ value.offsetOfObjArrayData = (DWORD)pEEInfoOut->offsetOfObjArrayData;
+ value.sizeOfReversePInvokeFrame = (DWORD)pEEInfoOut->sizeOfReversePInvokeFrame;
+ value.osPageSize = (DWORD)pEEInfoOut->osPageSize;
+ value.maxUncheckedOffsetForNullObject = (DWORD)pEEInfoOut->maxUncheckedOffsetForNullObject;
+ value.targetAbi = (DWORD)pEEInfoOut->targetAbi;
+ value.osType = (DWORD)pEEInfoOut->osType;
+ value.osMajor = (DWORD)pEEInfoOut->osMajor;
+ value.osMinor = (DWORD)pEEInfoOut->osMinor;
+ value.osBuild = (DWORD)pEEInfoOut->osBuild;
+
+ GetEEInfo->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetEEInfo((DWORD)0, value));
+}
+void MethodContext::dmpGetEEInfo(DWORD key, const Agnostic_CORINFO_EE_INFO& value)
+{
+ printf("GetEEInfo key %u, value icfi{sz-%u ogs-%u ofv-%u ofl-%u ocsp-%u ocsfp-%u oct-%u ora-%u} "
+ "otf-%u ogcs-%u odi-%u odft-%u osdic-%u otrp-%u orps-%u ooad-%u srpf-%u osps-%u muono-%u tabi-%u osType-%u osMajor-%u osMinor-%u osBuild-%u",
+ key,
+ value.inlinedCallFrameInfo.size,
+ value.inlinedCallFrameInfo.offsetOfGSCookie,
+ value.inlinedCallFrameInfo.offsetOfFrameVptr,
+ value.inlinedCallFrameInfo.offsetOfFrameLink,
+ value.inlinedCallFrameInfo.offsetOfCallSiteSP,
+ value.inlinedCallFrameInfo.offsetOfCalleeSavedFP,
+ value.inlinedCallFrameInfo.offsetOfCallTarget,
+ value.inlinedCallFrameInfo.offsetOfReturnAddress,
+ value.offsetOfThreadFrame,
+ value.offsetOfGCState,
+ value.offsetOfDelegateInstance,
+ value.offsetOfDelegateFirstTarget,
+ value.offsetOfSecureDelegateIndirectCell,
+ value.offsetOfTransparentProxyRP,
+ value.offsetOfRealProxyServer,
+ value.offsetOfObjArrayData,
+ value.sizeOfReversePInvokeFrame,
+ value.osPageSize,
+ value.maxUncheckedOffsetForNullObject,
+ value.targetAbi,
+ value.osType,
+ value.osMajor,
+ value.osMinor,
+ value.osBuild);
+}
+void MethodContext::repGetEEInfo(CORINFO_EE_INFO *pEEInfoOut)
+{
+ Agnostic_CORINFO_EE_INFO value;
+
+ int index = -1;
+ if (GetEEInfo != nullptr)
+ index = GetEEInfo->GetIndex((DWORD)0);
+ if (index >= 0)
+ {
+ value = GetEEInfo->Get((DWORD)0);
+ pEEInfoOut->inlinedCallFrameInfo.size = (unsigned)value.inlinedCallFrameInfo.size;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie = (unsigned)value.inlinedCallFrameInfo.offsetOfGSCookie;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr = (unsigned)value.inlinedCallFrameInfo.offsetOfFrameVptr;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink = (unsigned)value.inlinedCallFrameInfo.offsetOfFrameLink;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP = (unsigned)value.inlinedCallFrameInfo.offsetOfCallSiteSP;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP = (unsigned)value.inlinedCallFrameInfo.offsetOfCalleeSavedFP;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget = (unsigned)value.inlinedCallFrameInfo.offsetOfCallTarget;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress = (unsigned)value.inlinedCallFrameInfo.offsetOfReturnAddress;
+ pEEInfoOut->offsetOfThreadFrame = (unsigned)value.offsetOfThreadFrame;
+ pEEInfoOut->offsetOfGCState = (unsigned)value.offsetOfGCState;
+ pEEInfoOut->offsetOfDelegateInstance = (unsigned)value.offsetOfDelegateInstance;
+ pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)value.offsetOfDelegateFirstTarget;
+ pEEInfoOut->offsetOfSecureDelegateIndirectCell = (unsigned)value.offsetOfSecureDelegateIndirectCell;
+ pEEInfoOut->offsetOfTransparentProxyRP = (unsigned)value.offsetOfTransparentProxyRP;
+ pEEInfoOut->offsetOfRealProxyServer = (unsigned)value.offsetOfRealProxyServer;
+ pEEInfoOut->offsetOfObjArrayData = (unsigned)value.offsetOfObjArrayData;
+ pEEInfoOut->sizeOfReversePInvokeFrame = (unsigned)value.sizeOfReversePInvokeFrame;
+ pEEInfoOut->osPageSize = (size_t)value.osPageSize;
+ pEEInfoOut->maxUncheckedOffsetForNullObject = (size_t)value.maxUncheckedOffsetForNullObject;
+ pEEInfoOut->targetAbi = (CORINFO_RUNTIME_ABI)value.targetAbi;
+ pEEInfoOut->osType = (CORINFO_OS)value.osType;
+ pEEInfoOut->osMajor = (unsigned)value.osMajor;
+ pEEInfoOut->osMinor = (unsigned)value.osMinor;
+ pEEInfoOut->osBuild = (unsigned)value.osBuild;
+ DEBUG_REP(dmpGetEEInfo((DWORD)0, value));
+ }
+ else
+ {
+ pEEInfoOut->inlinedCallFrameInfo.size = (unsigned)0x40;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie = (unsigned)0;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr = (unsigned)0x8;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink = (unsigned)0x10;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP = (unsigned)0x28;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP = (unsigned)0x38;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget = (unsigned)0x18;
+ pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress = (unsigned)0x30;
+ pEEInfoOut->offsetOfThreadFrame = (unsigned)0x10;
+ pEEInfoOut->offsetOfGCState = (unsigned)0xc;
+ pEEInfoOut->offsetOfDelegateInstance = (unsigned)0x8;
+ pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)0x18;
+ pEEInfoOut->offsetOfSecureDelegateIndirectCell = (unsigned)0x40;
+ pEEInfoOut->offsetOfTransparentProxyRP = (unsigned)0x8;
+ pEEInfoOut->offsetOfRealProxyServer = (unsigned)0x18;
+ pEEInfoOut->offsetOfObjArrayData = (unsigned)0x18;
+ pEEInfoOut->sizeOfReversePInvokeFrame = (unsigned)0x8;
+ pEEInfoOut->osPageSize = (size_t)0x1000;
+ pEEInfoOut->maxUncheckedOffsetForNullObject = (size_t)((32*1024)-1);
+ pEEInfoOut->targetAbi = CORINFO_DESKTOP_ABI;
+ pEEInfoOut->osType = (CORINFO_OS)0;
+ pEEInfoOut->osMajor = (unsigned)0;
+ pEEInfoOut->osMinor = (unsigned)0;
+ pEEInfoOut->osBuild = (unsigned)0;
+#ifdef DEBUG_REP
+ printf("repGetEEInfo - fell to default params\n");
+#endif
+ }
+}
+
+void MethodContext::recGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal)
+{
+ if (GetGSCookie == nullptr)
+ GetGSCookie = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+
+ if (pCookieVal != nullptr)
+ value.A = (DWORDLONG)*pCookieVal;
+ else
+ value.A = (DWORDLONG)0;
+
+ if (ppCookieVal != nullptr)
+ value.B = (DWORDLONG)*ppCookieVal;
+ else
+ value.B = (DWORDLONG)0;
+ GetGSCookie->Add((DWORD)0, value);
+}
+void MethodContext::dmpGetGSCookie(DWORD key, DLDL value)
+{
+ printf("GetGSCookie key 0, value pCookieVal-%016llX ppCookieVal-%016llX", value.A, value.B);
+}
+void MethodContext::repGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal)
+{
+ DLDL value;
+
+ value = GetGSCookie->Get((DWORD)0);
+
+ if (pCookieVal != nullptr)
+ *pCookieVal = (GSCookie)value.A;
+ if (ppCookieVal != nullptr)
+ *ppCookieVal = (GSCookie*)value.B;
+}
+
+void MethodContext::recGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection, size_t result)
+{
+ if (GetClassModuleIdForStatics == nullptr)
+ GetClassModuleIdForStatics = new LightWeightMap<DWORDLONG, Agnostic_GetClassModuleIdForStatics>();
+
+ Agnostic_GetClassModuleIdForStatics value;
+
+ if (pModule != nullptr)
+ value.Module = (DWORDLONG)*pModule;
+ else
+ value.Module = (DWORDLONG)0;
+ if (ppIndirection != nullptr)
+ value.pIndirection = (DWORDLONG)*ppIndirection;
+ else
+ value.pIndirection = (DWORDLONG)0;
+ value.result = (DWORDLONG)result;
+ GetClassModuleIdForStatics->Add((DWORDLONG)cls, value);
+}
+void MethodContext::dmpGetClassModuleIdForStatics(DWORDLONG key, const Agnostic_GetClassModuleIdForStatics& value)
+{
+ printf("GetClassModuleIdForStatics key cls-%016llX, value mod-%016llX pp-%016llX res-%016llX", key, value.Module, value.pIndirection, value.result);
+}
+size_t MethodContext::repGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection)
+{
+ Agnostic_GetClassModuleIdForStatics value;
+
+ value = GetClassModuleIdForStatics->Get((DWORDLONG)cls);
+
+ if (pModule != nullptr)
+ *pModule = (CORINFO_MODULE_HANDLE)value.Module;
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.pIndirection;
+
+ return (size_t)value.result;
+}
+
+void MethodContext::recGetThreadTLSIndex(void **ppIndirection, DWORD result)
+{
+ if (GetThreadTLSIndex == nullptr)
+ GetThreadTLSIndex = new LightWeightMap<DWORD, DLD>();
+
+ DLD value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORD)result;
+
+ GetThreadTLSIndex->Add((DWORD)0, value);
+}
+void MethodContext::dmpGetThreadTLSIndex(DWORD key, DLD value)
+{
+ printf("GetThreadTLSIndex key 0, value ppIndirection-%016llX result-%08X", value.A, value.B);
+}
+DWORD MethodContext::repGetThreadTLSIndex(void **ppIndirection)
+{
+ DLD value;
+
+ value = GetThreadTLSIndex->Get((DWORD)0);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (DWORD)value.B;
+}
+
+void MethodContext::recGetInlinedCallFrameVptr(void **ppIndirection, const void * result)
+{
+ if (GetInlinedCallFrameVptr == nullptr)
+ GetInlinedCallFrameVptr = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetInlinedCallFrameVptr->Add((DWORD)0, value);
+}
+void MethodContext::dmpGetInlinedCallFrameVptr(DWORD key, DLDL value)
+{
+ printf("GetInlinedCallFrameVptr key 0, value ppIndirection-%016llX result-%016llX\n", value.A, value.B);
+}
+const void * MethodContext::repGetInlinedCallFrameVptr(void **ppIndirection)
+{
+ DLDL value;
+
+ value = GetInlinedCallFrameVptr->Get((DWORD)0);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (const void *)value.B;
+}
+
+void MethodContext::recGetAddrOfCaptureThreadGlobal(void **ppIndirection, LONG * result)
+{
+ if (GetAddrOfCaptureThreadGlobal == nullptr)
+ GetAddrOfCaptureThreadGlobal = new LightWeightMap<DWORD, DLDL>();
+
+ DLDL value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetAddrOfCaptureThreadGlobal->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetAddrOfCaptureThreadGlobal((DWORD)0, value));
+}
+void MethodContext::dmpGetAddrOfCaptureThreadGlobal(DWORD key, DLDL value)
+{
+ printf("GetAddrOfCaptureThreadGlobal key %u, value ppi-%016llX res-%016llX", key, value.A, value.B);
+}
+LONG * MethodContext::repGetAddrOfCaptureThreadGlobal(void **ppIndirection)
+{
+ DLDL value;
+
+ if ((GetAddrOfCaptureThreadGlobal == nullptr) || (GetAddrOfCaptureThreadGlobal->GetIndex((DWORD)0) == -1))
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetAddrOfCaptureThreadGlobal returning 0xCAFE0001");
+ return (LONG*)(size_t)0xCAFE0001;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find anything for GetAddrOfCaptureThreadGlobal", "");
+#endif
+ }
+ value = GetAddrOfCaptureThreadGlobal->Get((DWORD)0);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ DEBUG_REP(dmpGetAddrOfCaptureThreadGlobal((DWORD)0, value));
+ return (LONG *)value.B;
+}
+
+void MethodContext::recGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection, unsigned result)
+{
+ if (GetClassDomainID == nullptr)
+ GetClassDomainID = new LightWeightMap<DWORDLONG, DLD>();
+
+ DLD value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORD)result;
+
+ GetClassDomainID->Add((DWORDLONG)cls, value);
+ DEBUG_REC(dmpGetClassDomainID((DWORDLONG)cls, value));
+}
+void MethodContext::dmpGetClassDomainID(DWORDLONG key, DLD value)
+{
+ printf("GetClassDomainID key cls-%016llX, value pp-%016llX res-%u", key, value.A, value.B);
+}
+unsigned MethodContext::repGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection)
+{
+ DLD value;
+
+ AssertCodeMsg(GetClassDomainID != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)cls);
+ AssertCodeMsg(GetClassDomainID->GetIndex((DWORDLONG)cls) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)cls);
+ value = GetClassDomainID->Get((DWORDLONG)cls);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ DEBUG_REP(dmpGetClassDomainID((DWORDLONG)cls, value));
+ return (unsigned)value.B;
+}
+
+void MethodContext::recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND *result)
+{
+ if (GetLocationOfThisType == nullptr)
+ GetLocationOfThisType = new LightWeightMap<DWORDLONG, Agnostic_CORINFO_LOOKUP_KIND>();
+
+ Agnostic_CORINFO_LOOKUP_KIND value;
+
+ value.needsRuntimeLookup = (DWORD)result->needsRuntimeLookup;
+ value.runtimeLookupKind = (DWORD)result->runtimeLookupKind;
+ value.runtimeLookupFlags = (WORD)result->runtimeLookupFlags;
+ // We don't store result->runtimeLookupArgs, which is opaque data. Ok?
+
+ GetLocationOfThisType->Add((DWORDLONG)context, value);
+}
+void MethodContext::dmpGetLocationOfThisType(DWORDLONG key, const Agnostic_CORINFO_LOOKUP_KIND& value)
+{
+ printf("GetLocationOfThisType key ftn-%016llX, value nrl-%u rlk-%u", key, value.needsRuntimeLookup, value.runtimeLookupKind);
+}
+CORINFO_LOOKUP_KIND MethodContext::repGetLocationOfThisType(CORINFO_METHOD_HANDLE context)
+{
+ Agnostic_CORINFO_LOOKUP_KIND value1;
+ CORINFO_LOOKUP_KIND value2;
+
+ value1 = GetLocationOfThisType->Get((DWORDLONG)context);
+
+ value2.needsRuntimeLookup = value1.needsRuntimeLookup != 0;
+ value2.runtimeLookupKind = (CORINFO_RUNTIME_LOOKUP_KIND)value1.runtimeLookupKind;
+ value2.runtimeLookupFlags = (WORD)value1.runtimeLookupFlags;
+ value2.runtimeLookupArgs = nullptr; // We don't store this opaque data. Ok?
+
+ return value2;
+}
+
+void MethodContext::recGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData, CORINFO_METHOD_HANDLE result)
+{
+ if (GetDelegateCtor == nullptr)
+ GetDelegateCtor = new LightWeightMap<Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut>();
+
+ Agnostic_GetDelegateCtorIn key;
+ ZeroMemory(&key, sizeof(Agnostic_GetDelegateCtorIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetDelegateCtorOut value;
+
+ key.methHnd = (DWORDLONG)methHnd;
+ key.clsHnd = (DWORDLONG)clsHnd;
+ key.targetMethodHnd = (DWORDLONG)targetMethodHnd;
+
+ value.CtorData.pMethod = (DWORDLONG)pCtorData->pMethod;
+ value.CtorData.pArg3 = (DWORDLONG)pCtorData->pArg3;
+ value.CtorData.pArg4 = (DWORDLONG)pCtorData->pArg4;
+ value.CtorData.pArg5 = (DWORDLONG)pCtorData->pArg5;
+ value.result = (DWORDLONG)result;
+
+ GetDelegateCtor->Add(key, value);
+ DEBUG_REC(dmpGetDelegateCtor(key, value));
+}
+void MethodContext::dmpGetDelegateCtor(const Agnostic_GetDelegateCtorIn& key, const Agnostic_GetDelegateCtorOut& value)
+{
+ printf("GetDelegateCtor key ftn-%016llX cls-%016llX tftn-%016llX, value pm-%016llX a3-%016llX a4-%016llX a5-%016llX res-%016llX",
+ key.methHnd,
+ key.clsHnd,
+ key.targetMethodHnd,
+ value.CtorData.pMethod,
+ value.CtorData.pArg3,
+ value.CtorData.pArg4,
+ value.CtorData.pArg5,
+ value.result);
+}
+CORINFO_METHOD_HANDLE MethodContext::repGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData)
+{
+ Agnostic_GetDelegateCtorIn key;
+ ZeroMemory(&key, sizeof(Agnostic_GetDelegateCtorIn)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_GetDelegateCtorOut value;
+
+ key.methHnd = (DWORDLONG)methHnd;
+ key.clsHnd = (DWORDLONG)clsHnd;
+ key.targetMethodHnd = (DWORDLONG)targetMethodHnd;
+
+ AssertCodeMsg(GetDelegateCtor != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.methHnd);
+ AssertCodeMsg(GetDelegateCtor->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.methHnd);
+ value = GetDelegateCtor->Get(key);
+
+ pCtorData->pMethod = (void*)value.CtorData.pMethod;
+ pCtorData->pArg3 = (void*)value.CtorData.pArg3;
+ pCtorData->pArg4 = (void*)value.CtorData.pArg4;
+ pCtorData->pArg5 = (void*)value.CtorData.pArg5;
+ DEBUG_REP(dmpGetDelegateCtor(key, value));
+ return (CORINFO_METHOD_HANDLE)value.result;
+}
+
+void MethodContext::recGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult)
+{
+ if (GetFunctionFixedEntryPoint == nullptr)
+ GetFunctionFixedEntryPoint = new LightWeightMap<DWORDLONG, Agnostic_CORINFO_CONST_LOOKUP>();
+
+ Agnostic_CORINFO_CONST_LOOKUP value;
+
+ value.accessType = (DWORD)pResult->accessType;
+ value.handle = (DWORDLONG)pResult->handle;
+
+ GetFunctionFixedEntryPoint->Add((DWORDLONG)ftn, value);
+}
+void MethodContext::dmpGetFunctionFixedEntryPoint(DWORDLONG key, const Agnostic_CORINFO_CONST_LOOKUP& value)
+{
+ printf("GetFunctionFixedEntryPoint key ftn-%016llX, value at-%u han-%016llX", key, value.accessType, value.handle);
+}
+void MethodContext::repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult)
+{
+ Agnostic_CORINFO_CONST_LOOKUP value;
+
+ value = GetFunctionFixedEntryPoint->Get((DWORDLONG)ftn);
+
+ pResult->accessType = (InfoAccessType)value.accessType;
+ pResult->handle = (CORINFO_GENERIC_HANDLE)value.handle;
+}
+
+void MethodContext::recGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num, CORINFO_FIELD_HANDLE result)
+{
+ if (GetFieldInClass == nullptr)
+ GetFieldInClass = new LightWeightMap<DLD, DWORDLONG>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)clsHnd;
+ key.B = (DWORD)num;
+
+ GetFieldInClass->Add(key, (DWORDLONG)result);
+ DEBUG_REC(dmpGetFieldInClass(key, (DWORDLONG)result));
+}
+void MethodContext::dmpGetFieldInClass(DLD key, DWORDLONG value)
+{
+ printf("GetFieldInClass key cls-%016llX ind-%u, value %016llX", key.A, key.B, value);
+}
+CORINFO_FIELD_HANDLE MethodContext::repGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)clsHnd;
+ key.B = (DWORD)num;
+
+ AssertCodeMsg((GetFieldInClass != nullptr) && (GetFieldInClass->GetIndex(key) != -1),
+ EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.A);
+ CORINFO_FIELD_HANDLE temp = (CORINFO_FIELD_HANDLE)GetFieldInClass->Get(key);
+
+ DEBUG_REP(dmpGetFieldInClass(key, (DWORDLONG)temp));
+ return temp;
+}
+
+void MethodContext::recGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent, CorInfoType result)
+{
+ if (GetFieldType == nullptr)
+ GetFieldType = new LightWeightMap<DLDL, DLD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORDLONG)memberParent;
+
+ value.A = (DWORDLONG)*structType;
+ value.B = (DWORD)result;
+
+ GetFieldType->Add(key, value);
+ DEBUG_REC(dmpGetFieldType(key, value));
+}
+void MethodContext::dmpGetFieldType(DLDL key, DLD value)
+{
+ printf("GetFieldType key fld-%016llX cls-%016llX, value ch-%016llX cit-%u(%s)", key.A, key.B, value.A, value.B, toString((CorInfoType)value.B));
+}
+CorInfoType MethodContext::repGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORDLONG)memberParent;
+
+ AssertCodeMsg(GetFieldType != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.A);
+ AssertCodeMsg(GetFieldType->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.A);
+ value = GetFieldType->Get(key);
+
+
+ if (structType != nullptr)
+ *structType = (CORINFO_CLASS_HANDLE)value.A;
+
+ DEBUG_REP(dmpGetFieldType(key, value));
+ return (CorInfoType)value.B;
+}
+
+void MethodContext::recGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName, const char* result)
+{
+ if (GetFieldName == nullptr)
+ GetFieldName = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+
+ if (result != nullptr)
+ value.A = GetFieldName->AddBuffer((unsigned char *)result, (DWORD)strlen(result) + 1);
+ else
+ value.A = (DWORD)-1;
+
+ if (moduleName != nullptr) //protect strlen
+ value.B = (DWORD)GetFieldName->AddBuffer((unsigned char *)*moduleName, (DWORD)strlen(*moduleName) + 1);
+ else
+ value.B = (DWORD)-1;
+
+ GetFieldName->Add((DWORDLONG)ftn, value);
+}
+void MethodContext::dmpGetFieldName(DWORDLONG key, DD value)
+{
+ unsigned char *fieldName = (unsigned char *)GetFieldName->GetBuffer(value.A);
+ unsigned char *moduleName = (unsigned char *)GetFieldName->GetBuffer(value.B);
+ printf("GetFieldName key - ftn-%016llX, value fld-'%s', mod-'%s'", key, fieldName, moduleName);
+ GetFieldName->Unlock();
+}
+const char* MethodContext::repGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName)
+{
+ DD value;
+ if (GetFieldName == nullptr)
+ {
+ if (moduleName != nullptr)
+ *moduleName = "hackishModuleName";
+ return "hackishFieldName";
+ }
+ value = GetFieldName->Get((DWORDLONG)ftn);
+ if (moduleName != nullptr)
+ *moduleName = (const char *)GetFieldName->GetBuffer(value.B);
+ return (const char *)GetFieldName->GetBuffer(value.A);
+}
+
+void MethodContext::recCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (CanInlineTypeCheckWithObjectVTable == nullptr)
+ CanInlineTypeCheckWithObjectVTable = new LightWeightMap<DWORDLONG, DWORD>();
+
+ CanInlineTypeCheckWithObjectVTable->Add((DWORDLONG)cls, (DWORD)result);
+}
+void MethodContext::dmpCanInlineTypeCheckWithObjectVTable(DWORDLONG key, DWORD value)
+{
+ printf("CanInlineTypeCheckWithObjectVTable key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ return (BOOL)CanInlineTypeCheckWithObjectVTable->Get((DWORDLONG)cls);
+}
+
+void MethodContext::recSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method, BOOL result)
+{
+ if (SatisfiesMethodConstraints == nullptr)
+ SatisfiesMethodConstraints = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)parent;
+ key.B = (DWORDLONG)method;
+
+ SatisfiesMethodConstraints->Add(key, (DWORD)result);
+}
+void MethodContext::dmpSatisfiesMethodConstraints(DLDL key, DWORD value)
+{
+ printf("SatisfiesMethodConstraints key cls-%016llX ftn-%016llX, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)parent;
+ key.B = (DWORDLONG)method;
+
+ BOOL value = (BOOL)SatisfiesMethodConstraints->Get(key);
+ return value;
+}
+
+void MethodContext::recInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint)
+{
+ if (InitConstraintsForVerification == nullptr)
+ InitConstraintsForVerification = new LightWeightMap<DWORDLONG, DD>();
+
+ DD value;
+
+ value.A = (DWORD)*pfHasCircularClassConstraints;
+ value.B = (DWORD)*pfHasCircularMethodConstraint;
+
+ InitConstraintsForVerification->Add((DWORDLONG)method, value);
+}
+void MethodContext::dmpInitConstraintsForVerification(DWORDLONG key, DD value)
+{
+ printf("InitConstraintsForVerification key ftn-%016llX, value circ-%u cirm-%u", key, value.A, value.B);
+}
+void MethodContext::repInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint)
+{
+ DD value;
+
+ value = InitConstraintsForVerification->Get((DWORDLONG)method);
+
+ *pfHasCircularClassConstraints = (BOOL)value.A;
+ *pfHasCircularMethodConstraint = (BOOL)value.B;
+}
+
+void MethodContext::recIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result)
+{
+ if (IsValidStringRef == nullptr)
+ IsValidStringRef = new LightWeightMap<DLD, DWORD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ IsValidStringRef->Add(key, (DWORD)result);
+}
+void MethodContext::dmpIsValidStringRef(DLD key, DWORD value)
+{
+ printf("IsValidStringRef key mod-%016llX tok-%08X, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ BOOL value = (BOOL)IsValidStringRef->Get(key);
+ return value;
+}
+
+void MethodContext::recGetHelperName(CorInfoHelpFunc funcNum, const char* result)
+{
+ if (GetHelperName == nullptr)
+ GetHelperName = new LightWeightMap<DWORD, DWORD>();
+
+ DWORD value = (DWORD)-1;
+ if (result != nullptr)
+ value = (DWORD)GetHelperName->AddBuffer((unsigned char*)result, (DWORD)strlen(result) + 1);
+
+ GetHelperName->Add((DWORD)funcNum, value);
+ DEBUG_REC(dmpGetHelperName((DWORD)funcNum, value));
+}
+void MethodContext::dmpGetHelperName(DWORD key, DWORD value)
+{
+ printf("GetHelperName key ftn-%u, value '%s'", key, (const char*)GetHelperName->GetBuffer(value));
+ GetHelperName->Unlock();
+}
+const char* MethodContext::repGetHelperName(CorInfoHelpFunc funcNum)
+{
+ if (GetHelperName == nullptr) return "Yickish helper name";
+
+ int itemIndex = GetHelperName->GetIndex((DWORD)funcNum);
+ if (itemIndex < 0)
+ {
+ return "hackishHelperName";
+ }
+ else
+ {
+ unsigned int buffIndex = GetHelperName->Get((DWORD)funcNum);
+ DEBUG_REP(dmpGetHelperName((DWORD)funcNum, buffIndex));
+ return (const char*)GetHelperName->GetBuffer(buffIndex);
+ }
+}
+
+
+void MethodContext::recCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent, BOOL result)
+{
+ if (CanCast == nullptr)
+ CanCast = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)child;
+ key.B = (DWORDLONG)parent;
+
+ CanCast->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanCast(key, (DWORD)result));
+}
+void MethodContext::dmpCanCast(DLDL key, DWORD value)
+{
+ printf("CanCast key chd-%016llX par-%016llX, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)child;
+ key.B = (DWORDLONG)parent;
+
+ AssertCodeMsg(CanCast != nullptr, EXCEPTIONCODE_MC, "Didn't find anything %016llX, %016llX in map", (DWORDLONG)child, (DWORDLONG)parent);
+ AssertCodeMsg(CanCast->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX, %016llX %u in map", (DWORDLONG)child, (DWORDLONG)parent, CanCast->GetCount());
+ BOOL value = (BOOL)CanCast->Get(key);
+ DEBUG_REP(dmpCanCast(key, (DWORD)value));
+ return value;
+}
+
+void MethodContext::recGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet, CorInfoType result)
+{
+ if (GetChildType == nullptr)
+ GetChildType = new LightWeightMap<DWORDLONG, DLD>();
+
+ DLD value;
+
+ value.A = (DWORDLONG)*clsRet;
+ value.B = (DWORD)result;
+
+ GetChildType->Add((DWORDLONG)clsHnd, value);
+ DEBUG_REC(dmpGetChildType((DWORDLONG)clsHnd, value));
+}
+void MethodContext::dmpGetChildType(DWORDLONG key, DLD value)
+{
+ printf("GetChildType key cls-%016llX, value clsr-%016llX cit-%u(%s)", key, value.A, value.B, toString((CorInfoType)value.B));
+}
+CorInfoType MethodContext::repGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet)
+{
+ DLD value;
+
+ AssertCodeMsg(GetChildType != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)clsHnd);
+ AssertCodeMsg(GetChildType->GetIndex((DWORDLONG)clsHnd) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)clsHnd);
+ value = GetChildType->Get((DWORDLONG)clsHnd);
+
+ *clsRet = (CORINFO_CLASS_HANDLE)value.A;
+ DEBUG_REP(dmpGetChildType((DWORDLONG)clsHnd, value));
+ return (CorInfoType)value.B;
+}
+
+
+void MethodContext::recGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size, void *result)
+{
+ if (GetArrayInitializationData == nullptr)
+ GetArrayInitializationData = new LightWeightMap<DLD, DWORDLONG>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORD)size;
+
+ GetArrayInitializationData->Add(key, (DWORDLONG)result);
+}
+void MethodContext::dmpGetArrayInitializationData(DLD key, DWORDLONG value)
+{
+ printf("GetArrayInitializationData key field-%016llX size-%08X, value result-%016llX", key.A, key.B, value);
+}
+void *MethodContext::repGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)field;
+ key.B = (DWORD)size;
+
+ void *value = (void*)GetArrayInitializationData->Get(key);
+ return value;
+}
+
+void MethodContext::recFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers, int result)
+{
+ if (FilterException == nullptr)
+ FilterException = new LightWeightMap<DWORD, DWORD>();
+
+ FilterException->Add((DWORD)pExceptionPointers->ExceptionRecord->ExceptionCode, (DWORD)result);
+}
+void MethodContext::dmpFilterException(DWORD key, DWORD value)
+{
+ printf("FilterException key %u, value %u", key, value);
+}
+int MethodContext::repFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers)
+{
+ if (FilterException == nullptr)
+ return EXCEPTION_CONTINUE_SEARCH;
+ if (FilterException->GetIndex((DWORD)pExceptionPointers->ExceptionRecord->ExceptionCode) < 0)
+ return EXCEPTION_CONTINUE_SEARCH;
+ else
+ {
+ int result = FilterException->Get((DWORD)pExceptionPointers->ExceptionRecord->ExceptionCode);
+ return result;
+ }
+
+}
+
+void MethodContext::recHandleException(struct _EXCEPTION_POINTERS *pExceptionPointers)
+{
+ if (HandleException == nullptr)
+ HandleException = new DenseLightWeightMap<DWORD>();
+
+ HandleException->Append(pExceptionPointers->ExceptionRecord->ExceptionCode);
+}
+void MethodContext::dmpHandleException(DWORD key, DWORD value)
+{
+ printf("HandleException key %u, value %u", key, value);
+}
+
+void MethodContext::recGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result)
+{
+ if (GetAddressOfPInvokeFixup == nullptr)
+ GetAddressOfPInvokeFixup = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetAddressOfPInvokeFixup->Add((DWORDLONG)method, value);
+}
+void MethodContext::dmpGetAddressOfPInvokeFixup(DWORDLONG key, DLDL value)
+{
+ printf("GetAddressOfPInvokeFixup key ftn-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection)
+{
+ DLDL value;
+
+ value = GetAddressOfPInvokeFixup->Get((DWORDLONG)method);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+ return (void*)value.B;
+}
+void MethodContext::recGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup)
+{
+ if (GetAddressOfPInvokeTarget == nullptr)
+ GetAddressOfPInvokeTarget = new LightWeightMap<DWORDLONG, DLD>();
+
+ DLD value;
+
+ value.A = (DWORDLONG)pLookup->addr;
+ value.B = (DWORD)pLookup->accessType;
+
+ GetAddressOfPInvokeTarget->Add((DWORDLONG)method, value);
+}
+void MethodContext::dmpGetAddressOfPInvokeTarget(DWORDLONG key, DLD value)
+{
+ printf("GetAddressOfPInvokeTarget key ftn-%016llX, value addr-%016llX at-%u", key, value.A, value.B);
+}
+void MethodContext::repGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup)
+{
+ DLD value = GetAddressOfPInvokeTarget->Get((DWORDLONG)method);
+
+ pLookup->addr = (void *)value.A;
+ pLookup->accessType = (InfoAccessType)value.B;
+}
+
+
+void MethodContext::recSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls, BOOL result)
+{
+ if (SatisfiesClassConstraints == nullptr)
+ SatisfiesClassConstraints = new LightWeightMap<DWORDLONG, DWORD>();
+
+ SatisfiesClassConstraints->Add((DWORDLONG)cls, (DWORD)result);
+}
+void MethodContext::dmpSatisfiesClassConstraints(DWORDLONG key, DWORD value)
+{
+ printf("SatisfiesClassConstraints key cls-%016llX, value res-%u", key, value);
+}
+BOOL MethodContext::repSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls)
+{
+ return (BOOL)SatisfiesClassConstraints->Get((DWORDLONG)cls);
+}
+
+void MethodContext::recGetMethodHash(CORINFO_METHOD_HANDLE ftn, unsigned result)
+{
+ if (GetMethodHash == nullptr)
+ GetMethodHash = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetMethodHash->Add((DWORDLONG)ftn, (DWORD)result);
+ DEBUG_REC(dmpGetMethodHash((DWORDLONG)ftn, (DWORD) result));
+}
+void MethodContext::dmpGetMethodHash(DWORDLONG key, DWORD value)
+{
+ printf("GetMethodHash key %016llX, value %u", key, value);
+}
+unsigned MethodContext::repGetMethodHash(CORINFO_METHOD_HANDLE ftn)
+{
+ unsigned result = 0x43;
+ if (GetMethodHash != nullptr)
+ if (GetMethodHash->GetIndex((DWORDLONG)ftn) >= 0)
+ result = GetMethodHash->Get((DWORDLONG)ftn);
+ DEBUG_REP(dmpGetMethodHash((DWORDLONG)ftn, (DWORD) result));
+ return result;
+}
+
+void MethodContext::recCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd,
+ CORINFO_METHOD_HANDLE exactCalleeHnd, bool fIsTailPrefix, bool result)
+{
+ if (CanTailCall == nullptr)
+ CanTailCall = new LightWeightMap<Agnostic_CanTailCall, DWORD>();
+
+ Agnostic_CanTailCall key;
+ ZeroMemory(&key, sizeof(Agnostic_CanTailCall)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callerHnd = (DWORDLONG)callerHnd;
+ key.declaredCalleeHnd = (DWORDLONG)declaredCalleeHnd;
+ key.exactCalleeHnd = (DWORDLONG)exactCalleeHnd;
+ key.fIsTailPrefix = (DWORD)fIsTailPrefix;
+
+ CanTailCall->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanTailCall(key,(DWORD)result));
+}
+void MethodContext::dmpCanTailCall(const Agnostic_CanTailCall& key, DWORD value)
+{
+ printf("CanTailCall key clr-%016llX dcle-%016llX ecle-%016llX pfx-%u, value res-%u",
+ key.callerHnd,
+ key.declaredCalleeHnd,
+ key.exactCalleeHnd,
+ key.fIsTailPrefix,
+ value);
+}
+bool MethodContext::repCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd, CORINFO_METHOD_HANDLE exactCalleeHnd,
+ bool fIsTailPrefix)
+{
+ Agnostic_CanTailCall key;
+ ZeroMemory(&key, sizeof(Agnostic_CanTailCall)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callerHnd = (DWORDLONG)callerHnd;
+ key.declaredCalleeHnd = (DWORDLONG)declaredCalleeHnd;
+ key.exactCalleeHnd = (DWORDLONG)exactCalleeHnd;
+ key.fIsTailPrefix = (DWORD)fIsTailPrefix;
+
+ AssertCodeMsg(CanTailCall != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.callerHnd);
+ AssertCodeMsg(CanTailCall->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.callerHnd);
+ bool temp = CanTailCall->Get(key) != 0;
+ DEBUG_REP(dmpCanTailCall(key,(DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate, BOOL result)
+{
+ if (IsCompatibleDelegate == nullptr)
+ IsCompatibleDelegate = new LightWeightMap<Agnostic_IsCompatibleDelegate, DD>();
+ Agnostic_IsCompatibleDelegate key;
+ ZeroMemory(&key, sizeof(Agnostic_IsCompatibleDelegate)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DD value;
+
+ key.objCls = (DWORDLONG)objCls;
+ key.methodParentCls = (DWORDLONG)methodParentCls;
+ key.method = (DWORDLONG)method;
+ key.delegateCls = (DWORDLONG)delegateCls;
+
+ value.A = (DWORD)*pfIsOpenDelegate;
+ value.B = (DWORD)result;
+
+ IsCompatibleDelegate->Add(key, value);
+}
+void MethodContext::dmpIsCompatibleDelegate(const Agnostic_IsCompatibleDelegate& key, DD value)
+{
+ printf("IsCompatibleDelegate key objCls-%016llX methodParentCls-%016llX method-%016llX delegateCls-%016llX, value pfIsOpenDelegate-%08X result-%08X",
+ key.objCls, key.methodParentCls, key.method, key.delegateCls, value.A, value.B);
+}
+BOOL MethodContext::repIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate)
+{
+ Agnostic_IsCompatibleDelegate key;
+ ZeroMemory(&key, sizeof(Agnostic_IsCompatibleDelegate)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DD value;
+
+ key.objCls = (DWORDLONG)objCls;
+ key.methodParentCls = (DWORDLONG)methodParentCls;
+ key.method = (DWORDLONG)method;
+ key.delegateCls = (DWORDLONG)delegateCls;
+
+ value = IsCompatibleDelegate->Get(key);
+
+ *pfIsOpenDelegate = (BOOL)value.A;
+ return (BOOL)value.B;
+}
+
+void MethodContext::recIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd, BOOL result)
+{
+ if (IsDelegateCreationAllowed == nullptr)
+ IsDelegateCreationAllowed = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(key));
+ DWORD value;
+
+ key.A = (DWORDLONG)delegateHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ value = (DWORD)result;
+
+ IsDelegateCreationAllowed->Add(key, value);
+}
+void MethodContext::dmpIsDelegateCreationAllowed(DLDL key, DWORD value)
+{
+ printf("IsDelegateCreationAllowed key delegateHnd-%016llX calleeHnd-%016llX result-%08X",
+ key.A, key.B, value);
+}
+BOOL MethodContext::repIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(key));
+ DWORD value;
+
+ key.A = (DWORDLONG)delegateHnd;
+ key.B = (DWORDLONG)calleeHnd;
+
+ value = IsDelegateCreationAllowed->Get(key);
+
+ return (BOOL)value;
+}
+
+void MethodContext::recCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip, CorInfoCanSkipVerificationResult result)
+{
+ if (CanSkipMethodVerification == nullptr)
+ CanSkipMethodVerification = new LightWeightMap<DLD, DWORD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)ftnHandle;
+ key.B = (DWORD)skip;
+
+ CanSkipMethodVerification->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanSkipMethodVerification(key,(DWORD)result));
+}
+void MethodContext::dmpCanSkipMethodVerification(DLD key, DWORD value)
+{
+ printf("CanSkipMethodVerification key ftn-%016llX skp-%u, value res-%u", key.A, key.B, value);
+}
+CorInfoCanSkipVerificationResult MethodContext::repCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)ftnHandle;
+ key.B = (DWORD)skip;
+
+ AssertCodeMsg(CanSkipMethodVerification != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)ftnHandle);
+ AssertCodeMsg(CanSkipMethodVerification->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)ftnHandle);
+
+ CorInfoCanSkipVerificationResult temp = (CorInfoCanSkipVerificationResult)CanSkipMethodVerification->Get(key);
+ DEBUG_REP(dmpCanSkipMethodVerification(key,(DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ if (FindCallSiteSig == nullptr)
+ FindCallSiteSig = new LightWeightMap<Agnostic_FindCallSiteSig, Agnostic_CORINFO_SIG_INFO>();
+
+ Agnostic_FindCallSiteSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindCallSiteSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.methTok = (DWORD)methTOK;
+ key.context = (DWORDLONG)context;
+
+ value.callConv = (DWORD)sig->callConv;
+ value.retTypeClass = (DWORDLONG)sig->retTypeClass;
+ value.retTypeSigClass = (DWORDLONG)sig->retTypeSigClass;
+ value.retType = (DWORD)sig->retType;
+ value.flags = (DWORD)sig->flags;
+ value.numArgs = (DWORD)sig->numArgs;
+ value.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
+ value.sigInst_classInst_Index = FindCallSiteSig->AddBuffer((unsigned char*)sig->sigInst.classInst, sig->sigInst.classInstCount * 8); //porting issue
+ value.sigInst_methInstCount = (DWORD)sig->sigInst.methInstCount;
+ value.sigInst_methInst_Index = FindCallSiteSig->AddBuffer((unsigned char*)sig->sigInst.methInst, sig->sigInst.methInstCount * 8); //porting issue
+ value.args = (DWORDLONG)sig->args;
+ value.cbSig = (DWORD)sig->cbSig;
+ value.pSig = (DWORD)FindCallSiteSig->AddBuffer((unsigned char *)sig->pSig, sig->cbSig);
+ value.scope = (DWORDLONG)sig->scope;
+ value.token = (DWORD)sig->token;
+
+ FindCallSiteSig->Add(key, value);
+ DEBUG_REC(dmpFindCallSiteSig(key,value));
+}
+void MethodContext::dmpFindCallSiteSig(const Agnostic_FindCallSiteSig& key, const Agnostic_CORINFO_SIG_INFO& value)
+{
+ printf("dmpFindCallSiteSig key module-%016llX methTok-%08X context-%016llX", key.module, key.methTok, key.context);
+ printf(", value callConv-%08X retTypeClass-%016llX retTypeSigClass-%016llX retType-%u(%s) flags-%08X numArgs-%08X classInstCount-%08X classInd-%08X "
+ "methInstCount-%08X methInd-%08X args-%016llX cbSig-%08X pSig-%08X scope-%016llX token-%08X",
+ value.callConv,
+ value.retTypeClass,
+ value.retTypeSigClass,
+ value.retType,
+ toString((CorInfoType)value.retType),
+ value.flags,
+ value.numArgs,
+ value.sigInst_classInstCount,
+ value.sigInst_classInst_Index,
+ value.sigInst_methInstCount,
+ value.sigInst_methInst_Index,
+ value.args,
+ value.cbSig,
+ value.pSig,
+ value.scope,
+ value.token);
+}
+void MethodContext::repFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig)
+{
+ Agnostic_FindCallSiteSig key;
+ ZeroMemory(&key, sizeof(Agnostic_FindCallSiteSig)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ Agnostic_CORINFO_SIG_INFO value;
+
+ key.module = (DWORDLONG)module;
+ key.methTok = (DWORD)methTOK;
+ key.context = (DWORDLONG)context;
+
+ AssertCodeMsg(FindCallSiteSig != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %08X", (DWORD)key.methTok);
+ AssertCodeMsg(FindCallSiteSig->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %08X", (DWORD)key.methTok);
+ value = FindCallSiteSig->Get(key);
+
+ sig->callConv = (CorInfoCallConv)value.callConv;
+ sig->retTypeClass = (CORINFO_CLASS_HANDLE)value.retTypeClass;
+ sig->retTypeSigClass = (CORINFO_CLASS_HANDLE)value.retTypeSigClass;
+ sig->retType = (CorInfoType)value.retType;
+ sig->flags = (unsigned)value.flags;
+ sig->numArgs = (unsigned)value.numArgs;
+ sig->sigInst.classInstCount = (unsigned)value.sigInst_classInstCount;
+ sig->sigInst.classInst = (CORINFO_CLASS_HANDLE*)FindCallSiteSig->GetBuffer(value.sigInst_classInst_Index);
+ sig->sigInst.methInstCount = (unsigned)value.sigInst_methInstCount;
+ sig->sigInst.methInst = (CORINFO_CLASS_HANDLE*)FindCallSiteSig->GetBuffer(value.sigInst_methInst_Index);
+ sig->args = (CORINFO_ARG_LIST_HANDLE)value.args;
+ sig->cbSig = (unsigned int)value.cbSig;
+ sig->pSig = (PCCOR_SIGNATURE)FindCallSiteSig->GetBuffer(value.pSig);
+ sig->scope = (CORINFO_MODULE_HANDLE)value.scope;
+ sig->token = (mdToken)value.token;
+ DEBUG_REP(dmpFindCallSiteSig(key,value));
+}
+
+void MethodContext::recShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope, BOOL result)
+{
+ if (ShouldEnforceCallvirtRestriction == nullptr)
+ ShouldEnforceCallvirtRestriction = new LightWeightMap<DWORDLONG, DWORD>();
+ ShouldEnforceCallvirtRestriction->Add((DWORDLONG)scope, (DWORD)result);
+ DEBUG_REC(dmpShouldEnforceCallvirtRestriction((DWORDLONG)scope, (DWORD)result));
+}
+void MethodContext::dmpShouldEnforceCallvirtRestriction(DWORDLONG key, DWORD value)
+{
+ printf("ShouldEnforceCallvirtRestriction key %016llX, value %u", key, value);
+}
+BOOL MethodContext::repShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope)
+{
+ AssertCodeMsg(ShouldEnforceCallvirtRestriction != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)scope);
+ AssertCodeMsg(ShouldEnforceCallvirtRestriction->GetIndex((DWORDLONG)scope) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)scope);
+ BOOL temp = (BOOL)ShouldEnforceCallvirtRestriction->Get((DWORDLONG)scope);
+ DEBUG_REC(dmpShouldEnforceCallvirtRestriction((DWORDLONG)scope, (DWORD)temp));
+ return temp;
+}
+
+void MethodContext::recGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection, void* result)
+{
+ if (GetMethodSync == nullptr)
+ GetMethodSync = new LightWeightMap<DWORDLONG, DLDL>();
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetMethodSync->Add((DWORDLONG)ftn, value);
+}
+void MethodContext::dmpGetMethodSync(DWORDLONG key, DLDL value)
+{
+ printf("GetMethodSync key %016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection)
+{
+ DLDL value;
+
+ value = (DLDL)GetMethodSync->Get((DWORDLONG)ftn);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+
+ return (void*)value.B;
+}
+
+void MethodContext::recGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection, CORINFO_VARARGS_HANDLE result)
+{
+ if (GetVarArgsHandle == nullptr)
+ GetVarArgsHandle = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DLDL>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)pSig->cbSig;
+ key.pSig = (DWORD)GetVarArgsHandle->AddBuffer((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetVarArgsHandle->Add(key, value);
+}
+void MethodContext::dmpGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DLDL value)
+{
+ printf("GetVarArgsHandle key cbSig-%08X pSig-%08X scope-%016llX token-%08X",
+ key.cbSig,
+ key.pSig,
+ key.scope,
+ key.token);
+ printf(", value ppIndirection-%016llX result-%016llX", value.A, value.B);
+}
+CORINFO_VARARGS_HANDLE MethodContext::repGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)pSig->cbSig;
+ key.pSig = (DWORD)GetVarArgsHandle->Contains((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ value = (DLDL)GetVarArgsHandle->Get(key);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+
+ return (CORINFO_VARARGS_HANDLE)value.B;
+}
+
+void MethodContext::recCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig, bool result)
+{
+ if (CanGetVarArgsHandle == nullptr)
+ CanGetVarArgsHandle = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DWORD>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ CanGetVarArgsHandle->Add(key, (DWORD)result);
+ DEBUG_REC(dmpCanGetVarArgsHandle(key, (DWORD)result));
+}
+void MethodContext::dmpCanGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DWORD value)
+{
+ printf("CanGetVarArgsHandle key scope-%016llX token-%08X, value result-%08X", key.scope, key.token, value);
+}
+bool MethodContext::repCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)pSig->scope;
+ key.token = (DWORD)pSig->token;
+
+ AssertCodeMsg(CanGetVarArgsHandle != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)key.token);
+ AssertCodeMsg(CanGetVarArgsHandle->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.token);
+ bool value = CanGetVarArgsHandle->Get(key) != 0;
+ DEBUG_REP(dmpCanGetVarArgsHandle(key, (DWORD)value));
+ return value;
+}
+
+void MethodContext::recGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection, DWORD result)
+{
+ if (GetFieldThreadLocalStoreID == nullptr)
+ GetFieldThreadLocalStoreID = new LightWeightMap<DWORDLONG, DLD>();;
+
+ DLD value;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORD)result;
+
+ GetFieldThreadLocalStoreID->Add((DWORDLONG)field, value);
+}
+void MethodContext::dmpGetFieldThreadLocalStoreID(DWORDLONG key, DLD value)
+{
+ printf("GetFieldThreadLocalStoreID key field-%016llX, value ppIndirection-%016llX result-%08X", key, value.A, value.B);
+}
+DWORD MethodContext::repGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection)
+{
+ DLD value;
+ value = (DLD)GetFieldThreadLocalStoreID->Get((DWORDLONG)field);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+ return (DWORD)value.B;
+}
+
+void MethodContext::recGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns, HRESULT result)
+{
+ if (GetBBProfileData == nullptr)
+ GetBBProfileData = new LightWeightMap<DWORDLONG, Agnostic_GetBBProfileData>();
+
+ Agnostic_GetBBProfileData value;
+
+ value.count = (DWORD)*count;
+ value.profileBuffer_index = GetBBProfileData->AddBuffer((unsigned char*)*profileBuffer, sizeof(ICorJitInfo::ProfileBuffer)*(*count));
+ value.numRuns = (DWORD)*numRuns;
+ value.result = (DWORD)result;
+
+ GetBBProfileData->Add((DWORDLONG)ftnHnd, value);
+}
+void MethodContext::dmpGetBBProfileData(DWORDLONG key, const Agnostic_GetBBProfileData& value)
+{
+ printf("GetBBProfileData key ftn-%016llX, value cnt-%u profileBuf-", key, value.count);
+ ICorJitInfo::ProfileBuffer* pBuf = (ICorJitInfo::ProfileBuffer *)GetBBProfileData->GetBuffer(value.profileBuffer_index);
+ for (DWORD i = 0; i < value.count; i++, pBuf++)
+ {
+ printf("{il-%u,cnt-%u}", pBuf->ILOffset, pBuf->ExecutionCount);
+ }
+ GetBBProfileData->Unlock();
+ printf(" numRuns-%u result-%u", value.numRuns, value.result);
+}
+HRESULT MethodContext::repGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns)
+{
+ Agnostic_GetBBProfileData tempValue;
+
+ tempValue = GetBBProfileData->Get((DWORDLONG)ftnHnd);
+
+ *count = (ULONG)tempValue.count;
+ *profileBuffer = (ICorJitInfo::ProfileBuffer *)GetBBProfileData->GetBuffer(tempValue.profileBuffer_index);
+ *numRuns = (ULONG)tempValue.numRuns;
+ HRESULT result = (HRESULT)tempValue.result;
+ return result;
+}
+
+void MethodContext::recMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, CORINFO_CLASS_HANDLE result)
+{
+ if (MergeClasses == nullptr)
+ MergeClasses = new LightWeightMap<DLDL, DWORDLONG>();
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ MergeClasses->Add(key, (DWORDLONG)result);
+}
+void MethodContext::dmpMergeClasses(DLDL key, DWORDLONG value)
+{
+ printf("MergeClasses NYI");
+}
+CORINFO_CLASS_HANDLE MethodContext::repMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DWORDLONG value;
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ AssertCodeMsg(MergeClasses->GetIndex(key) != -1, EXCEPTIONCODE_MC,
+ "Didn't find %016llX %016llX", (DWORDLONG)cls1, (DWORDLONG)cls2);
+ value = MergeClasses->Get(key);
+
+ return (CORINFO_CLASS_HANDLE)value;
+}
+
+void MethodContext::recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection, LPVOID result)
+{
+ if (GetCookieForPInvokeCalliSig == nullptr)
+ GetCookieForPInvokeCalliSig = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DLDL>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)szMetaSig->cbSig;
+ key.pSig = (DWORD)GetCookieForPInvokeCalliSig->AddBuffer((unsigned char *)szMetaSig->pSig, szMetaSig->cbSig);
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ GetCookieForPInvokeCalliSig->Add(key, value);
+}
+void MethodContext::dmpGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DLDL value)
+{
+ printf("GetCookieForPInvokeCalliSig NYI");
+}
+LPVOID MethodContext::repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLDL value;
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)szMetaSig->cbSig;
+ key.pSig = (DWORD)GetCookieForPInvokeCalliSig->Contains((unsigned char *)szMetaSig->pSig, szMetaSig->cbSig);
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ value = (DLDL)GetCookieForPInvokeCalliSig->Get(key);
+
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void *)value.A;
+
+ return (CORINFO_VARARGS_HANDLE)value.B;
+}
+
+void MethodContext::recCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, bool result)
+{
+ if (CanGetCookieForPInvokeCalliSig == nullptr)
+ CanGetCookieForPInvokeCalliSig = new LightWeightMap<Agnostic_CORINFO_SIG_INFO, DWORD>();
+
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ CanGetCookieForPInvokeCalliSig->Add(key, (DWORD)result);
+}
+void MethodContext::dmpCanGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DWORD value)
+{
+ printf("CanGetCookieForPInvokeCalliSig key scope-%016llX token-%08X, value result-%08X", key.scope, key.token, value);
+}
+bool MethodContext::repCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig)
+{
+ Agnostic_CORINFO_SIG_INFO key;
+ ZeroMemory(&key, sizeof(Agnostic_CORINFO_SIG_INFO)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.callConv = (DWORD)0;
+ key.retTypeClass = (DWORDLONG)0;
+ key.retTypeSigClass = (DWORDLONG)0;
+ key.retType = (DWORD)0;
+ key.flags = (DWORD)0;
+ key.numArgs = (DWORD)0;
+ key.sigInst_classInstCount = (DWORD)0;
+ key.sigInst_classInst_Index = (DWORD)0;
+ key.sigInst_methInstCount = (DWORD)0;
+ key.sigInst_methInst_Index = (DWORD)0;
+ key.args = (DWORDLONG)0;
+ key.cbSig = (DWORD)0;
+ key.pSig = (DWORD)0;
+ key.scope = (DWORDLONG)szMetaSig->scope;
+ key.token = (DWORD)szMetaSig->token;
+
+ DWORD temp = CanGetCookieForPInvokeCalliSig->Get(key);
+ return temp != 0;
+}
+
+void MethodContext::recCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType, BOOL result)
+{
+ if (CanAccessFamily == nullptr)
+ CanAccessFamily = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)hCaller;
+ key.B = (DWORDLONG)hInstanceType;
+
+ CanAccessFamily->Add(key, (DWORD)result);
+}
+void MethodContext::dmpCanAccessFamily(DLDL key, DWORD value)
+{
+ printf("CanAccessFamily key cal-%016llX inst-%016llX, value %u", key.A, key.B, value);
+}
+BOOL MethodContext::repCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)hCaller;
+ key.B = (DWORDLONG)hInstanceType;
+
+ DWORD temp = CanAccessFamily->Get(key);
+ return (BOOL)temp;
+}
+
+void MethodContext::recErrorList(const char *error)
+{
+ if (ErrorList == nullptr)
+ ErrorList = new DenseLightWeightMap<DWORD>();
+
+ DWORD temp = (DWORD)-1;
+
+ if (error != nullptr)
+ temp = (DWORD)ErrorList->AddBuffer((unsigned char *)error, (DWORD)strlen(error) + 1);
+
+ ErrorList->Append(temp);
+}
+void MethodContext::dmpErrorList(DWORD key, DWORD value)
+{
+ printf("ErrorList NYI");
+}
+
+void MethodContext::recGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles)
+{
+ if (GetProfilingHandle == nullptr)
+ GetProfilingHandle = new LightWeightMap<DWORD, Agnostic_GetProfilingHandle>();
+
+ Agnostic_GetProfilingHandle value;
+ ZeroMemory(&value, sizeof(Agnostic_GetProfilingHandle)); //We use the input structs as a value and use memcmp to compare.. so we need to zero out padding too
+
+ value.bHookFunction = (DWORD)*pbHookFunction;
+ value.ProfilerHandle = (DWORDLONG)*pProfilerHandle;
+ value.bIndirectedHandles = (DWORD)*pbIndirectedHandles;
+ GetProfilingHandle->Add((DWORD)0, value);
+ DEBUG_REC(dmpGetProfilingHandle(0, value));
+}
+void MethodContext::dmpGetProfilingHandle(DWORD key, const Agnostic_GetProfilingHandle& value)
+{
+ printf("GetProfilingHandle key %u, value bHookFtn-%u profHnd-%016llX bIndHnd-%u",
+ key, value.bHookFunction, value.ProfilerHandle, value.bIndirectedHandles);
+}
+void MethodContext::repGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles)
+{
+ Agnostic_GetProfilingHandle value;
+
+ value = GetProfilingHandle->Get((DWORD)0);
+
+ *pbHookFunction = (BOOL)value.bHookFunction;
+ *pProfilerHandle = (void*)value.ProfilerHandle;
+ *pbIndirectedHandles = (BOOL)value.bIndirectedHandles;
+ DEBUG_REP(dmpGetProfilingHandle(0, value));
+}
+
+void MethodContext::recEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection, CORINFO_FIELD_HANDLE result)
+{
+ if (EmbedFieldHandle == nullptr)
+ EmbedFieldHandle = new LightWeightMap<DWORDLONG, DLDL>();
+
+ DLDL value;
+ if (ppIndirection != nullptr)
+ value.A = (DWORDLONG)*ppIndirection;
+ else
+ value.A = (DWORDLONG)0;
+ value.B = (DWORDLONG)result;
+
+ EmbedFieldHandle->Add((DWORDLONG)handle, value);
+}
+void MethodContext::dmpEmbedFieldHandle(DWORDLONG key, DLDL value)
+{
+ printf("EmbedFieldHandle NYI");
+}
+CORINFO_FIELD_HANDLE MethodContext::repEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection)
+{
+ DLDL value;
+
+ value = EmbedFieldHandle->Get((DWORDLONG)handle);
+ if (ppIndirection != nullptr)
+ *ppIndirection = (void*)value.A;
+ return (CORINFO_FIELD_HANDLE)value.B;
+}
+
+void MethodContext::recAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, BOOL result)
+{
+ if (AreTypesEquivalent == nullptr)
+ AreTypesEquivalent = new LightWeightMap<DLDL, DWORD>();
+
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ AreTypesEquivalent->Add(key, (DWORD)result);
+}
+void MethodContext::dmpAreTypesEquivalent(DLDL key, DWORD value)
+{
+ printf("AreTypesEquivalent NYI");
+}
+BOOL MethodContext::repAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2)
+{
+ DLDL key;
+ ZeroMemory(&key, sizeof(DLDL)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)cls1;
+ key.B = (DWORDLONG)cls2;
+
+ AssertCodeMsg(AreTypesEquivalent->GetIndex(key) != -1, EXCEPTIONCODE_MC,
+ "Didn't find %016llX %016llX", (DWORDLONG)cls1, (DWORDLONG)cls2);
+ BOOL value = (BOOL)AreTypesEquivalent->Get(key);
+ return value;
+}
+
+void MethodContext::recFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity, size_t result)
+{
+ if (FindNameOfToken == nullptr)
+ FindNameOfToken = new LightWeightMap<DLD, DLD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ value.A = result;
+ value.B = FindNameOfToken->AddBuffer((unsigned char*)szFQName, (unsigned int)result);
+
+ FindNameOfToken->Add(key, value);
+ DEBUG_REC(dmpFindNameOfToken(key, value));
+}
+void MethodContext::dmpFindNameOfToken(DLD key, DLD value)
+{
+ //practically the name of a token wont be bigger than 4gb...
+ unsigned char *buff = new unsigned char[(unsigned int)value.A + 1];
+ ZeroMemory(buff, (unsigned int)value.A + 1);
+ memcpy(buff, FindNameOfToken->GetBuffer(value.B), (unsigned int)value.A);
+ FindNameOfToken->Unlock();
+ printf("FindNameOfToken key mod-%016llX tok-%08X, value '%s'", key.A, key.B, buff);
+ delete[]buff;
+}
+size_t MethodContext::repFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ DLD value;
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+
+ value = FindNameOfToken->Get(key);
+
+ unsigned char* temp = nullptr;
+ if (value.B != (DWORD)-1)
+ {
+ temp = FindNameOfToken->GetBuffer(value.B);
+ memcpy(szFQName, temp, (size_t)value.A);
+ }
+
+ DEBUG_REP(dmpFindNameOfToken(key, value));
+ return (size_t)value.A;
+}
+
+void MethodContext::recGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr, bool result)
+{
+ if (GetSystemVAmd64PassStructInRegisterDescriptor == nullptr)
+ GetSystemVAmd64PassStructInRegisterDescriptor = new LightWeightMap<DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor>();
+
+ DWORDLONG key;
+ Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor value;
+
+ key = (DWORDLONG)structHnd;
+
+ value.passedInRegisters = (DWORD)structPassInRegDescPtr->passedInRegisters;
+ value.eightByteCount = (DWORD)structPassInRegDescPtr->eightByteCount;
+ for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
+ {
+ value.eightByteClassifications[i] = (DWORD)structPassInRegDescPtr->eightByteClassifications[i];
+ value.eightByteSizes[i] = (DWORD)structPassInRegDescPtr->eightByteSizes[i];
+ value.eightByteOffsets[i] = (DWORD)structPassInRegDescPtr->eightByteOffsets[i];
+ }
+ value.result = result ? 1 : 0;
+
+ GetSystemVAmd64PassStructInRegisterDescriptor->Add(key, value);
+ DEBUG_REC(dmpGetSystemVAmd64PassStructInRegisterDescriptor(key, value));
+}
+void MethodContext::dmpGetSystemVAmd64PassStructInRegisterDescriptor(DWORDLONG key, const Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor& value)
+{
+ printf("GetSystemVAmd64PassStructInRegisterDescriptor key structHnd-%016llX, value passInReg-%u 8bCount-%u", key, value.passedInRegisters, value.eightByteCount);
+ for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
+ {
+ printf(" 8bClass[%u]-%u 8bSz[%u]-%u 8bOff[%u]-%u",
+ i, value.eightByteClassifications[i],
+ i, value.eightByteSizes[i],
+ i, value.eightByteOffsets[i]);
+ }
+ printf(" result %u", value.result);
+}
+bool MethodContext::repGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
+{
+ DWORDLONG key;
+ Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor value;
+
+ key = (DWORDLONG)structHnd;
+
+ value = GetSystemVAmd64PassStructInRegisterDescriptor->Get(key);
+
+ structPassInRegDescPtr->passedInRegisters = value.passedInRegisters ? true : false;
+ structPassInRegDescPtr->eightByteCount = (unsigned __int8)value.eightByteCount;
+ for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
+ {
+ structPassInRegDescPtr->eightByteClassifications[i] = (SystemVClassificationType)value.eightByteClassifications[i];
+ structPassInRegDescPtr->eightByteSizes[i] = (unsigned __int8)value.eightByteSizes[i];
+ structPassInRegDescPtr->eightByteOffsets[i] = (unsigned __int8)value.eightByteOffsets[i];
+ }
+
+ DEBUG_REP(dmpGetSystemVAmd64PassStructInRegisterDescriptor(key, value));
+ return value.result ? true : false;
+}
+
+void MethodContext::recGetRelocTypeHint(void * target, WORD result)
+{
+ if (GetRelocTypeHint == nullptr)
+ GetRelocTypeHint = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetRelocTypeHint->Add((DWORDLONG)target, (DWORD)result);
+ DEBUG_REC(dmpGetRelocTypeHint((DWORDLONG)target, (DWORD)result));
+}
+void MethodContext::dmpGetRelocTypeHint(DWORDLONG key, DWORD value)
+{
+ printf("GetRelocTypeHint key tgt-%016llX, value hint-%u", key, value);
+}
+WORD MethodContext::repGetRelocTypeHint(void * target)
+{
+ if (GetRelocTypeHint == nullptr)
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetRelocTypeHint yielding fake answer...");
+ return 65535;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)target);
+#endif
+ }
+ if (GetRelocTypeHint->GetIndex((DWORDLONG)target) == -1)
+ {
+ void *origAddr = cr->repAddressMap((void *)target);
+ if (origAddr != (void*)-1 && origAddr != nullptr)
+ {
+ if (GetRelocTypeHint->GetIndex((DWORDLONG)origAddr) == -1)
+ target = origAddr;
+ }
+ else
+ {
+#ifdef sparseMC
+ LogDebug("Sparse - repGetRelocTypeHint yielding fake answer...");
+ return 65535;
+#else
+ LogException(EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)target);
+#endif
+ }
+ }
+
+ int index = GetRelocTypeHint->GetIndex((DWORDLONG)target);
+ WORD retVal = 0;
+ if (index == -1)
+ {
+ void *subtarget = cr->searchAddressMap(target);
+
+ int index2 = GetRelocTypeHint->GetIndex((DWORDLONG)subtarget);
+ if (index2 == -1)
+ {
+ // __debugbreak(); // seems like a source of pain
+ retVal = IMAGE_REL_BASED_REL32;
+ }
+ else
+ retVal = (WORD)GetRelocTypeHint->Get((DWORDLONG)subtarget);
+ }
+ else
+ retVal = (WORD)GetRelocTypeHint->Get((DWORDLONG)target);
+ DEBUG_REP(dmpGetRelocTypeHint((DWORDLONG)target, retVal));
+ return retVal;
+
+}
+
+void MethodContext::recIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field, bool result)
+{
+ if (IsWriteBarrierHelperRequired == nullptr)
+ IsWriteBarrierHelperRequired = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsWriteBarrierHelperRequired->Add((DWORDLONG)field, (DWORD)result);
+ DEBUG_REC(dmpIsWriteBarrierHelperRequired((DWORDLONG)field, (DWORD)result));
+}
+void MethodContext::dmpIsWriteBarrierHelperRequired(DWORDLONG key, DWORD value)
+{
+ printf("IsWriteBarrierHelperRequired key fld-%016llX, value res-%u", key, value);
+}
+bool MethodContext::repIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field)
+{
+ bool result = IsWriteBarrierHelperRequired->Get((DWORDLONG)field) != 0;
+ DEBUG_REP(dmpIsWriteBarrierHelperRequired((DWORDLONG)field, result));
+ return result;
+}
+
+void MethodContext::recIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result)
+{
+ if (IsValidToken == nullptr)
+ IsValidToken = new LightWeightMap<DLD, DWORD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+ IsValidToken->Add(key, (DWORD)result);
+}
+void MethodContext::dmpIsValidToken(DLD key, DWORD value)
+{
+ printf("IsValidToken key mod-%016llX tok-%08X, value res-%u", key.A, key.B, value);
+}
+BOOL MethodContext::repIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK)
+{
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.A = (DWORDLONG)module;
+ key.B = (DWORD)metaTOK;
+ BOOL value = (BOOL)IsValidToken->Get(key);
+ return value;
+}
+
+void MethodContext::recGetClassName(CORINFO_CLASS_HANDLE cls, const char* result)
+{
+ if (GetClassName == nullptr)
+ GetClassName = new LightWeightMap<DWORDLONG, DWORD>();
+
+ DWORD temp = (DWORD)-1;
+ if (result != nullptr)
+ temp = (DWORD)GetClassName->AddBuffer((unsigned char*)result, (unsigned int)strlen(result) + 1);
+
+ GetClassName->Add((DWORDLONG)cls, (DWORD)temp);
+ DEBUG_REC(dmpGetClassName((DWORDLONG)cls, (DWORD)temp));
+}
+void MethodContext::dmpGetClassName(DWORDLONG key, DWORD value)
+{
+ printf("GetClassName key %016llX, value %s", key, GetClassName->GetBuffer(value));
+ GetClassName->Unlock();
+}
+const char* MethodContext::repGetClassName(CORINFO_CLASS_HANDLE cls)
+{
+ if (GetClassName == nullptr)
+ return "hackishClassName";
+ int index = GetClassName->GetIndex((DWORDLONG)cls);
+ if (index == -1)
+ return "hackishClassName";
+ int offset = GetClassName->Get((DWORDLONG)cls);
+ const char* name = (const char*)GetClassName->GetBuffer(offset);
+ DEBUG_REC(dmpGetClassName((DWORDLONG)cls, (DWORD)offset));
+ return name;
+}
+
+void MethodContext::recAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly, const WCHAR* result)
+{
+ if (AppendClassName == nullptr)
+ AppendClassName = new LightWeightMap<Agnostic_AppendClassName, DWORD>();
+
+ Agnostic_AppendClassName key;
+ ZeroMemory(&key, sizeof(Agnostic_AppendClassName)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.classHandle = (DWORDLONG) cls;
+ key.fNamespace = fNamespace;
+ key.fFullInst = fFullInst;
+ key.fAssembly = fAssembly;
+
+ DWORD temp = (DWORD)-1;
+ if (result != nullptr)
+ temp = (DWORD)AppendClassName->AddBuffer((unsigned char*)result, (unsigned int)((wcslen(result) * 2) + 2));
+
+ AppendClassName->Add(key, (DWORD)temp);
+ DEBUG_REC(dmpAppendClassName(key, (DWORD)temp));
+}
+
+void MethodContext::dmpAppendClassName(const Agnostic_AppendClassName& key, DWORD value)
+{
+ printf("AppendClassName key cls-%016llX ns-%u fi-%u as-%u, value %s",
+ key.classHandle, key.fNamespace, key.fFullInst, key.fAssembly, AppendClassName->GetBuffer(value));
+ AppendClassName->Unlock();
+}
+
+const WCHAR* MethodContext::repAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly)
+{
+ if (AppendClassName == nullptr)
+ return W("hackishClassName");
+
+ Agnostic_AppendClassName key;
+ ZeroMemory(&key, sizeof(Agnostic_AppendClassName)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+ key.classHandle = (DWORDLONG) cls;
+ key.fNamespace = fNamespace;
+ key.fFullInst = fFullInst;
+ key.fAssembly = fAssembly;
+
+ int index = AppendClassName->GetIndex(key);
+ if (index == -1)
+ return W("hackishClassName");
+ int offset = AppendClassName->Get(key);
+ const WCHAR* name = (const WCHAR*)AppendClassName->GetBuffer(offset);
+ DEBUG_REC(dmpAppendClassName(key, (DWORD)offset));
+ return name;
+}
+
+void MethodContext::recGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags, void* result)
+{
+ if (GetTailCallCopyArgsThunk == nullptr)
+ GetTailCallCopyArgsThunk = new LightWeightMap<Agnostic_GetTailCallCopyArgsThunk, DWORDLONG>();
+
+ Agnostic_GetTailCallCopyArgsThunk key;
+ ZeroMemory(&key, sizeof(Agnostic_GetTailCallCopyArgsThunk)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.Sig.callConv = (DWORD)pSig->callConv;
+ key.Sig.retTypeClass = (DWORDLONG)pSig->retTypeClass;
+ key.Sig.retTypeSigClass = (DWORDLONG)pSig->retTypeSigClass;
+ key.Sig.retType = (DWORD)pSig->retType;
+ key.Sig.flags = (DWORD)pSig->flags;
+ key.Sig.numArgs = (DWORD)pSig->numArgs;
+ key.Sig.sigInst_classInstCount = (DWORD)pSig->sigInst.classInstCount;
+ key.Sig.sigInst_classInst_Index = (DWORD)GetTailCallCopyArgsThunk->AddBuffer((unsigned char*)pSig->sigInst.classInst, pSig->sigInst.classInstCount * 8); //porting issue
+ key.Sig.sigInst_methInstCount = (DWORD)pSig->sigInst.methInstCount;
+ key.Sig.sigInst_methInst_Index = (DWORD)GetTailCallCopyArgsThunk->AddBuffer((unsigned char*)pSig->sigInst.methInst, pSig->sigInst.methInstCount * 8); //porting issue
+ key.Sig.args = (DWORDLONG)pSig->args;
+ key.Sig.cbSig = (DWORD)pSig->cbSig;
+ key.Sig.pSig = (DWORD)GetTailCallCopyArgsThunk->AddBuffer((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.Sig.scope = (DWORDLONG)pSig->scope;
+ key.Sig.token = (DWORD)pSig->token;
+ key.flags = (DWORD)flags;
+
+ GetTailCallCopyArgsThunk->Add(key, (DWORDLONG)result);
+ DEBUG_REC(dmpGetTailCallCopyArgsThunk(key, (DWORDLONG)result));
+}
+void MethodContext::dmpGetTailCallCopyArgsThunk(const Agnostic_GetTailCallCopyArgsThunk& key, DWORDLONG value)
+{
+ printf("GetTailCallCopyArgsThunk key sig{cc-%u rtc-%016llX rts-%016llX rt-%u flg-%08X na-%u cc-%u ci-%u mc-%u mi-%u sig-%u pSig-%u scp-%016llX tok-%08X} flg-%08X",
+ key.Sig.callConv,
+ key.Sig.retTypeClass,
+ key.Sig.retTypeSigClass,
+ key.Sig.retType,
+ key.Sig.flags,
+ key.Sig.numArgs,
+ key.Sig.sigInst_classInstCount,
+ key.Sig.sigInst_classInst_Index,
+ key.Sig.sigInst_methInstCount,
+ key.Sig.sigInst_methInst_Index,
+ key.Sig.cbSig,
+ key.Sig.pSig,
+ key.Sig.scope,
+ key.Sig.token,
+ key.flags);
+ printf(", value res-%016llX", value);
+}
+void* MethodContext::repGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags)
+{
+ Agnostic_GetTailCallCopyArgsThunk key;
+ ZeroMemory(&key, sizeof(Agnostic_GetTailCallCopyArgsThunk)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ AssertCodeMsg(GetTailCallCopyArgsThunk != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for ...");
+ key.Sig.callConv = (DWORD)pSig->callConv;
+ key.Sig.retTypeClass = (DWORDLONG)pSig->retTypeClass;
+ key.Sig.retTypeSigClass = (DWORDLONG)pSig->retTypeSigClass;
+ key.Sig.retType = (DWORD)pSig->retType;
+ key.Sig.flags = (DWORD)pSig->flags;
+ key.Sig.numArgs = (DWORD)pSig->numArgs;
+ key.Sig.sigInst_classInstCount = (DWORD)pSig->sigInst.classInstCount;
+ key.Sig.sigInst_classInst_Index = (DWORD)GetTailCallCopyArgsThunk->Contains((unsigned char*)pSig->sigInst.classInst, pSig->sigInst.classInstCount * 8); //porting issue
+ key.Sig.sigInst_methInstCount = (DWORD)pSig->sigInst.methInstCount;
+ key.Sig.sigInst_methInst_Index = (DWORD)GetTailCallCopyArgsThunk->Contains((unsigned char*)pSig->sigInst.methInst, pSig->sigInst.methInstCount * 8); //porting issue
+ key.Sig.args = (DWORDLONG)pSig->args;
+ key.Sig.cbSig = (DWORD)pSig->cbSig;
+ key.Sig.pSig = (DWORD)GetTailCallCopyArgsThunk->Contains((unsigned char *)pSig->pSig, pSig->cbSig);
+ key.Sig.scope = (DWORDLONG)pSig->scope;
+ key.Sig.token = (DWORD)pSig->token;
+ key.flags = (DWORD)flags;
+
+ AssertCodeMsg(GetTailCallCopyArgsThunk->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)key.Sig.retTypeClass);
+ void *result = (void*)GetTailCallCopyArgsThunk->Get(key);
+ cr->recAddressMap((void*)0x424242, (void*)result, 1);
+ DEBUG_REP(dmpGetTailCallCopyArgsThunk(key, (DWORDLONG)result));
+ return result;
+}
+
+void MethodContext::recGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod, mdMethodDef result)
+{
+ if (GetMethodDefFromMethod == nullptr)
+ GetMethodDefFromMethod = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetMethodDefFromMethod->Add((DWORDLONG)hMethod, (DWORD)result);
+}
+void MethodContext::dmpGetMethodDefFromMethod(DWORDLONG key, DWORD value)
+{
+ printf("GetMethodDefFromMethod key ftn-%016llX, value res-%u", key, value);
+}
+mdMethodDef MethodContext::repGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod)
+{
+ // Since this is diagnostic, fake up a result if one wasn't recorded.
+ if (GetMethodDefFromMethod == nullptr)
+ return (mdMethodDef)0x06000000;
+
+ int index = GetMethodDefFromMethod->GetIndex((DWORDLONG)hMethod);
+ if (index < 0)
+ return (mdMethodDef)0x06000001;
+
+ return (mdMethodDef)GetMethodDefFromMethod->Get((DWORDLONG)hMethod);
+}
+
+void MethodContext::recCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional, BOOL result)
+{
+ if (CheckMethodModifier == nullptr)
+ CheckMethodModifier = new LightWeightMap<Agnostic_CheckMethodModifier, DWORD>();
+
+ Agnostic_CheckMethodModifier key;
+ ZeroMemory(&key, sizeof(Agnostic_CheckMethodModifier)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.hMethod = (DWORDLONG)hMethod;
+ //If the input matches something already in the buffer, just re-use that slot.. easier than searching for a soft key on rep.
+ if (modifier != nullptr)
+ key.modifier = (DWORD)CheckMethodModifier->AddBuffer((unsigned char*)modifier, (unsigned int)strlen(modifier) + 1);
+ else
+ key.modifier = (DWORD)-1;
+
+ key.fOptional = (DWORD)fOptional;
+
+ CheckMethodModifier->Add(key, (DWORD)result);
+}
+void MethodContext::dmpCheckMethodModifier(const Agnostic_CheckMethodModifier& key, DWORD value)
+{
+ printf("CheckMethodModifier key, ftn-%016llX mod-'%s' opt-%u, value res-%u", key.hMethod,
+ (unsigned char *)CheckMethodModifier->GetBuffer(key.modifier), key.fOptional, value);
+ CheckMethodModifier->Unlock();
+}
+BOOL MethodContext::repCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional)
+{
+ Agnostic_CheckMethodModifier key;
+ ZeroMemory(&key, sizeof(Agnostic_CheckMethodModifier)); //We use the input structs as a key and use memcmp to compare.. so we need to zero out padding too
+
+ key.hMethod = (DWORDLONG)hMethod;
+ if (modifier != nullptr)
+ key.modifier = (DWORD)CheckMethodModifier->Contains((unsigned char *)modifier, (unsigned int)strlen(modifier) + 1);
+ else
+ key.modifier = (DWORD)-1;
+
+ key.fOptional = (DWORD)fOptional;
+
+ BOOL value = (BOOL)CheckMethodModifier->Get(key);
+ return value;
+}
+
+void MethodContext::recGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result)
+{
+ if (GetPInvokeUnmanagedTarget == nullptr)
+ GetPInvokeUnmanagedTarget = new LightWeightMap<DWORDLONG, DLDL>();
+ DLDL temp;
+ temp.A = (DWORDLONG)*ppIndirection;
+ temp.B = (DWORDLONG)result;
+
+ GetPInvokeUnmanagedTarget->Add((DWORDLONG)method, temp);
+ DEBUG_REC(dmpGetPInvokeUnmanagedTarget((DWORDLONG)method, temp));
+}
+void MethodContext::dmpGetPInvokeUnmanagedTarget(DWORDLONG key, DLDL value)
+{
+ printf("GetPInvokeUnmanagedTarget key ftn-%016llX, value pp-%016llX res-%016llX", key, value.A, value.B);
+}
+void* MethodContext::repGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection)
+{
+ DLDL temp = (DLDL)GetPInvokeUnmanagedTarget->Get((DWORDLONG)method);
+ *ppIndirection = (void *)temp.A;
+ DEBUG_REP(dmpGetPInvokeUnmanagedTarget((DWORDLONG)method, temp));
+
+ return (void *)temp.B;
+}
+
+void MethodContext::recGetArrayRank(CORINFO_CLASS_HANDLE cls, unsigned result)
+{
+ if (GetArrayRank == nullptr)
+ GetArrayRank = new LightWeightMap<DWORDLONG, DWORD>();
+
+ GetArrayRank->Add((DWORDLONG)cls, (DWORD)result);
+}
+void MethodContext::dmpGetArrayRank(DWORDLONG key, DWORD value)
+{
+ printf("GetArrayRank key %016llX, value %u", key, value);
+}
+unsigned MethodContext::repGetArrayRank(CORINFO_CLASS_HANDLE cls)
+{
+ return (unsigned)GetArrayRank->Get((DWORDLONG)cls);
+}
+
+void MethodContext::recIsFieldStatic(CORINFO_FIELD_HANDLE fhld, bool result)
+{
+ if (IsFieldStatic == nullptr)
+ IsFieldStatic = new LightWeightMap<DWORDLONG, DWORD>();
+
+ IsFieldStatic->Add((DWORDLONG)fhld, (DWORD)result);
+ DEBUG_REC(dmpIsFieldStatic((DWORDLONG)fhld, (DWORD)result));
+}
+void MethodContext::dmpIsFieldStatic(DWORDLONG key, DWORD value)
+{
+ printf("IsFieldStatic key %016llX, value %u", key, value);
+}
+bool MethodContext::repIsFieldStatic(CORINFO_FIELD_HANDLE fhld)
+{
+ AssertCodeMsg(IsFieldStatic != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)fhld);
+ AssertCodeMsg(IsFieldStatic->GetIndex((DWORDLONG)fhld) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)fhld);
+ bool result = (bool)(IsFieldStatic->Get((DWORDLONG)fhld) != 0);
+ DEBUG_REP(dmpIsFieldStatic((DWORDLONG)fhld, (DWORD)result));
+ return result;
+}
+
+void MethodContext::recGetIntConfigValue(const wchar_t *name, int defaultValue, int result)
+{
+ if (GetIntConfigValue == nullptr)
+ GetIntConfigValue = new LightWeightMap<Agnostic_ConfigIntInfo, DWORD>();
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ Agnostic_ConfigIntInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_ConfigIntInfo));
+
+ DWORD index = (DWORD)GetIntConfigValue->AddBuffer((unsigned char*)name, sizeof(wchar_t) * ((unsigned int)wcslen(name) + 1));
+
+ key.nameIndex = index;
+ key.defaultValue = defaultValue;
+
+ GetIntConfigValue->Add(key, result);
+ DEBUG_REC(dmpGetIntConfigValue(key, result));
+}
+
+void MethodContext::dmpGetIntConfigValue(const Agnostic_ConfigIntInfo& key, int value)
+{
+ const wchar_t *name = (const wchar_t*)GetIntConfigValue->GetBuffer(key.nameIndex);
+ printf("GetIntConfigValue name %S, default value %d, value %d",
+ name, key.defaultValue, value);
+ GetIntConfigValue->Unlock();
+}
+
+int MethodContext::repGetIntConfigValue(const wchar_t *name, int defaultValue)
+{
+ if (GetIntConfigValue == nullptr)
+ return defaultValue;
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ Agnostic_ConfigIntInfo key;
+ ZeroMemory(&key, sizeof(Agnostic_ConfigIntInfo));
+
+ size_t nameLenInBytes = sizeof(wchar_t) * (wcslen(name) + 1);
+ int nameIndex = GetIntConfigValue->Contains((unsigned char *)name, (unsigned int)nameLenInBytes);
+ if (nameIndex == -1) // config name not in map
+ return defaultValue;
+
+ key.nameIndex = (DWORD)nameIndex;
+ key.defaultValue = defaultValue;
+
+ DWORD result = GetIntConfigValue->Get(key);
+ DEBUG_REP(dmpGetIntConfigValue(key, result));
+ return (int)result;
+}
+
+void MethodContext::recGetStringConfigValue(const wchar_t *name, const wchar_t *result)
+{
+ if (GetStringConfigValue == nullptr)
+ GetStringConfigValue = new LightWeightMap<DWORD, DWORD>();
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ DWORD nameIndex = (DWORD)GetStringConfigValue->AddBuffer((unsigned char*)name, sizeof(wchar_t) * ((unsigned int)wcslen(name) + 1));
+
+ DWORD resultIndex = (DWORD)-1;
+ if (result != nullptr)
+ resultIndex = (DWORD)GetStringConfigValue->AddBuffer((unsigned char*)result, sizeof(wchar_t) * ((unsigned int)wcslen(result) + 1));
+
+ GetStringConfigValue->Add(nameIndex, resultIndex);
+ DEBUG_REC(dmpGetStringConfigValue(nameIndex, resultIndex));
+}
+
+void MethodContext::dmpGetStringConfigValue(DWORD nameIndex, DWORD resultIndex)
+{
+ const wchar_t *name = (const wchar_t*)GetStringConfigValue->GetBuffer(nameIndex);
+ const wchar_t *result = (const wchar_t*)GetStringConfigValue->GetBuffer(resultIndex);
+ printf("GetStringConfigValue name %S, result %S", name, result);
+ GetStringConfigValue->Unlock();
+}
+
+const wchar_t *MethodContext::repGetStringConfigValue(const wchar_t *name)
+{
+ if (GetStringConfigValue == nullptr)
+ return nullptr;
+
+ AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");
+
+ size_t nameLenInBytes = sizeof(wchar_t) * (wcslen(name) + 1);
+ int nameIndex = GetStringConfigValue->Contains((unsigned char *)name, (unsigned int)nameLenInBytes);
+ if (nameIndex == -1) // config name not in map
+ return nullptr;
+
+ int resultIndex = GetStringConfigValue->Get(nameIndex);
+ const wchar_t *value = (const wchar_t *)GetStringConfigValue->GetBuffer(resultIndex);
+
+ DEBUG_REP(dmpGetStringConfigValue(nameIndex, resultIndex));
+
+ return value;
+}
+
+struct EnvironmentVariable
+{
+ char *name;
+ DWORD val_index;
+};
+int __cdecl compareEnvironmentVariable(const void *arg1, const void *arg2)
+{
+ return _stricmp(((EnvironmentVariable *)arg1)->name, ((EnvironmentVariable *)arg2)->name);
+}
+int MethodContext::dumpMethodIdentityInfoToBuffer(char *buff, int len)
+{
+ char *obuff = buff;
+
+ if (len < METHOD_IDENTITY_INFO_SIZE)
+ return -1;
+
+ //Obtain the Method Info structure for this method
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+
+ repCompileMethod(&info, &flags);
+
+ //Add the Method Signature
+ int t = sprintf_s(buff, len, "%s -- ", CallUtils::GetMethodFullName(this, info.ftn, info.args));
+ buff += t;
+ len -= t;
+
+ //Add Calling convention information, CorInfoOptions and CorInfoRegionKind
+ t = sprintf_s(buff, len, "CallingConvention: %d, CorInfoOptions: %d, CorInfoRegionKind: %d ",
+ info.args.callConv, info.options, info.regionKind);
+ buff += t;
+ len -= t;
+
+ //Add COMPLUS_* & dbflag environment variables to method Identity
+ //except complus_version and complus_defaultversion
+ //since they change the compilation behaviour of JIT
+ //we also need to sort them to ensure we don't produce a different
+ //hash based on the order of these variables
+ if (Environment != nullptr)
+ {
+ Agnostic_Environment val;
+ EnvironmentVariable *envValues = new EnvironmentVariable[Environment->GetCount()];
+ int envValCount = 0;
+
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ val = Environment->Get((DWORD)i);
+ char *envVariable = (char *)Environment->GetBuffer(val.name_index);
+ if ((_strnicmp(envVariable, "complus_", 8) == 0 || _strnicmp(envVariable, "dbflag", 6) == 0) &&
+ (_stricmp(envVariable, "complus_version") != 0) &&
+ (_stricmp(envVariable, "complus_defaultversion") != 0))
+ {
+ envValues[envValCount].name = envVariable;
+ envValues[envValCount++].val_index = val.val_index;
+ }
+ }
+
+ //Do a quick sort on envValues if needed
+ if (envValCount > 1)
+ qsort(envValues, envValCount, sizeof(EnvironmentVariable), compareEnvironmentVariable);
+
+ //Append these values to the IdentityInfobuffer
+ for (int i = 0; i < envValCount; i++)
+ {
+ t = sprintf_s(buff, len, "%s=%s ", _strlwr(envValues[i].name),
+ _strlwr((char *)Environment->GetBuffer(envValues[i].val_index)));
+ buff += t;
+ len -= t;
+ }
+
+ delete[] envValues;
+ }
+
+ //Hash the IL Code for this method and append it to the ID info
+ char ilHash[MD5_HASH_BUFFER_SIZE];
+ dumpMD5HashToBuffer(info.ILCode, info.ILCodeSize, ilHash, MD5_HASH_BUFFER_SIZE);
+ t = sprintf_s(buff, len, "ILCode Hash: %s", ilHash);
+ buff += t;
+ len -= t;
+
+ return (int)(buff - obuff);
+}
+int MethodContext::dumpMethodMD5HashToBuffer(char *buff, int len)
+{
+ char bufferIdentityInfo[METHOD_IDENTITY_INFO_SIZE];
+
+ int cbLen = dumpMethodIdentityInfoToBuffer(bufferIdentityInfo, METHOD_IDENTITY_INFO_SIZE);
+
+ if (cbLen < 0)
+ return cbLen;
+
+ cbLen = dumpMD5HashToBuffer((BYTE *)bufferIdentityInfo, cbLen, buff, len);
+
+ return cbLen;
+}
+
+int MethodContext::dumpMD5HashToBuffer(BYTE *pBuffer, int bufLen, char *hash, int hashLen)
+{
+#ifdef FEATURE_PAL
+
+ MD5HASHDATA md5_hashdata;
+ MD5 md5_hasher;
+
+ if (hashLen < MD5_HASH_BUFFER_SIZE)
+ return -1;
+
+ md5_hasher.Hash(pBuffer, (ULONG)bufLen, &md5_hashdata);
+
+ DWORD md5_hashdata_size = sizeof(md5_hashdata.rgb) / sizeof(BYTE);
+ Assert(md5_hashdata_size == MD5_HASH_BYTE_SIZE);
+
+ for (DWORD i = 0; i < md5_hashdata_size; i++)
+ {
+ sprintf_s(hash + i * 2, hashLen - i * 2, "%02X", md5_hashdata.rgb[i]);
+ }
+
+ return MD5_HASH_BUFFER_SIZE; //if we had success we wrote MD5_HASH_BUFFER_SIZE bytes to the buffer
+
+#else // !FEATURE_PAL
+
+ HCRYPTPROV hProv = NULL; //CryptoProvider
+ HCRYPTHASH hHash = NULL;
+ BYTE bHash[MD5_HASH_BYTE_SIZE];
+ DWORD cbHash = MD5_HASH_BYTE_SIZE;
+
+ if (hashLen < MD5_HASH_BUFFER_SIZE)
+ return -1;
+
+ // Get handle to the crypto provider
+ if (!CryptAcquireContextA(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ goto OnError;
+
+ if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
+ goto OnError;
+
+ if (!CryptHashData(hHash, pBuffer, bufLen, 0))
+ goto OnError;
+
+ if (!CryptGetHashParam(hHash, HP_HASHVAL, bHash, &cbHash, 0))
+ goto OnError;
+
+ if (cbHash != MD5_HASH_BYTE_SIZE)
+ goto OnError;
+
+ for (DWORD i = 0; i < MD5_HASH_BYTE_SIZE; i++)
+ {
+ sprintf_s(hash + i * 2, hashLen - i * 2, "%02X", bHash[i]);
+ }
+
+ if (hHash != NULL)
+ CryptDestroyHash(hHash);
+ if (hProv != NULL)
+ CryptReleaseContext(hProv, 0);
+
+ return MD5_HASH_BUFFER_SIZE; //if we had success we wrote MD5_HASH_BUFFER_SIZE bytes to the buffer
+
+OnError:
+ AssertMsg(false, "Failed to create a hash using the Crypto API (Error %X)", GetLastError());
+
+ if (hHash != NULL)
+ CryptDestroyHash(hHash);
+ if (hProv != NULL)
+ CryptReleaseContext(hProv, 0);
+ return -1;
+
+#endif // !FEATURE_PAL
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
new file mode 100644
index 0000000000..5869c85b45
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
@@ -0,0 +1,1179 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContext.h - Primary structure to store all the EE-JIT details required to replay creation of a method
+// CompileResult contains the stuff generated by a compilation
+//----------------------------------------------------------
+#ifndef _MethodContext
+#define _MethodContext
+
+#include "runtimedetails.h"
+#include "compileresult.h"
+#include "lightweightmap.h"
+#include "errorhandling.h"
+
+#define METHOD_IDENTITY_INFO_SIZE 0x10000 //We assume that the METHOD_IDENTITY_INFO_SIZE will not exceed 64KB
+
+#define MD5_HASH_BYTE_SIZE 16 //MD5 is 128-bit, so we need 16 bytes to store it
+#define MD5_HASH_BUFFER_SIZE 33 //MD5 is 128-bit, so we need 32 chars + 1 char to store null-terminator
+
+class MethodContext
+{
+public:
+#pragma pack(push, 1)
+ struct Agnostic_CORINFO_SIG_INFO
+ {
+ DWORD callConv;
+ DWORDLONG retTypeClass;
+ DWORDLONG retTypeSigClass;
+ DWORD retType;
+ DWORD flags;
+ DWORD numArgs;
+ DWORD sigInst_classInstCount;
+ DWORD sigInst_classInst_Index;
+ DWORD sigInst_methInstCount;
+ DWORD sigInst_methInst_Index;
+ DWORDLONG args;
+ DWORD pSig;
+ DWORD cbSig;
+ DWORDLONG scope;
+ DWORD token;
+ };
+ struct Agnostic_CORINFO_METHOD_INFO
+ {
+ DWORDLONG ftn;
+ DWORDLONG scope;
+ DWORD ILCode_offset;
+ DWORD ILCodeSize;
+ DWORD maxStack;
+ DWORD EHcount;
+ DWORD options;
+ DWORD regionKind;
+ Agnostic_CORINFO_SIG_INFO args;
+ Agnostic_CORINFO_SIG_INFO locals;
+ };
+ struct Agnostic_CompileMethod
+ {
+ Agnostic_CORINFO_METHOD_INFO info;
+ DWORD flags;
+ };
+ struct Agnostic_InitClass
+ {
+ DWORDLONG field;
+ DWORDLONG method;
+ DWORDLONG context;
+ DWORD speculative;
+ };
+ struct DLDL
+ {
+ DWORDLONG A;
+ DWORDLONG B;
+ };
+ struct Agnostic_CanInline
+ {
+ DWORD Restrictions;
+ DWORD result;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetClassGClayout
+ {
+ DWORD gcPtrs_Index;
+ DWORD len;
+ DWORD valCount;
+ };
+ struct DLD
+ {
+ DWORDLONG A;
+ DWORD B;
+ };
+ struct Agnostic_CORINFO_RESOLVED_TOKENin
+ {
+ DWORDLONG tokenContext;
+ DWORDLONG tokenScope;
+ DWORD token;
+ DWORD tokenType;
+ };
+ struct Agnostic_CORINFO_RESOLVED_TOKENout
+ {
+ DWORDLONG hClass;
+ DWORDLONG hMethod;
+ DWORDLONG hField;
+ DWORD pTypeSpec_Index;
+ DWORD cbTypeSpec;
+ DWORD pMethodSpec_Index;
+ DWORD cbMethodSpec;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetArgType
+ {
+ Agnostic_CORINFO_SIG_INFO sig;
+ DWORDLONG args;
+ };
+ struct Agnostic_GetArgClass
+ {
+ Agnostic_CORINFO_SIG_INFO sig;
+ DWORDLONG args;
+ };
+ struct Agnostic_GetBoundaries
+ {
+ DWORD cILOffsets;
+ DWORD pILOffset_offset;
+ DWORD implicitBoundaries;
+ };
+ struct Agnostic_CORINFO_EE_INFO
+ {
+ struct Agnostic_InlinedCallFrameInfo
+ {
+ DWORD size;
+ DWORD offsetOfGSCookie;
+ DWORD offsetOfFrameVptr;
+ DWORD offsetOfFrameLink;
+ DWORD offsetOfCallSiteSP;
+ DWORD offsetOfCalleeSavedFP;
+ DWORD offsetOfCallTarget;
+ DWORD offsetOfReturnAddress;
+ }
+ inlinedCallFrameInfo;
+ DWORD offsetOfThreadFrame;
+ DWORD offsetOfGCState;
+ DWORD offsetOfDelegateInstance;
+ DWORD offsetOfDelegateFirstTarget;
+ DWORD offsetOfSecureDelegateIndirectCell;
+ DWORD offsetOfTransparentProxyRP;
+ DWORD offsetOfRealProxyServer;
+ DWORD offsetOfObjArrayData;
+ DWORD sizeOfReversePInvokeFrame;
+ DWORD osPageSize;
+ DWORD maxUncheckedOffsetForNullObject;
+ DWORD targetAbi;
+ DWORD osType;
+ DWORD osMajor;
+ DWORD osMinor;
+ DWORD osBuild;
+ };
+ struct Agnostic_GetFieldAddress
+ {
+ DWORDLONG ppIndirection;
+ DWORDLONG fieldAddress;
+ DWORD fieldValue;
+ };
+ struct Agnostic_CORINFO_RESOLVED_TOKEN
+ {
+ DWORDLONG tokenContext;
+ DWORDLONG tokenScope;
+ DWORD token;
+ DWORD tokenType;
+ DWORDLONG hClass;
+ DWORDLONG hMethod;
+ DWORDLONG hField;
+ DWORD typeSpec_Index;
+ DWORD cbTypeSpec;
+ DWORD methodSpec_Index;
+ DWORD cbMethodSpec;
+ };
+ struct Agnostic_GetFieldInfo
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORDLONG callerHandle;
+ DWORD flags;
+ };
+ struct Agnostic_CORINFO_HELPER_ARG
+ {
+ DWORDLONG constant; //one view of a large union of ptr size
+ DWORD argType;
+ };
+ struct Agnostic_CORINFO_HELPER_DESC
+ {
+ DWORD helperNum;
+ DWORD numArgs;
+ Agnostic_CORINFO_HELPER_ARG args[CORINFO_ACCESS_ALLOWED_MAX_ARGS];
+ };
+ struct Agnostic_CORINFO_FIELD_INFO
+ {
+ DWORD fieldAccessor;
+ DWORD fieldFlags;
+ DWORD helper;
+ DWORD offset;
+ DWORD fieldType;
+ DWORDLONG structType;
+ DWORD accessAllowed;
+ Agnostic_CORINFO_HELPER_DESC accessCalloutHelper;
+ };
+ struct DD
+ {
+ DWORD A;
+ DWORD B;
+ };
+ struct Agnostic_CanTailCall
+ {
+ DWORDLONG callerHnd;
+ DWORDLONG declaredCalleeHnd;
+ DWORDLONG exactCalleeHnd;
+ WORD fIsTailPrefix;
+ };
+ struct Agnostic_Environment
+ {
+ DWORD name_index;;
+ DWORD val_index;
+ };
+ struct Agnostic_GetCallInfo
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ Agnostic_CORINFO_RESOLVED_TOKEN ConstrainedResolvedToken;
+ DWORDLONG callerHandle;
+ DWORD flags;
+ };
+ struct Agnostic_CORINFO_LOOKUP_KIND
+ {
+ DWORD needsRuntimeLookup;
+ DWORD runtimeLookupKind;
+ WORD runtimeLookupFlags;
+ };
+ struct Agnostic_CORINFO_RUNTIME_LOOKUP
+ {
+ DWORDLONG signature;
+ DWORD helper;
+ DWORD indirections;
+ DWORD testForNull;
+ DWORD testForFixup;
+ DWORDLONG offsets[CORINFO_MAXINDIRECTIONS];
+ };
+ struct Agnostic_CORINFO_CONST_LOOKUP
+ {
+ DWORD accessType;
+ DWORDLONG handle; // actually a union of two pointer sized things
+ };
+ struct Agnostic_CORINFO_LOOKUP
+ {
+ Agnostic_CORINFO_LOOKUP_KIND lookupKind;
+ Agnostic_CORINFO_RUNTIME_LOOKUP runtimeLookup; //This and constLookup actually a union, but with different layouts.. :-| copy the right one based on lookupKinds value
+ Agnostic_CORINFO_CONST_LOOKUP constLookup;
+ };
+ struct Agnostic_CORINFO_CALL_INFO
+ {
+ DWORDLONG hMethod;
+ DWORD methodFlags;
+ DWORD classFlags;
+ Agnostic_CORINFO_SIG_INFO sig;
+ DWORD verMethodFlags;
+ Agnostic_CORINFO_SIG_INFO verSig;
+ DWORD accessAllowed;
+ Agnostic_CORINFO_HELPER_DESC callsiteCalloutHelper;
+ DWORD thisTransform;
+ DWORD kind;
+ DWORD nullInstanceCheck;
+ DWORDLONG contextHandle;
+ DWORD exactContextNeedsRuntimeLookup;
+ Agnostic_CORINFO_LOOKUP stubLookup;//first view of union. others are matching or subordinate
+ Agnostic_CORINFO_CONST_LOOKUP instParamLookup;
+ DWORD secureDelegateInvoke;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetMethodInfo
+ {
+ Agnostic_CORINFO_METHOD_INFO info;
+ bool result;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_FindSig
+ {
+ DWORDLONG module;
+ DWORD sigTOK;
+ DWORDLONG context;
+ };
+ struct Agnostic_PInvokeMarshalingRequired
+ {
+ DWORDLONG method;
+ Agnostic_CORINFO_SIG_INFO callSiteSig;
+ };
+ struct Agnostic_CORINFO_EH_CLAUSE
+ {
+ DWORD Flags;
+ DWORD TryOffset;
+ DWORD TryLength;
+ DWORD HandlerOffset;
+ DWORD HandlerLength;
+ DWORD ClassToken;//first view of a two dword union
+ };
+ struct Agnostic_GetVars
+ {
+ DWORD cVars;
+ DWORD vars_offset;
+ DWORD extendOthers;
+ };
+ struct Agnostic_CanAccessClassIn
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORDLONG callerHandle;
+ };
+ struct Agnostic_CanAccessClassOut
+ {
+ Agnostic_CORINFO_HELPER_DESC AccessHelper;
+ DWORD result;
+ };
+ struct Agnostic_AppendClassName
+ {
+ DWORDLONG classHandle;
+ DWORD fNamespace;
+ DWORD fFullInst;
+ DWORD fAssembly;
+ };
+ struct Agnostic_CheckMethodModifier
+ {
+ DWORDLONG hMethod;
+ DWORD modifier;
+ DWORD fOptional;
+ };
+ struct Agnostic_EmbedGenericHandle
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORD fEmbedParent;
+ };
+ struct Agnostic_CORINFO_GENERICHANDLE_RESULT
+ {
+
+ Agnostic_CORINFO_LOOKUP lookup;
+ DWORDLONG compileTimeHandle;
+ DWORD handleType;
+ };
+ struct Agnostic_GetDelegateCtorIn
+ {
+ DWORDLONG methHnd;
+ DWORDLONG clsHnd;
+ DWORDLONG targetMethodHnd;
+ };
+ struct Agnostic_DelegateCtorArgs
+ {
+ DWORDLONG pMethod;
+ DWORDLONG pArg3;
+ DWORDLONG pArg4;
+ DWORDLONG pArg5;
+ };
+ struct Agnostic_GetDelegateCtorOut
+ {
+ Agnostic_DelegateCtorArgs CtorData;
+ DWORDLONG result;
+ };
+ struct Agnostic_FindCallSiteSig
+ {
+ DWORDLONG module;
+ DWORD methTok;
+ DWORDLONG context;
+ };
+ struct Agnostic_GetNewHelper
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORDLONG callerHandle;
+ };
+ struct Agnostic_GetCastingHelper
+ {
+ Agnostic_CORINFO_RESOLVED_TOKEN ResolvedToken;
+ DWORD fThrowing;
+ };
+ struct Agnostic_GetClassModuleIdForStatics
+ {
+ DWORDLONG Module;
+ DWORDLONG pIndirection;
+ DWORDLONG result;
+ };
+ struct Agnostic_IsCompatibleDelegate
+ {
+ DWORDLONG objCls;
+ DWORDLONG methodParentCls;
+ DWORDLONG method;
+ DWORDLONG delegateCls;
+ };
+ struct Agnostic_GetBBProfileData
+ {
+ DWORD count;
+ DWORD profileBuffer_index;
+ DWORD numRuns;
+ DWORD result;
+ };
+ struct Agnostic_GetProfilingHandle
+ {
+ DWORD bHookFunction;
+ DWORDLONG ProfilerHandle;
+ DWORD bIndirectedHandles;
+ };
+ struct Agnostic_GetTailCallCopyArgsThunk
+ {
+ Agnostic_CORINFO_SIG_INFO Sig;
+ DWORD flags;
+ };
+ struct Agnostic_GetArgClass_Value
+ {
+ DWORDLONG result;
+ DWORD exceptionCode;
+ };
+ struct Agnostic_GetArgType_Value
+ {
+ DWORDLONG vcTypeRet;
+ DWORD result;
+ DWORD exceptionCode;
+ };
+
+ // Agnostic_ConfigIntInfo combines as a single key the name
+ // and defaultValue of a integer config query.
+ // Note: nameIndex is treated as a DWORD index to the name string.
+ struct Agnostic_ConfigIntInfo
+ {
+ DWORD nameIndex;
+ DWORD defaultValue;
+ };
+
+ // SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR
+ struct Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor
+ {
+ DWORD passedInRegisters; // Whether the struct is passable/passed (this includes struct returning) in registers.
+ DWORD eightByteCount; // Number of eightbytes for this struct.
+ DWORD eightByteClassifications[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The eightbytes type classification.
+ DWORD eightByteSizes[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The size of the eightbytes (an eightbyte could include padding. This represents the no padding size of the eightbyte).
+ DWORD eightByteOffsets[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The start offset of the eightbytes (in bytes).
+ DWORD result;
+ };
+
+#pragma pack(pop)
+
+ MethodContext();
+
+private:
+
+ MethodContext(HANDLE hFile);
+ MethodContext(unsigned char *buff, unsigned int totalLen);
+
+ void MethodInitHelper(unsigned char *buff, unsigned int totalLen);
+ void MethodInitHelperFile(HANDLE hFile);
+
+ bool Initialize(int loadedCount, unsigned char* buff, DWORD size);
+ bool Initialize(int loadedCount, HANDLE hFile);
+
+ int dumpMD5HashToBuffer(BYTE *pBuffer, int bufLen, char *buff, int len);
+
+public:
+
+ static bool Initialize(int loadedCount, unsigned char* buff, DWORD size, /* OUT */ MethodContext** ppmc);
+ static bool Initialize(int loadedCount, HANDLE hFile, /* OUT */ MethodContext** ppmc);
+ ~MethodContext();
+ void Destroy();
+
+ bool Equal(MethodContext *other);
+ unsigned int saveToFile(HANDLE hFile);
+ unsigned int calculateFileSize();
+ unsigned int calculateRawFileSize();
+ void dumpToConsole(int mcNumber = -1); // if mcNumber is not -1, display the method context number before the dumped info
+ int dumpStatToBuffer(char *buff, int len);
+ static int dumpStatTitleToBuffer(char *buff, int len);
+ int methodSize;
+
+ int dumpMethodIdentityInfoToBuffer(char *buff, int len);
+ int dumpMethodMD5HashToBuffer(char *buff, int len);
+
+ void recGlobalContext(const MethodContext& other);
+
+ void recEnvironment();
+ void dmpEnvironment(DWORD key, const Agnostic_Environment& value);
+ void repEnvironmentSet();
+ void repEnvironmentUnset();
+ int repEnvironmentGetCount();
+ int repGetTestID();
+
+ void recCompileMethod(CORINFO_METHOD_INFO *info, unsigned flags);
+ void dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& value);
+ void repCompileMethod(CORINFO_METHOD_INFO *info, unsigned *flags);
+
+ void recGetMethodClass(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CLASS_HANDLE classHandle);
+ void dmpGetMethodClass(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetMethodClass(CORINFO_METHOD_HANDLE methodHandle);
+
+ void recGetClassAttribs(CORINFO_CLASS_HANDLE classHandle, DWORD attribs);
+ void dmpGetClassAttribs(DWORDLONG key, DWORD value);
+ DWORD repGetClassAttribs(CORINFO_CLASS_HANDLE classHandle);
+
+ void recGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle, DWORD attribs);
+ void dmpGetMethodAttribs(DWORDLONG key, DWORD value);
+ DWORD repGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle);
+
+ void recGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars, bool *extendOthers);
+ void dmpGetVars(DWORDLONG key, const Agnostic_GetVars& value);
+ void repGetVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars, bool *extendOthers);
+
+ void recGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries);
+ void dmpGetBoundaries(DWORDLONG key, const Agnostic_GetBoundaries& value);
+ void repGetBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int *cILOffsets, DWORD **pILOffsets, ICorDebugInfo::BoundaryTypes *implictBoundaries);
+
+ void recInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative, CorInfoInitClassResult result);
+ void dmpInitClass(const Agnostic_InitClass& key, DWORD value);
+ CorInfoInitClassResult repInitClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context, BOOL speculative);
+
+ void recGetMethodName(CORINFO_METHOD_HANDLE ftn, char *methodname, const char **moduleName);
+ void dmpGetMethodName(DLD key, DD value);
+ const char *repGetMethodName(CORINFO_METHOD_HANDLE ftn, const char **moduleName);
+
+ void MethodContext::recGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes, DWORD result);
+ void MethodContext::dmpGetJitFlags(DWORD key, DD value);
+ DWORD MethodContext::repGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes);
+
+ void recGetJitTimeLogFilename(LPCWSTR tempFileName);
+ void dmpGetJitTimeLogFilename(DWORD key, DWORD value);
+ LPCWSTR repGetJitTimeLogFilename();
+
+ void recCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD *pRestrictions, CorInfoInline response, DWORD exceptionCode);
+ void dmpCanInline(DLDL key, const Agnostic_CanInline& value);
+ CorInfoInline repCanInline(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE calleeHnd, DWORD* pRestrictions, DWORD *exceptionCode);
+
+ void recResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD exceptionCode);
+ void dmpResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value);
+ void repResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, DWORD *exceptionCode);
+
+ void recTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool success);
+ void dmpTryResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const Agnostic_CORINFO_RESOLVED_TOKENout& value);
+ bool repTryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken);
+
+ void recGetCallInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO *pResult, DWORD exceptionCode);
+ void dmpGetCallInfo(const Agnostic_GetCallInfo& key, const Agnostic_CORINFO_CALL_INFO& value);
+ void repGetCallInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO *pResult, DWORD *exceptionCode);
+ void repGetCallInfoFromMethodHandle(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CALL_INFO *pResult);
+
+ void recGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand, CorInfoIntrinsics result);
+ void dmpGetIntrinsicID(DWORDLONG key, DD value);
+ CorInfoIntrinsics repGetIntrinsicID(CORINFO_METHOD_HANDLE method, bool* pMustExpand);
+
+ void recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CorInfoUnmanagedCallConv result);
+ void dmpGetUnmanagedCallConv(DWORDLONG key, DWORD result);
+ CorInfoUnmanagedCallConv repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method);
+
+ void recIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method, CorInfoInstantiationVerification result);
+ void dmpIsInstantiationOfVerifiedGeneric(DWORDLONG key, DWORD value);
+ CorInfoInstantiationVerification repIsInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE method);
+
+ void recAsCorInfoType(CORINFO_CLASS_HANDLE cls, CorInfoType result);
+ void dmpAsCorInfoType(DWORDLONG key, DWORD value);
+ CorInfoType repAsCorInfoType(CORINFO_CLASS_HANDLE cls);
+
+ void recIsValueClass(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsValueClass(DWORDLONG key, DWORD value);
+ BOOL repIsValueClass(CORINFO_CLASS_HANDLE cls);
+
+ void recIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsStructRequiringStackAllocRetBuf(DWORDLONG key, DWORD value);
+ BOOL repIsStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls);
+
+ void recGetClassSize(CORINFO_CLASS_HANDLE cls, unsigned result);
+ void dmpGetClassSize(DWORDLONG key, DWORD val);
+ unsigned repGetClassSize(CORINFO_CLASS_HANDLE cls);
+
+ void recGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls, unsigned result);
+ void dmpGetClassNumInstanceFields(DWORDLONG key, DWORD value);
+ unsigned repGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls);
+
+ void recGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls, CorInfoHelpFunc result);
+ void dmpGetNewArrHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls);
+
+ void recGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd, CorInfoHelpFunc result);
+ void dmpGetSharedCCtorHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd);
+
+ void recGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn, CorInfoHelpFunc result);
+ void dmpGetSecurityPrologHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn);
+
+ void recGetTypeForBox(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
+ void dmpGetTypeForBox(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetTypeForBox(CORINFO_CLASS_HANDLE cls);
+
+ void recGetBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result);
+ void dmpGetBoxHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetBoxHelper(CORINFO_CLASS_HANDLE cls);
+
+ void recGetBuiltinClass(CorInfoClassId classId, CORINFO_CLASS_HANDLE result);
+ void dmpGetBuiltinClass(DWORD key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetBuiltinClass(CorInfoClassId classId);
+
+ void recGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls, CorInfoType result);
+ void dmpGetTypeForPrimitiveValueClass(DWORDLONG key, DWORD value);
+ CorInfoType repGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls);
+
+ void recGetParentType(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
+ void dmpGetParentType(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetParentType(CORINFO_CLASS_HANDLE cls);
+
+ void recIsSDArray(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsSDArray(DWORDLONG key, DWORD value);
+ BOOL repIsSDArray(CORINFO_CLASS_HANDLE cls);
+
+ void recIsInSIMDModule(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpIsInSIMDModule(DWORDLONG key, DWORD value);
+ BOOL repIsInSIMDModule(CORINFO_CLASS_HANDLE cls);
+
+ void recGetFieldClass(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE result);
+ void dmpGetFieldClass(DWORDLONG key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetFieldClass(CORINFO_FIELD_HANDLE field);
+
+ void recGetFieldOffset(CORINFO_FIELD_HANDLE field, unsigned result);
+ void dmpGetFieldOffset(DWORDLONG key, DWORD value);
+ unsigned repGetFieldOffset(CORINFO_FIELD_HANDLE field);
+
+ void recGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle, CorInfoHelpFunc result);
+ void dmpGetLazyStringLiteralHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle);
+
+ void recGetUnBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result);
+ void dmpGetUnBoxHelper(DWORDLONG key, DWORD value);
+ CorInfoHelpFunc repGetUnBoxHelper(CORINFO_CLASS_HANDLE cls);
+
+ void recGetReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_LOOKUP_KIND* pGenericLookupKind, CorInfoHelpFunc id, CORINFO_CONST_LOOKUP* pLookup, bool result);
+ void dmpGetReadyToRunHelper(DWORDLONG key, DWORD value);
+ bool repGetReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_LOOKUP_KIND* pGenericLookupKind, CorInfoHelpFunc id, CORINFO_CONST_LOOKUP* pLookup);
+
+ void recGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod, CORINFO_CLASS_HANDLE delegateType, CORINFO_CONST_LOOKUP* pLookup);
+ void dmpGetReadyToRunDelegateCtorHelper(DWORDLONG key, DWORD value);
+ void repGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod, CORINFO_CLASS_HANDLE delegateType, CORINFO_CONST_LOOKUP* pLookup);
+
+ void recGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection, void *result);
+ void dmpGetHelperFtn(DWORD key, DLDL value);
+ void* repGetHelperFtn(CorInfoHelpFunc ftnNum, void **ppIndirection);
+ bool fndGetHelperFtn(void *functionAddress, CorInfoHelpFunc *pResult);
+
+ void recGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection, CORINFO_JUST_MY_CODE_HANDLE result);
+ void dmpGetJustMyCodeHandle(DWORDLONG key, DLDL value);
+ CORINFO_JUST_MY_CODE_HANDLE repGetJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE **ppIndirection);
+
+ void recGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags);
+ void dmpGetFunctionEntryPoint(DLD key, DLD value);
+ void repGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult, CORINFO_ACCESS_FLAGS accessFlags);
+ bool fndGetFunctionEntryPoint(DLD value, CORINFO_METHOD_HANDLE *pResult);
+
+ void recConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void *ppValue, InfoAccessType result);
+ void dmpConstructStringLiteral(DLD key, DLD value);
+ InfoAccessType repConstructStringLiteral(CORINFO_MODULE_HANDLE module, mdToken metaTok, void **ppValue);
+
+ void recEmptyStringLiteral(void **ppValue, InfoAccessType result);
+ void dmpEmptyStringLiteral(DWORD key, DLD value);
+ InfoAccessType repEmptyStringLiteral(void **ppValue);
+
+ void recGetArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, CorInfoTypeWithMod result, DWORD exception);
+ void dmpGetArgType(const Agnostic_GetArgType& key, const Agnostic_GetArgType_Value& value);
+ CorInfoTypeWithMod repGetArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE *vcTypeRet, DWORD *exception);
+
+ void recGetArgNext(CORINFO_ARG_LIST_HANDLE args, CORINFO_ARG_LIST_HANDLE result);
+ void dmpGetArgNext(DWORDLONG key, DWORDLONG value);
+ CORINFO_ARG_LIST_HANDLE repGetArgNext(CORINFO_ARG_LIST_HANDLE args);
+
+ void recGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent);
+ void dmpGetMethodSig(DLDL key, const Agnostic_CORINFO_SIG_INFO& value);
+ void repGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent);
+
+ void recGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE result, DWORD exceptionCode);
+ void dmpGetArgClass(const Agnostic_GetArgClass& key, const Agnostic_GetArgClass_Value& value);
+ CORINFO_CLASS_HANDLE repGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, DWORD *exceptionCode);
+
+ void recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, bool result, DWORD exceptionCode);
+ void dmpGetMethodInfo(DWORDLONG key, const Agnostic_GetMethodInfo& value);
+ bool repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, DWORD *exceptionCode);
+
+ void recGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CorInfoHelpFunc result);
+ void dmpGetNewHelper(const Agnostic_GetNewHelper& key, DWORD value);
+ CorInfoHelpFunc repGetNewHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle);
+
+ void recEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult);
+ void dmpEmbedGenericHandle(const Agnostic_EmbedGenericHandle& key, const Agnostic_CORINFO_GENERICHANDLE_RESULT& value);
+ void repEmbedGenericHandle(CORINFO_RESOLVED_TOKEN *pResolvedToken, BOOL fEmbedParent, CORINFO_GENERICHANDLE_RESULT *pResult);
+
+ void recGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause);
+ void dmpGetEHinfo(DLD key, const Agnostic_CORINFO_EH_CLAUSE& value);
+ void repGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE *clause);
+
+ void recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection);
+ void dmpGetMethodVTableOffset(DWORDLONG key, DD value);
+ void repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method, unsigned *offsetOfIndirection, unsigned* offsetAfterIndirection);
+
+ void recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_CLASS_HANDLE result);
+ void dmpGetTokenTypeAsHandle(const Agnostic_CORINFO_RESOLVED_TOKEN& key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN * pResolvedToken);
+
+ void recGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult);
+ void dmpGetFieldInfo(const Agnostic_GetFieldInfo& key, const Agnostic_CORINFO_FIELD_INFO& value);
+ void repGetFieldInfo(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult);
+
+ void recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection, CORINFO_METHOD_HANDLE result);
+ void dmpEmbedMethodHandle(DWORDLONG key, DLDL value);
+ CORINFO_METHOD_HANDLE repEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void **ppIndirection);
+
+ void recGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection, void *result, CorInfoType cit);
+ void dmpGetFieldAddress(DWORDLONG key, const Agnostic_GetFieldAddress& value);
+ void* repGetFieldAddress(CORINFO_FIELD_HANDLE field, void **ppIndirection);
+
+ void recGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs, unsigned len, unsigned result);
+ void dmpGetClassGClayout(DWORDLONG key, const Agnostic_GetClassGClayout& value);
+ unsigned repGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE *gcPtrs);
+
+ void recGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint, unsigned result);
+ void dmpGetClassAlignmentRequirement(DLD key, DWORD value);
+ unsigned repGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, BOOL fDoubleAlignHint);
+
+ void recCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper, CorInfoIsAccessAllowedResult result);
+ void dmpCanAccessClass(const Agnostic_CanAccessClassIn& key, const Agnostic_CanAccessClassOut& value);
+ CorInfoIsAccessAllowedResult repCanAccessClass(CORINFO_RESOLVED_TOKEN *pResolvedToken, CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper);
+
+ void recGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing, CorInfoHelpFunc result);
+ void dmpGetCastingHelper(const Agnostic_GetCastingHelper& key, DWORD value);
+ CorInfoHelpFunc repGetCastingHelper(CORINFO_RESOLVED_TOKEN *pResolvedToken, bool fThrowing);
+
+ void recEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection, CORINFO_MODULE_HANDLE result);
+ void dmpEmbedModuleHandle(DWORDLONG key, DLDL value);
+ CORINFO_MODULE_HANDLE repEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection);
+
+ void recEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection, CORINFO_CLASS_HANDLE result);
+ void dmpEmbedClassHandle(DWORDLONG key, DLDL value);
+ CORINFO_CLASS_HANDLE repEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void **ppIndirection);
+
+ void recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, BOOL result);
+ void dmpPInvokeMarshalingRequired(const Agnostic_PInvokeMarshalingRequired& key, DWORD value);
+ BOOL repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig);
+
+ void recFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+ void dmpFindSig(const Agnostic_FindSig& key, const Agnostic_CORINFO_SIG_INFO& value);
+ void repFindSig(CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+
+ void recGetEEInfo(CORINFO_EE_INFO *pEEInfoOut);
+ void dmpGetEEInfo(DWORD key, const Agnostic_CORINFO_EE_INFO& value);
+ void repGetEEInfo(CORINFO_EE_INFO *pEEInfoOut);
+
+ void recGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal);
+ void dmpGetGSCookie(DWORD key, DLDL value);
+ void repGetGSCookie(GSCookie *pCookieVal, GSCookie **ppCookieVal);
+
+ void recGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection, size_t result);
+ void dmpGetClassModuleIdForStatics(DWORDLONG key, const Agnostic_GetClassModuleIdForStatics& value);
+ size_t repGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE *pModule, void **ppIndirection);
+
+ void recGetThreadTLSIndex(void **ppIndirection, DWORD result);
+ void dmpGetThreadTLSIndex(DWORD key, DLD value);
+ DWORD repGetThreadTLSIndex(void **ppIndirection);
+
+ void recGetInlinedCallFrameVptr(void **ppIndirection, const void * result);
+ void dmpGetInlinedCallFrameVptr(DWORD key, DLDL value);
+ const void * repGetInlinedCallFrameVptr(void **ppIndirection);
+
+ void recGetAddrOfCaptureThreadGlobal(void **ppIndirection, LONG * result);
+ void dmpGetAddrOfCaptureThreadGlobal(DWORD key, DLDL value);
+ LONG * repGetAddrOfCaptureThreadGlobal(void **ppIndirection);
+
+ void recGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection, unsigned result);
+ void dmpGetClassDomainID(DWORDLONG key, DLD value);
+ unsigned repGetClassDomainID(CORINFO_CLASS_HANDLE cls, void **ppIndirection);
+
+ void recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND *result);
+ void dmpGetLocationOfThisType(DWORDLONG key, const Agnostic_CORINFO_LOOKUP_KIND& value);
+ CORINFO_LOOKUP_KIND repGetLocationOfThisType(CORINFO_METHOD_HANDLE context);
+
+ void recGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData, CORINFO_METHOD_HANDLE result);
+ void dmpGetDelegateCtor(const Agnostic_GetDelegateCtorIn& key, const Agnostic_GetDelegateCtorOut& value);
+ CORINFO_METHOD_HANDLE repGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs *pCtorData);
+
+ void recGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult);
+ void dmpGetFunctionFixedEntryPoint(DWORDLONG key, const Agnostic_CORINFO_CONST_LOOKUP& value);
+ void repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP *pResult);
+
+ void recGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num, CORINFO_FIELD_HANDLE result);
+ void dmpGetFieldInClass(DLD key, DWORDLONG value);
+ CORINFO_FIELD_HANDLE repGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num);
+
+ void recGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent, CorInfoType result);
+ void dmpGetFieldType(DLDL key, DLD value);
+ CorInfoType repGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE *structType, CORINFO_CLASS_HANDLE memberParent);
+
+ void recGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName, const char* result);
+ void dmpGetFieldName(DWORDLONG key, DD value);
+ const char* repGetFieldName(CORINFO_FIELD_HANDLE ftn, const char **moduleName);
+
+ void recCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpCanInlineTypeCheckWithObjectVTable(DWORDLONG key, DWORD value);
+ BOOL repCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls);
+
+ void recSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method, BOOL result);
+ void dmpSatisfiesMethodConstraints(DLDL key, DWORD value);
+ BOOL repSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method);
+
+ void recInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint);
+ void dmpInitConstraintsForVerification(DWORDLONG key, DD value);
+ void repInitConstraintsForVerification(CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints,
+ BOOL *pfHasCircularMethodConstraint);
+
+ void recIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result);
+ void dmpIsValidStringRef(DLD key, DWORD value);
+ BOOL repIsValidStringRef(CORINFO_MODULE_HANDLE module, unsigned metaTOK);
+
+ void recGetHelperName(CorInfoHelpFunc funcNum, const char* result);
+ void dmpGetHelperName(DWORD key, DWORD value);
+ const char* repGetHelperName(CorInfoHelpFunc funcNum);
+
+ void recCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent, BOOL result);
+ void dmpCanCast(DLDL key, DWORD value);
+ BOOL repCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent);
+
+ void recGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet, CorInfoType result);
+ void dmpGetChildType(DWORDLONG key, DLD value);
+ CorInfoType repGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE *clsRet);
+
+ void recGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size, void *result);
+ void dmpGetArrayInitializationData(DLD key, DWORDLONG value);
+ void *repGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size);
+
+ void recFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers, int result);
+ void dmpFilterException(DWORD key, DWORD value);
+ int repFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers);
+
+ void recHandleException(struct _EXCEPTION_POINTERS *pExceptionPointers);
+ void dmpHandleException(DWORD key, DWORD value);
+
+ void recGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result);
+ void dmpGetAddressOfPInvokeFixup(DWORDLONG key, DLDL value);
+ void* repGetAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, void **ppIndirection);
+
+ void recGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup);
+ void dmpGetAddressOfPInvokeTarget(DWORDLONG key, DLD value);
+ void repGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup);
+
+ void recSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls, BOOL result);
+ void dmpSatisfiesClassConstraints(DWORDLONG key, DWORD value);
+ BOOL repSatisfiesClassConstraints(CORINFO_CLASS_HANDLE cls);
+
+ void recGetMethodHash(CORINFO_METHOD_HANDLE ftn, unsigned result);
+ void dmpGetMethodHash(DWORDLONG key, DWORD value);
+ unsigned repGetMethodHash(CORINFO_METHOD_HANDLE ftn);
+
+ void recCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd, CORINFO_METHOD_HANDLE exactCalleeHnd,
+ bool fIsTailPrefix, bool result);
+ void dmpCanTailCall(const Agnostic_CanTailCall& key, DWORD value);
+ bool repCanTailCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_METHOD_HANDLE declaredCalleeHnd, CORINFO_METHOD_HANDLE exactCalleeHnd,
+ bool fIsTailPrefix);
+
+ void recIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate, BOOL result);
+ void dmpIsCompatibleDelegate(const Agnostic_IsCompatibleDelegate& key, DD value);
+ BOOL repIsCompatibleDelegate(CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls,
+ CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate);
+
+ void recIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd, BOOL result);
+ void dmpIsDelegateCreationAllowed(DLDL key, DWORD value);
+ BOOL repIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd);
+
+ void recCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip, CorInfoCanSkipVerificationResult result);
+ void dmpCanSkipMethodVerification(DLD key, DWORD value);
+ CorInfoCanSkipVerificationResult repCanSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHandle, BOOL skip);
+
+ void recFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+ void dmpFindCallSiteSig(const Agnostic_FindCallSiteSig& key, const Agnostic_CORINFO_SIG_INFO& value);
+ void repFindCallSiteSig(CORINFO_MODULE_HANDLE module, unsigned methTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig);
+
+ void recShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope, BOOL result);
+ void dmpShouldEnforceCallvirtRestriction(DWORDLONG key, DWORD value);
+ BOOL repShouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope);
+
+ void recGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection, void* result);
+ void dmpGetMethodSync(DWORDLONG key, DLDL value);
+ void* repGetMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection);
+
+ void recGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection, CORINFO_VARARGS_HANDLE result);
+ void dmpGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DLDL value);
+ CORINFO_VARARGS_HANDLE repGetVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection);
+
+ void recCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig, bool result);
+ void dmpCanGetVarArgsHandle(const Agnostic_CORINFO_SIG_INFO& key, DWORD value);
+ bool repCanGetVarArgsHandle(CORINFO_SIG_INFO *pSig);
+
+ void recGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection, DWORD result);
+ void dmpGetFieldThreadLocalStoreID(DWORDLONG key, DLD value);
+ DWORD repGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void **ppIndirection);
+
+ void recGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns, HRESULT result);
+ void dmpGetBBProfileData(DWORDLONG key, const Agnostic_GetBBProfileData& value);
+ HRESULT repGetBBProfileData(CORINFO_METHOD_HANDLE ftnHnd, ULONG *count, ICorJitInfo::ProfileBuffer **profileBuffer,
+ ULONG *numRuns);
+
+ void recMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, CORINFO_CLASS_HANDLE result);
+ void dmpMergeClasses(DLDL key, DWORDLONG value);
+ CORINFO_CLASS_HANDLE repMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2);
+
+ void recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection, LPVOID result);
+ void dmpGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DLDL value);
+ LPVOID repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection);
+
+ void recCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, bool result);
+ void dmpCanGetCookieForPInvokeCalliSig(const Agnostic_CORINFO_SIG_INFO& key, DWORD value);
+ bool repCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig);
+
+ void recCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType, BOOL result);
+ void dmpCanAccessFamily(DLDL key, DWORD value);
+ BOOL repCanAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType);
+
+ void recErrorList(const char *error);
+ void dmpErrorList(DWORD key, DWORD value);
+
+ void recGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles);
+ void dmpGetProfilingHandle(DWORD key, const Agnostic_GetProfilingHandle& value);
+ void repGetProfilingHandle(BOOL *pbHookFunction, void **pProfilerHandle, BOOL *pbIndirectedHandles);
+
+ void recEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection, CORINFO_FIELD_HANDLE result);
+ void dmpEmbedFieldHandle(DWORDLONG key, DLDL value);
+ CORINFO_FIELD_HANDLE repEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void **ppIndirection);
+
+ void recAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, BOOL result);
+ void dmpAreTypesEquivalent(DLDL key, DWORD value);
+ BOOL repAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2);
+
+ void recFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity, size_t result);
+ void dmpFindNameOfToken(DLD key, DLD value);
+ size_t repFindNameOfToken(CORINFO_MODULE_HANDLE module, mdToken metaTOK, char * szFQName, size_t FQNameCapacity);
+
+ void recGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr, bool result);
+ void dmpGetSystemVAmd64PassStructInRegisterDescriptor(DWORDLONG key, const Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor& value);
+ bool repGetSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr);
+
+ void recGetRelocTypeHint(void * target, WORD result);
+ void dmpGetRelocTypeHint(DWORDLONG key, DWORD value);
+ WORD repGetRelocTypeHint(void * target);
+
+ void recIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field, bool result);
+ void dmpIsWriteBarrierHelperRequired(DWORDLONG key, DWORD value);
+ bool repIsWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field);
+
+ void recIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK, BOOL result);
+ void dmpIsValidToken(DLD key, DWORD value);
+ BOOL repIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK);
+
+ void recGetClassName(CORINFO_CLASS_HANDLE cls, const char* result);
+ void dmpGetClassName(DWORDLONG key, DWORD value);
+ const char* repGetClassName(CORINFO_CLASS_HANDLE cls);
+
+ void recAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly, const WCHAR* result);
+ void dmpAppendClassName(const Agnostic_AppendClassName& key, DWORD value);
+ const WCHAR* repAppendClassName(CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly);
+
+ void recGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags, void* result);
+ void dmpGetTailCallCopyArgsThunk(const Agnostic_GetTailCallCopyArgsThunk& key, DWORDLONG value);
+ void* repGetTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, CorInfoHelperTailCallSpecialHandling flags);
+
+ void recGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod, mdMethodDef result);
+ void dmpGetMethodDefFromMethod(DWORDLONG key, DWORD value);
+ mdMethodDef repGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod);
+
+ void recCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional, BOOL result);
+ void dmpCheckMethodModifier(const Agnostic_CheckMethodModifier& key, DWORD value);
+ BOOL repCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, BOOL fOptional);
+
+ void recGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection, void* result);
+ void dmpGetPInvokeUnmanagedTarget(DWORDLONG key, DLDL value);
+ void* repGetPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, void **ppIndirection);
+
+ void recGetArrayRank(CORINFO_CLASS_HANDLE cls, unsigned result);
+ void dmpGetArrayRank(DWORDLONG key, DWORD value);
+ unsigned repGetArrayRank(CORINFO_CLASS_HANDLE cls);
+
+ void recIsFieldStatic(CORINFO_FIELD_HANDLE fhld, bool result);
+ void dmpIsFieldStatic(DWORDLONG key, DWORD value);
+ bool repIsFieldStatic(CORINFO_FIELD_HANDLE fhld);
+
+ void recGetIntConfigValue(const wchar_t *name, int defaultValue, int result);
+ void dmpGetIntConfigValue(const Agnostic_ConfigIntInfo& key, int value);
+ int repGetIntConfigValue(const wchar_t *name, int defaultValue);
+
+ void recGetStringConfigValue(const wchar_t *name, const wchar_t *result);
+ void dmpGetStringConfigValue(DWORD nameIndex, DWORD result);
+ const wchar_t *repGetStringConfigValue(const wchar_t *name);
+
+ CompileResult *cr;
+ CompileResult *originalCR;
+ int index;
+
+private:
+
+ #define LWM(map,key,value) LightWeightMap<key, value>* map;
+ #define DENSELWM(map,value) DenseLightWeightMap<value>* map;
+ #include "lwmlist.h"
+
+};
+
+
+// ********************* Please keep this up-to-date to ease adding more ***************
+// Highest packet number: 158
+// *************************************************************************************
+enum mcPackets
+{
+ Packet_AppendClassName = 149, // Added 8/6/2014 - needed for SIMD
+ Packet_AreTypesEquivalent = 1,
+ Packet_AsCorInfoType = 2,
+ Packet_CanAccessClass = 3,
+ Packet_CanAccessFamily = 4,
+ Packet_CanCast = 5,
+ Retired8 = 6,
+ Packet_GetLazyStringLiteralHelper = 147, //Added 12/20/2013 - as a replacement for CanEmbedModuleHandleForHelper
+ Packet_CanGetCookieForPInvokeCalliSig = 7,
+ Packet_CanGetVarArgsHandle = 8,
+ Packet_CanInline = 9,
+ Packet_CanInlineTypeCheckWithObjectVTable = 10,
+ Packet_CanSkipMethodVerification = 11,
+ Packet_CanTailCall = 12,
+ Retired4 = 13,
+ Packet_CheckMethodModifier = 142, //retired as 13 on 2013/07/04
+ Retired3 = 14,
+ Retired5 = 141, //retired as 14 on 2013/07/03
+ Packet_CompileMethod = 143, //retired as 141 on 2013/07/09
+ Packet_ConstructStringLiteral = 15,
+ Packet_EmbedClassHandle = 16,
+ Packet_EmbedFieldHandle = 17,
+ Packet_EmbedGenericHandle = 18,
+ Packet_EmbedMethodHandle = 19,
+ Packet_EmbedModuleHandle = 20,
+ Packet_EmptyStringLiteral = 21,
+ Packet_Environment = 136, //Added 4/3/2013
+ Packet_ErrorList = 22,
+ Packet_FilterException = 134,
+ Packet_FindCallSiteSig = 23,
+ Retired7 = 24,
+ Packet_FindNameOfToken = 145,//Added 7/19/2013 - adjusted members to proper types
+ Packet_GetSystemVAmd64PassStructInRegisterDescriptor = 156, //Added 2/17/2016
+ Packet_FindSig = 25,
+ Packet_GetAddressOfPInvokeFixup = 26,
+ Packet_GetAddressOfPInvokeTarget = 153, //Added 2/3/2016
+ Packet_GetAddrOfCaptureThreadGlobal = 27,
+ Retired1 = 28,
+ Packet_GetArgClass = 139, //retired as 28 on 2013/07/03
+ Packet_GetArgNext = 29,
+ Retired2 = 30,
+ Packet_GetArgType = 140, //retired as 30 on 2013/07/03
+ Packet_GetArrayInitializationData = 31,
+ Packet_GetArrayRank = 32,
+ Packet_GetBBProfileData = 33,
+ Packet_GetBoundaries = 34,
+ Packet_GetBoxHelper = 35,
+ Packet_GetBuiltinClass = 36,
+ Packet_GetCallInfo = 37,
+ Packet_GetCastingHelper = 38,
+ Packet_GetChildType = 39,
+ Packet_GetClassAlignmentRequirement = 40,
+ Packet_GetClassAttribs = 41,
+ Packet_GetClassDomainID = 42,
+ Packet_GetClassGClayout = 43,
+ Packet_GetClassModuleIdForStatics = 44,
+ Packet_GetClassName = 45,
+ Packet_GetClassNumInstanceFields = 46,
+ Packet_GetClassSize = 47,
+ Packet_GetIntConfigValue = 151, //Added 2/12/2015
+ Packet_GetStringConfigValue = 152, // Added 2/12/2015
+ Packet_GetCookieForPInvokeCalliSig = 48,
+ Packet_GetDelegateCtor = 49,
+ Packet_GetEEInfo = 50,
+ Packet_GetEHinfo = 51,
+ Packet_GetFieldAddress = 52,
+ Packet_GetFieldClass = 53,
+ Packet_GetFieldInClass = 54,
+ Packet_GetFieldInfo = 55,
+ Packet_GetFieldName = 56,
+ Packet_GetFieldOffset = 57,
+ Packet_GetFieldThreadLocalStoreID = 58,
+ Packet_GetFieldType = 59,
+ Packet_GetFunctionEntryPoint = 60,
+ Packet_GetFunctionFixedEntryPoint = 61,
+ Packet_GetGSCookie = 62,
+ Packet_GetHelperFtn = 63,
+ Packet_GetHelperName = 64,
+ Packet_GetInlinedCallFrameVptr = 65,
+ Packet_GetIntrinsicID = 66,
+ Packet_GetJitFlags = 154, //Added 2/3/2016
+ Packet_GetJitTimeLogFilename = 67,
+ Packet_GetJustMyCodeHandle = 68,
+ Packet_GetLocationOfThisType = 69,
+ Packet_GetMethodAttribs = 70,
+ Packet_GetMethodClass = 71,
+ Packet_GetMethodDefFromMethod = 72,
+ Packet_GetMethodHash = 73,
+ Packet_GetMethodInfo = 74,
+ Packet_GetMethodName = 75,
+ Packet_GetMethodSig = 76,
+ Packet_GetMethodSync = 77,
+ Packet_GetMethodVTableOffset = 78,
+ Packet_GetNewArrHelper = 79,
+ Packet_GetNewHelper = 80,
+ Packet_GetParentType = 81,
+ Packet_GetPInvokeUnmanagedTarget = 82,
+ Packet_GetProfilingHandle = 83,
+ Packet_GetRelocTypeHint = 84,
+ Packet_GetSecurityPrologHelper = 85,
+ Packet_GetSharedCCtorHelper = 86,
+ Packet_GetTailCallCopyArgsThunk = 87,
+ Packet_GetThreadTLSIndex = 88,
+ Packet_GetTokenTypeAsHandle = 89,
+ Packet_GetTypeForBox = 90,
+ Packet_GetTypeForPrimitiveValueClass = 91,
+ Packet_GetUnBoxHelper = 92,
+ Packet_GetReadyToRunHelper = 150, //Added 10/10/2014
+ Packet_GetReadyToRunDelegateCtorHelper = 157, //Added 3/30/2016
+ Packet_GetUnmanagedCallConv = 94,
+ Packet_GetVarArgsHandle = 95,
+ Packet_GetVars = 96,
+ Packet_HandleException = 135,
+ Packet_InitClass = 97,
+ Packet_InitConstraintsForVerification = 98,
+ Packet_IsCompatibleDelegate = 99,
+ Packet_IsDelegateCreationAllowed = 155,
+ Packet_IsFieldStatic = 137,//Added 4/9/2013 - needed for 4.5.1
+ Packet_IsInSIMDModule = 148,// Added 6/18/2014 - SIMD support
+ Packet_IsInstantiationOfVerifiedGeneric = 100,
+ Packet_IsSDArray = 101,
+ Packet_IsStructRequiringStackAllocRetBuf = 102,
+ Packet_IsValidStringRef = 103,
+ Retired6 = 104,
+ Packet_IsValidToken = 144,//Added 7/19/2013 - adjusted members to proper types
+ Packet_IsValueClass = 105,
+ Packet_IsWriteBarrierHelperRequired = 106,
+ Packet_MergeClasses = 107,
+ Packet_PInvokeMarshalingRequired = 108,
+ Packet_ResolveToken = 109,
+ Packet_TryResolveToken = 158, //Added 4/26/2016
+ Packet_SatisfiesClassConstraints = 110,
+ Packet_SatisfiesMethodConstraints = 111,
+ Packet_ShouldEnforceCallvirtRestriction = 112,
+
+ PacketCR_AddressMap = 113,
+ PacketCR_AllocBBProfileBuffer = 131,
+ PacketCR_AllocGCInfo = 114,
+ PacketCR_AllocMem = 115,
+ PacketCR_AllocUnwindInfo = 132,
+ PacketCR_AssertLog = 138, //Added 6/10/2013 - added to nicely support ilgen
+ PacketCR_CallLog = 116,
+ PacketCR_ClassMustBeLoadedBeforeCodeIsRun = 117,
+ PacketCR_CompileMethod = 118,
+ PacketCR_MessageLog = 119,
+ PacketCR_MethodMustBeLoadedBeforeCodeIsRun = 120,
+ PacketCR_ProcessName = 121,
+ PacketCR_RecordRelocation = 122,
+ PacketCR_ReportFatalError = 123,
+ PacketCR_ReportInliningDecision = 124,
+ PacketCR_ReportTailCallDecision = 125,
+ PacketCR_ReserveUnwindInfo = 133,
+ PacketCR_SetBoundaries = 126,
+ PacketCR_SetEHcount = 127,
+ PacketCR_SetEHinfo = 128,
+ PacketCR_SetMethodAttribs = 129,
+ PacketCR_SetVars = 130,
+ PacketCR_RecordCallSite = 146, //Added 10/28/2013 - to support indirect calls
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp
new file mode 100644
index 0000000000..55f8045938
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.cpp
@@ -0,0 +1,127 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "methodcontext.h"
+#include "methodcontextiterator.h"
+
+
+bool MethodContextIterator::Initialize(const char* fileName)
+{
+ m_hFile = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (m_hFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open file '%s'. GetLastError()=%u", fileName, GetLastError());
+ return false;
+ }
+
+ LARGE_INTEGER DataTemp;
+ if (GetFileSizeEx(m_hFile, &DataTemp)==0)
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ CloseHandle(m_hFile);
+ m_hFile = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ m_fileSize = DataTemp.QuadPart;
+
+ if (m_progressReport)
+ {
+ m_timer->Start();
+ }
+
+ return true;
+}
+
+bool MethodContextIterator::Destroy()
+{
+ bool ret = true; // assume success
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle(m_hFile))
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ ret = false;
+ }
+ m_hFile = INVALID_HANDLE_VALUE;
+ }
+ delete m_mc;
+ m_mc = nullptr;
+
+ if (m_index < m_indexCount)
+ {
+ LogWarning("Didn't use all of index count input: %d < %d (i.e., didn't see MC #%d)",
+ m_index, m_indexCount, m_indexes[m_index]);
+ }
+
+ delete m_timer;
+ m_timer = nullptr;
+
+ return ret;
+}
+
+bool MethodContextIterator::MoveNext()
+{
+ if (m_mc != nullptr)
+ {
+ delete m_mc;
+ m_mc = nullptr;
+ }
+
+ while (true)
+ {
+ // Figure out where the pointer is currently.
+ LARGE_INTEGER pos;
+ pos.QuadPart = 0;
+ if (SetFilePointerEx(m_hFile, pos, &m_pos, FILE_CURRENT) == 0)
+ {
+ LogError("SetFilePointerEx failed. GetLastError()=%u", GetLastError());
+ return false; // any failure causes us to bail out.
+ }
+
+ if (m_pos.QuadPart >= m_fileSize)
+ {
+ return false;
+ }
+
+ // Load the current method context.
+ m_methodContextNumber++;
+
+ if (m_progressReport)
+ {
+ if (m_methodContextNumber % 500 == 0)
+ {
+ m_timer->Stop();
+ LogVerbose("Loaded %d at %d per second", m_methodContextNumber, (int)((double)500 / m_timer->GetSeconds()));
+ m_timer->Start();
+ }
+ }
+
+ if (!MethodContext::Initialize(m_methodContextNumber, m_hFile, &m_mc))
+ return false;
+
+ // If we have an array of indexes, skip the loaded indexes that have not been specified.
+
+ if (m_indexCount == -1)
+ {
+ break;
+ }
+ else if (m_index == m_indexCount)
+ {
+ return false; // we're beyond the array of indexes
+ }
+ else if (m_index < m_indexCount)
+ {
+ if (m_indexes[m_index] == m_methodContextNumber)
+ {
+ m_index++;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h
new file mode 100644
index 0000000000..d6002ba852
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextiterator.h
@@ -0,0 +1,104 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "simpletimer.h"
+
+// Class to implement method context hive reading and iterating.
+
+class MethodContextIterator
+{
+public:
+
+ MethodContextIterator(bool progressReport = false)
+ : m_hFile(INVALID_HANDLE_VALUE)
+ , m_fileSize(0)
+ , m_methodContextNumber(0)
+ , m_mc(nullptr)
+ , m_indexCount(-1)
+ , m_index(0)
+ , m_indexes(nullptr)
+ , m_progressReport(progressReport)
+ , m_timer(nullptr)
+ {
+ if (m_progressReport)
+ {
+ m_timer = new SimpleTimer();
+ }
+ }
+
+ MethodContextIterator(const int indexCount, const int* indexes, bool progressReport = false)
+ : m_hFile(INVALID_HANDLE_VALUE)
+ , m_fileSize(0)
+ , m_methodContextNumber(0)
+ , m_mc(nullptr)
+ , m_indexCount(indexCount)
+ , m_index(0)
+ , m_indexes(indexes)
+ , m_progressReport(progressReport)
+ , m_timer(nullptr)
+ {
+ if (m_progressReport)
+ {
+ m_timer = new SimpleTimer();
+ }
+ }
+
+ ~MethodContextIterator()
+ {
+ Destroy();
+ }
+
+ bool Initialize(const char* fileName);
+
+ bool Destroy();
+
+ bool MoveNext();
+
+ // The iterator class owns the memory returned by Current(); the caller should not delete it.
+ MethodContext* Current()
+ {
+ return m_mc;
+ }
+
+ // In this case, we are giving ownership of the MethodContext* to the caller. So, null out m_mc
+ // before we return, so we don't attempt to delete it in this class.
+ MethodContext* CurrentTakeOwnership()
+ {
+ MethodContext* ret = m_mc;
+ m_mc = nullptr;
+ return ret;
+ }
+
+ // Return the file position offset of the current method context.
+ __int64 CurrentPos()
+ {
+ return m_pos.QuadPart;
+ }
+
+ int MethodContextNumber()
+ {
+ return m_methodContextNumber;
+ }
+
+private:
+
+ HANDLE m_hFile;
+ int64_t m_fileSize;
+ int m_methodContextNumber;
+ MethodContext* m_mc;
+ LARGE_INTEGER m_pos;
+
+ // If m_indexCount==-1, use all method contexts. Otherwise, m_indexCount is the number of elements in the
+ // m_indexes array, which contains a sorted set of method context indexes to return. In this case, m_index
+ // is the index of the current element in m_indexes.
+ const int m_indexCount;
+ int m_index;
+ const int* m_indexes;
+
+ // Should we log a progress report as we are loading the method contexts?
+ // The timer is only used when m_progressReport==true.
+ bool m_progressReport;
+ SimpleTimer* m_timer;
+}; \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp
new file mode 100644
index 0000000000..1e375e0d6a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp
@@ -0,0 +1,469 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContextReader.cpp - Abstraction for reading MethodContexts
+// Should eventually support multithreading
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "tocfile.h"
+#include "methodcontextreader.h"
+#include "methodcontext.h"
+#include "logging.h"
+#include "runtimedetails.h"
+
+// Just a helper...
+HANDLE MethodContextReader::OpenFile(const char *inputFile, DWORD flags)
+{
+ HANDLE fileHandle = CreateFileA(inputFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | flags, NULL);
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open file '%s'. GetLastError()=%u", inputFile, GetLastError());
+ }
+ return fileHandle;
+}
+
+static std::string to_lower(const std::string &input)
+{
+ std::string res = input;
+ std::transform(input.cbegin(), input.cend(), res.begin(), tolower);
+ return res;
+}
+
+// Looks for a file named foo.origSuffix.newSuffix or foo.newSuffix
+// but only if foo.origSuffix exists.
+//
+// Note: filename extensions must be lower-case, even on case-sensitive file systems!
+std::string MethodContextReader::CheckForPairedFile(const std::string &fileName, const std::string &origSuffix, const std::string &newSuffix)
+{
+ std::string tmp = to_lower(origSuffix);
+
+ // First, check to see if foo.origSuffix exists
+ size_t suffix_offset = fileName.find_last_of('.');
+ if ((SSIZE_T)suffix_offset <= 0
+ || (tmp != to_lower(fileName.substr(suffix_offset)))
+ || (GetFileAttributesA(fileName.c_str()) == INVALID_FILE_ATTRIBUTES))
+ return std::string();
+
+ // next, check foo.orig.new from foo.orig
+ tmp = fileName + newSuffix;
+ if (GetFileAttributesA(tmp.c_str()) != INVALID_FILE_ATTRIBUTES)
+ return tmp;
+
+ // Now let's check for foo.new from foo.new.orig
+ tmp = fileName.substr(0, suffix_offset);
+ if (GetFileAttributesA(tmp.c_str()) != INVALID_FILE_ATTRIBUTES)
+ return tmp;
+
+ // Finally, lets try foo.orig from foo.new
+ tmp += newSuffix;
+ if (GetFileAttributesA(tmp.c_str()) != INVALID_FILE_ATTRIBUTES)
+ return tmp;
+
+ return std::string();
+}
+
+
+MethodContextReader::MethodContextReader(const char *inputFileName, const int *indexes, int indexCount, char *hash, int offset, int increment)
+ : fileHandle(INVALID_HANDLE_VALUE)
+ , fileSize(0)
+ , curMCIndex(0)
+ , Indexes(indexes)
+ , IndexCount(indexCount)
+ , curIndexPos(0)
+ , Hash(hash)
+ , curTOCIndex(0)
+ , Offset(offset)
+ , Increment(increment)
+{
+ this->mutex = CreateMutexA(NULL, FALSE, nullptr);
+
+ std::string tocFileName, mchFile;
+
+ // First, check to see if they passed an MCH file (look for a paired MCT file)
+ tocFileName = MethodContextReader::CheckForPairedFile(inputFileName, ".mch", ".mct");
+ if (!tocFileName.empty())
+ {
+ mchFile = inputFileName;
+ }
+ else
+ {
+ // Okay, it wasn't an MCH file, let's check to see if it was an MCT file
+ // so check for a paired MCH file instead
+ mchFile = MethodContextReader::CheckForPairedFile(inputFileName, ".mct", ".mch");
+ if (!mchFile.empty())
+ {
+ tocFileName = inputFileName;
+ }
+ else
+ {
+ mchFile = inputFileName;
+ }
+ }
+
+ if (!tocFileName.empty())
+ this->tocFile.LoadToc(tocFileName.c_str());
+
+ // we'll get here even if we don't have a valid index file
+ this->fileHandle = OpenFile(mchFile.c_str(), (this->hasTOC() && this->hasIndex()) ? FILE_ATTRIBUTE_NORMAL : FILE_FLAG_SEQUENTIAL_SCAN);
+ if (this->fileHandle != INVALID_HANDLE_VALUE)
+ {
+ GetFileSizeEx(this->fileHandle, (PLARGE_INTEGER)&this->fileSize);
+ }
+}
+
+MethodContextReader::~MethodContextReader()
+{
+ if (fileHandle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(this->fileHandle);
+ }
+
+ CloseHandle(this->mutex);
+}
+
+bool MethodContextReader::AcquireLock()
+{
+ DWORD res = WaitForSingleObject(this->mutex, INFINITE);
+ return (res == WAIT_OBJECT_0);
+}
+
+void MethodContextReader::ReleaseLock()
+{
+ ReleaseMutex(this->mutex);
+}
+
+bool MethodContextReader::atEof()
+{
+ __int64 pos = 0;
+ SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_CURRENT); // LARGE_INTEGER is a crime against humanity
+ return pos == this->fileSize;
+}
+
+MethodContextBuffer MethodContextReader::ReadMethodContextNoLock(bool justSkip)
+{
+ DWORD bytesRead;
+ char buff[512];
+ unsigned int totalLen = 0;
+ if (atEof())
+ {
+ return MethodContextBuffer();
+ }
+ Assert(ReadFile(this->fileHandle, buff, 2 + sizeof(unsigned int), &bytesRead, NULL) == TRUE);
+ AssertMsg((buff[0] == 'm') && (buff[1] == 'c'), "Didn't find magic number");
+ memcpy(&totalLen, &buff[2], sizeof(unsigned int));
+ if (justSkip)
+ {
+ __int64 pos = totalLen + 2;
+ // Just move the file pointer ahead the correct number of bytes
+ AssertMsg(SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_CURRENT) == TRUE, "SetFilePointerEx failed (Error %X)", GetLastError());
+
+ //Increment curMCIndex as we advanced the file pointer by another MC
+ ++curMCIndex;
+
+ return MethodContextBuffer(0);
+ }
+ else
+ {
+ unsigned char *buff2 = new unsigned char[totalLen + 2]; //total + End Canary
+ Assert(ReadFile(this->fileHandle, buff2, totalLen + 2, &bytesRead, NULL) == TRUE);
+
+ //Increment curMCIndex as we read another MC
+ ++curMCIndex;
+
+ return MethodContextBuffer(buff2, totalLen);
+ }
+}
+
+MethodContextBuffer MethodContextReader::ReadMethodContext(bool acquireLock, bool justSkip)
+{
+ if (acquireLock && !this->AcquireLock())
+ {
+ LogError("Can't acquire the reader lock!");
+ return MethodContextBuffer(-1);
+ }
+
+ struct Param {
+ MethodContextReader* pThis;
+ MethodContextBuffer ret;
+ bool justSkip;
+ } param;
+ param.pThis = this;
+ param.ret = MethodContextBuffer(-2);
+ param.justSkip = justSkip;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->ret = pParam->pThis->ReadMethodContextNoLock(pParam->justSkip);
+ }
+ PAL_FINALLY
+ {
+ this->ReleaseLock();
+ }
+ PAL_ENDTRY
+
+ return param.ret;
+}
+
+// Read a method context buffer from the ContextCollection
+// (either a hive [single] or an index)
+MethodContextBuffer MethodContextReader::GetNextMethodContext()
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException
+ {
+ MethodContextReader* pThis;
+ MethodContextBuffer mcb;
+ } param;
+ param.pThis = this;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ pParam->mcb = pParam->pThis->GetNextMethodContextHelper();
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop)
+ {
+ LogError("Method %d is of low integrity.", GetMethodContextIndex());
+ param.mcb = MethodContextBuffer(-1);
+ }
+ PAL_ENDTRY
+
+ return param.mcb;
+}
+
+MethodContextBuffer MethodContextReader::GetNextMethodContextHelper()
+{
+ //If we have an offset/increment combo
+ if (this->Offset > 0 && this->Increment > 0)
+ return GetNextMethodContextFromOffsetIncrement();
+
+ //If we have an index
+ if (this->hasIndex())
+ {
+ if (this->curIndexPos < this->IndexCount)
+ {
+ //If we are not done with all of them
+ return GetNextMethodContextFromIndexes();
+ }
+ else //We are done with all of them, return
+ return MethodContextBuffer();
+ }
+
+ //If we have a hash
+ if (this->Hash != nullptr)
+ return GetNextMethodContextFromHash();
+
+ //If we don't have any of these options return all MCs one by one
+ return this->ReadMethodContext(true);
+}
+
+// Read a method context buffer from the ContextCollection using Indexes
+MethodContextBuffer MethodContextReader::GetNextMethodContextFromIndexes()
+{
+ //Assert if we don't have an Index or we are done with all the indexes
+ Assert(this->hasIndex() && this->curIndexPos < this->IndexCount);
+
+ if (this->hasTOC())
+ {
+ // If we have an index & we have a TOC, we can just jump to that method!
+ return this->GetSpecificMethodContext(this->Indexes[this->curIndexPos++]);
+ }
+ else
+ {
+ // Find the current method (either #0, or the previous index)
+ int curMethod = this->curIndexPos ? this->Indexes[this->curIndexPos - 1] : 0;
+ // Get the next method
+ int nextMethod = this->Indexes[this->curIndexPos++];
+ // Skip over methods until we get to the right now
+ while (++curMethod < nextMethod)
+ {
+ // Skip a method context
+ MethodContextBuffer mcb = this->ReadMethodContext(true, true);
+ if (mcb.allDone() || mcb.Error())
+ return mcb;
+ }
+ }
+ return this->ReadMethodContext(true);
+}
+
+// Read a method context buffer from the ContextCollection using Hash
+MethodContextBuffer MethodContextReader::GetNextMethodContextFromHash()
+{
+ //Assert if we don't have a valid hash
+ Assert(this->Hash != nullptr);
+
+ if (this->hasTOC())
+ {
+ //We have a TOC so lets go through the TOCElements
+ //one-by-one till we find a matching hash
+ for (; curTOCIndex < (int)this->tocFile.GetTocCount(); curTOCIndex++)
+ {
+ if (_strnicmp(this->Hash, this->tocFile.GetElementPtr(curTOCIndex)->Hash, MD5_HASH_BUFFER_SIZE) == 0)
+ {
+ //We found a match, return this specific method
+ return this->GetSpecificMethodContext(this->tocFile.GetElementPtr(curTOCIndex++)->Number);
+ }
+ }
+
+ //No more matches in the TOC for our hash value
+ return MethodContextBuffer();
+ }
+ else
+ {
+ // Keep reading all MCs until we hit a match
+ //or we reach the end or hit an error
+ while (true)
+ {
+ // Read a method context
+ // we can't skip because we need to calculate hashes
+ MethodContextBuffer mcb = this->ReadMethodContext(true, false);
+ if (mcb.allDone() || mcb.Error())
+ return mcb;
+
+ char mcHash[MD5_HASH_BUFFER_SIZE];
+
+ //Create a temporary copy of mcb.buff plus ending 2-byte canary
+ //this will get freed up by MethodContext constructor
+ unsigned char *buff = new unsigned char[mcb.size + 2];
+ memcpy(buff, mcb.buff, mcb.size + 2);
+
+ MethodContext *mc;
+
+ if (!MethodContext::Initialize(-1, buff, mcb.size, &mc))
+ return MethodContextBuffer(-1);
+
+ mc->dumpMethodMD5HashToBuffer(mcHash, MD5_HASH_BUFFER_SIZE);
+ delete mc;
+
+ if (_strnicmp(this->Hash, mcHash, MD5_HASH_BUFFER_SIZE) == 0)
+ {
+ //We found a match, return this specific method
+ return mcb;
+ }
+ }
+ }
+
+ //We should never get here under normal conditions
+ AssertMsg(true, "Unexpected condition hit while reading input file.");
+ return MethodContextBuffer(-1);
+}
+
+// Read a method context buffer from the ContextCollection using offset/increment
+MethodContextBuffer MethodContextReader::GetNextMethodContextFromOffsetIncrement()
+{
+ //Assert if we don't have a valid increment/offset combo
+ Assert(this->Offset > 0 && this->Increment > 0);
+
+ int methodNumber = this->curMCIndex > 0 ? this->curMCIndex + this->Increment : this->Offset;
+
+ if (this->hasTOC())
+ {
+ //Check if we are within the TOC
+ if ((int)this->tocFile.GetTocCount() >= methodNumber)
+ {
+ //We have a TOC so we can request a specific method context
+ return this->GetSpecificMethodContext(methodNumber);
+ }
+ else
+ return MethodContextBuffer();
+ }
+ else
+ {
+ // Keep skipping MCs until we get to the one we need to return
+ while (this->curMCIndex + 1 < methodNumber)
+ {
+ //skip over a method
+ MethodContextBuffer mcb = this->ReadMethodContext(true, true);
+ if (mcb.allDone() || mcb.Error())
+ return mcb;
+ }
+ }
+ return this->ReadMethodContext(true);
+}
+
+bool MethodContextReader::hasIndex()
+{
+ return this->IndexCount > 0;
+}
+
+bool MethodContextReader::hasTOC()
+{
+ return this->tocFile.GetTocCount() > 0;
+}
+
+bool MethodContextReader::isValid()
+{
+ return this->fileHandle != INVALID_HANDLE_VALUE && this->mutex != INVALID_HANDLE_VALUE;
+}
+
+double MethodContextReader::PercentComplete()
+{
+ if (this->hasIndex() && this->hasTOC())
+ {
+ // Best estimate I can come up with...
+ return 100.0 * (double)this->curIndexPos / (double)this->IndexCount;
+ }
+ this->AcquireLock();
+ __int64 pos = 0;
+ SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_CURRENT);
+ this->ReleaseLock();
+ return 100.0 * (double)pos / (double)this->fileSize;
+}
+
+// Binary search to get this method number from the index
+// Returns -1 for not found, or -2 for not indexed
+// Interview question alert: hurray for CLR headers incompatibility with STL :-(
+// Note that TOC is 0 based and MC# are 1 based!
+__int64 MethodContextReader::GetOffset(unsigned int methodNumber)
+{
+ if (!this->hasTOC())
+ return -2;
+ size_t high = this->tocFile.GetTocCount() - 1;
+ size_t low = 0;
+ while (low <= high)
+ {
+ size_t pos = (high + low) / 2;
+ unsigned int num = this->tocFile.GetElementPtr(pos)->Number;
+ if (num == methodNumber)
+ return this->tocFile.GetElementPtr(pos)->Offset;
+ if (num > methodNumber)
+ high = pos - 1;
+ else
+ low = pos + 1;
+ }
+ return -1;
+}
+
+MethodContextBuffer MethodContextReader::GetSpecificMethodContext(unsigned int methodNumber)
+{
+ __int64 pos = this->GetOffset(methodNumber);
+ if (pos < 0)
+ {
+ return MethodContextBuffer(-3);
+ }
+
+ // Take the IO lock before we set the file pointer, so we can do this on multiple threads
+ if (!this->AcquireLock())
+ {
+ return MethodContextBuffer(-2);
+ }
+ if (SetFilePointerEx(this->fileHandle, *(PLARGE_INTEGER)&pos, (PLARGE_INTEGER)&pos, FILE_BEGIN))
+ {
+ // ReadMethodContext will release the lock, but we already acquired it
+ MethodContextBuffer mcb = this->ReadMethodContext(false);
+
+ //The curMCIndex value updated by ReadMethodContext() is incorrect
+ //since we are repositioning the file pointer we need to update it
+ curMCIndex = methodNumber;
+
+ return mcb;
+ }
+ else
+ {
+ // Don't forget to release the lock!
+ this->ReleaseLock();
+ return MethodContextBuffer(-4);
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h
new file mode 100644
index 0000000000..0068fa231f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.h
@@ -0,0 +1,123 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// MethodContextReader.h - Abstraction for reading MethodContexts
+// Should eventually support multithreading
+//----------------------------------------------------------
+#ifndef _MethodContextReader
+#define _MethodContextReader
+
+#include "methodcontext.h"
+#include "tocfile.h"
+
+struct MethodContextBuffer
+{
+private:
+ static const int Completed = 0x1234abcd;
+public:
+ unsigned char *buff;
+ DWORD size;
+
+ MethodContextBuffer() : buff(nullptr), size(Completed) {}
+ MethodContextBuffer(DWORD error) : buff(nullptr), size(error) {}
+ MethodContextBuffer(unsigned char *b, DWORD e) : buff(b), size(e) {}
+
+ bool allDone() { return size == Completed && buff == nullptr; }
+ bool Error() { return size != 0 && size != Completed && buff == nullptr; }
+};
+
+// The pack(4) directive is so that each entry is 12 bytes, intead of 16
+#pragma pack(push)
+#pragma pack(4)
+class MethodContextReader
+{
+private:
+
+ // The MC/MCH file
+ HANDLE fileHandle;
+
+ // The size of the MC/MCH file
+ __int64 fileSize;
+
+ // Current MC index in the input MC/MCH file
+ int curMCIndex;
+
+ // The synchronization mutex
+ HANDLE mutex;
+ bool AcquireLock();
+ void ReleaseLock();
+
+ TOCFile tocFile;
+
+ // Method ranges to process
+ // If you have an index file, these things get processed
+ // much faster, now
+ const int *Indexes;
+ int IndexCount;
+ int curIndexPos;
+
+ // Method hash to process
+ // If you have an index file, these things get processed
+ // much faster, now
+ char *Hash;
+ int curTOCIndex;
+
+ // Offset/increment if running in parallel mode
+ // If you have an index file, these things get processed
+ // much faster, now
+ int Offset;
+ int Increment;
+
+ // Binary search to get this method number from the index
+ // Returns -1 for not found, or -2 for not indexed
+ __int64 GetOffset(unsigned int methodNumber);
+
+ // Just a helper...
+ static HANDLE OpenFile(const char *inputFile, DWORD flags = FILE_ATTRIBUTE_NORMAL);
+
+ MethodContextBuffer ReadMethodContextNoLock(bool justSkip = false);
+ MethodContextBuffer ReadMethodContext(bool acquireLock, bool justSkip = false);
+ MethodContextBuffer GetSpecificMethodContext(unsigned int methodNumber);
+
+ MethodContextBuffer GetNextMethodContextFromIndexes();
+ MethodContextBuffer GetNextMethodContextFromHash();
+ MethodContextBuffer GetNextMethodContextFromOffsetIncrement();
+ MethodContextBuffer GetNextMethodContextHelper();
+
+ // Looks for a file named foo.origSuffix.newSuffix or foo.newSuffix
+ // but only if foo.origSuffix exists
+ static std::string CheckForPairedFile(const std::string &fileName, const std::string &origSuffix, const std::string &newSuffix);
+
+ // are we're at the end of the file...
+ bool atEof();
+
+ // Do we have a valid TOC?
+ bool hasTOC();
+
+ // Do we have a valid index?
+ bool hasIndex();
+
+public:
+
+ MethodContextReader(const char *inputFileName, const int *indexes = nullptr, int indexCount = -1, char *hash = nullptr, int offset = -1, int increment = -1);
+ ~MethodContextReader();
+
+ // Read a method context buffer from the ContextCollection
+ // (either a hive [single] or an index)
+ MethodContextBuffer GetNextMethodContext();
+ // No C++ exceptions, so the constructor has to always succeed...
+ bool isValid();
+ double PercentComplete();
+
+ //Returns the index of the last MethodContext read by GetNextMethodContext
+ inline int GetMethodContextIndex()
+ {
+ return curMCIndex;
+ }
+};
+#pragma pack(pop)
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/registertablexarch.h b/src/ToolBox/superpmi/superpmi-shared/registertablexarch.h
new file mode 100644
index 0000000000..4704881082
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/registertablexarch.h
@@ -0,0 +1,124 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// RegisterTableXarch.h - X macro table for x86/AMD64 registers, for use with DISX86
+//----------------------------------------------------------
+
+#ifndef REGDEF
+#error Must define REGDEF macro before including this file
+#endif
+
+/*
+REGDEF(msdisID, name)
+*/
+
+// 32 bit general purpose registers
+REGDEF(DISX86::REGA::regaEax, "eax")
+REGDEF(DISX86::REGA::regaEcx, "ecx")
+REGDEF(DISX86::REGA::regaEdx, "edx")
+REGDEF(DISX86::REGA::regaEbx, "ebx")
+REGDEF(DISX86::REGA::regaEsp, "esp")
+REGDEF(DISX86::REGA::regaEbp, "ebp")
+REGDEF(DISX86::REGA::regaEsi, "esi")
+REGDEF(DISX86::REGA::regaEdi, "edi")
+REGDEF(DISX86::REGA::regaR8d, "r8d")
+REGDEF(DISX86::REGA::regaR9d, "r9d")
+REGDEF(DISX86::REGA::regaR10d, "r10d")
+REGDEF(DISX86::REGA::regaR11d, "r11d")
+REGDEF(DISX86::REGA::regaR12d, "r12d")
+REGDEF(DISX86::REGA::regaR13d, "r13d")
+REGDEF(DISX86::REGA::regaR14d, "r14d")
+REGDEF(DISX86::REGA::regaR15d, "r15d")
+
+// 64 bit general purpose registers
+REGDEF(DISX86::REGA::regaRax, "rax")
+REGDEF(DISX86::REGA::regaRcx, "rcx")
+REGDEF(DISX86::REGA::regaRdx, "rdx")
+REGDEF(DISX86::REGA::regaRbx, "rbx")
+REGDEF(DISX86::REGA::regaRsp, "rsp")
+REGDEF(DISX86::REGA::regaRbp, "rbp")
+REGDEF(DISX86::REGA::regaRsi, "rsi")
+REGDEF(DISX86::REGA::regaRdi, "rdi")
+REGDEF(DISX86::REGA::regaR8, "r8")
+REGDEF(DISX86::REGA::regaR9, "r9")
+REGDEF(DISX86::REGA::regaR10, "r10")
+REGDEF(DISX86::REGA::regaR11, "r11")
+REGDEF(DISX86::REGA::regaR12, "r12")
+REGDEF(DISX86::REGA::regaR13, "r13")
+REGDEF(DISX86::REGA::regaR14, "r14")
+REGDEF(DISX86::REGA::regaR15, "r15")
+
+// 16 bit general purpose registers
+REGDEF(DISX86::REGA::regaAx, "ax")
+REGDEF(DISX86::REGA::regaCx, "cx")
+REGDEF(DISX86::REGA::regaDx, "dx")
+REGDEF(DISX86::REGA::regaBx, "bx")
+REGDEF(DISX86::REGA::regaSp, "sp")
+REGDEF(DISX86::REGA::regaBp, "bp")
+REGDEF(DISX86::REGA::regaSi, "si")
+REGDEF(DISX86::REGA::regaDi, "di")
+REGDEF(DISX86::REGA::regaR8w, "r8w")
+REGDEF(DISX86::REGA::regaR9w, "r9w")
+REGDEF(DISX86::REGA::regaR10w, "r10w")
+REGDEF(DISX86::REGA::regaR11w, "r11w")
+REGDEF(DISX86::REGA::regaR12w, "r12w")
+REGDEF(DISX86::REGA::regaR13w, "r13w")
+REGDEF(DISX86::REGA::regaR14w, "r14w")
+REGDEF(DISX86::REGA::regaR15w, "r15w")
+
+// 8 bit general purpose registers
+REGDEF(DISX86::REGA::regaAl, "al")
+REGDEF(DISX86::REGA::regaCl, "cl")
+REGDEF(DISX86::REGA::regaDl, "dl")
+REGDEF(DISX86::REGA::regaBl, "bl")
+REGDEF(DISX86::REGA::regaSpl, "spl")
+REGDEF(DISX86::REGA::regaBpl, "bpl")
+REGDEF(DISX86::REGA::regaSil, "sil")
+REGDEF(DISX86::REGA::regaDil, "dil")
+REGDEF(DISX86::REGA::regaR8b, "r8b")
+REGDEF(DISX86::REGA::regaR9b, "r9b")
+REGDEF(DISX86::REGA::regaR10b, "r10b")
+REGDEF(DISX86::REGA::regaR11b, "r11b")
+REGDEF(DISX86::REGA::regaR12b, "r12b")
+REGDEF(DISX86::REGA::regaR13b, "r13b")
+REGDEF(DISX86::REGA::regaR14b, "r14b")
+REGDEF(DISX86::REGA::regaR15b, "r15b")
+
+// 8 bit general purpose registers
+REGDEF(DISX86::REGA::regaAh, "ah")
+REGDEF(DISX86::REGA::regaCh, "ch")
+REGDEF(DISX86::REGA::regaDh, "dh")
+REGDEF(DISX86::REGA::regaBh, "bh")
+
+// x87 floating point stack
+REGDEF(DISX86::REGA::regaSt0, "st0")
+REGDEF(DISX86::REGA::regaSt1, "st1")
+REGDEF(DISX86::REGA::regaSt2, "st2")
+REGDEF(DISX86::REGA::regaSt3, "st3")
+REGDEF(DISX86::REGA::regaSt4, "st4")
+REGDEF(DISX86::REGA::regaSt5, "st5")
+REGDEF(DISX86::REGA::regaSt6, "st6")
+REGDEF(DISX86::REGA::regaSt7, "st7")
+
+// XMM registers
+REGDEF(DISX86::REGA::regaXmm0, "xmm0")
+REGDEF(DISX86::REGA::regaXmm1, "xmm1")
+REGDEF(DISX86::REGA::regaXmm2, "xmm2")
+REGDEF(DISX86::REGA::regaXmm3, "xmm3")
+REGDEF(DISX86::REGA::regaXmm4, "xmm4")
+REGDEF(DISX86::REGA::regaXmm5, "xmm5")
+REGDEF(DISX86::REGA::regaXmm6, "xmm6")
+REGDEF(DISX86::REGA::regaXmm7, "xmm7")
+REGDEF(DISX86::REGA::regaXmm8, "xmm8")
+REGDEF(DISX86::REGA::regaXmm9, "xmm9")
+REGDEF(DISX86::REGA::regaXmm10, "xmm10")
+REGDEF(DISX86::REGA::regaXmm11, "xmm11")
+REGDEF(DISX86::REGA::regaXmm12, "xmm12")
+REGDEF(DISX86::REGA::regaXmm13, "xmm13")
+REGDEF(DISX86::REGA::regaXmm14, "xmm14")
+REGDEF(DISX86::REGA::regaXmm15, "xmm15")
+
+#undef REGDEF
diff --git a/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h b/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h
new file mode 100644
index 0000000000..4677299e3a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h
@@ -0,0 +1,43 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// RuntimeDetails.h - the collection of runtime includes that we need to access.
+//----------------------------------------------------------
+#ifndef _RuntimeDetails
+#define _RuntimeDetails
+
+//Our little collection of enough of the CLR data to get the JIT up and working...
+#define FEATURE_CLRSQM
+
+#if !defined(_TARGET_AMD64_) && !defined(_TARGET_X86_) && !defined(_TARGET_ARM64_) && !defined(_TARGET_ARM_)
+#if defined(_M_X64)
+#define _TARGET_AMD64_ 1
+#elif defined(_M_IX86)
+#define _TARGET_X86_ 1
+#endif
+#endif // _TARGET_* not previously defined
+
+#define __EXCEPTION_RECORD_CLR //trick out clrntexception.h to not include another exception record....
+
+#include <mscoree.h>
+#include <corjit.h>
+#include <utilcode.h>
+
+///Turn back on direct access to a few OS level things...
+#undef HeapCreate
+#undef HeapAlloc
+#undef HeapFree
+#undef HeapDestroy
+#undef TlsAlloc
+#undef TlsGetValue
+#undef TlsSetValue
+
+//Jit Exports
+typedef ICorJitCompiler* (__stdcall *PgetJit)();
+typedef void (__stdcall *PjitStartup)(ICorJitHost* host);
+typedef void (__stdcall *PsxsJitStartup)(CoreClrCallbacks const & cccallbacks);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp b/src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp
new file mode 100644
index 0000000000..e9c76339bc
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/simpletimer.cpp
@@ -0,0 +1,56 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "logging.h"
+#include "simpletimer.h"
+
+SimpleTimer::SimpleTimer()
+{
+ start.QuadPart = 0;
+ stop.QuadPart = 0;
+
+ BOOL retVal = ::QueryPerformanceFrequency(&proc_freq);
+ if(retVal == FALSE)
+ {
+ LogDebug("SimpleTimer::SimpleTimer unable to QPF. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+SimpleTimer::~SimpleTimer()
+{
+}
+
+void SimpleTimer::Start()
+{
+ BOOL retVal = ::QueryPerformanceCounter(&start);
+ if(retVal == FALSE)
+ {
+ LogDebug("SimpleTimer::Start unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+void SimpleTimer::Stop()
+{
+ BOOL retVal = ::QueryPerformanceCounter(&stop);
+ if(retVal == FALSE)
+ {
+ LogDebug("SimpleTimer::Stop unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+double SimpleTimer::GetMilliseconds()
+{
+ return GetSeconds()*1000.0;
+}
+
+double SimpleTimer::GetSeconds()
+{
+ return ((stop.QuadPart - start.QuadPart) / (double)proc_freq.QuadPart);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shared/simpletimer.h b/src/ToolBox/superpmi/superpmi-shared/simpletimer.h
new file mode 100644
index 0000000000..b5edd7c65e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/simpletimer.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _SimpleTimer
+#define _SimpleTimer
+
+class SimpleTimer
+{
+public:
+ SimpleTimer();
+ ~SimpleTimer();
+
+ void Start();
+ void Stop();
+ double GetMilliseconds();
+ double GetSeconds();
+
+private:
+ LARGE_INTEGER proc_freq;
+ LARGE_INTEGER start;
+ LARGE_INTEGER stop;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp b/src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp
new file mode 100644
index 0000000000..e99e6f4ae2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/spmiutil.cpp
@@ -0,0 +1,109 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SPMIUtil.cpp - General utility functions
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "logging.h"
+#include "spmiutil.h"
+
+bool breakOnDebugBreakorAV = false;
+
+void DebugBreakorAV(int val)
+{
+ if (IsDebuggerPresent())
+ {
+ if (val == 0)
+ __debugbreak();
+ if (breakOnDebugBreakorAV)
+ __debugbreak();
+ }
+
+ int exception_code = EXCEPTIONCODE_DebugBreakorAV + val;
+ // assert((EXCEPTIONCODE_DebugBreakorAV <= exception_code) && (exception_code < EXCEPTIONCODE_DebugBreakorAV_MAX))
+ LogException(exception_code, "DebugBreak or AV Exception %d", val);
+}
+
+char* GetEnvironmentVariableWithDefaultA(const char* envVarName, const char* defaultValue)
+{
+ char* retString = nullptr;
+
+ // Figure out how much space we need to allocate
+ DWORD dwRetVal = ::GetEnvironmentVariableA(envVarName, nullptr, 0);
+ if (dwRetVal != 0)
+ {
+ retString = new char[dwRetVal];
+ dwRetVal = ::GetEnvironmentVariableA(envVarName, retString, dwRetVal);
+ }
+ else
+ {
+ if (defaultValue != nullptr)
+ {
+ dwRetVal = (DWORD)strlen(defaultValue) + 1; // add one for null terminator
+ retString = new char[dwRetVal];
+ memcpy_s(retString, dwRetVal, defaultValue, dwRetVal);
+ }
+ }
+
+ return retString;
+}
+
+WCHAR* GetEnvironmentVariableWithDefaultW(const WCHAR* envVarName, const WCHAR* defaultValue)
+{
+ WCHAR* retString = nullptr;
+
+ // Figure out how much space we need to allocate
+ DWORD dwRetVal = ::GetEnvironmentVariableW(envVarName, nullptr, 0);
+ if (dwRetVal != 0)
+ {
+ retString = new WCHAR[dwRetVal];
+ dwRetVal = ::GetEnvironmentVariableW(envVarName, retString, dwRetVal);
+ }
+ else
+ {
+ if (defaultValue != nullptr)
+ {
+ dwRetVal = (DWORD)wcslen(defaultValue) + 1; // add one for null terminator
+ retString = new WCHAR[dwRetVal];
+ memcpy_s(retString, dwRetVal * sizeof(WCHAR), defaultValue, dwRetVal * sizeof(WCHAR));
+ }
+ }
+
+ return retString;
+}
+
+#ifdef FEATURE_PAL
+// For some reason, the PAL doesn't have GetCommandLineA(). So write it.
+LPSTR GetCommandLineA()
+{
+ LPSTR pCmdLine = nullptr;
+ LPWSTR pwCmdLine = GetCommandLineW();
+
+ if (pwCmdLine != nullptr)
+ {
+ // Convert to ASCII
+
+ int n = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, nullptr, 0, nullptr, nullptr);
+ if (n == 0)
+ {
+ LogError("MultiByteToWideChar failed %d", GetLastError());
+ return nullptr;
+ }
+
+ pCmdLine = new char[n];
+
+ int n2 = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, pCmdLine, n, nullptr, nullptr);
+ if ((n2 == 0) || (n2 != n))
+ {
+ LogError("MultiByteToWideChar failed %d", GetLastError());
+ return nullptr;
+ }
+ }
+
+ return pCmdLine;
+}
+#endif // FEATURE_PAL
diff --git a/src/ToolBox/superpmi/superpmi-shared/spmiutil.h b/src/ToolBox/superpmi/superpmi-shared/spmiutil.h
new file mode 100644
index 0000000000..6c8db3948a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/spmiutil.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//----------------------------------------------------------
+// SPMIUtil.h - General utility functions
+//----------------------------------------------------------
+#ifndef _SPMIUtil
+#define _SPMIUtil
+
+#include "methodcontext.h"
+
+extern bool breakOnDebugBreakorAV;
+
+extern void DebugBreakorAV(int val); //Global(ish) error handler
+
+extern char* GetEnvironmentVariableWithDefaultA(const char* envVarName, const char* defaultValue = nullptr);
+
+extern WCHAR* GetEnvironmentVariableWithDefaultW(const WCHAR* envVarName, const WCHAR* defaultValue = nullptr);
+
+#ifdef FEATURE_PAL
+extern LPSTR GetCommandLineA();
+#endif // FEATURE_PAL
+
+#endif // !_SPMIUtil
diff --git a/src/ToolBox/superpmi/superpmi-shared/standardpch.cpp b/src/ToolBox/superpmi/superpmi-shared/standardpch.cpp
new file mode 100644
index 0000000000..b61444351e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/standardpch.cpp
@@ -0,0 +1,7 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// Don't touch this file: it's just here to create a precompiled header...
+#include "standardpch.h"
diff --git a/src/ToolBox/superpmi/superpmi-shared/standardpch.h b/src/ToolBox/superpmi/superpmi-shared/standardpch.h
new file mode 100644
index 0000000000..4164244085
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/standardpch.h
@@ -0,0 +1,106 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// The point of a PCH file is to never reparse files that never change.
+// Only include files here that will almost NEVER change. Headers for the project
+// itself are probably inappropriate, because if you change them, the entire
+// project will require a recompile. Generally just put SDK style stuff here...
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif // WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#ifdef INTERNAL_BUILD
+// There are a few features that reference Microsoft internal resources. We can't build these
+// in the open source version.
+#define USE_MSVCDIS
+
+// Disable CoreDisTools until coredistools.dll is statically-linked to the CRT, or until it is delayload linked.
+//#define USE_COREDISTOOLS
+#endif // INTERNAL_BUILD
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4996) // The compiler encountered a deprecated declaration.
+
+// On Windows, we build against PAL macros that convert to Windows SEH. But we don't want all the
+// Contract stuff that normally gets pulled it. Defining JIT_BUILD prevents this, just as it does
+// when building the JIT using parts of utilcode.
+#define JIT_BUILD
+
+// Defining this prevents:
+// error C2338 : / RTCc rejects conformant code, so it isn't supported by the C++ Standard Library.
+// Either remove this compiler option, or define _ALLOW_RTCc_IN_STL to acknowledge that you have received this warning.
+#ifndef _ALLOW_RTCc_IN_STL
+#define _ALLOW_RTCc_IN_STL
+#endif
+
+#define MSC_ONLY(x) x
+#else // !_MSC_VER
+#define MSC_ONLY(x)
+#endif // !_MSC_VER
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif // _CRT_SECURE_NO_WARNINGS
+
+#define _CRT_RAND_S
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <malloc.h>
+#include <assert.h>
+#include <wchar.h>
+#include <tchar.h>
+#include <specstrings.h>
+#include <math.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+// Getting STL to work with PAL is difficult, so reimplement STL functionality to not require it.
+#ifdef FEATURE_PAL
+#include "clr_std/string"
+#include "clr_std/algorithm"
+#else // !FEATURE_PAL
+#ifndef USE_STL
+#define USE_STL
+#endif // USE_STL
+#include <string>
+#include <algorithm>
+#endif // !FEATURE_PAL
+
+#ifdef USE_MSVCDIS
+#define DISLIB
+#include "..\external\msvcdis\inc\msvcdis.h"
+#include "..\external\msvcdis\inc\disx86.h"
+#include "..\external\msvcdis\inc\disarm64.h"
+#endif // USE_MSVCDIS
+
+#ifndef DIRECTORY_SEPARATOR_CHAR_A
+#define DIRECTORY_SEPARATOR_CHAR_A '\\'
+#endif
+#ifndef DIRECTORY_SEPARATOR_STR_A
+#define DIRECTORY_SEPARATOR_STR_A "\\"
+#endif
+
+#ifndef W
+#ifdef PLATFORM_UNIX
+#define W(str) u##str
+#else // PLATFORM_UNIX
+#define W(str) L##str
+#endif // PLATFORM_UNIX
+#endif // !W
+
+#ifdef FEATURE_PAL
+#define PLATFORM_SHARED_LIB_SUFFIX_A PAL_SHLIB_SUFFIX
+#else // !FEATURE_PAL
+#define PLATFORM_SHARED_LIB_SUFFIX_A ".dll"
+#endif // !FEATURE_PAL
+
+#define DEFAULT_REAL_JIT_NAME_A MAKEDLLNAME_A("clrjit2")
+#define DEFAULT_REAL_JIT_NAME_W MAKEDLLNAME_W("clrjit2")
diff --git a/src/ToolBox/superpmi/superpmi-shared/tocfile.cpp b/src/ToolBox/superpmi/superpmi-shared/tocfile.cpp
new file mode 100644
index 0000000000..c91ca89c93
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/tocfile.cpp
@@ -0,0 +1,79 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// TOCFile.cpp - Abstraction for reading a TOC file
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "tocfile.h"
+#include "logging.h"
+
+// Tries to load a Table of Contents
+void TOCFile::LoadToc(const char *inputFileName, bool validate)
+{
+ HANDLE hIndex = CreateFileA(inputFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hIndex == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open file '%s'. GetLastError()=%u", inputFileName, GetLastError());
+ return;
+ }
+
+ // Now read the index file
+ LARGE_INTEGER val; // I'm abusing LARGE_INTEGER here...
+ DWORD read;
+ if (!ReadFile(hIndex, &val, sizeof(val), &read, nullptr) ||
+ (val.u.LowPart != *(DWORD *)("INDX")))
+ {
+ CloseHandle(hIndex);
+ LogWarning("The index file %s is invalid: it seems to be missing the starting sentinel/length", inputFileName);
+ return;
+ }
+
+ this->m_tocCount = val.u.HighPart;
+ this->m_tocArray = new TOCElement[this->m_tocCount];
+
+ // Read the whole array
+ if (!ReadFile(hIndex, &this->m_tocArray[0], (DWORD)(this->m_tocCount * sizeof(TOCElement)), &read, nullptr) ||
+ (read != (DWORD)(this->m_tocCount * sizeof(TOCElement))))
+ {
+ CloseHandle(hIndex);
+ this->Clear();
+ LogWarning("The index file %s is invalid: it appears to be truncated.", inputFileName);
+ return;
+ }
+
+ // Get the last 4 byte token (more abuse of LARGE_INTEGER)
+ if (!ReadFile(hIndex, &val.u.HighPart, sizeof(DWORD), &read, nullptr) ||
+ (read != sizeof(DWORD)) ||
+ (val.u.LowPart != val.u.HighPart))
+ {
+ CloseHandle(hIndex);
+ this->Clear();
+ LogWarning("The index file %s is invalid: it appears to be missing the ending sentinel.", inputFileName);
+ return;
+ }
+
+ CloseHandle(hIndex);
+
+ if (validate)
+ {
+ int lastNum = -1;
+
+ // Quickly validate that the index is sorted
+ for (size_t i = 0; i < this->m_tocCount; i++)
+ {
+ int nextNum = this->m_tocArray[i].Number;
+ if (nextNum <= lastNum)
+ {
+ // It wasn't sorted: abort
+ this->Clear();
+ LogWarning("The index file %s is invalid: it is not sorted.", inputFileName);
+ return;
+ }
+ lastNum = nextNum;
+ }
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/tocfile.h b/src/ToolBox/superpmi/superpmi-shared/tocfile.h
new file mode 100644
index 0000000000..a0e7bde146
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/tocfile.h
@@ -0,0 +1,78 @@
+
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// TOCFile.h - Abstraction for reading a TOC file
+//----------------------------------------------------------
+#ifndef _TOCFile
+#define _TOCFile
+
+#include "methodcontext.h"
+
+class TOCElement
+{
+public:
+ __int64 Offset;
+ int Number;
+ char Hash[MD5_HASH_BUFFER_SIZE];
+
+ TOCElement()
+ {
+ }
+
+ TOCElement(int number, __int64 offset)
+ : Offset(offset)
+ , Number(number)
+ {
+ }
+};
+
+class TOCFile
+{
+private:
+
+ TOCElement* m_tocArray;
+ size_t m_tocCount;
+
+public:
+
+ TOCFile()
+ : m_tocArray(nullptr)
+ , m_tocCount(0)
+ {
+ }
+
+ ~TOCFile()
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ delete[] m_tocArray;
+ m_tocArray = nullptr;
+ m_tocCount = 0;
+ }
+
+ void LoadToc(const char *inputFileName, bool validate = true);
+
+ size_t GetTocCount()
+ {
+ return m_tocCount;
+ }
+
+ const TOCElement* GetElementPtr(size_t i)
+ {
+ if (i >= m_tocCount)
+ {
+ // error!
+ return nullptr;
+ }
+ return &m_tocArray[i];
+ }
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/typeutils.cpp b/src/ToolBox/superpmi/superpmi-shared/typeutils.cpp
new file mode 100644
index 0000000000..0b2eee6b4a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/typeutils.cpp
@@ -0,0 +1,169 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// TypeUtils.cpp - Utility code for working with managed types
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "typeutils.h"
+#include "errorhandling.h"
+
+// Returns a string representation of the given CorInfoType. The naming scheme is based on JITtype2varType
+// in src/jit/ee_il_dll.hpp.
+const char *TypeUtils::GetCorInfoTypeName(CorInfoType type)
+{
+ switch (type)
+ {
+ case CORINFO_TYPE_VOID:
+ return "void";
+
+ case CORINFO_TYPE_BOOL:
+ return "bool";
+
+ case CORINFO_TYPE_CHAR:
+ return "char";
+
+ case CORINFO_TYPE_BYTE:
+ return "byte";
+
+ case CORINFO_TYPE_UBYTE:
+ return "ubyte";
+
+ case CORINFO_TYPE_SHORT:
+ return "short";
+
+ case CORINFO_TYPE_USHORT:
+ return "ushort";
+
+ case CORINFO_TYPE_INT:
+ return "int";
+
+ case CORINFO_TYPE_UINT:
+ return "uint";
+
+ case CORINFO_TYPE_LONG:
+ return "long";
+
+ case CORINFO_TYPE_ULONG:
+ return "ulong";
+
+ case CORINFO_TYPE_FLOAT:
+ return "float";
+
+ case CORINFO_TYPE_DOUBLE:
+ return "double";
+
+ case CORINFO_TYPE_BYREF:
+ return "byref";
+
+ case CORINFO_TYPE_VALUECLASS:
+ case CORINFO_TYPE_REFANY:
+ return "struct";
+
+ case CORINFO_TYPE_STRING:
+ case CORINFO_TYPE_CLASS:
+ case CORINFO_TYPE_VAR:
+ return "ref";
+
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ // Emulates the JIT's concept of TYP_I_IMPL
+#if defined(_TARGET_AMD64_) // TODO: should be _TARGET_64BIT_
+ return "long";
+#else
+ return "int";
+#endif
+
+ case CORINFO_TYPE_PTR:
+ // The JIT just treats this as a TYP_I_IMPL because this isn't a GC root,
+ // but we don't care about GC-ness: we care about pointer-sized.
+ return "ptr";
+
+ default:
+ LogException(EXCEPTIONCODE_TYPEUTILS, "Unknown type passed into GetCorInfoTypeName (0x%x)", type);
+ return "UNKNOWN";
+ }
+}
+
+bool TypeUtils::IsFloatingPoint(CorInfoType type)
+{
+ return (type == CORINFO_TYPE_FLOAT || type == CORINFO_TYPE_DOUBLE);
+}
+
+bool TypeUtils::IsPointer(CorInfoType type)
+{
+ return (type == CORINFO_TYPE_STRING || type == CORINFO_TYPE_PTR ||
+ type == CORINFO_TYPE_BYREF || type == CORINFO_TYPE_CLASS);
+}
+
+bool TypeUtils::IsValueClass(CorInfoType type)
+{
+ return (type == CORINFO_TYPE_VALUECLASS || type == CORINFO_TYPE_REFANY);
+}
+
+// Determines if a value class, represented by the given class handle, is required to be passed
+// by reference (i.e. it cannot be stuffed as-is into a register or stack slot).
+bool TypeUtils::ValueClassRequiresByref(MethodContext *mc, CORINFO_CLASS_HANDLE clsHnd)
+{
+#if defined(_TARGET_AMD64_)
+ size_t size = mc->repGetClassSize(clsHnd);
+ return ((size > sizeof(void *)) || ((size & (size - 1)) != 0));
+#else
+ LogException(EXCEPTIONCODE_TYPEUTILS, "unsupported architecture", "");
+ return false;
+#endif
+}
+
+// Returns the size of the given CorInfoType. If there is no applicable size (e.g. CORINFO_TYPE_VOID,
+// CORINFO_TYPE_UNDEF), this throws an exception and returns -1. Taken largely from the MSDN documentation
+// for managed sizeof.
+size_t TypeUtils::SizeOfCorInfoType(CorInfoType type)
+{
+ switch (type)
+ {
+ case CORINFO_TYPE_BYTE:
+ case CORINFO_TYPE_UBYTE:
+ case CORINFO_TYPE_BOOL:
+ return sizeof(BYTE);
+
+ case CORINFO_TYPE_CHAR: // 2 bytes for Unicode
+ case CORINFO_TYPE_SHORT:
+ case CORINFO_TYPE_USHORT:
+ return sizeof(WORD);
+
+ case CORINFO_TYPE_INT:
+ case CORINFO_TYPE_UINT:
+ case CORINFO_TYPE_FLOAT:
+ return sizeof(DWORD);
+
+ case CORINFO_TYPE_LONG:
+ case CORINFO_TYPE_ULONG:
+ case CORINFO_TYPE_DOUBLE:
+ return sizeof(DWORDLONG);
+
+ case CORINFO_TYPE_NATIVEINT:
+ case CORINFO_TYPE_NATIVEUINT:
+ case CORINFO_TYPE_STRING:
+ case CORINFO_TYPE_PTR:
+ case CORINFO_TYPE_BYREF:
+ case CORINFO_TYPE_CLASS:
+ return sizeof(void *);
+
+ // This should be obtained via repGetClassSize
+ case CORINFO_TYPE_VALUECLASS:
+ case CORINFO_TYPE_REFANY:
+ LogException(EXCEPTIONCODE_TYPEUTILS,
+ "SizeOfCorInfoType does not support value types; use repGetClassSize instead (type: 0x%x)",
+ type);
+ return 0;
+
+ case CORINFO_TYPE_UNDEF:
+ case CORINFO_TYPE_VOID:
+ default:
+ LogException(EXCEPTIONCODE_TYPEUTILS, "Unsupported type (0x%x) passed into SizeOfCorInfoType", type);
+ return 0;
+ }
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/typeutils.h b/src/ToolBox/superpmi/superpmi-shared/typeutils.h
new file mode 100644
index 0000000000..31cb478020
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shared/typeutils.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// TypeUtils.h - Utility code for working with managed types
+//----------------------------------------------------------
+#ifndef _TypeUtils
+#define _TypeUtils
+
+#include "methodcontext.h"
+
+class TypeUtils
+{
+public:
+ static const char *GetCorInfoTypeName(CorInfoType type);
+ static bool IsFloatingPoint(CorInfoType type);
+ static bool IsPointer(CorInfoType type);
+ static bool IsValueClass(CorInfoType type);
+ static bool ValueClassRequiresByref(MethodContext *mc, CORINFO_CLASS_HANDLE clsHnd);
+ static size_t SizeOfCorInfoType(CorInfoType type);
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/.gitmirror b/src/ToolBox/superpmi/superpmi-shim-collector/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt b/src/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt
new file mode 100644
index 0000000000..ded4e9630a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt
@@ -0,0 +1,72 @@
+project(superpmi-shim-collector)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+
+if(WIN32)
+ #use static crt
+ add_definitions(-MT)
+endif(WIN32)
+
+include_directories(.)
+include_directories(../superpmi-shared)
+
+set(SUPERPMI_SHIM_COLLECTOR_SOURCES
+ coreclrcallbacks.cpp
+ jithost.cpp
+ icorjitcompiler.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ superpmi-shim-collector.cpp
+ ../superpmi-shared/callutils.cpp
+ ../superpmi-shared/compileresult.cpp
+ ../superpmi-shared/errorhandling.cpp
+ ../superpmi-shared/logging.cpp
+ ../superpmi-shared/mclist.cpp
+ ../superpmi-shared/methodcontext.cpp
+ ../superpmi-shared/methodcontextreader.cpp
+ ../superpmi-shared/simpletimer.cpp
+ ../superpmi-shared/spmiutil.cpp
+ ../superpmi-shared/tocfile.cpp
+ ../superpmi-shared/typeutils.cpp
+)
+
+add_precompiled_header(
+ standardpch.h
+ ../superpmi-shared/standardpch.cpp
+ SUPERPMI_SHIM_COLLECTOR_SOURCES
+)
+
+if (WIN32)
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/superpmi-shim-collector.def ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-collector.def)
+
+ list(APPEND SUPERPMI_SHIM_COLLECTOR_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-collector.def)
+endif (WIN32)
+
+add_library(superpmi-shim-collector
+ SHARED
+ ${SUPERPMI_SHIM_COLLECTOR_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi-shim-collector
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi-shim-collector
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi-shim-collector.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi-shim-collector DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..5b764f2fa5
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp
@@ -0,0 +1,62 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+typedef LPVOID (__stdcall * pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
+typedef BOOL (__stdcall * pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
+
+CoreClrCallbacks *original_CoreClrCallbacks = nullptr;
+pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
+pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ interceptor_IEE *iee = new interceptor_IEE();
+ iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ if(original_EEHeapAllocInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+ if(original_EEHeapFreeInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ {
+ original_EEHeapAllocInProcessHeap =
+ (pfnEEHeapAllocInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapAllocInProcessHeap");
+ return (void*)EEHeapAllocInProcessHeap;
+ }
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ {
+ original_EEHeapFreeInProcessHeap =
+ (pfnEEHeapFreeInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapFreeInProcessHeap");
+ return (void*)EEHeapFreeInProcessHeap;
+ }
+ return original_CoreClrCallbacks->m_pfnGetCLRFunction(functionName);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h
new file mode 100644
index 0000000000..c8c3e27c66
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _CoreClrCallbacks
+#define _CoreClrCallbacks
+
+#include "runtimedetails.h"
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t();
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength);
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes);
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem);
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName);
+
+extern CoreClrCallbacks *original_CoreClrCallbacks;
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp
new file mode 100644
index 0000000000..e3f5ae2764
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp
@@ -0,0 +1,120 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "spmiutil.h"
+#include "icorjitcompiler.h"
+#include "icorjitinfo.h"
+#include "jithost.h"
+#include "superpmi-shim-collector.h"
+
+#define fatMC //this is nice to have on so ildump works...
+interceptor_IEEMM *current_IEEMM = nullptr; //we want this to live beyond the scope of a single compileMethodCall
+
+CorJitResult __stdcall interceptor_ICJC::compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ )
+{
+ interceptor_ICJI our_ICorJitInfo;
+ our_ICorJitInfo.original_ICorJitInfo = comp;
+
+ if(current_IEEMM == nullptr)
+ current_IEEMM = new interceptor_IEEMM();
+
+ auto* mc = new MethodContext();
+ if (g_ourJitHost != nullptr)
+ {
+ g_ourJitHost->setMethodContext(mc);
+ }
+
+ our_ICorJitInfo.mc = mc;
+ our_ICorJitInfo.mc->cr->recProcessName(GetCommandLineA());
+
+ our_ICorJitInfo.mc->recCompileMethod(info, flags);
+
+//force some extra data into our tables..
+ //data probably not needed with RyuJIT, but needed in 4.5 and 4.5.1 to help with catching cached values
+ our_ICorJitInfo.getBuiltinClass(CLASSID_SYSTEM_OBJECT);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_TYPED_BYREF);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_TYPE_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_FIELD_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_METHOD_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_STRING);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_ARGUMENT_HANDLE);
+ our_ICorJitInfo.getBuiltinClass(CLASSID_RUNTIME_TYPE);
+
+#ifdef fatMC
+ //to build up a fat mc
+ CORINFO_CLASS_HANDLE ourClass = our_ICorJitInfo.getMethodClass(info->ftn);
+ our_ICorJitInfo.getClassAttribs(ourClass);
+ our_ICorJitInfo.getClassName(ourClass);
+ our_ICorJitInfo.isValueClass(ourClass);
+ our_ICorJitInfo.asCorInfoType(ourClass);
+#endif
+
+ // Record data from the global context, if any
+ if (g_globalContext != nullptr)
+ {
+ our_ICorJitInfo.mc->recGlobalContext(*g_globalContext);
+ }
+
+ //Record a simple view of the environment
+ our_ICorJitInfo.mc->recEnvironment();
+
+ CorJitResult temp = original_ICorJitCompiler->compileMethod(&our_ICorJitInfo, info, flags, nativeEntry, nativeSizeOfCode);
+
+ if(temp == CORJIT_OK)
+ {
+ //capture the results of compilation
+ our_ICorJitInfo.mc->cr->recCompileMethod(nativeEntry, nativeSizeOfCode, temp);
+
+ our_ICorJitInfo.mc->cr->recAllocMemCapture();
+ our_ICorJitInfo.mc->cr->recAllocGCInfoCapture();
+ our_ICorJitInfo.mc->saveToFile(hFile);
+ }
+
+ delete mc;
+
+ if (g_ourJitHost != nullptr)
+ {
+ g_ourJitHost->setMethodContext(g_globalContext);
+ }
+
+ return temp;
+}
+
+void interceptor_ICJC::clearCache()
+{
+ original_ICorJitCompiler->clearCache();
+}
+
+BOOL interceptor_ICJC::isCacheCleanupRequired()
+{
+ return original_ICorJitCompiler->isCacheCleanupRequired();
+}
+
+void interceptor_ICJC::ProcessShutdownWork(ICorStaticInfo* info)
+{
+ original_ICorJitCompiler->ProcessShutdownWork(info);
+}
+
+void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */)
+{
+ original_ICorJitCompiler->getVersionIdentifier(versionIdentifier);
+}
+
+unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags)
+{
+ return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags);
+}
+
+void interceptor_ICJC::setRealJit(ICorJitCompiler* realJitCompiler)
+{
+ original_ICorJitCompiler->setRealJit(realJitCompiler);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h
new file mode 100644
index 0000000000..97dbebd9a9
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitCompiler
+#define _ICorJitCompiler
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+
+class interceptor_ICJC : public ICorJitCompiler
+{
+
+#include "icorjitcompilerimpl.h"
+
+public:
+ // Added to help us track the original icjc and be able to easily indirect to it.
+ ICorJitCompiler *original_ICorJitCompiler;
+ HANDLE hFile;
+};
+
+extern interceptor_IEEMM *current_IEEMM; //we want this to live beyond the scope of a single compileMethodCall
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
new file mode 100644
index 0000000000..fb9163629d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
@@ -0,0 +1,2284 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "icorjitinfo.h"
+#include "superpmi-shim-collector.h"
+#include "ieememorymanager.h"
+#include "icorjitcompiler.h"
+#include "methodcontext.h"
+#include "errorhandling.h"
+#include "logging.h"
+
+#define fatMC //this is nice to have on so ildump works...
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ mc->cr->AddCall("getMethodAttribs");
+ DWORD temp = original_ICorJitInfo->getMethodAttribs(ftn);
+ mc->recGetMethodAttribs(ftn, temp);
+ return temp;
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void interceptor_ICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ mc->cr->AddCall("setMethodAttribs");
+ original_ICorJitInfo->setMethodAttribs(ftn, attribs);
+ mc->cr->recSetMethodAttribs(ftn, attribs);
+}
+
+// Given a method descriptor ftnHnd, extract signature information into sigInfo
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+void interceptor_ICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mc->cr->AddCall("getMethodSig");
+ original_ICorJitInfo->getMethodSig(ftn, sig, memberParent);
+ mc->recGetMethodSig(ftn, sig, memberParent);
+}
+
+ /*********************************************************************
+ * Note the following methods can only be used on functions known
+ * to be IL. This includes the method being compiled and any method
+ * that 'getMethodInfo' returns true for
+ *********************************************************************/
+ // return information about a method private to the implementation
+ // returns false if method is not IL, or is otherwise unavailable.
+ // This method is used to fetch data needed to inline functions
+ bool interceptor_ICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_METHOD_HANDLE ftn;
+ CORINFO_METHOD_INFO* info;
+ bool temp;
+ } param;
+ param.pThis = this;
+ param.ftn = ftn;
+ param.info = info;
+ param.temp = false;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getMethodInfo");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->getMethodInfo(pParam->ftn, pParam->info);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetMethodInfo(ftn, info, param.temp, param.exceptionCode);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// Decides if you have any limitations for inlining. If everything's OK, it will return
+// INLINE_PASS and will fill out pRestrictions with a mask of restrictions the caller of this
+// function must respect. If caller passes pRestrictions = nullptr, if there are any restrictions
+// INLINE_FAIL will be returned
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+//
+// The inlined method need not be verified
+
+CorInfoInline interceptor_ICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_METHOD_HANDLE callerHnd;
+ CORINFO_METHOD_HANDLE calleeHnd;
+ DWORD* pRestrictions;
+ CorInfoInline temp;
+ } param;
+ param.pThis = this;
+ param.callerHnd = callerHnd;
+ param.calleeHnd = calleeHnd;
+ param.pRestrictions = pRestrictions;
+ param.temp = INLINE_NEVER;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("canInline");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->canInline(pParam->callerHnd, pParam->calleeHnd, pParam->pRestrictions);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recCanInline(callerHnd, calleeHnd, pRestrictions, param.temp, param.exceptionCode);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all
+// inlining results when it returns INLINE_FAIL and INLINE_NEVER. All other results are reported by the
+// JIT.
+void interceptor_ICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ mc->cr->AddCall("reportInliningDecision");
+ original_ICorJitInfo->reportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
+ mc->cr->recReportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
+}
+
+
+// Returns false if the call is across security boundaries thus we cannot tailcall
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+bool interceptor_ICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ mc->cr->AddCall("canTailCall");
+ bool temp = original_ICorJitInfo->canTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix);
+ mc->recCanTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix, temp);
+ return temp;
+}
+
+// Reports whether or not a method can be tail called, and why.
+// canTailCall is responsible for reporting all results when it returns
+// false. All other results are reported by the JIT.
+void interceptor_ICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ mc->cr->AddCall("reportTailCallDecision");
+ original_ICorJitInfo->reportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+ mc->cr->recReportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void interceptor_ICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ mc->cr->AddCall("getEHinfo");
+ original_ICorJitInfo->getEHinfo(ftn, EHnumber, clause);
+ mc->recGetEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("getMethodClass");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getMethodClass(method);
+ mc->recGetMethodClass(method, temp);
+ return temp;
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("getMethodModule");
+ return original_ICorJitInfo->getMethodModule(method);
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void interceptor_ICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ mc->cr->AddCall("getMethodVTableOffset");
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+ mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ mc->cr->AddCall("getIntrinsicID");
+ CorInfoIntrinsics temp = original_ICorJitInfo->getIntrinsicID(method, pMustExpand);
+ mc->recGetIntrinsicID(method, pMustExpand, temp);
+ return temp;
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool interceptor_ICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ mc->cr->AddCall("isInSIMDModule");
+ bool temp = original_ICorJitInfo->isInSIMDModule(classHnd);
+ mc->recIsInSIMDModule(classHnd, temp);
+ return temp;
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("getUnmanagedCallConv");
+ CorInfoUnmanagedCallConv temp = original_ICorJitInfo->getUnmanagedCallConv(method);
+ mc->recGetUnmanagedCallConv(method, temp);
+ return temp;
+}
+
+// return if any marshaling is required for PInvoke methods. Note that
+// method == 0 => calli. The call site sig is only needed for the varargs or calli case
+BOOL interceptor_ICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ mc->cr->AddCall("pInvokeMarshalingRequired");
+ BOOL temp = original_ICorJitInfo->pInvokeMarshalingRequired(method, callSiteSig);
+ mc->recPInvokeMarshalingRequired(method, callSiteSig, temp);
+ return temp;
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL interceptor_ICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("satisfiesMethodConstraints");
+ BOOL temp = original_ICorJitInfo->satisfiesMethodConstraints(parent, method);
+ mc->recSatisfiesMethodConstraints(parent, method, temp);
+ return temp;
+}
+
+// Given a delegate target class, a target method parent class, a target method,
+// a delegate class, check if the method signature is compatible with the Invoke method of the delegate
+// (under the typical instantiation of any free type variables in the memberref signatures).
+BOOL interceptor_ICJI::isCompatibleDelegate(
+ CORINFO_CLASS_HANDLE objCls, /* type of the delegate target, if any */
+ CORINFO_CLASS_HANDLE methodParentCls, /* exact parent of the target method, if any */
+ CORINFO_METHOD_HANDLE method, /* (representative) target method, if any */
+ CORINFO_CLASS_HANDLE delegateCls, /* exact type of the delegate */
+ BOOL *pfIsOpenDelegate /* is the delegate open */
+ )
+{
+ mc->cr->AddCall("isCompatibleDelegate");
+ BOOL temp = original_ICorJitInfo->isCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+ mc->recIsCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate, temp);
+ return temp;
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL interceptor_ICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ mc->cr->AddCall("isDelegateCreationAllowed");
+ BOOL temp = original_ICorJitInfo->isDelegateCreationAllowed(delegateHnd, calleeHnd);
+ mc->recIsDelegateCreationAllowed(delegateHnd, calleeHnd, temp);
+ return temp;
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification interceptor_ICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ mc->cr->AddCall("isInstantiationOfVerifiedGeneric");
+ CorInfoInstantiationVerification temp = original_ICorJitInfo->isInstantiationOfVerifiedGeneric(method);
+ mc->recIsInstantiationOfVerifiedGeneric(method, temp);
+ return temp;
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void interceptor_ICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ mc->cr->AddCall("initConstraintsForVerification");
+ original_ICorJitInfo->initConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+ mc->recInitConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+// Returns enum whether the method does not require verification
+// Also see ICorModuleInfo::canSkipVerification
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ mc->cr->AddCall("canSkipMethodVerification");
+ CorInfoCanSkipVerificationResult temp = original_ICorJitInfo->canSkipMethodVerification(ftnHandle);
+ mc->recCanSkipMethodVerification(ftnHandle, FALSE, temp);
+ return temp;
+}
+
+// load and restore the method
+void interceptor_ICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("methodMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->methodMustBeLoadedBeforeCodeIsRun(method);
+ mc->cr->recMethodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mc->cr->AddCall("mapMethodDeclToMethodImpl");
+ return original_ICorJitInfo->mapMethodDeclToMethodImpl(method);
+}
+
+// Returns the global cookie for the /GS unsafe buffer checks
+// The cookie might be a constant value (JIT), or a handle to memory location (Ngen)
+void interceptor_ICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ mc->cr->AddCall("getGSCookie");
+ original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal);
+ mc->recGetGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+// Resolve metadata token into runtime method handles.
+void interceptor_ICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_RESOLVED_TOKEN* pResolvedToken;
+ } param;
+ param.pThis = this;
+ param.pResolvedToken = pResolvedToken;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("resolveToken");
+ pParam->pThis->original_ICorJitInfo->resolveToken(pParam->pResolvedToken);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recResolveToken(param.pResolvedToken, param.exceptionCode);
+ }
+ PAL_ENDTRY
+}
+
+bool interceptor_ICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ mc->cr->AddCall("tryResolveToken");
+ bool success = original_ICorJitInfo->tryResolveToken(pResolvedToken);
+ mc->recResolveToken(pResolvedToken, success);
+ return success;
+}
+
+// Signature information about the call sig
+void interceptor_ICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mc->cr->AddCall("findSig");
+ original_ICorJitInfo->findSig(module, sigTOK, context, sig);
+ mc->recFindSig(module, sigTOK, context, sig);
+}
+
+// for Varargs, the signature at the call site may differ from
+// the signature at the definition. Thus we need a way of
+// fetching the call site information
+void interceptor_ICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mc->cr->AddCall("findCallSiteSig");
+ original_ICorJitInfo->findCallSiteSig(module, methTOK, context, sig);
+ mc->recFindCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ mc->cr->AddCall("getTokenTypeAsHandle");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getTokenTypeAsHandle(pResolvedToken);
+ mc->recGetTokenTypeAsHandle(pResolvedToken, temp);
+ return temp;
+}
+
+// Returns true if the module does not require verification
+//
+// If fQuickCheckOnlyWithoutCommit=TRUE, the function only checks that the
+// module does not currently require verification in the current AppDomain.
+// This decision could change in the future, and so should not be cached.
+// If it is cached, it should only be used as a hint.
+// This is only used by ngen for calculating certain hints.
+//
+// Returns enum whether the module does not require verification
+// Also see ICorMethodInfo::canSkipMethodVerification();
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ mc->cr->AddCall("canSkipVerification");
+ return original_ICorJitInfo->canSkipVerification(module);
+}
+
+// Checks if the given metadata token is valid
+BOOL interceptor_ICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mc->cr->AddCall("isValidToken");
+ BOOL result = original_ICorJitInfo->isValidToken(module, metaTOK);
+ mc->recIsValidToken(module, metaTOK, result);
+ return result;
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL interceptor_ICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mc->cr->AddCall("isValidStringRef");
+ BOOL temp = original_ICorJitInfo->isValidStringRef(module, metaTOK);
+ mc->recIsValidStringRef(module, metaTOK, temp);
+ return temp;
+}
+
+BOOL interceptor_ICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ mc->cr->AddCall("shouldEnforceCallvirtRestriction");
+ BOOL temp = original_ICorJitInfo->shouldEnforceCallvirtRestriction(scope);
+ mc->recShouldEnforceCallvirtRestriction(scope, temp);
+ return temp;
+}
+
+/**********************************************************************************/
+//
+// ICorClassInfo
+//
+/**********************************************************************************/
+
+// If the value class 'cls' is isomorphic to a primitive type it will
+// return that type, otherwise it will return CORINFO_TYPE_VALUECLASS
+CorInfoType interceptor_ICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("asCorInfoType");
+ CorInfoType temp = original_ICorJitInfo->asCorInfoType(cls);
+ mc->recAsCorInfoType(cls, temp);
+ return temp;
+}
+
+// for completeness
+const char* interceptor_ICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassName");
+ const char* result = original_ICorJitInfo->getClassName(cls);
+ mc->recGetClassName(cls, result);
+ return result;
+}
+
+// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
+// If fNamespace=TRUE, include the namespace/enclosing classes
+// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
+// If fAssembly=TRUE, suffix with a comma and the full assembly qualification
+// return size of representation
+int interceptor_ICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ mc->cr->AddCall("appendClassName");
+ WCHAR* pBuf = *ppBuf;
+ int nLen = original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
+ mc->recAppendClassName(cls, fNamespace, fFullInst, fAssembly, pBuf);
+ return nLen;
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("isValueClass");
+ BOOL temp = original_ICorJitInfo->isValueClass(cls);
+ mc->recIsValueClass(cls, temp);
+ return temp;
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("canInlineTypeCheckWithObjectVTable");
+ BOOL temp = original_ICorJitInfo->canInlineTypeCheckWithObjectVTable(cls);
+ mc->recCanInlineTypeCheckWithObjectVTable(cls, temp);
+ return temp;
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassAttribs");
+ DWORD temp = original_ICorJitInfo->getClassAttribs(cls);
+ mc->recGetClassAttribs(cls, temp);
+ return temp;
+}
+
+// Returns "TRUE" iff "cls" is a struct type such that return buffers used for returning a value
+// of this type must be stack-allocated. This will generally be true only if the struct
+// contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
+// an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
+// returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
+// buffers do not require GC write barriers.
+BOOL interceptor_ICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("isStructRequiringStackAllocRetBuf");
+ BOOL temp = original_ICorJitInfo->isStructRequiringStackAllocRetBuf(cls);
+ mc->recIsStructRequiringStackAllocRetBuf(cls, temp);
+ return temp;
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassModule");
+ return original_ICorJitInfo->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE interceptor_ICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ mc->cr->AddCall("getModuleAssembly");
+ return original_ICorJitInfo->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* interceptor_ICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ mc->cr->AddCall("getAssemblyName");
+ return original_ICorJitInfo->getAssemblyName(assem);
+}
+
+// Allocate and delete process-lifetime objects. Should only be
+// referred to from static fields, lest a leak occur.
+// Note that "LongLifetimeFree" does not execute destructors, if "obj"
+// is an array of a struct type with a destructor.
+void* interceptor_ICJI::LongLifetimeMalloc(size_t sz)
+{
+ mc->cr->AddCall("LongLifetimeMalloc");
+ return original_ICorJitInfo->LongLifetimeMalloc(sz);
+}
+
+void interceptor_ICJI::LongLifetimeFree(void* obj)
+{
+ mc->cr->AddCall("LongLifetimeFree");
+ original_ICorJitInfo->LongLifetimeFree(obj);
+}
+
+size_t interceptor_ICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getClassModuleIdForStatics");
+ size_t temp = original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection);
+ mc->recGetClassModuleIdForStatics(cls, pModule, ppIndirection, temp);
+ return temp;
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned interceptor_ICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getClassSize");
+ unsigned temp = original_ICorJitInfo->getClassSize(cls);
+ mc->recGetClassSize(cls, temp);
+ return temp;
+}
+
+unsigned interceptor_ICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ mc->cr->AddCall("getClassAlignmentRequirement");
+ unsigned temp = original_ICorJitInfo->getClassAlignmentRequirement(cls, fDoubleAlignHint);
+ mc->recGetClassAlignmentRequirement(cls, fDoubleAlignHint, temp);
+ return temp;
+}
+
+// This is only called for Value classes. It returns a boolean array
+// in representing of 'cls' from a GC perspective. The class is
+// assumed to be an array of machine words
+// (of length // getClassSize(cls) / sizeof(void*)),
+// 'gcPtrs' is a poitner to an array of BYTEs of this length.
+// getClassGClayout fills in this array so that gcPtrs[i] is set
+// to one of the CorInfoGCType values which is the GC type of
+// the i-th machine word of an object of type 'cls'
+// returns the number of GC pointers in the array
+unsigned interceptor_ICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ mc->cr->AddCall("getClassGClayout");
+ unsigned temp = original_ICorJitInfo->getClassGClayout(cls, gcPtrs);
+ unsigned len = (getClassSize(cls) + sizeof(void *) - 1)/sizeof(void*);
+ mc->recGetClassGClayout(cls, gcPtrs, len, temp);
+ return temp;
+}
+
+// returns the number of instance fields in a class
+unsigned interceptor_ICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ mc->cr->AddCall("getClassNumInstanceFields");
+ unsigned temp = original_ICorJitInfo->getClassNumInstanceFields(cls);
+ mc->recGetClassNumInstanceFields(cls, temp);
+ return temp;
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ mc->cr->AddCall("getFieldInClass");
+ CORINFO_FIELD_HANDLE temp = original_ICorJitInfo->getFieldInClass(clsHnd, num);
+ mc->recGetFieldInClass(clsHnd, num, temp);
+ return temp;
+}
+
+BOOL interceptor_ICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ mc->cr->AddCall("checkMethodModifier");
+ BOOL result = original_ICorJitInfo->checkMethodModifier(hMethod, modifier, fOptional);
+ mc->recCheckMethodModifier(hMethod, modifier, fOptional, result);
+ return result;
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc interceptor_ICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ mc->cr->AddCall("getNewHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getNewHelper(pResolvedToken, callerHandle);
+ mc->recGetNewHelper(pResolvedToken, callerHandle, temp);
+ return temp;
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ mc->cr->AddCall("getNewArrHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getNewArrHelper(arrayCls);
+ mc->recGetNewArrHelper(arrayCls, temp);
+ return temp;
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ mc->cr->AddCall("getCastingHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
+ mc->recGetCastingHelper(pResolvedToken, fThrowing, temp);
+ return temp;
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ mc->cr->AddCall("getSharedCCtorHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getSharedCCtorHelper(clsHnd);
+ mc->recGetSharedCCtorHelper(clsHnd, temp);
+ return temp;
+}
+
+CorInfoHelpFunc interceptor_ICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ mc->cr->AddCall("getSecurityPrologHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getSecurityPrologHelper(ftn);
+ mc->recGetSecurityPrologHelper(ftn, temp);
+ return temp;
+}
+
+// This is not pretty. Boxing nullable<T> actually returns
+// a boxed<T> not a boxed Nullable<T>. This call allows the verifier
+// to call back to the EE on the 'box' instruction and get the transformed
+// type to use for verification.
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getTypeForBox");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getTypeForBox(cls);
+ mc->recGetTypeForBox(cls, temp);
+ return temp;
+}
+
+// returns the correct box helper for a particular class. Note
+// that if this returns CORINFO_HELP_BOX, the JIT can assume
+// 'standard' boxing (allocate object and copy), and optimize
+CorInfoHelpFunc interceptor_ICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getBoxHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getBoxHelper(cls);
+ mc->recGetBoxHelper(cls, temp);
+ return temp;
+}
+
+// returns the unbox helper. If 'helperCopies' points to a true
+// value it means the JIT is requesting a helper that unboxes the
+// value into a particular location and thus has the signature
+// void unboxHelper(void* dest, CORINFO_CLASS_HANDLE cls, Object* obj)
+// Otherwise (it is null or points at a FALSE value) it is requesting
+// a helper that returns a poitner to the unboxed data
+// void* unboxHelper(CORINFO_CLASS_HANDLE cls, Object* obj)
+// The EE has the option of NOT returning the copy style helper
+// (But must be able to always honor the non-copy style helper)
+// The EE set 'helperCopies' on return to indicate what kind of
+// helper has been created.
+CorInfoHelpFunc interceptor_ICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getUnBoxHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getUnBoxHelper(cls);
+ mc->recGetUnBoxHelper(cls, temp);
+ return temp;
+}
+
+bool interceptor_ICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mc->cr->AddCall("getReadyToRunHelper");
+ bool result = original_ICorJitInfo->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+ mc->recGetReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup, result);
+ return result;
+}
+
+void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mc->cr->AddCall("getReadyToRunDelegateCtorHelper");
+ original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+ mc->recGetReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* interceptor_ICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ mc->cr->AddCall("getHelperName");
+ const char* temp = original_ICorJitInfo->getHelperName(funcNum);
+ mc->recGetHelperName(funcNum, temp);
+ return temp;
+}
+
+// This function tries to initialize the class (run the class constructor).
+// this function returns whether the JIT must insert helper calls before
+// accessing static field or method.
+//
+// See code:ICorClassInfo#ClassConstruction.
+CorInfoInitClassResult interceptor_ICJI::initClass(
+ CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
+ // nullptr - inquire about cctor trigger in method prolog
+ CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
+ CORINFO_CONTEXT_HANDLE context, // Exact context of method
+ BOOL speculative // TRUE means don't actually run it
+ )
+{
+ mc->cr->AddCall("initClass");
+ CorInfoInitClassResult temp = original_ICorJitInfo->initClass(field, method, context, speculative);
+ mc->recInitClass(field, method, context, speculative, temp);
+ return temp;
+}
+
+// This used to be called "loadClass". This records the fact
+// that the class must be loaded (including restored if necessary) before we execute the
+// code that we are currently generating. When jitting code
+// the function loads the class immediately. When zapping code
+// the zapper will if necessary use the call to record the fact that we have
+// to do a fixup/restore before running the method currently being generated.
+//
+// This is typically used to ensure value types are loaded before zapped
+// code that manipulates them is executed, so that the GC can access information
+// about those value types.
+void interceptor_ICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("classMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->classMustBeLoadedBeforeCodeIsRun(cls);
+ mc->cr->recClassMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE interceptor_ICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ mc->cr->AddCall("getBuiltinClass");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getBuiltinClass(classId);
+ mc->recGetBuiltinClass(classId, temp);
+ return temp;
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType interceptor_ICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getTypeForPrimitiveValueClass");
+ CorInfoType temp = original_ICorJitInfo->getTypeForPrimitiveValueClass(cls);
+ mc->recGetTypeForPrimitiveValueClass(cls, temp);
+ return temp;
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL interceptor_ICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ mc->cr->AddCall("canCast");
+ BOOL temp = original_ICorJitInfo->canCast(child, parent);
+ mc->recCanCast(child, parent, temp);
+ return temp;
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL interceptor_ICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mc->cr->AddCall("areTypesEquivalent");
+ BOOL temp = original_ICorJitInfo->areTypesEquivalent(cls1, cls2);
+ mc->recAreTypesEquivalent(cls1, cls2, temp);
+ return temp;
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mc->cr->AddCall("mergeClasses");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->mergeClasses(cls1, cls2);
+ mc->recMergeClasses(cls1, cls2, temp);
+ return temp;
+}
+
+// Given a class handle, returns the Parent type.
+// For COMObjectType, it returns Class Handle of System.Object.
+// Returns 0 if System.Object is passed in.
+CORINFO_CLASS_HANDLE interceptor_ICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getParentType");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getParentType(cls);
+ mc->recGetParentType(cls, temp);
+ return temp;
+}
+
+// Returns the CorInfoType of the "child type". If the child type is
+// not a primitive type, *clsRet will be set.
+// Given an Array of Type Foo, returns Foo.
+// Given BYREF Foo, returns Foo
+CorInfoType interceptor_ICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ mc->cr->AddCall("getChildType");
+ CorInfoType temp = original_ICorJitInfo->getChildType(clsHnd, clsRet);
+ mc->recGetChildType(clsHnd, clsRet, temp);
+ return temp;
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL interceptor_ICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("satisfiesClassConstraints");
+ BOOL temp = original_ICorJitInfo->satisfiesClassConstraints(cls);
+ mc->recSatisfiesClassConstraints(cls, temp);
+ return temp;
+
+}
+
+// Check if this is a single dimensional array type
+BOOL interceptor_ICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("isSDArray");
+ BOOL temp = original_ICorJitInfo->isSDArray(cls);
+ mc->recIsSDArray(cls, temp);
+ return temp;
+}
+
+// Get the numbmer of dimensions in an array
+unsigned interceptor_ICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mc->cr->AddCall("getArrayRank");
+ unsigned result = original_ICorJitInfo->getArrayRank(cls);
+ mc->recGetArrayRank(cls, result);
+ return result;
+}
+
+// Get static field data for an array
+void * interceptor_ICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ mc->cr->AddCall("getArrayInitializationData");
+ void *temp = original_ICorJitInfo->getArrayInitializationData(field, size);
+ mc->recGetArrayInitializationData(field, size, temp);
+ return temp;
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult interceptor_ICJI::canAccessClass(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper /* If canAccessMethod returns something other
+ than ALLOWED, then this is filled in. */
+ )
+{
+ mc->cr->AddCall("canAccessClass");
+ CorInfoIsAccessAllowedResult temp = original_ICorJitInfo->canAccessClass(pResolvedToken, callerHandle, pAccessHelper);
+ mc->recCanAccessClass(pResolvedToken, callerHandle, pAccessHelper, temp);
+ return temp;
+}
+
+/**********************************************************************************/
+//
+// ICorFieldInfo
+//
+/**********************************************************************************/
+// this function is for debugging only. It returns the field name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* interceptor_ICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mc->cr->AddCall("getFieldName");
+ const char* temp = original_ICorJitInfo->getFieldName(ftn, moduleName);
+ mc->recGetFieldName(ftn, moduleName, temp);
+ return temp;
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mc->cr->AddCall("getFieldClass");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getFieldClass(field);
+ mc->recGetFieldClass(field, temp);
+ return temp;
+}
+
+// Return the field's type, if it is CORINFO_TYPE_VALUECLASS 'structType' is set
+// the field's value class (if 'structType' == 0, then don't bother
+// the structure info).
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+CorInfoType interceptor_ICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mc->cr->AddCall("getFieldType");
+ CorInfoType temp = original_ICorJitInfo->getFieldType(field, structType, memberParent);
+ mc->recGetFieldType(field, structType, memberParent, temp);
+ return temp;
+}
+
+// return the data member's instance offset
+unsigned interceptor_ICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mc->cr->AddCall("getFieldOffset");
+ unsigned temp = original_ICorJitInfo->getFieldOffset(field);
+ mc->recGetFieldOffset(field, temp);
+ return temp;
+}
+
+// TODO: jit64 should be switched to the same plan as the i386 jits - use
+// getClassGClayout to figure out the need for writebarrier helper, and inline the copying.
+// The interpretted value class copy is slow. Once this happens, USE_WRITE_BARRIER_HELPERS
+bool interceptor_ICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ mc->cr->AddCall("isWriteBarrierHelperRequired");
+ bool result = original_ICorJitInfo->isWriteBarrierHelperRequired(field);
+ mc->recIsWriteBarrierHelperRequired(field, result);
+ return result;
+}
+
+void interceptor_ICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ mc->cr->AddCall("getFieldInfo");
+ original_ICorJitInfo->getFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+ mc->recGetFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ mc->cr->AddCall("isFieldStatic");
+ bool result = original_ICorJitInfo->isFieldStatic(fldHnd);
+ mc->recIsFieldStatic(fldHnd, result);
+ return result;
+}
+
+/*********************************************************************************/
+//
+// ICorDebugInfo
+//
+/*********************************************************************************/
+// Query the EE to find out where interesting break points
+// in the code are. The native compiler will ensure that these places
+// have a corresponding break point in native code.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void interceptor_ICJI::getBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ unsigned int *cILOffsets, // [OUT] size of pILOffsets
+ DWORD **pILOffsets, // [OUT] IL offsets of interest
+ // jit MUST free with freeArray!
+ ICorDebugInfo::BoundaryTypes *implictBoundaries // [OUT] tell jit, all boundries of this type
+ )
+{
+ mc->cr->AddCall("getBoundaries");
+ original_ICorJitInfo->getBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+ mc->recGetBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+}
+
+// Report back the mapping from IL to native code,
+// this map should include all boundaries that 'getBoundaries'
+// reported as interesting to the debugger.
+
+// Note that debugger (and profiler) is assuming that all of the
+// offsets form a contiguous block of memory, and that the
+// OffsetMapping is sorted in order of increasing native offset.
+//Note - Ownership of pMap is transfered with this call. We need to record it before its passed on to the EE.
+void interceptor_ICJI::setBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cMap, // [IN] size of pMap
+ ICorDebugInfo::OffsetMapping *pMap // [IN] map including all points of interest.
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ mc->cr->AddCall("setBoundaries");
+ mc->cr->recSetBoundaries(ftn, cMap, pMap); //Since the EE frees, we've gotta record before its sent to the EE.
+ original_ICorJitInfo->setBoundaries(ftn, cMap, pMap);
+}
+
+// Query the EE to find out the scope of local varables.
+// normally the JIT would trash variables after last use, but
+// under debugging, the JIT needs to keep them live over their
+// entire scope so that they can be inspected.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void interceptor_ICJI::getVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 *cVars, // [OUT] size of 'vars'
+ ICorDebugInfo::ILVarInfo **vars, // [OUT] scopes of variables of interest
+ // jit MUST free with freeArray!
+ bool *extendOthers // [OUT] it TRUE, then assume the scope
+ // of unmentioned vars is entire method
+ )
+{
+ mc->cr->AddCall("getVars");
+ original_ICorJitInfo->getVars(ftn, cVars, vars, extendOthers);
+ mc->recGetVars(ftn, cVars, vars, extendOthers);
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+//Note - Ownership of vars is transfered with this call. We need to record it before its passed on to the EE.
+void interceptor_ICJI::setVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cVars, // [IN] size of 'vars'
+ ICorDebugInfo::NativeVarInfo *vars // [IN] map telling where local vars are stored at what points
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ mc->cr->AddCall("setVars");
+ mc->cr->recSetVars(ftn, cVars, vars); //Since the EE frees, we've gotta record before its sent to the EE.
+ original_ICorJitInfo->setVars(ftn, cVars, vars);
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * interceptor_ICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ mc->cr->AddCall("allocateArray");
+ return original_ICorJitInfo->allocateArray(cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void interceptor_ICJI::freeArray(
+ void *array
+ )
+{
+ mc->cr->AddCall("freeArray");
+ original_ICorJitInfo->freeArray(array);
+}
+
+/*********************************************************************************/
+//
+// ICorArgInfo
+//
+/*********************************************************************************/
+// advance the pointer to the argument list.
+// a ptr of 0, is special and always means the first argument
+CORINFO_ARG_LIST_HANDLE interceptor_ICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ mc->cr->AddCall("getArgNext");
+ CORINFO_ARG_LIST_HANDLE temp = original_ICorJitInfo->getArgNext(args);
+ mc->recGetArgNext(args, temp);
+ return temp;
+}
+
+// Get the type of a particular argument
+// CORINFO_TYPE_UNDEF is returned when there are no more arguments
+// If the type returned is a primitive type (or an enum) *vcTypeRet set to nullptr
+// otherwise it is set to the TypeHandle associted with the type
+// Enumerations will always look their underlying type (probably should fix this)
+// Otherwise vcTypeRet is the type as would be seen by the IL,
+// The return value is the type that is used for calling convention purposes
+// (Thus if the EE wants a value class to be passed like an int, then it will
+// return CORINFO_TYPE_INT
+CorInfoTypeWithMod interceptor_ICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_SIG_INFO* sig;
+ CORINFO_ARG_LIST_HANDLE args;
+ CORINFO_CLASS_HANDLE* vcTypeRet;
+ CorInfoTypeWithMod temp;
+ } param;
+ param.pThis = this;
+ param.sig = sig;
+ param.args = args;
+ param.vcTypeRet = vcTypeRet;
+ param.temp = (CorInfoTypeWithMod)CORINFO_TYPE_UNDEF;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getArgType");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->getArgType(pParam->sig, pParam->args, pParam->vcTypeRet);
+
+#ifdef fatMC
+ CORINFO_CLASS_HANDLE temp3 = pParam->pThis->getArgClass(pParam->sig, pParam->args);
+#endif
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetArgType(sig, args, vcTypeRet, param.temp, param.exceptionCode);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_SIG_INFO* sig;
+ CORINFO_ARG_LIST_HANDLE args;
+ CORINFO_CLASS_HANDLE temp;
+ } param;
+ param.pThis = this;
+ param.sig = sig;
+ param.args = args;
+ param.temp = 0;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getArgClass");
+ pParam->temp = pParam->pThis->original_ICorJitInfo->getArgClass(pParam->sig, pParam->args);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetArgClass(sig, args, param.temp, param.exceptionCode);
+
+ //to build up a fat mc
+ getClassName(param.temp);
+ }
+ PAL_ENDTRY
+
+ return param.temp;
+ }
+
+// Returns type of HFA for valuetype
+CorInfoType interceptor_ICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ mc->cr->AddCall("getHFAType");
+ return original_ICorJitInfo->getHFAType(hClass);
+}
+
+/*****************************************************************************
+* ICorErrorInfo contains methods to deal with SEH exceptions being thrown
+* from the corinfo interface. These methods may be called when an exception
+* with code EXCEPTION_COMPLUS is caught.
+*****************************************************************************/
+// Returns the HRESULT of the current exception
+HRESULT interceptor_ICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mc->cr->AddCall("GetErrorHRESULT");
+ return original_ICorJitInfo->GetErrorHRESULT(pExceptionPointers);
+}
+
+// Fetches the message of the current exception
+// Returns the size of the message (including terminating null). This can be
+// greater than bufferLength if the buffer is insufficient.
+ULONG interceptor_ICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ mc->cr->AddCall("GetErrorMessage");
+ return original_ICorJitInfo->GetErrorMessage(buffer, bufferLength);
+}
+
+// returns EXCEPTION_EXECUTE_HANDLER if it is OK for the compile to handle the
+// exception, abort some work (like the inlining) and continue compilation
+// returns EXCEPTION_CONTINUE_SEARCH if exception must always be handled by the EE
+// things like ThreadStoppedException ...
+// returns EXCEPTION_CONTINUE_EXECUTION if exception is fixed up by the EE
+int interceptor_ICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mc->cr->AddCall("FilterException");
+ int temp = original_ICorJitInfo->FilterException(pExceptionPointers);
+ mc->recFilterException(pExceptionPointers, temp);
+ return temp;
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void interceptor_ICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ //bswHack?
+ mc->cr->AddCall("HandleException");
+ original_ICorJitInfo->HandleException(pExceptionPointers);
+ mc->recHandleException(pExceptionPointers);
+}
+
+void interceptor_ICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ mc->cr->AddCall("ThrowExceptionForJitResult");
+ original_ICorJitInfo->ThrowExceptionForJitResult(result);
+}
+
+//Throws an exception defined by the given throw helper.
+void interceptor_ICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ mc->cr->AddCall("ThrowExceptionForHelper");
+ original_ICorJitInfo->ThrowExceptionForHelper(throwHelper);
+}
+
+/*****************************************************************************
+ * ICorStaticInfo contains EE interface methods which return values that are
+ * constant from invocation to invocation. Thus they may be embedded in
+ * persisted information like statically generated code. (This is of course
+ * assuming that all code versions are identical each time.)
+ *****************************************************************************/
+// Return details about EE internal data structures
+void interceptor_ICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ mc->cr->AddCall("getEEInfo");
+ original_ICorJitInfo->getEEInfo(pEEInfoOut);
+ mc->recGetEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR interceptor_ICJI::getJitTimeLogFilename()
+{
+ mc->cr->AddCall("getJitTimeLogFilename");
+ LPCWSTR temp = original_ICorJitInfo->getJitTimeLogFilename();
+ mc->recGetJitTimeLogFilename(temp);
+ return temp;
+}
+
+/*********************************************************************************/
+//
+// Diagnostic methods
+//
+/*********************************************************************************/
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef interceptor_ICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ mc->cr->AddCall("getMethodDefFromMethod");
+ mdMethodDef result = original_ICorJitInfo->getMethodDefFromMethod(hMethod);
+ mc->recGetMethodDefFromMethod(hMethod, result);
+ return result;
+
+}
+
+// this function is for debugging only. It returns the method name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* interceptor_ICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mc->cr->AddCall("getMethodName");
+ const char* temp = original_ICorJitInfo->getMethodName(ftn, moduleName);
+ mc->recGetMethodName(ftn, (char *)temp, moduleName);
+ return temp;
+}
+
+// this function is for debugging only. It returns a value that
+// is will always be the same for a given method. It is used
+// to implement the 'jitRange' functionality
+unsigned interceptor_ICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ mc->cr->AddCall("getMethodHash");
+ unsigned temp = original_ICorJitInfo->getMethodHash(ftn);
+ mc->recGetMethodHash(ftn, temp);
+ return temp;
+}
+
+// this function is for debugging only.
+size_t interceptor_ICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ mc->cr->AddCall("findNameOfToken");
+ size_t result = original_ICorJitInfo->findNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+ mc->recFindNameOfToken(module, metaTOK, szFQName, FQNameCapacity, result);
+ return result;
+}
+
+bool interceptor_ICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ mc->cr->AddCall("getSystemVAmd64PassStructInRegisterDescriptor");
+ bool result = original_ICorJitInfo->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+ mc->recGetSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr, result);
+ return result;
+}
+
+//Stuff on ICorDynamicInfo
+DWORD interceptor_ICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getThreadTLSIndex");
+ DWORD temp = original_ICorJitInfo->getThreadTLSIndex(ppIndirection);
+ mc->recGetThreadTLSIndex(ppIndirection, temp);
+ return temp;
+}
+
+const void * interceptor_ICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getInlinedCallFrameVptr");
+ const void* temp = original_ICorJitInfo->getInlinedCallFrameVptr(ppIndirection);
+ mc->recGetInlinedCallFrameVptr(ppIndirection, temp);
+ return temp;
+}
+
+LONG * interceptor_ICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getAddrOfCaptureThreadGlobal");
+ LONG * temp = original_ICorJitInfo->getAddrOfCaptureThreadGlobal(ppIndirection);
+ mc->recGetAddrOfCaptureThreadGlobal(ppIndirection, temp);
+ return temp;
+}
+
+SIZE_T* interceptor_ICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ mc->cr->AddCall("getAddrModuleDomainID");
+ return original_ICorJitInfo->getAddrModuleDomainID(module);
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* interceptor_ICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getHelperFtn");
+ void *temp = original_ICorJitInfo->getHelperFtn(ftnNum, ppIndirection);
+ mc->recGetHelperFtn(ftnNum, ppIndirection, temp);
+ return temp;
+}
+
+// return a callable address of the function (native code). This function
+// may return a different value (depending on whether the method has
+// been JITed or not.
+void interceptor_ICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ mc->cr->AddCall("getFunctionEntryPoint");
+ original_ICorJitInfo->getFunctionEntryPoint(ftn, pResult, accessFlags);
+ mc->recGetFunctionEntryPoint(ftn, pResult, accessFlags);
+}
+
+// return a directly callable address. This can be used similarly to the
+// value returned by getFunctionEntryPoint() except that it is
+// guaranteed to be multi callable entrypoint.
+void interceptor_ICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ mc->cr->AddCall("getFunctionFixedEntryPoint");
+ original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult);
+ mc->recGetFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* interceptor_ICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getMethodSync");
+ void *temp = original_ICorJitInfo->getMethodSync(ftn, ppIndirection);
+ mc->recGetMethodSync(ftn, ppIndirection, temp);
+ return temp;
+}
+
+// These entry points must be called if a handle is being embedded in
+// the code to be passed to a JIT helper function. (as opposed to just
+// being passed back into the ICorInfo interface.)
+
+// get slow lazy string literal helper to use (CORINFO_HELP_STRCNS*).
+// Returns CORINFO_HELP_UNDEF if lazy string literal helper cannot be used.
+CorInfoHelpFunc interceptor_ICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ mc->cr->AddCall("getLazyStringLiteralHelper");
+ CorInfoHelpFunc temp = original_ICorJitInfo->getLazyStringLiteralHelper(handle);
+ mc->recGetLazyStringLiteralHelper(handle, temp);
+ return temp;
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedModuleHandle");
+ CORINFO_MODULE_HANDLE temp = original_ICorJitInfo->embedModuleHandle(handle, ppIndirection);
+ mc->recEmbedModuleHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedClassHandle");
+ CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->embedClassHandle(handle, ppIndirection);
+ mc->recEmbedClassHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedMethodHandle");
+ CORINFO_METHOD_HANDLE temp = original_ICorJitInfo->embedMethodHandle(handle, ppIndirection);
+ mc->recEmbedMethodHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("embedFieldHandle");
+ CORINFO_FIELD_HANDLE temp = original_ICorJitInfo->embedFieldHandle(handle, ppIndirection);
+ mc->recEmbedFieldHandle(handle, ppIndirection, temp);
+ return temp;
+}
+
+// Given a module scope (module), a method handle (context) and
+// a metadata token (metaTOK), fetch the handle
+// (type, field or method) associated with the token.
+// If this is not possible at compile-time (because the current method's
+// code is shared and the token contains generic parameters)
+// then indicate how the handle should be looked up at run-time.
+//
+void interceptor_ICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ mc->cr->AddCall("embedGenericHandle");
+ original_ICorJitInfo->embedGenericHandle(pResolvedToken, fEmbedParent, pResult);
+ mc->recEmbedGenericHandle(pResolvedToken, fEmbedParent, pResult);
+}
+
+// Return information used to locate the exact enclosing type of the current method.
+// Used only to invoke .cctor method from code shared across generic instantiations
+// !needsRuntimeLookup statically known (enclosing type of method itself)
+// needsRuntimeLookup:
+// CORINFO_LOOKUP_THISOBJ use vtable pointer of 'this' param
+// CORINFO_LOOKUP_CLASSPARAM use vtable hidden param
+// CORINFO_LOOKUP_METHODPARAM use enclosing type of method-desc hidden param
+CORINFO_LOOKUP_KIND interceptor_ICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ mc->cr->AddCall("getLocationOfThisType");
+ CORINFO_LOOKUP_KIND temp = original_ICorJitInfo->getLocationOfThisType(context);
+ mc->recGetLocationOfThisType(context, &temp);
+ return temp;
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* interceptor_ICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getPInvokeUnmanagedTarget");
+ void *result = original_ICorJitInfo->getPInvokeUnmanagedTarget(method, ppIndirection);
+ mc->recGetPInvokeUnmanagedTarget(method, ppIndirection, result);
+ return result;
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* interceptor_ICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getAddressOfPInvokeFixup");
+ void *temp = original_ICorJitInfo->getAddressOfPInvokeFixup(method, ppIndirection);
+ mc->recGetAddressOfPInvokeFixup(method, ppIndirection, temp);
+ return temp;
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void interceptor_ICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ mc->cr->AddCall("getAddressOfPInvokeTarget");
+ original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup);
+ mc->recGetAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ mc->cr->AddCall("GetCookieForPInvokeCalliSig");
+ LPVOID temp = original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection);
+ mc->recGetCookieForPInvokeCalliSig(szMetaSig, ppIndirection, temp);
+ return temp;
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool interceptor_ICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ mc->cr->AddCall("canGetCookieForPInvokeCalliSig");
+ bool temp = original_ICorJitInfo->canGetCookieForPInvokeCalliSig(szMetaSig);
+ mc->recCanGetCookieForPInvokeCalliSig(szMetaSig, temp);
+ return temp;
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE interceptor_ICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ mc->cr->AddCall("getJustMyCodeHandle");
+ CORINFO_JUST_MY_CODE_HANDLE temp = original_ICorJitInfo->getJustMyCodeHandle(method, ppIndirection);
+ mc->recGetJustMyCodeHandle(method, ppIndirection, temp);
+ return temp;
+}
+
+// Gets a method handle that can be used to correlate profiling data.
+// This is the IP of a native method, or the address of the descriptor struct
+// for IL. Always guaranteed to be unique per process, and not to move. */
+void interceptor_ICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ mc->cr->AddCall("GetProfilingHandle");
+ original_ICorJitInfo->GetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+ mc->recGetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void interceptor_ICJI::getCallInfo(
+ // Token info
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ //Generics info
+ CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
+ //Security info
+ CORINFO_METHOD_HANDLE callerHandle,
+ //Jit info
+ CORINFO_CALLINFO_FLAGS flags,
+ //out params
+ CORINFO_CALL_INFO *pResult
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException {
+ interceptor_ICJI* pThis;
+ CORINFO_RESOLVED_TOKEN* pResolvedToken;
+ CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken;
+ CORINFO_METHOD_HANDLE callerHandle;
+ CORINFO_CALLINFO_FLAGS flags;
+ CORINFO_CALL_INFO* pResult;
+ } param;
+ param.pThis = this;
+ param.pResolvedToken = pResolvedToken;
+ param.pConstrainedResolvedToken = pConstrainedResolvedToken;
+ param.callerHandle = callerHandle;
+ param.flags = flags;
+ param.pResult = pResult;
+
+ PAL_TRY(Param*, pOuterParam, &param)
+ {
+ PAL_TRY(Param*, pParam, pOuterParam)
+ {
+ pParam->pThis->mc->cr->AddCall("getCallInfo");
+ pParam->pThis->original_ICorJitInfo->getCallInfo(pParam->pResolvedToken, pParam->pConstrainedResolvedToken, pParam->callerHandle, pParam->flags, pParam->pResult);
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue)
+ {
+ }
+ PAL_ENDTRY
+ }
+ PAL_FINALLY
+ {
+ this->mc->recGetCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult, param.exceptionCode);
+ }
+ PAL_ENDTRY
+ }
+
+BOOL interceptor_ICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+{
+ mc->cr->AddCall("canAccessFamily");
+ BOOL temp = original_ICorJitInfo->canAccessFamily(hCaller, hInstanceType);
+ mc->recCanAccessFamily(hCaller, hInstanceType, temp);
+ return temp;
+}
+
+// Returns TRUE if the Class Domain ID is the RID of the class (currently true for every class
+// except reflection emitted classes and generics)
+BOOL interceptor_ICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ mc->cr->AddCall("isRIDClassDomainID");
+ return original_ICorJitInfo->isRIDClassDomainID(cls);
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned interceptor_ICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getClassDomainID");
+ unsigned temp = original_ICorJitInfo->getClassDomainID(cls, ppIndirection);
+ mc->recGetClassDomainID(cls, ppIndirection, temp);
+ return temp;
+}
+
+// return the data's address (for static fields only)
+void* interceptor_ICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getFieldAddress");
+ void *temp = original_ICorJitInfo->getFieldAddress(field, ppIndirection);
+
+ //Figure out the element type so we know how much we can load
+ CORINFO_CLASS_HANDLE cch;
+ CorInfoType cit = getFieldType(field, &cch, NULL);
+ mc->recGetFieldAddress(field, ppIndirection, temp, cit);
+ return temp;
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getVarArgsHandle");
+ CORINFO_VARARGS_HANDLE temp = original_ICorJitInfo->getVarArgsHandle(pSig, ppIndirection);
+ mc->recGetVarArgsHandle(pSig, ppIndirection, temp);
+ return temp;
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool interceptor_ICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ mc->cr->AddCall("canGetVarArgsHandle");
+ bool temp = original_ICorJitInfo->canGetVarArgsHandle(pSig);
+ mc->recCanGetVarArgsHandle(pSig, temp);
+ return temp;
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType interceptor_ICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ mc->cr->AddCall("constructStringLiteral");
+ InfoAccessType temp = original_ICorJitInfo->constructStringLiteral(module, metaTok, ppValue);
+ mc->recConstructStringLiteral(module, metaTok, *ppValue, temp);
+ return temp;
+}
+
+InfoAccessType interceptor_ICJI::emptyStringLiteral(void **ppValue)
+{
+ mc->cr->AddCall("emptyStringLiteral");
+ InfoAccessType temp = original_ICorJitInfo->emptyStringLiteral(ppValue);
+ mc->recEmptyStringLiteral(ppValue, temp);
+ return temp;
+}
+
+// (static fields only) given that 'field' refers to thread local store,
+// return the ID (TLS index), which is used to find the begining of the
+// TLS data area for the particular DLL 'field' is associated with.
+DWORD interceptor_ICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mc->cr->AddCall("getFieldThreadLocalStoreID");
+ DWORD temp = original_ICorJitInfo->getFieldThreadLocalStoreID(field, ppIndirection);
+ mc->recGetFieldThreadLocalStoreID(field, ppIndirection, temp);
+ return temp;
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void interceptor_ICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ mc->cr->AddCall("setOverride");
+ original_ICorJitInfo->setOverride(pOverride, currentMethod);
+}
+
+// Adds an active dependency from the context method's module to the given module
+// This is internal callback for the EE. JIT should not call it directly.
+void interceptor_ICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ mc->cr->AddCall("addActiveDependency");
+ original_ICorJitInfo->addActiveDependency(moduleFrom, moduleTo);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ mc->cr->AddCall("GetDelegateCtor");
+ CORINFO_METHOD_HANDLE temp = original_ICorJitInfo->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+ mc->recGetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData, temp);
+ return temp;
+}
+
+void interceptor_ICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ mc->cr->AddCall("MethodCompileComplete");
+ original_ICorJitInfo->MethodCompileComplete(methHnd);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* interceptor_ICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ mc->cr->AddCall("getTailCallCopyArgsThunk");
+ void *result = original_ICorJitInfo->getTailCallCopyArgsThunk(pSig, flags);
+ mc->recGetTailCallCopyArgsThunk(pSig, flags, result);
+ return result;
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD interceptor_ICJI::getJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes)
+{
+ mc->cr->AddCall("getJitFlags");
+ DWORD result = original_ICorJitInfo->getJitFlags(jitFlags, sizeInBytes);
+ mc->recGetJitFlags(jitFlags, sizeInBytes, result);
+ return result;
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully. We don't
+// record the results of the call: when this call gets played back,
+// its result will depend on whether or not `function` calls something
+// that throws at playback time rather than at capture time.
+bool interceptor_ICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ mc->cr->AddCall("runWithErrorTrap");
+ return original_ICorJitInfo->runWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* interceptor_ICJI::getMemoryManager()
+{
+ mc->cr->AddCall("getMemoryManager");
+ if(current_IEEMM->original_IEEMM == nullptr)
+ current_IEEMM->original_IEEMM = original_ICorJitInfo->getMemoryManager();
+ return current_IEEMM;
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void interceptor_ICJI::allocMem (
+ ULONG hotCodeSize, /* IN */
+ ULONG coldCodeSize, /* IN */
+ ULONG roDataSize, /* IN */
+ ULONG xcptnsCount, /* IN */
+ CorJitAllocMemFlag flag, /* IN */
+ void ** hotCodeBlock, /* OUT */
+ void ** coldCodeBlock, /* OUT */
+ void ** roDataBlock /* OUT */
+ )
+{
+ mc->cr->AddCall("allocMem");
+ original_ICorJitInfo->allocMem(hotCodeSize, coldCodeSize, roDataSize, xcptnsCount, flag, hotCodeBlock, coldCodeBlock, roDataBlock);
+ mc->cr->recAllocMem(hotCodeSize, coldCodeSize, roDataSize, xcptnsCount, flag, hotCodeBlock, coldCodeBlock, roDataBlock);
+}
+
+// Reserve memory for the method/funclet's unwind information.
+// Note that this must be called before allocMem. It should be
+// called once for the main method, once for every funclet, and
+// once for every block of cold code for which allocUnwindInfo
+// will be called.
+//
+// This is necessary because jitted code must allocate all the
+// memory needed for the unwindInfo at the allocMem call.
+// For prejitted code we split up the unwinding information into
+// separate sections .rdata and .pdata.
+//
+void interceptor_ICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ mc->cr->AddCall("reserveUnwindInfo");
+ original_ICorJitInfo->reserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+ mc->cr->recReserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+}
+
+// Allocate and initialize the .rdata and .pdata for this method or
+// funclet, and get the block of memory needed for the machine-specific
+// unwind information (the info for crawling the stack frame).
+// Note that allocMem must be called first.
+//
+// Parameters:
+//
+// pHotCode main method code buffer, always filled in
+// pColdCode cold code buffer, only filled in if this is cold code,
+// null otherwise
+// startOffset start of code block, relative to appropriate code buffer
+// (e.g. pColdCode if cold, pHotCode if hot).
+// endOffset end of code block, relative to appropriate code buffer
+// unwindSize size of unwind info pointed to by pUnwindBlock
+// pUnwindBlock pointer to unwind info
+// funcKind type of funclet (main method code, handler, filter)
+//
+void interceptor_ICJI::allocUnwindInfo(
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ mc->cr->AddCall("allocUnwindInfo");
+ original_ICorJitInfo->allocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock, funcKind);
+ mc->cr->recAllocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock, funcKind);
+}
+
+// Get a block of memory needed for the code manager information,
+// (the info for enumerating the GC pointers while crawling the
+// stack frame).
+// Note that allocMem must be called first
+void *interceptor_ICJI::allocGCInfo(size_t size /* IN */)
+{
+ mc->cr->AddCall("allocGCInfo");
+ void *temp = original_ICorJitInfo->allocGCInfo(size);
+ mc->cr->recAllocGCInfo(size, temp);
+ return temp;
+}
+
+//only used on x64
+void interceptor_ICJI::yieldExecution()
+{
+ mc->cr->AddCall("yieldExecution"); //Nothing to record
+ original_ICorJitInfo->yieldExecution();
+}
+
+// Indicate how many exception handler blocks are to be returned.
+// This is guaranteed to be called before any 'setEHinfo' call.
+// Note that allocMem must be called before this method can be called.
+void interceptor_ICJI::setEHcount (unsigned cEH /* IN */)
+{
+ mc->cr->AddCall("setEHcount");
+ original_ICorJitInfo->setEHcount(cEH);
+ mc->cr->recSetEHcount(cEH);
+}
+
+// Set the values for one particular exception handler block.
+// Handler regions should be lexically contiguous.
+// This is because FinallyIsUnwinding() uses lexicality to
+// determine if a "finally" clause is executing.
+void interceptor_ICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ mc->cr->AddCall("setEHinfo");
+ original_ICorJitInfo->setEHinfo(EHnumber, clause);
+ mc->cr->recSetEHinfo(EHnumber, clause);
+}
+
+// Level 1 -> fatalError, Level 2 -> Error, Level 3 -> Warning
+// Level 4 means happens 10 times in a run, level 5 means 100, level 6 means 1000 ...
+// returns non-zero if the logging succeeded
+BOOL interceptor_ICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ mc->cr->AddCall("logMsg");
+ return original_ICorJitInfo->logMsg(level, fmt, args);
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int interceptor_ICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ mc->cr->AddCall("doAssert");
+ return original_ICorJitInfo->doAssert(szFile, iLine, szExpr);
+}
+
+void interceptor_ICJI::reportFatalError(CorJitResult result)
+{
+ mc->cr->AddCall("reportFatalError");
+ original_ICorJitInfo->reportFatalError(result);
+ mc->cr->recReportFatalError(result);
+}
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT interceptor_ICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer **profileBuffer
+ )
+{
+ mc->cr->AddCall("allocBBProfileBuffer");
+ HRESULT result = original_ICorJitInfo->allocBBProfileBuffer(count, profileBuffer);
+ mc->cr->recAllocBBProfileBuffer(count, profileBuffer, result);
+ return result;
+}
+
+// get profile information to be used for optimizing the current method. The format
+// of the buffer is the same as the format the JIT passes to allocBBProfileBuffer.
+HRESULT interceptor_ICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG *count, // The number of basic blocks that we have
+ ProfileBuffer **profileBuffer,
+ ULONG *numRuns
+ )
+{
+ mc->cr->AddCall("getBBProfileData");
+ HRESULT temp = original_ICorJitInfo->getBBProfileData(ftnHnd, count, profileBuffer, numRuns);
+ mc->recGetBBProfileData(ftnHnd, count, profileBuffer, numRuns, temp);
+ return temp;
+}
+
+// Associates a native call site, identified by its offset in the native code stream, with
+// the signature information and method handle the JIT used to lay out the call site. If
+// the call site has no signature information (e.g. a helper call) or has no method handle
+// (e.g. a CALLI P/Invoke), then null should be passed instead.
+void interceptor_ICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ mc->cr->AddCall("recordCallSite");
+ original_ICorJitInfo->recordCallSite(instrOffset, callSig, methodHandle);
+ mc->cr->recRecordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void interceptor_ICJI::recordRelocation(
+ void *location, /* IN */
+ void *target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ mc->cr->AddCall("recordRelocation");
+ original_ICorJitInfo->recordRelocation(location, target, fRelocType, slotNum, addlDelta);
+ mc->cr->recRecordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD interceptor_ICJI::getRelocTypeHint(void *target)
+{
+ mc->cr->AddCall("getRelocTypeHint");
+ WORD result = original_ICorJitInfo->getRelocTypeHint(target);
+ mc->recGetRelocTypeHint(target, result);
+ return result;
+}
+
+// A callback to identify the range of address known to point to
+// compiler-generated native entry points that call back into
+// MSIL.
+void interceptor_ICJI::getModuleNativeEntryPointRange(
+ void **pStart, /* OUT */
+ void **pEnd /* OUT */
+ )
+{
+ mc->cr->AddCall("getModuleNativeEntryPointRange");
+ original_ICorJitInfo->getModuleNativeEntryPointRange(pStart, pEnd);
+}
+
+// For what machine does the VM expect the JIT to generate code? The VM
+// returns one of the IMAGE_FILE_MACHINE_* values. Note that if the VM
+// is cross-compiling (such as the case for crossgen), it will return a
+// different value than if it was compiling for the host architecture.
+//
+DWORD interceptor_ICJI::getExpectedTargetArchitecture()
+{
+ return original_ICorJitInfo->getExpectedTargetArchitecture();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h
new file mode 100644
index 0000000000..08d7643613
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.h
@@ -0,0 +1,30 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitInfo
+#define _ICorJitInfo
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+#include "methodcontext.h"
+
+class interceptor_ICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+private:
+
+ void makeFatMC_ClassHandle(CORINFO_CLASS_HANDLE cls, bool getAttribs);
+
+public:
+
+ //Added to help us track the original icji and be able to easily indirect
+ //to it. And a simple way to keep one memory manager instance per instance.
+ ICorJitInfo *original_ICorJitInfo;
+ MethodContext *mc;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp
new file mode 100644
index 0000000000..e0021244cd
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.cpp
@@ -0,0 +1,72 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "ieememorymanager.h"
+#include "superpmi-shim-collector.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEEMM->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::AddRef()
+{
+ return original_IEEMM->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::Release()
+{
+ return original_IEEMM->Release();
+}
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ return original_IEEMM->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return original_IEEMM->ClrVirtualFree(lpAddress, dwSize, dwFreeType);
+}
+SIZE_T STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return original_IEEMM->ClrVirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return original_IEEMM->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessHeap()
+{
+ return original_IEEMM->ClrGetProcessHeap();
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ return original_IEEMM->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ return original_IEEMM->ClrHeapDestroy(hHeap);
+}
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_IEEMM->ClrHeapAlloc(hHeap, dwFlags, dwBytes);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return original_IEEMM->ClrHeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ return original_IEEMM->ClrHeapValidate(hHeap, dwFlags, lpMem);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessExecutableHeap()
+{
+ return original_IEEMM->ClrGetProcessExecutableHeap();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h
new file mode 100644
index 0000000000..4855d383b5
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/ieememorymanager.h
@@ -0,0 +1,43 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IEEMemoryManager
+#define _IEEMemoryManager
+
+#include "runtimedetails.h"
+
+class interceptor_IEEMM : public IEEMemoryManager
+{
+public:
+ interceptor_IEEMM()
+ : original_IEEMM(nullptr)
+ { }
+private:
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IEEMemoryManager methods for locking
+ //***************************************************************************
+ LPVOID STDMETHODCALLTYPE ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
+ BOOL STDMETHODCALLTYPE ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
+ SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+ BOOL STDMETHODCALLTYPE ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessHeap();
+ HANDLE STDMETHODCALLTYPE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
+ BOOL STDMETHODCALLTYPE ClrHeapDestroy(HANDLE hHeap);
+ LPVOID STDMETHODCALLTYPE ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
+ BOOL STDMETHODCALLTYPE ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
+ BOOL STDMETHODCALLTYPE ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap();
+
+public:
+ IEEMemoryManager *original_IEEMM;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp
new file mode 100644
index 0000000000..6bdde6ec8b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.cpp
@@ -0,0 +1,154 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// IExecutionEngine.cpp - core shim implementation for IEE stuff
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "iexecutionengine.h"
+#include "superpmi-shim-collector.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEE::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEE->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::AddRef()
+{
+ return original_IEE->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::Release()
+{
+ return original_IEE->Release();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ original_IEE->TLS_AssociateCallback(slot, callback);
+}
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE interceptor_IEE::TLS_GetDataBlock()
+{
+ return original_IEE->TLS_GetDataBlock();
+}
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE interceptor_IEE::TLS_GetValue(DWORD slot)
+{
+ return original_IEE->TLS_GetValue(slot);
+}
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE interceptor_IEE::TLS_CheckValue(DWORD slot, LPVOID *pValue)
+{
+ return original_IEE->TLS_CheckValue(slot, pValue);
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ original_IEE->TLS_SetValue(slot, pData);
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_ThreadDetaching()
+{
+ original_IEE->TLS_ThreadDetaching();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+CRITSEC_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return original_IEE->CreateLock(szTag, level, flags);
+}
+void STDMETHODCALLTYPE interceptor_IEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->DestroyLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->AcquireLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->ReleaseLock(lock);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateAutoEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateAutoEvent(bInitialState);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateManualEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateManualEvent(bInitialState);
+}
+void STDMETHODCALLTYPE interceptor_IEE::CloseEvent(EVENT_COOKIE event)
+{
+ original_IEE->CloseEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrSetEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrResetEvent(event);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->WaitForEvent(event, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ return original_IEE->WaitForSingleObject(handle, dwMilliseconds);
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ return original_IEE->ClrCreateSemaphore(dwInitial, dwMax);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ original_IEE->ClrCloseSemaphore(semaphore);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ return original_IEE->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount);
+}
+MUTEX_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName)
+{
+ return original_IEE->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ original_IEE->ClrCloseMutex(mutex);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ return original_IEE->ClrReleaseMutex(mutex);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForMutex(MUTEX_COOKIE mutex, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrSleepEx(dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrAllocationDisallowed()
+{
+ return original_IEE->ClrAllocationDisallowed();
+}
+void STDMETHODCALLTYPE interceptor_IEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ original_IEE->GetLastThrownObjectExceptionFromThread(ppvException);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h
new file mode 100644
index 0000000000..49c247ff99
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/iexecutionengine.h
@@ -0,0 +1,70 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// IExecutionEngine.h - core shim implementation for IEE stuff
+//----------------------------------------------------------
+#ifndef _IExecutionEngine
+#define _IExecutionEngine
+
+#include "ieememorymanager.h"
+
+class interceptor_IEE : public IExecutionEngine
+{
+private:
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IExecutionEngine methods for TLS
+ //***************************************************************************
+ // Associate a callback for cleanup with a TLS slot
+ VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
+ // Get the TLS block for fast Get/Set operations
+ LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
+ // Get the value at a slot
+ LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID *pValue);
+ // Set the value at a slot
+ VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
+ // Free TLS memory block and make callback
+ VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
+
+ //***************************************************************************
+ // IExecutionEngine methods for locking
+ //***************************************************************************
+ CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags);
+ void STDMETHODCALLTYPE DestroyLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE AcquireLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE ReleaseLock(CRITSEC_COOKIE lock);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateAutoEvent(BOOL bInitialState);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateManualEvent(BOOL bInitialState);
+ void STDMETHODCALLTYPE CloseEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrSetEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrResetEvent(EVENT_COOKIE event);
+ DWORD STDMETHODCALLTYPE WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds);
+ SEMAPHORE_COOKIE STDMETHODCALLTYPE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax);
+ void STDMETHODCALLTYPE ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore);
+ DWORD STDMETHODCALLTYPE ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount);
+ MUTEX_COOKIE STDMETHODCALLTYPE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);
+ void STDMETHODCALLTYPE ClrCloseMutex(MUTEX_COOKIE mutex);
+ BOOL STDMETHODCALLTYPE ClrReleaseMutex(MUTEX_COOKIE mutex);
+ DWORD STDMETHODCALLTYPE ClrWaitForMutex(MUTEX_COOKIE mutex, DWORD dwMilliseconds, BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrAllocationDisallowed();
+ void STDMETHODCALLTYPE GetLastThrownObjectExceptionFromThread(void **ppvException);
+
+public:
+ IExecutionEngine *original_IEE; //Our extra value that holds a pointer to the original IEE we'll pass calls along to
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp
new file mode 100644
index 0000000000..5e63a76be7
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "runtimedetails.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+JitHost* g_ourJitHost;
+
+JitHost::JitHost(ICorJitHost* wrappedHost, MethodContext* methodContext) : wrappedHost(wrappedHost), mc(methodContext)
+{
+}
+
+void JitHost::setMethodContext(MethodContext* methodContext)
+{
+ this->mc = methodContext;
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return wrappedHost->allocateMemory(size, usePageAllocator);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ return wrappedHost->freeMemory(block, usePageAllocator);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ mc->cr->AddCall("getIntConfigValue");
+ int result = wrappedHost->getIntConfigValue(key, defaultValue);
+
+ // The JIT eagerly asks about every config value. If we store all these
+ // queries, it takes almost half the MC file space. So only store the
+ // non-default answers.
+ if (result != defaultValue)
+ {
+ mc->recGetIntConfigValue(key, defaultValue, result);
+ }
+ return result;
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ mc->cr->AddCall("getStringConfigValue");
+ const wchar_t *result = wrappedHost->getStringConfigValue(key);
+
+ // Don't store null returns, which is the default
+ if (result != nullptr)
+ {
+ mc->recGetStringConfigValue(key, result);
+ }
+ return result;
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ mc->cr->AddCall("freeStringConfigValue");
+ wrappedHost->freeStringConfigValue(value);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/jithost.h b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.h
new file mode 100644
index 0000000000..2950a55682
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/jithost.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JITHOST
+#define _JITHOST
+
+class JitHost : public ICorJitHost
+{
+public:
+ JitHost(ICorJitHost* wrappedHost, MethodContext* methodContext);
+
+ void setMethodContext(MethodContext* methodContext);
+
+#include "icorjithostimpl.h"
+
+private:
+ ICorJitHost* wrappedHost;
+ MethodContext* mc;
+};
+
+extern JitHost* g_ourJitHost;
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp
new file mode 100644
index 0000000000..1a6d73da0a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp
@@ -0,0 +1,309 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim-Collector.cpp - Shim that collects and yields .mc (method context) files.
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "coreclrcallbacks.h"
+#include "icorjitcompiler.h"
+#include "runtimedetails.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+//Assumptions:
+// -We'll never be unloaded - we leak memory and have no facility to unload libraries
+// -printf output to console is okay
+
+HMODULE g_hRealJit = 0; //We leak this currently (could do the proper shutdown in process_detach)
+WCHAR* g_realJitPath = nullptr; //We leak this (could do the proper shutdown in process_detach)
+WCHAR* g_logPath = nullptr; //Again, we leak this one too...
+WCHAR* g_dataFileName = nullptr; //We leak this
+char* g_logFilePath = nullptr; //We *don't* leak this, hooray!
+WCHAR* g_HomeDirectory = nullptr;
+WCHAR* g_DefaultRealJitPath = nullptr;
+MethodContext* g_globalContext = nullptr;
+
+void SetDefaultPaths()
+{
+ if (g_HomeDirectory == nullptr)
+ {
+ g_HomeDirectory = GetEnvironmentVariableWithDefaultW(W("HOME"), W("."));
+ }
+
+ if (g_DefaultRealJitPath == nullptr)
+ {
+ size_t len = wcslen(g_HomeDirectory) + 1 + wcslen(DEFAULT_REAL_JIT_NAME_W) + 1;
+ g_DefaultRealJitPath = new WCHAR[len];
+ wcscpy_s(g_DefaultRealJitPath, len, g_HomeDirectory);
+ wcscat_s(g_DefaultRealJitPath, len, DIRECTORY_SEPARATOR_STR_W);
+ wcscat_s(g_DefaultRealJitPath, len, DEFAULT_REAL_JIT_NAME_W);
+ }
+}
+
+void SetLibName()
+{
+ if (g_realJitPath == nullptr)
+ {
+ g_realJitPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimPath"), g_DefaultRealJitPath);
+ }
+}
+
+void SetLogPath()
+{
+ if (g_logPath == nullptr)
+ {
+ g_logPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimLogPath"), g_HomeDirectory);
+ }
+}
+
+void SetLogPathName()
+{
+ // NOTE: under PAL, we don't get the comand line, so we depend on the random number generator to give us a unique filename.
+ WCHAR *OriginalExecutableName = GetCommandLineW();//TODO-Cleanup: not cool to write to the process view of commandline....
+ size_t len = wcslen(OriginalExecutableName);
+ WCHAR *ExecutableName = new WCHAR[len+1];
+ wcscpy_s(ExecutableName, len+1, OriginalExecutableName);
+ ExecutableName[len] = W('\0');
+ WCHAR *quote1 = NULL;
+
+ //if there are any quotes in filename convert them to spaces.
+ while ((quote1 = wcsstr(ExecutableName, W("\""))) != NULL)
+ *quote1 = W(' ');
+
+ //remove any illegal or annoying characters from file name by converting them to underscores
+ while ((quote1 = wcspbrk(ExecutableName, W("=<>:\"/\\|?! *.,"))) != NULL)
+ *quote1 = W('_');
+
+ const WCHAR *DataFileExtension = W(".mc");
+ size_t ExecutableNameLength = wcslen(ExecutableName);
+ size_t DataFileExtensionLength = wcslen(DataFileExtension);
+ size_t logPathLength = wcslen(g_logPath);
+
+ size_t dataFileNameLength = logPathLength + 1 + ExecutableNameLength + 1 + DataFileExtensionLength + 1;
+
+ const size_t MaxAcceptablePathLength = MAX_PATH - 20; // subtract 20 to leave buffer, for possible random number addition
+ if (dataFileNameLength >= MaxAcceptablePathLength)
+ {
+ // The path name is too long; creating the file will fail. This can happen because we use the command line,
+ // which for ngen includes lots of environment variables, for example.
+
+ // Assume (!) the extra space is all in the ExecutableName, so shorten that.
+ ExecutableNameLength -= dataFileNameLength - MaxAcceptablePathLength;
+
+ dataFileNameLength = MaxAcceptablePathLength;
+ }
+
+ // Always add a random number, just in case the above doesn't give us a unique filename.
+#ifdef FEATURE_PAL
+ unsigned __int64 randNumber = 0;
+ const size_t RandNumberLength = sizeof(randNumber) * 2 + 1; // 16 hex digits + null
+ WCHAR RandNumberString[RandNumberLength];
+ PAL_Random(/* bStrong */ FALSE, &randNumber, sizeof(randNumber));
+ swprintf_s(RandNumberString, RandNumberLength, W("%016llX"), randNumber);
+#else // !FEATURE_PAL
+ unsigned int randNumber = 0;
+ const size_t RandNumberLength = sizeof(randNumber) * 2 + 1; // 8 hex digits + null
+ WCHAR RandNumberString[RandNumberLength];
+ rand_s(&randNumber);
+ swprintf_s(RandNumberString, RandNumberLength, W("%08X"), randNumber);
+#endif // !FEATURE_PAL
+
+ dataFileNameLength += RandNumberLength - 1;
+
+ // Construct the full pathname we're going to use.
+ g_dataFileName = new WCHAR[dataFileNameLength];
+ g_dataFileName[0] = 0;
+ wcsncat_s(g_dataFileName, dataFileNameLength, g_logPath, logPathLength);
+ wcsncat_s(g_dataFileName, dataFileNameLength, DIRECTORY_SEPARATOR_STR_W, 1);
+ wcsncat_s(g_dataFileName, dataFileNameLength, ExecutableName, ExecutableNameLength);
+
+ if (RandNumberLength > 0)
+ {
+ wcsncat_s(g_dataFileName, dataFileNameLength, RandNumberString, RandNumberLength);
+ }
+
+ wcsncat_s(g_dataFileName, dataFileNameLength, DataFileExtension, DataFileExtensionLength);
+}
+
+// TODO: this only works for ANSI file paths...
+void SetLogFilePath()
+{
+ if (g_logFilePath == nullptr)
+ {
+ // If the environment variable isn't set, we don't enable file logging
+ g_logFilePath = GetEnvironmentVariableWithDefaultA("SuperPMIShimLogFilePath", nullptr);
+ }
+}
+
+extern "C"
+BOOL
+#ifndef FEATURE_PAL
+APIENTRY
+#endif // !FEATURE_PAL
+DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef FEATURE_PAL
+ if (0 != PAL_InitializeDLL())
+ {
+ fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+ SetLogFilePath();
+ Logger::OpenLogFile(g_logFilePath);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ Logger::Shutdown();
+
+ delete[] g_logFilePath;
+ g_logFilePath = nullptr;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+// Exported via def file
+extern "C"
+void __stdcall jitStartup(ICorJitHost* host)
+{
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if (g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if (g_hRealJit == 0)
+ {
+ LogError("jitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ // Get the required entrypoint
+ PjitStartup pnjitStartup = (PjitStartup)::GetProcAddress(g_hRealJit, "jitStartup");
+ if (pnjitStartup == nullptr)
+ {
+ // This portion of the interface is not used by the JIT under test.
+ return;
+ }
+
+ g_globalContext = new MethodContext();
+ g_ourJitHost = new JitHost(host, g_globalContext);
+ pnjitStartup(g_ourJitHost);
+}
+
+//Exported via def file
+extern "C"
+ICorJitCompiler* __stdcall getJit()
+{
+ DWORD dwRetVal = 0;
+ PgetJit pngetJit;
+ interceptor_ICJC *pJitInstance = nullptr;
+ ICorJitCompiler *tICJI = nullptr;
+
+ SetDefaultPaths();
+ SetLibName();
+ SetLogPath();
+ SetLogPathName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return nullptr;
+ }
+ }
+
+ //get the required entrypoints
+ pngetJit = (PgetJit)::GetProcAddress(g_hRealJit, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("getJit() - GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return nullptr;
+ }
+
+ tICJI = pngetJit();
+ if(tICJI == nullptr)
+ {
+ LogError("getJit() - pngetJit gave us null");
+ return nullptr;
+ }
+
+ pJitInstance = new interceptor_ICJC();
+ pJitInstance->original_ICorJitCompiler = tICJI;
+
+ //create our datafile
+ pJitInstance->hFile = CreateFileW(g_dataFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (pJitInstance->hFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Couldn't open file '%ws': error %d", g_dataFileName, GetLastError());
+ }
+
+ return pJitInstance;
+}
+
+//Exported via def file
+extern "C"
+void __stdcall sxsJitStartup(CoreClrCallbacks const & original_cccallbacks)
+{
+ PsxsJitStartup pnsxsJitStartup;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("sxsJitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ //get entry point
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(g_hRealJit, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("sxsJitStartup() - GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return;
+ }
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ original_CoreClrCallbacks = new CoreClrCallbacks();
+ original_CoreClrCallbacks->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ original_CoreClrCallbacks->m_pfnIEE = original_cccallbacks.m_pfnIEE;
+ original_CoreClrCallbacks->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ original_CoreClrCallbacks->m_pfnGetCLRFunction = original_cccallbacks.m_pfnGetCLRFunction;
+
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+
+ temp->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ pnsxsJitStartup(*temp);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def
new file mode 100644
index 0000000000..436434c3de
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.def
@@ -0,0 +1,5 @@
+LIBRARY
+EXPORTS
+ getJit
+ jitStartup
+ sxsJitStartup
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h
new file mode 100644
index 0000000000..62c813fae1
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.h
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim-Collector.h - Shim that collects and yields .mc (method context) files.
+//----------------------------------------------------------
+#ifndef _SuperPMIShim
+#define _SuperPMIShim
+
+class MethodContext;
+extern MethodContext* g_globalContext;
+
+void DebugBreakorAV(int val);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/.gitmirror b/src/ToolBox/superpmi/superpmi-shim-counter/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt b/src/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt
new file mode 100644
index 0000000000..2243053b3f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt
@@ -0,0 +1,73 @@
+project(superpmi-shim-counter)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+
+if(WIN32)
+ #use static crt
+ add_definitions(-MT)
+endif(WIN32)
+
+include_directories(.)
+include_directories(../superpmi-shared)
+
+set(SUPERPMI_SHIM_COUNTER_SOURCES
+ coreclrcallbacks.cpp
+ jithost.cpp
+ icorjitcompiler.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ methodcallsummarizer.cpp
+ superpmi-shim-counter.cpp
+ ../superpmi-shared/callutils.cpp
+ ../superpmi-shared/compileresult.cpp
+ ../superpmi-shared/errorhandling.cpp
+ ../superpmi-shared/logging.cpp
+ ../superpmi-shared/mclist.cpp
+ ../superpmi-shared/methodcontext.cpp
+ ../superpmi-shared/methodcontextreader.cpp
+ ../superpmi-shared/simpletimer.cpp
+ ../superpmi-shared/spmiutil.cpp
+ ../superpmi-shared/tocfile.cpp
+ ../superpmi-shared/typeutils.cpp
+)
+
+add_precompiled_header(
+ standardpch.h
+ ../superpmi-shared/standardpch.cpp
+ SUPERPMI_SHIM_COUNTER_SOURCES
+)
+
+if (WIN32)
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/superpmi-shim-counter.def ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-counter.def)
+
+ list(APPEND SUPERPMI_SHIM_COUNTER_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-counter.def)
+endif (WIN32)
+
+add_library(superpmi-shim-counter
+ SHARED
+ ${SUPERPMI_SHIM_COUNTER_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi-shim-counter
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi-shim-counter
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi-shim-counter.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi-shim-counter DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..5b764f2fa5
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp
@@ -0,0 +1,62 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+typedef LPVOID (__stdcall * pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
+typedef BOOL (__stdcall * pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
+
+CoreClrCallbacks *original_CoreClrCallbacks = nullptr;
+pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
+pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ interceptor_IEE *iee = new interceptor_IEE();
+ iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ if(original_EEHeapAllocInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+ if(original_EEHeapFreeInProcessHeap == nullptr)
+ __debugbreak();
+ return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ {
+ original_EEHeapAllocInProcessHeap =
+ (pfnEEHeapAllocInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapAllocInProcessHeap");
+ return (void*)EEHeapAllocInProcessHeap;
+ }
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ {
+ original_EEHeapFreeInProcessHeap =
+ (pfnEEHeapFreeInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapFreeInProcessHeap");
+ return (void*)EEHeapFreeInProcessHeap;
+ }
+ return original_CoreClrCallbacks->m_pfnGetCLRFunction(functionName);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h
new file mode 100644
index 0000000000..c8c3e27c66
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _CoreClrCallbacks
+#define _CoreClrCallbacks
+
+#include "runtimedetails.h"
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t();
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength);
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes);
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem);
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName);
+
+extern CoreClrCallbacks *original_CoreClrCallbacks;
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp
new file mode 100644
index 0000000000..da766cc51d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp
@@ -0,0 +1,68 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "icorjitcompiler.h"
+#include "icorjitinfo.h"
+
+interceptor_IEEMM *current_IEEMM = nullptr; //we want this to live beyond the scope of a single compileMethodCall
+
+CorJitResult __stdcall interceptor_ICJC::compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ )
+{
+ interceptor_ICJI our_ICorJitInfo;
+ our_ICorJitInfo.original_ICorJitInfo = comp;
+
+ if(current_IEEMM == nullptr)
+ current_IEEMM = new interceptor_IEEMM();
+ our_ICorJitInfo.mcs = mcs;
+
+ mcs->AddCall("compileMethod");
+ CorJitResult temp = original_ICorJitCompiler->compileMethod(&our_ICorJitInfo, info, flags, nativeEntry, nativeSizeOfCode);
+
+ mcs->SaveTextFile();
+ return temp;
+}
+
+void interceptor_ICJC::clearCache()
+{
+ mcs->AddCall("clearCache");
+ original_ICorJitCompiler->clearCache();
+}
+
+BOOL interceptor_ICJC::isCacheCleanupRequired()
+{
+ mcs->AddCall("isCacheCleanupRequired");
+ return original_ICorJitCompiler->isCacheCleanupRequired();
+}
+
+void interceptor_ICJC::ProcessShutdownWork(ICorStaticInfo* info)
+{
+ mcs->AddCall("ProcessShutdownWork");
+ original_ICorJitCompiler->ProcessShutdownWork(info);
+}
+
+void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */)
+{
+ mcs->AddCall("getVersionIdentifier");
+ original_ICorJitCompiler->getVersionIdentifier(versionIdentifier);
+}
+
+unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags)
+{
+ mcs->AddCall("getMaxIntrinsicSIMDVectorLength");
+ return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags);
+}
+
+void interceptor_ICJC::setRealJit(ICorJitCompiler* realJitCompiler)
+{
+ mcs->AddCall("setRealJit");
+ original_ICorJitCompiler->setRealJit(realJitCompiler);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.h b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.h
new file mode 100644
index 0000000000..1abcd0a77b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitCompiler
+#define _ICorJitCompiler
+
+#include "runtimedetails.h"
+#include "methodcallsummarizer.h"
+#include "ieememorymanager.h"
+
+class interceptor_ICJC : public ICorJitCompiler
+{
+
+#include "icorjitcompilerimpl.h"
+
+public:
+ // Added to help us track the original icjc and be able to easily indirect to it.
+ ICorJitCompiler *original_ICorJitCompiler;
+ MethodCallSummarizer *mcs;
+};
+
+extern interceptor_IEEMM *current_IEEMM; //we want this to live beyond the scope of a single compileMethodCall
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
new file mode 100644
index 0000000000..77519caa84
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
@@ -0,0 +1,1901 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "icorjitinfo.h"
+#include "superpmi-shim-counter.h"
+#include "ieememorymanager.h"
+#include "icorjitcompiler.h"
+#include "spmiutil.h"
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ mcs->AddCall("getMethodAttribs");
+ return original_ICorJitInfo->getMethodAttribs(ftn);
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void interceptor_ICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ mcs->AddCall("setMethodAttribs");
+ original_ICorJitInfo->setMethodAttribs(ftn, attribs);
+}
+
+
+// Given a method descriptor ftnHnd, extract signature information into sigInfo
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+void interceptor_ICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mcs->AddCall("getMethodSig");
+ original_ICorJitInfo->getMethodSig(ftn, sig, memberParent);
+}
+
+
+ /*********************************************************************
+ * Note the following methods can only be used on functions known
+ * to be IL. This includes the method being compiled and any method
+ * that 'getMethodInfo' returns true for
+ *********************************************************************/
+
+ // return information about a method private to the implementation
+ // returns false if method is not IL, or is otherwise unavailable.
+ // This method is used to fetch data needed to inline functions
+ bool interceptor_ICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ mcs->AddCall("getMethodInfo");
+ return original_ICorJitInfo->getMethodInfo(ftn, info);
+}
+
+// Decides if you have any limitations for inlining. If everything's OK, it will return
+// INLINE_PASS and will fill out pRestrictions with a mask of restrictions the caller of this
+// function must respect. If caller passes pRestrictions = nullptr, if there are any restrictions
+// INLINE_FAIL will be returned
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+//
+// The inlined method need not be verified
+
+CorInfoInline interceptor_ICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ mcs->AddCall("canInline");
+ return original_ICorJitInfo->canInline(callerHnd, calleeHnd, pRestrictions);
+}
+
+// Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all
+// inlining results when it returns INLINE_FAIL and INLINE_NEVER. All other results are reported by the
+// JIT.
+void interceptor_ICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ mcs->AddCall("reportInliningDecision");
+ original_ICorJitInfo->reportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
+}
+
+
+// Returns false if the call is across security boundaries thus we cannot tailcall
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+bool interceptor_ICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ mcs->AddCall("canTailCall");
+ return original_ICorJitInfo->canTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix);
+}
+
+// Reports whether or not a method can be tail called, and why.
+// canTailCall is responsible for reporting all results when it returns
+// false. All other results are reported by the JIT.
+void interceptor_ICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ mcs->AddCall("reportTailCallDecision");
+ original_ICorJitInfo->reportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void interceptor_ICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ mcs->AddCall("getEHinfo");
+ original_ICorJitInfo->getEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("getMethodClass");
+ return original_ICorJitInfo->getMethodClass(method);
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("getMethodModule");
+ return original_ICorJitInfo->getMethodModule(method);
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void interceptor_ICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ mcs->AddCall("getMethodVTableOffset");
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ mcs->AddCall("getIntrinsicID");
+ return original_ICorJitInfo->getIntrinsicID(method, pMustExpand);
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool interceptor_ICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ mcs->AddCall("isInSIMDModule");
+ return original_ICorJitInfo->isInSIMDModule(classHnd);
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("getUnmanagedCallConv");
+ return original_ICorJitInfo->getUnmanagedCallConv(method);
+}
+
+// return if any marshaling is required for PInvoke methods. Note that
+// method == 0 => calli. The call site sig is only needed for the varargs or calli case
+BOOL interceptor_ICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ mcs->AddCall("pInvokeMarshalingRequired");
+ return original_ICorJitInfo->pInvokeMarshalingRequired(method, callSiteSig);
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL interceptor_ICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("satisfiesMethodConstraints");
+ return original_ICorJitInfo->satisfiesMethodConstraints(parent, method);
+}
+
+// Given a delegate target class, a target method parent class, a target method,
+// a delegate class, check if the method signature is compatible with the Invoke method of the delegate
+// (under the typical instantiation of any free type variables in the memberref signatures).
+BOOL interceptor_ICJI::isCompatibleDelegate(
+ CORINFO_CLASS_HANDLE objCls, /* type of the delegate target, if any */
+ CORINFO_CLASS_HANDLE methodParentCls, /* exact parent of the target method, if any */
+ CORINFO_METHOD_HANDLE method, /* (representative) target method, if any */
+ CORINFO_CLASS_HANDLE delegateCls, /* exact type of the delegate */
+ BOOL *pfIsOpenDelegate /* is the delegate open */
+ )
+{
+ mcs->AddCall("isCompatibleDelegate");
+ return original_ICorJitInfo->isCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL interceptor_ICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ mcs->AddCall("isDelegateCreationAllowed");
+ return original_ICorJitInfo->isDelegateCreationAllowed(delegateHnd, calleeHnd);
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification interceptor_ICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ mcs->AddCall("isInstantiationOfVerifiedGeneric");
+ return original_ICorJitInfo->isInstantiationOfVerifiedGeneric(method);
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void interceptor_ICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ mcs->AddCall("initConstraintsForVerification");
+ original_ICorJitInfo->initConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+// Returns enum whether the method does not require verification
+// Also see ICorModuleInfo::canSkipVerification
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ mcs->AddCall("canSkipMethodVerification");
+ return original_ICorJitInfo->canSkipMethodVerification(ftnHandle);
+}
+
+// load and restore the method
+void interceptor_ICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("methodMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->methodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ mcs->AddCall("mapMethodDeclToMethodImpl");
+ return original_ICorJitInfo->mapMethodDeclToMethodImpl(method);
+}
+
+// Returns the global cookie for the /GS unsafe buffer checks
+// The cookie might be a constant value (JIT), or a handle to memory location (Ngen)
+void interceptor_ICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ mcs->AddCall("getGSCookie");
+ original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+
+// Resolve metadata token into runtime method handles.
+void interceptor_ICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ mcs->AddCall("resolveToken");
+ original_ICorJitInfo->resolveToken(pResolvedToken);
+}
+
+bool interceptor_ICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ mcs->AddCall("tryResolveToken");
+ return original_ICorJitInfo->tryResolveToken(pResolvedToken);
+}
+
+// Signature information about the call sig
+void interceptor_ICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mcs->AddCall("findSig");
+ original_ICorJitInfo->findSig(module, sigTOK, context, sig);
+}
+
+// for Varargs, the signature at the call site may differ from
+// the signature at the definition. Thus we need a way of
+// fetching the call site information
+void interceptor_ICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ mcs->AddCall("findCallSiteSig");
+ original_ICorJitInfo->findCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ mcs->AddCall("getTokenTypeAsHandle");
+ return original_ICorJitInfo->getTokenTypeAsHandle(pResolvedToken);
+}
+
+// Returns true if the module does not require verification
+//
+// If fQuickCheckOnlyWithoutCommit=TRUE, the function only checks that the
+// module does not currently require verification in the current AppDomain.
+// This decision could change in the future, and so should not be cached.
+// If it is cached, it should only be used as a hint.
+// This is only used by ngen for calculating certain hints.
+//
+
+// Returns enum whether the module does not require verification
+// Also see ICorMethodInfo::canSkipMethodVerification();
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ mcs->AddCall("canSkipVerification");
+ return original_ICorJitInfo->canSkipVerification(module);
+}
+
+// Checks if the given metadata token is valid
+BOOL interceptor_ICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mcs->AddCall("isValidToken");
+ return original_ICorJitInfo->isValidToken(module, metaTOK);
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL interceptor_ICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ mcs->AddCall("isValidStringRef");
+ return original_ICorJitInfo->isValidStringRef(module, metaTOK);
+}
+
+BOOL interceptor_ICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ mcs->AddCall("shouldEnforceCallvirtRestriction");
+ return original_ICorJitInfo->shouldEnforceCallvirtRestriction(scope);
+}
+
+/**********************************************************************************/
+//
+// ICorClassInfo
+//
+/**********************************************************************************/
+
+// If the value class 'cls' is isomorphic to a primitive type it will
+// return that type, otherwise it will return CORINFO_TYPE_VALUECLASS
+CorInfoType interceptor_ICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("asCorInfoType");
+ return original_ICorJitInfo->asCorInfoType(cls);
+}
+
+// for completeness
+const char* interceptor_ICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassName");
+ return original_ICorJitInfo->getClassName(cls);
+}
+
+
+// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
+// If fNamespace=TRUE, include the namespace/enclosing classes
+// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
+// If fAssembly=TRUE, suffix with a comma and the full assembly qualification
+// return size of representation
+int interceptor_ICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ mcs->AddCall("appendClassName");
+ return original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("isValueClass");
+ return original_ICorJitInfo->isValueClass(cls);
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("canInlineTypeCheckWithObjectVTable");
+ return original_ICorJitInfo->canInlineTypeCheckWithObjectVTable(cls);
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassAttribs");
+ return original_ICorJitInfo->getClassAttribs(cls);
+}
+
+// Returns "TRUE" iff "cls" is a struct type such that return buffers used for returning a value
+// of this type must be stack-allocated. This will generally be true only if the struct
+// contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
+// an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
+// returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
+// buffers do not require GC write barriers.
+BOOL interceptor_ICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("isStructRequiringStackAllocRetBuf");
+ return original_ICorJitInfo->isStructRequiringStackAllocRetBuf(cls);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassModule");
+ return original_ICorJitInfo->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE interceptor_ICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ mcs->AddCall("getModuleAssembly");
+ return original_ICorJitInfo->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* interceptor_ICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ mcs->AddCall("getAssemblyName");
+ return original_ICorJitInfo->getAssemblyName(assem);
+}
+
+// Allocate and delete process-lifetime objects. Should only be
+// referred to from static fields, lest a leak occur.
+// Note that "LongLifetimeFree" does not execute destructors, if "obj"
+// is an array of a struct type with a destructor.
+void* interceptor_ICJI::LongLifetimeMalloc(size_t sz)
+{
+ mcs->AddCall("LongLifetimeMalloc");
+ return original_ICorJitInfo->LongLifetimeMalloc(sz);
+}
+
+void interceptor_ICJI::LongLifetimeFree(void* obj)
+{
+ mcs->AddCall("LongLifetimeFree");
+ original_ICorJitInfo->LongLifetimeFree(obj);
+}
+
+size_t interceptor_ICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getClassModuleIdForStatics");
+ return original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection);
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned interceptor_ICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getClassSize");
+ return original_ICorJitInfo->getClassSize(cls);
+}
+
+unsigned interceptor_ICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ mcs->AddCall("getClassAlignmentRequirement");
+ return original_ICorJitInfo->getClassAlignmentRequirement(cls, fDoubleAlignHint);
+}
+
+// This is only called for Value classes. It returns a boolean array
+// in representing of 'cls' from a GC perspective. The class is
+// assumed to be an array of machine words
+// (of length // getClassSize(cls) / sizeof(void*)),
+// 'gcPtrs' is a poitner to an array of BYTEs of this length.
+// getClassGClayout fills in this array so that gcPtrs[i] is set
+// to one of the CorInfoGCType values which is the GC type of
+// the i-th machine word of an object of type 'cls'
+// returns the number of GC pointers in the array
+unsigned interceptor_ICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ mcs->AddCall("getClassGClayout");
+ return original_ICorJitInfo->getClassGClayout(cls, gcPtrs);
+}
+
+// returns the number of instance fields in a class
+unsigned interceptor_ICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ mcs->AddCall("getClassNumInstanceFields");
+ return original_ICorJitInfo->getClassNumInstanceFields(cls);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ mcs->AddCall("getFieldInClass");
+ return original_ICorJitInfo->getFieldInClass(clsHnd, num);
+}
+
+BOOL interceptor_ICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ mcs->AddCall("checkMethodModifier");
+ return original_ICorJitInfo->checkMethodModifier(hMethod, modifier, fOptional);
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc interceptor_ICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ mcs->AddCall("getNewHelper");
+ return original_ICorJitInfo->getNewHelper(pResolvedToken, callerHandle);
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ mcs->AddCall("getNewArrHelper");
+ return original_ICorJitInfo->getNewArrHelper(arrayCls);
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ mcs->AddCall("getCastingHelper");
+ return original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ mcs->AddCall("getSharedCCtorHelper");
+ return original_ICorJitInfo->getSharedCCtorHelper(clsHnd);
+}
+
+CorInfoHelpFunc interceptor_ICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ mcs->AddCall("getSecurityPrologHelper");
+ return original_ICorJitInfo->getSecurityPrologHelper(ftn);
+}
+
+// This is not pretty. Boxing nullable<T> actually returns
+// a boxed<T> not a boxed Nullable<T>. This call allows the verifier
+// to call back to the EE on the 'box' instruction and get the transformed
+// type to use for verification.
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getTypeForBox");
+ return original_ICorJitInfo->getTypeForBox(cls);
+}
+
+// returns the correct box helper for a particular class. Note
+// that if this returns CORINFO_HELP_BOX, the JIT can assume
+// 'standard' boxing (allocate object and copy), and optimize
+CorInfoHelpFunc interceptor_ICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getBoxHelper");
+ return original_ICorJitInfo->getBoxHelper(cls);
+}
+
+// returns the unbox helper. If 'helperCopies' points to a true
+// value it means the JIT is requesting a helper that unboxes the
+// value into a particular location and thus has the signature
+// void unboxHelper(void* dest, CORINFO_CLASS_HANDLE cls, Object* obj)
+// Otherwise (it is null or points at a FALSE value) it is requesting
+// a helper that returns a poitner to the unboxed data
+// void* unboxHelper(CORINFO_CLASS_HANDLE cls, Object* obj)
+// The EE has the option of NOT returning the copy style helper
+// (But must be able to always honor the non-copy style helper)
+// The EE set 'helperCopies' on return to indicate what kind of
+// helper has been created.
+
+CorInfoHelpFunc interceptor_ICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getUnBoxHelper");
+ return original_ICorJitInfo->getUnBoxHelper(cls);
+}
+
+bool interceptor_ICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mcs->AddCall("getReadyToRunHelper");
+ return original_ICorJitInfo->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+}
+
+void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ mcs->AddCall("getReadyToRunDelegateCtorHelper");
+ original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* interceptor_ICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ mcs->AddCall("getHelperName");
+ return original_ICorJitInfo->getHelperName(funcNum);
+}
+
+// This function tries to initialize the class (run the class constructor).
+// this function returns whether the JIT must insert helper calls before
+// accessing static field or method.
+//
+// See code:ICorClassInfo#ClassConstruction.
+CorInfoInitClassResult interceptor_ICJI::initClass(
+ CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
+ // nullptr - inquire about cctor trigger in method prolog
+ CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
+ CORINFO_CONTEXT_HANDLE context, // Exact context of method
+ BOOL speculative // TRUE means don't actually run it
+ )
+{
+ mcs->AddCall("initClass");
+ return original_ICorJitInfo->initClass(field, method, context, speculative);
+}
+
+// This used to be called "loadClass". This records the fact
+// that the class must be loaded (including restored if necessary) before we execute the
+// code that we are currently generating. When jitting code
+// the function loads the class immediately. When zapping code
+// the zapper will if necessary use the call to record the fact that we have
+// to do a fixup/restore before running the method currently being generated.
+//
+// This is typically used to ensure value types are loaded before zapped
+// code that manipulates them is executed, so that the GC can access information
+// about those value types.
+void interceptor_ICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("classMustBeLoadedBeforeCodeIsRun");
+ original_ICorJitInfo->classMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE interceptor_ICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ mcs->AddCall("getBuiltinClass");
+ return original_ICorJitInfo->getBuiltinClass(classId);
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType interceptor_ICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getTypeForPrimitiveValueClass");
+ return original_ICorJitInfo->getTypeForPrimitiveValueClass(cls);
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL interceptor_ICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ mcs->AddCall("canCast");
+ return original_ICorJitInfo->canCast(child, parent);
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL interceptor_ICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mcs->AddCall("areTypesEquivalent");
+ return original_ICorJitInfo->areTypesEquivalent(cls1, cls2);
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ mcs->AddCall("mergeClasses");
+ return original_ICorJitInfo->mergeClasses(cls1, cls2);
+}
+
+// Given a class handle, returns the Parent type.
+// For COMObjectType, it returns Class Handle of System.Object.
+// Returns 0 if System.Object is passed in.
+CORINFO_CLASS_HANDLE interceptor_ICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getParentType");
+ return original_ICorJitInfo->getParentType(cls);
+}
+
+// Returns the CorInfoType of the "child type". If the child type is
+// not a primitive type, *clsRet will be set.
+// Given an Array of Type Foo, returns Foo.
+// Given BYREF Foo, returns Foo
+CorInfoType interceptor_ICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ mcs->AddCall("getChildType");
+ return original_ICorJitInfo->getChildType(clsHnd, clsRet);
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL interceptor_ICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("satisfiesClassConstraints");
+ return original_ICorJitInfo->satisfiesClassConstraints(cls);
+}
+
+// Check if this is a single dimensional array type
+BOOL interceptor_ICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("isSDArray");
+ return original_ICorJitInfo->isSDArray(cls);
+}
+
+// Get the numbmer of dimensions in an array
+unsigned interceptor_ICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ mcs->AddCall("getArrayRank");
+ return original_ICorJitInfo->getArrayRank(cls);
+}
+
+// Get static field data for an array
+void * interceptor_ICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ mcs->AddCall("getArrayInitializationData");
+ return original_ICorJitInfo->getArrayInitializationData(field, size);
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult interceptor_ICJI::canAccessClass(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper /* If canAccessMethod returns something other
+ than ALLOWED, then this is filled in. */
+ )
+{
+ mcs->AddCall("canAccessClass");
+ return original_ICorJitInfo->canAccessClass(pResolvedToken, callerHandle, pAccessHelper);
+}
+
+/**********************************************************************************/
+//
+// ICorFieldInfo
+//
+/**********************************************************************************/
+
+// this function is for debugging only. It returns the field name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* interceptor_ICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mcs->AddCall("getFieldName");
+ return original_ICorJitInfo->getFieldName(ftn, moduleName);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mcs->AddCall("getFieldClass");
+ return original_ICorJitInfo->getFieldClass(field);
+}
+
+// Return the field's type, if it is CORINFO_TYPE_VALUECLASS 'structType' is set
+// the field's value class (if 'structType' == 0, then don't bother
+// the structure info).
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+CorInfoType interceptor_ICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ mcs->AddCall("getFieldType");
+ return original_ICorJitInfo->getFieldType(field, structType, memberParent);
+}
+
+// return the data member's instance offset
+unsigned interceptor_ICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ mcs->AddCall("getFieldOffset");
+ return original_ICorJitInfo->getFieldOffset(field);
+}
+
+// TODO: jit64 should be switched to the same plan as the i386 jits - use
+// getClassGClayout to figure out the need for writebarrier helper, and inline the copying.
+// The interpretted value class copy is slow. Once this happens, USE_WRITE_BARRIER_HELPERS
+bool interceptor_ICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ mcs->AddCall("isWriteBarrierHelperRequired");
+ return original_ICorJitInfo->isWriteBarrierHelperRequired(field);
+}
+
+void interceptor_ICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ mcs->AddCall("getFieldInfo");
+ original_ICorJitInfo->getFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ mcs->AddCall("isFieldStatic");
+ return original_ICorJitInfo->isFieldStatic(fldHnd);
+}
+
+/*********************************************************************************/
+//
+// ICorDebugInfo
+//
+/*********************************************************************************/
+
+// Query the EE to find out where interesting break points
+// in the code are. The native compiler will ensure that these places
+// have a corresponding break point in native code.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void interceptor_ICJI::getBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ unsigned int *cILOffsets, // [OUT] size of pILOffsets
+ DWORD **pILOffsets, // [OUT] IL offsets of interest
+ // jit MUST free with freeArray!
+ ICorDebugInfo::BoundaryTypes *implictBoundaries // [OUT] tell jit, all boundries of this type
+ )
+{
+ mcs->AddCall("getBoundaries");
+ original_ICorJitInfo->getBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+}
+
+// Report back the mapping from IL to native code,
+// this map should include all boundaries that 'getBoundaries'
+// reported as interesting to the debugger.
+
+// Note that debugger (and profiler) is assuming that all of the
+// offsets form a contiguous block of memory, and that the
+// OffsetMapping is sorted in order of increasing native offset.
+void interceptor_ICJI::setBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cMap, // [IN] size of pMap
+ ICorDebugInfo::OffsetMapping *pMap // [IN] map including all points of interest.
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ mcs->AddCall("setBoundaries");
+ original_ICorJitInfo->setBoundaries(ftn, cMap, pMap);
+}
+
+// Query the EE to find out the scope of local varables.
+// normally the JIT would trash variables after last use, but
+// under debugging, the JIT needs to keep them live over their
+// entire scope so that they can be inspected.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void interceptor_ICJI::getVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 *cVars, // [OUT] size of 'vars'
+ ICorDebugInfo::ILVarInfo **vars, // [OUT] scopes of variables of interest
+ // jit MUST free with freeArray!
+ bool *extendOthers // [OUT] it TRUE, then assume the scope
+ // of unmentioned vars is entire method
+ )
+{
+ mcs->AddCall("getVars");
+ original_ICorJitInfo->getVars(ftn, cVars, vars, extendOthers);
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+
+void interceptor_ICJI::setVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cVars, // [IN] size of 'vars'
+ ICorDebugInfo::NativeVarInfo *vars // [IN] map telling where local vars are stored at what points
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ mcs->AddCall("setVars");
+ original_ICorJitInfo->setVars(ftn, cVars, vars);
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * interceptor_ICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ mcs->AddCall("allocateArray");
+ return original_ICorJitInfo->allocateArray(cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void interceptor_ICJI::freeArray(
+ void *array
+ )
+{
+ mcs->AddCall("freeArray");
+ original_ICorJitInfo->freeArray(array);
+}
+
+/*********************************************************************************/
+//
+// ICorArgInfo
+//
+/*********************************************************************************/
+
+// advance the pointer to the argument list.
+// a ptr of 0, is special and always means the first argument
+CORINFO_ARG_LIST_HANDLE interceptor_ICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ mcs->AddCall("getArgNext");
+ return original_ICorJitInfo->getArgNext(args);
+}
+
+// Get the type of a particular argument
+// CORINFO_TYPE_UNDEF is returned when there are no more arguments
+// If the type returned is a primitive type (or an enum) *vcTypeRet set to nullptr
+// otherwise it is set to the TypeHandle associted with the type
+// Enumerations will always look their underlying type (probably should fix this)
+// Otherwise vcTypeRet is the type as would be seen by the IL,
+// The return value is the type that is used for calling convention purposes
+// (Thus if the EE wants a value class to be passed like an int, then it will
+// return CORINFO_TYPE_INT
+CorInfoTypeWithMod interceptor_ICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ mcs->AddCall("getArgType");
+ return original_ICorJitInfo->getArgType(sig, args, vcTypeRet);
+}
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ mcs->AddCall("getArgClass");
+ return original_ICorJitInfo->getArgClass(sig, args);
+}
+
+// Returns type of HFA for valuetype
+CorInfoType interceptor_ICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ mcs->AddCall("getHFAType");
+ return original_ICorJitInfo->getHFAType(hClass);
+}
+
+/*****************************************************************************
+* ICorErrorInfo contains methods to deal with SEH exceptions being thrown
+* from the corinfo interface. These methods may be called when an exception
+* with code EXCEPTION_COMPLUS is caught.
+*****************************************************************************/
+
+// Returns the HRESULT of the current exception
+HRESULT interceptor_ICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mcs->AddCall("GetErrorHRESULT");
+ return original_ICorJitInfo->GetErrorHRESULT(pExceptionPointers);
+}
+
+// Fetches the message of the current exception
+// Returns the size of the message (including terminating null). This can be
+// greater than bufferLength if the buffer is insufficient.
+ULONG interceptor_ICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ mcs->AddCall("GetErrorMessage");
+ return original_ICorJitInfo->GetErrorMessage(buffer, bufferLength);
+}
+
+// returns EXCEPTION_EXECUTE_HANDLER if it is OK for the compile to handle the
+// exception, abort some work (like the inlining) and continue compilation
+// returns EXCEPTION_CONTINUE_SEARCH if exception must always be handled by the EE
+// things like ThreadStoppedException ...
+// returns EXCEPTION_CONTINUE_EXECUTION if exception is fixed up by the EE
+
+int interceptor_ICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mcs->AddCall("FilterException");
+ return original_ICorJitInfo->FilterException(pExceptionPointers);
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void interceptor_ICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ mcs->AddCall("HandleException");
+ original_ICorJitInfo->HandleException(pExceptionPointers);
+}
+
+void interceptor_ICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ mcs->AddCall("ThrowExceptionForJitResult");
+ original_ICorJitInfo->ThrowExceptionForJitResult(result);
+}
+
+//Throws an exception defined by the given throw helper.
+void interceptor_ICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ mcs->AddCall("ThrowExceptionForHelper");
+ original_ICorJitInfo->ThrowExceptionForHelper(throwHelper);
+}
+
+/*****************************************************************************
+ * ICorStaticInfo contains EE interface methods which return values that are
+ * constant from invocation to invocation. Thus they may be embedded in
+ * persisted information like statically generated code. (This is of course
+ * assuming that all code versions are identical each time.)
+ *****************************************************************************/
+
+// Return details about EE internal data structures
+void interceptor_ICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ mcs->AddCall("getEEInfo");
+ original_ICorJitInfo->getEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR interceptor_ICJI::getJitTimeLogFilename()
+{
+ mcs->AddCall("getJitTimeLogFilename");
+ return original_ICorJitInfo->getJitTimeLogFilename();
+}
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef interceptor_ICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ mcs->AddCall("getMethodDefFromMethod");
+ return original_ICorJitInfo->getMethodDefFromMethod(hMethod);
+}
+
+// this function is for debugging only. It returns the method name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* interceptor_ICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ mcs->AddCall("getMethodName");
+ return original_ICorJitInfo->getMethodName(ftn, moduleName);
+}
+
+// this function is for debugging only. It returns a value that
+// is will always be the same for a given method. It is used
+// to implement the 'jitRange' functionality
+unsigned interceptor_ICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ mcs->AddCall("getMethodHash");
+ return original_ICorJitInfo->getMethodHash(ftn);
+}
+
+// this function is for debugging only.
+size_t interceptor_ICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ mcs->AddCall("findNameOfToken");
+ return original_ICorJitInfo->findNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+}
+
+bool interceptor_ICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ mcs->AddCall("getSystemVAmd64PassStructInRegisterDescriptor");
+ return original_ICorJitInfo->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+}
+
+//Stuff on ICorDynamicInfo
+DWORD interceptor_ICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getThreadTLSIndex");
+ return original_ICorJitInfo->getThreadTLSIndex(ppIndirection);
+}
+
+const void * interceptor_ICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getInlinedCallFrameVptr");
+ return original_ICorJitInfo->getInlinedCallFrameVptr(ppIndirection);
+}
+
+LONG * interceptor_ICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getAddrOfCaptureThreadGlobal");
+ return original_ICorJitInfo->getAddrOfCaptureThreadGlobal(ppIndirection);
+}
+
+SIZE_T* interceptor_ICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ mcs->AddCall("getAddrModuleDomainID");
+ return original_ICorJitInfo->getAddrModuleDomainID(module);
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* interceptor_ICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getHelperFtn");
+ return original_ICorJitInfo->getHelperFtn(ftnNum, ppIndirection);
+}
+
+// return a callable address of the function (native code). This function
+// may return a different value (depending on whether the method has
+// been JITed or not.
+void interceptor_ICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ mcs->AddCall("getFunctionEntryPoint");
+ original_ICorJitInfo->getFunctionEntryPoint(ftn, pResult, accessFlags);
+}
+
+// return a directly callable address. This can be used similarly to the
+// value returned by getFunctionEntryPoint() except that it is
+// guaranteed to be multi callable entrypoint.
+void interceptor_ICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ mcs->AddCall("getFunctionFixedEntryPoint");
+ original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* interceptor_ICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getMethodSync");
+ return original_ICorJitInfo->getMethodSync(ftn, ppIndirection);
+}
+
+// These entry points must be called if a handle is being embedded in
+// the code to be passed to a JIT helper function. (as opposed to just
+// being passed back into the ICorInfo interface.)
+
+// get slow lazy string literal helper to use (CORINFO_HELP_STRCNS*).
+// Returns CORINFO_HELP_UNDEF if lazy string literal helper cannot be used.
+CorInfoHelpFunc interceptor_ICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ mcs->AddCall("getLazyStringLiteralHelper");
+ return original_ICorJitInfo->getLazyStringLiteralHelper(handle);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedModuleHandle");
+ return original_ICorJitInfo->embedModuleHandle(handle, ppIndirection);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedClassHandle");
+ return original_ICorJitInfo->embedClassHandle(handle, ppIndirection);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedMethodHandle");
+ return original_ICorJitInfo->embedMethodHandle(handle, ppIndirection);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("embedFieldHandle");
+ return original_ICorJitInfo->embedFieldHandle(handle, ppIndirection);
+}
+
+// Given a module scope (module), a method handle (context) and
+// a metadata token (metaTOK), fetch the handle
+// (type, field or method) associated with the token.
+// If this is not possible at compile-time (because the current method's
+// code is shared and the token contains generic parameters)
+// then indicate how the handle should be looked up at run-time.
+//
+void interceptor_ICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ mcs->AddCall("embedGenericHandle");
+ original_ICorJitInfo->embedGenericHandle(pResolvedToken, fEmbedParent, pResult);
+}
+
+// Return information used to locate the exact enclosing type of the current method.
+// Used only to invoke .cctor method from code shared across generic instantiations
+// !needsRuntimeLookup statically known (enclosing type of method itself)
+// needsRuntimeLookup:
+// CORINFO_LOOKUP_THISOBJ use vtable pointer of 'this' param
+// CORINFO_LOOKUP_CLASSPARAM use vtable hidden param
+// CORINFO_LOOKUP_METHODPARAM use enclosing type of method-desc hidden param
+CORINFO_LOOKUP_KIND interceptor_ICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ mcs->AddCall("getLocationOfThisType");
+ return original_ICorJitInfo->getLocationOfThisType(context);
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* interceptor_ICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getPInvokeUnmanagedTarget");
+ return original_ICorJitInfo->getPInvokeUnmanagedTarget(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* interceptor_ICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getAddressOfPInvokeFixup");
+ return original_ICorJitInfo->getAddressOfPInvokeFixup(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void interceptor_ICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ mcs->AddCall("getAddressOfPInvokeTarget");
+ original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ mcs->AddCall("GetCookieForPInvokeCalliSig");
+ return original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool interceptor_ICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ mcs->AddCall("canGetCookieForPInvokeCalliSig");
+ return original_ICorJitInfo->canGetCookieForPInvokeCalliSig(szMetaSig);
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE interceptor_ICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ mcs->AddCall("getJustMyCodeHandle");
+ return original_ICorJitInfo->getJustMyCodeHandle(method, ppIndirection);
+}
+
+// Gets a method handle that can be used to correlate profiling data.
+// This is the IP of a native method, or the address of the descriptor struct
+// for IL. Always guaranteed to be unique per process, and not to move. */
+void interceptor_ICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ mcs->AddCall("GetProfilingHandle");
+ original_ICorJitInfo->GetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void interceptor_ICJI::getCallInfo(
+ // Token info
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+
+ //Generics info
+ CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
+
+ //Security info
+ CORINFO_METHOD_HANDLE callerHandle,
+
+ //Jit info
+ CORINFO_CALLINFO_FLAGS flags,
+
+ //out params
+ CORINFO_CALL_INFO *pResult
+ )
+{
+ mcs->AddCall("getCallInfo");
+ original_ICorJitInfo->getCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult);
+}
+
+BOOL interceptor_ICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+
+{
+ mcs->AddCall("canAccessFamily");
+ return original_ICorJitInfo->canAccessFamily(hCaller, hInstanceType);
+}
+// Returns TRUE if the Class Domain ID is the RID of the class (currently true for every class
+// except reflection emitted classes and generics)
+BOOL interceptor_ICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ mcs->AddCall("isRIDClassDomainID");
+ return original_ICorJitInfo->isRIDClassDomainID(cls);
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned interceptor_ICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getClassDomainID");
+ return original_ICorJitInfo->getClassDomainID(cls, ppIndirection);
+}
+
+
+// return the data's address (for static fields only)
+void* interceptor_ICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getFieldAddress");
+ return original_ICorJitInfo->getFieldAddress(field, ppIndirection);
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getVarArgsHandle");
+ return original_ICorJitInfo->getVarArgsHandle(pSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool interceptor_ICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ mcs->AddCall("canGetVarArgsHandle");
+ return original_ICorJitInfo->canGetVarArgsHandle(pSig);
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType interceptor_ICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ mcs->AddCall("constructStringLiteral");
+ return original_ICorJitInfo->constructStringLiteral(module, metaTok, ppValue);
+}
+
+InfoAccessType interceptor_ICJI::emptyStringLiteral(
+ void **ppValue
+ )
+{
+ mcs->AddCall("emptyStringLiteral");
+ return original_ICorJitInfo->emptyStringLiteral(ppValue);
+}
+
+// (static fields only) given that 'field' refers to thread local store,
+// return the ID (TLS index), which is used to find the begining of the
+// TLS data area for the particular DLL 'field' is associated with.
+DWORD interceptor_ICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ mcs->AddCall("getFieldThreadLocalStoreID");
+ return original_ICorJitInfo->getFieldThreadLocalStoreID(field, ppIndirection);
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void interceptor_ICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ mcs->AddCall("setOverride");
+ original_ICorJitInfo->setOverride(pOverride, currentMethod);
+}
+
+// Adds an active dependency from the context method's module to the given module
+// This is internal callback for the EE. JIT should not call it directly.
+void interceptor_ICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ mcs->AddCall("addActiveDependency");
+ original_ICorJitInfo->addActiveDependency(moduleFrom, moduleTo);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ mcs->AddCall("GetDelegateCtor");
+ return original_ICorJitInfo->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+}
+
+void interceptor_ICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ mcs->AddCall("MethodCompileComplete");
+ original_ICorJitInfo->MethodCompileComplete(methHnd);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* interceptor_ICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ mcs->AddCall("getTailCallCopyArgsThunk");
+ return original_ICorJitInfo->getTailCallCopyArgsThunk(pSig, flags);
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD interceptor_ICJI::getJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes)
+{
+ mcs->AddCall("getJitFlags");
+ return original_ICorJitInfo->getJitFlags(jitFlags, sizeInBytes);
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully. We don't
+// record the results of the call: when this call gets played back,
+// its result will depend on whether or not `function` calls something
+// that throws at playback time rather than at capture time.
+bool interceptor_ICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ mcs->AddCall("runWithErrorTrap");
+ return original_ICorJitInfo->runWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* interceptor_ICJI::getMemoryManager()
+{
+ mcs->AddCall("getMemoryManager");
+ if(current_IEEMM->original_IEEMM == nullptr)
+ current_IEEMM->original_IEEMM = original_ICorJitInfo->getMemoryManager();
+
+ return current_IEEMM;
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void interceptor_ICJI::allocMem (
+ ULONG hotCodeSize, /* IN */
+ ULONG coldCodeSize, /* IN */
+ ULONG roDataSize, /* IN */
+ ULONG xcptnsCount, /* IN */
+ CorJitAllocMemFlag flag, /* IN */
+ void ** hotCodeBlock, /* OUT */
+ void ** coldCodeBlock, /* OUT */
+ void ** roDataBlock /* OUT */
+ )
+{
+ mcs->AddCall("allocMem");
+ return original_ICorJitInfo->allocMem(hotCodeSize, coldCodeSize, roDataSize, xcptnsCount, flag, hotCodeBlock, coldCodeBlock, roDataBlock);
+}
+
+// Reserve memory for the method/funclet's unwind information.
+// Note that this must be called before allocMem. It should be
+// called once for the main method, once for every funclet, and
+// once for every block of cold code for which allocUnwindInfo
+// will be called.
+//
+// This is necessary because jitted code must allocate all the
+// memory needed for the unwindInfo at the allocMem call.
+// For prejitted code we split up the unwinding information into
+// separate sections .rdata and .pdata.
+//
+void interceptor_ICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ mcs->AddCall("reserveUnwindInfo");
+ original_ICorJitInfo->reserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+}
+
+// Allocate and initialize the .rdata and .pdata for this method or
+// funclet, and get the block of memory needed for the machine-specific
+// unwind information (the info for crawling the stack frame).
+// Note that allocMem must be called first.
+//
+// Parameters:
+//
+// pHotCode main method code buffer, always filled in
+// pColdCode cold code buffer, only filled in if this is cold code,
+// null otherwise
+// startOffset start of code block, relative to appropriate code buffer
+// (e.g. pColdCode if cold, pHotCode if hot).
+// endOffset end of code block, relative to appropriate code buffer
+// unwindSize size of unwind info pointed to by pUnwindBlock
+// pUnwindBlock pointer to unwind info
+// funcKind type of funclet (main method code, handler, filter)
+//
+void interceptor_ICJI::allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ mcs->AddCall("allocUnwindInfo");
+ original_ICorJitInfo->allocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock, funcKind);
+}
+
+// Get a block of memory needed for the code manager information,
+// (the info for enumerating the GC pointers while crawling the
+// stack frame).
+// Note that allocMem must be called first
+void * interceptor_ICJI::allocGCInfo (
+ size_t size /* IN */
+ )
+{
+ mcs->AddCall("allocGCInfo");
+ return original_ICorJitInfo->allocGCInfo(size);
+}
+
+//only used on x64
+void interceptor_ICJI::yieldExecution()
+{
+ mcs->AddCall("yieldExecution");
+ original_ICorJitInfo->yieldExecution();
+}
+
+// Indicate how many exception handler blocks are to be returned.
+// This is guaranteed to be called before any 'setEHinfo' call.
+// Note that allocMem must be called before this method can be called.
+void interceptor_ICJI::setEHcount (
+ unsigned cEH /* IN */
+ )
+{
+ mcs->AddCall("setEHcount");
+ original_ICorJitInfo->setEHcount(cEH);
+}
+
+// Set the values for one particular exception handler block.
+//
+// Handler regions should be lexically contiguous.
+// This is because FinallyIsUnwinding() uses lexicality to
+// determine if a "finally" clause is executing.
+void interceptor_ICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ mcs->AddCall("setEHinfo");
+ original_ICorJitInfo->setEHinfo(EHnumber, clause);
+}
+
+// Level 1 -> fatalError, Level 2 -> Error, Level 3 -> Warning
+// Level 4 means happens 10 times in a run, level 5 means 100, level 6 means 1000 ...
+// returns non-zero if the logging succeeded
+BOOL interceptor_ICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ mcs->AddCall("logMsg");
+ return original_ICorJitInfo->logMsg(level, fmt, args);
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int interceptor_ICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ mcs->AddCall("doAssert");
+ return original_ICorJitInfo->doAssert(szFile, iLine, szExpr);
+}
+
+void interceptor_ICJI::reportFatalError(CorJitResult result)
+{
+ mcs->AddCall("reportFatalError");
+ original_ICorJitInfo->reportFatalError(result);
+}
+
+/*
+struct ProfileBuffer // Also defined here: code:CORBBTPROF_BLOCK_DATA
+{
+ ULONG ILOffset;
+ ULONG ExecutionCount;
+};
+*/
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT interceptor_ICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer
+ )
+{
+ mcs->AddCall("allocBBProfileBuffer");
+ return original_ICorJitInfo->allocBBProfileBuffer(count, profileBuffer);
+}
+
+// get profile information to be used for optimizing the current method. The format
+// of the buffer is the same as the format the JIT passes to allocBBProfileBuffer.
+HRESULT interceptor_ICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * numRuns
+ )
+{
+ mcs->AddCall("getBBProfileData");
+ return original_ICorJitInfo->getBBProfileData(ftnHnd, count, profileBuffer, numRuns);
+}
+
+// Associates a native call site, identified by its offset in the native code stream, with
+// the signature information and method handle the JIT used to lay out the call site. If
+// the call site has no signature information (e.g. a helper call) or has no method handle
+// (e.g. a CALLI P/Invoke), then null should be passed instead.
+void interceptor_ICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ mcs->AddCall("recordCallSite");
+ return original_ICorJitInfo->recordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void interceptor_ICJI::recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ mcs->AddCall("recordRelocation");
+ original_ICorJitInfo->recordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD interceptor_ICJI::getRelocTypeHint(void * target)
+{
+ mcs->AddCall("getRelocTypeHint");
+ return original_ICorJitInfo->getRelocTypeHint(target);
+}
+
+// A callback to identify the range of address known to point to
+// compiler-generated native entry points that call back into
+// MSIL.
+void interceptor_ICJI::getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ )
+{
+ mcs->AddCall("getModuleNativeEntryPointRange");
+ original_ICorJitInfo->getModuleNativeEntryPointRange(pStart, pEnd);
+}
+
+// For what machine does the VM expect the JIT to generate code? The VM
+// returns one of the IMAGE_FILE_MACHINE_* values. Note that if the VM
+// is cross-compiling (such as the case for crossgen), it will return a
+// different value than if it was compiling for the host architecture.
+//
+DWORD interceptor_ICJI::getExpectedTargetArchitecture()
+{
+ mcs->AddCall("getExpectedTargetArchitecture");
+ return original_ICorJitInfo->getExpectedTargetArchitecture();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.h b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.h
new file mode 100644
index 0000000000..ab90cfc646
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitInfo
+#define _ICorJitInfo
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+#include "methodcallsummarizer.h"
+
+class interceptor_ICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+public:
+
+ //Added to help us track the original icji and be able to easily indirect
+ //to it. And a simple way to keep one memory manager instance per instance.
+ ICorJitInfo *original_ICorJitInfo;
+ MethodCallSummarizer *mcs;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp
new file mode 100644
index 0000000000..863d542202
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.cpp
@@ -0,0 +1,72 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "ieememorymanager.h"
+#include "superpmi-shim-counter.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEEMM->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::AddRef()
+{
+ return original_IEEMM->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::Release()
+{
+ return original_IEEMM->Release();
+}
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ return original_IEEMM->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return original_IEEMM->ClrVirtualFree(lpAddress, dwSize, dwFreeType);
+}
+SIZE_T STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return original_IEEMM->ClrVirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return original_IEEMM->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessHeap()
+{
+ return original_IEEMM->ClrGetProcessHeap();
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ return original_IEEMM->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ return original_IEEMM->ClrHeapDestroy(hHeap);
+}
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_IEEMM->ClrHeapAlloc(hHeap, dwFlags, dwBytes);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return original_IEEMM->ClrHeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ return original_IEEMM->ClrHeapValidate(hHeap, dwFlags, lpMem);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessExecutableHeap()
+{
+ return original_IEEMM->ClrGetProcessExecutableHeap();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h
new file mode 100644
index 0000000000..4a72e809ee
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/ieememorymanager.h
@@ -0,0 +1,107 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IEEMemoryManager
+#define _IEEMemoryManager
+
+#include "runtimedetails.h"
+
+/*
+interface IEEMemoryManager : IUnknown
+{
+ LPVOID ClrVirtualAlloc(
+ [in] LPVOID lpAddress, // region to reserve or commit
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD flAllocationType, // type of allocation
+ [in] DWORD flProtect // type of access protection
+ )
+
+ BOOL ClrVirtualFree(
+ [in] LPVOID lpAddress, // address of region
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD dwFreeType // operation type
+ )
+
+ SIZE_T ClrVirtualQuery(
+ [in] const void* lpAddress, // address of region
+ [in] PMEMORY_BASIC_INFORMATION lpBuffer, // information buffer
+ [in] SIZE_T dwLength // size of buffer
+ )
+
+ BOOL ClrVirtualProtect(
+ [in] LPVOID lpAddress, // region of committed pages
+ [in] SIZE_T dwSize, // size of the region
+ [in] DWORD flNewProtect, // desired access protection
+ [in] DWORD* lpflOldProtect // old protection
+ )
+
+ HANDLE ClrGetProcessHeap()
+
+ HANDLE ClrHeapCreate(
+ [in] DWORD flOptions, // heap allocation attributes
+ [in] SIZE_T dwInitialSize, // initial heap size
+ [in] SIZE_T dwMaximumSize // maximum heap size
+ )
+
+ BOOL ClrHeapDestroy(
+ [in] HANDLE hHeap // handle to heap
+ )
+
+ LPVOID ClrHeapAlloc(
+ [in] HANDLE hHeap, // handle to private heap block
+ [in] DWORD dwFlags, // heap allocation control
+ [in] SIZE_T dwBytes // number of bytes to allocate
+ )
+
+ BOOL ClrHeapFree(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap free options
+ [in] LPVOID lpMem // pointer to memory
+ )
+
+ BOOL ClrHeapValidate(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap access options
+ [in] const void* lpMem // optional pointer to memory block
+ )
+
+ HANDLE ClrGetProcessExecutableHeap()
+
+}; // interface IEEMemoryManager
+
+*/
+
+class interceptor_IEEMM : public IEEMemoryManager
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IEEMemoryManager methods for locking
+ //***************************************************************************
+ LPVOID STDMETHODCALLTYPE ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
+ BOOL STDMETHODCALLTYPE ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
+ SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+ BOOL STDMETHODCALLTYPE ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessHeap();
+ HANDLE STDMETHODCALLTYPE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
+ BOOL STDMETHODCALLTYPE ClrHeapDestroy(HANDLE hHeap);
+ LPVOID STDMETHODCALLTYPE ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
+ BOOL STDMETHODCALLTYPE ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
+ BOOL STDMETHODCALLTYPE ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap();
+
+public:
+ IEEMemoryManager *original_IEEMM;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp
new file mode 100644
index 0000000000..0e2a311ed3
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.cpp
@@ -0,0 +1,157 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "iexecutionengine.h"
+#include "superpmi-shim-counter.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEE::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEE->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::AddRef()
+{
+ return original_IEE->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::Release()
+{
+ return original_IEE->Release();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ original_IEE->TLS_AssociateCallback(slot, callback);
+}
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE interceptor_IEE::TLS_GetDataBlock()
+{
+ return original_IEE->TLS_GetDataBlock();
+}
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE interceptor_IEE::TLS_GetValue(DWORD slot)
+{
+ return original_IEE->TLS_GetValue(slot);
+}
+
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE interceptor_IEE::TLS_CheckValue(DWORD slot, LPVOID * pValue)
+{
+ return original_IEE->TLS_CheckValue(slot, pValue);
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ original_IEE->TLS_SetValue(slot, pData);
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_ThreadDetaching()
+{
+ original_IEE->TLS_ThreadDetaching();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+CRITSEC_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return original_IEE->CreateLock(szTag, level, flags);
+}
+void STDMETHODCALLTYPE interceptor_IEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->DestroyLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->AcquireLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->ReleaseLock(lock);
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateAutoEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateAutoEvent(bInitialState);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateManualEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateManualEvent(bInitialState);
+}
+void STDMETHODCALLTYPE interceptor_IEE::CloseEvent(EVENT_COOKIE event)
+{
+ original_IEE->CloseEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrSetEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrResetEvent(event);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->WaitForEvent(event, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ return original_IEE->WaitForSingleObject(handle, dwMilliseconds);
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ return original_IEE->ClrCreateSemaphore(dwInitial, dwMax);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ original_IEE->ClrCloseSemaphore(semaphore);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ return original_IEE->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount);
+}
+MUTEX_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ return original_IEE->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ original_IEE->ClrCloseMutex(mutex);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ return original_IEE->ClrReleaseMutex(mutex);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable);
+}
+
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrSleepEx(dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrAllocationDisallowed()
+{
+ return original_IEE->ClrAllocationDisallowed();
+}
+void STDMETHODCALLTYPE interceptor_IEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ original_IEE->GetLastThrownObjectExceptionFromThread(ppvException);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h
new file mode 100644
index 0000000000..642c145374
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/iexecutionengine.h
@@ -0,0 +1,149 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IExecutionEngine
+#define _IExecutionEngine
+
+#include "ieememorymanager.h"
+
+/*
+interface IExecutionEngine : IUnknown
+{
+ // Thread Local Storage is based on logical threads. The underlying
+ // implementation could be threads, fibers, or something more exotic.
+ // Slot numbers are predefined. This is not a general extensibility
+ // mechanism.
+
+ // Associate a callback function for releasing TLS on thread/fiber death.
+ // This can be NULL.
+ void TLS_AssociateCallback([in] DWORD slot, [in] PTLS_CALLBACK_FUNCTION callback)
+
+ // May be called once to get the master TLS block slot for fast Get/Set operations
+ DWORD TLS_GetMasterSlotIndex()
+
+ // Get the value at a slot
+ PVOID TLS_GetValue([in] DWORD slot)
+
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL TLS_CheckValue([in] DWORD slot, [out] PVOID * pValue)
+
+ // Set the value at a slot
+ void TLS_SetValue([in] DWORD slot, [in] PVOID pData)
+
+ // Free TLS memory block and make callback
+ void TLS_ThreadDetaching()
+
+ // Critical Sections are sometimes exposed to the host and therefore need to be
+ // reflected from all CLR DLLs to the EE.
+ //
+ // In addition, we always monitor interactions between the lock & the GC, based
+ // on the GC mode in which the lock is acquired and we restrict what operations
+ // are permitted while holding the lock based on this.
+ //
+ // Finally, we we rank all our locks to prevent deadlock across all the DLLs of
+ // the CLR. This is the level argument to CreateLock.
+ //
+ // All usage of these locks must be exception-safe. To achieve this, we suggest
+ // using Holders (see holder.h & crst.h). In fact, within the EE code cannot
+ // hold locks except by using exception-safe holders.
+
+ CRITSEC_COOKIE CreateLock([in] LPCSTR szTag, [in] LPCSTR level, [in] CrstFlags flags)
+
+ void DestroyLock([in] CRITSEC_COOKIE lock)
+
+ void AcquireLock([in] CRITSEC_COOKIE lock)
+
+ void ReleaseLock([in] CRITSEC_COOKIE lock)
+
+ EVENT_COOKIE CreateAutoEvent([in] BOOL bInitialState)
+ EVENT_COOKIE CreateManualEvent([in] BOOL bInitialState)
+ void CloseEvent([in] EVENT_COOKIE event)
+ BOOL ClrSetEvent([in] EVENT_COOKIE event)
+ BOOL ClrResetEvent([in] EVENT_COOKIE event)
+ DWORD WaitForEvent([in] EVENT_COOKIE event, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ DWORD WaitForSingleObject([in] HANDLE handle, [in] DWORD dwMilliseconds)
+
+ // OS header file defines CreateSemaphore.
+ SEMAPHORE_COOKIE ClrCreateSemaphore([in] DWORD dwInitial, [in] DWORD dwMax)
+ void ClrCloseSemaphore([in] SEMAPHORE_COOKIE semaphore)
+ DWORD ClrWaitForSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] LONG lReleaseCount, [in] LONG *lpPreviousCount)
+
+ MUTEX_COOKIE ClrCreateMutex([in]LPSECURITY_ATTRIBUTES lpMutexAttributes, [in]BOOL bInitialOwner, [in]LPCTSTR lpName)
+ DWORD ClrWaitForMutex([in] MUTEX_COOKIE mutex, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseMutex([in] MUTEX_COOKIE mutex)
+ void ClrCloseMutex([in] MUTEX_COOKIE mutex)
+
+ DWORD ClrSleepEx([in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+
+ BOOL ClrAllocationDisallowed()
+
+ void GetLastThrownObjectExceptionFromThread([out] void **ppvException)
+
+}; // interface IExecutionEngine
+*/
+
+class interceptor_IEE : public IExecutionEngine
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IExecutionEngine methods for TLS
+ //***************************************************************************
+ // Associate a callback for cleanup with a TLS slot
+ VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
+ // Get the TLS block for fast Get/Set operations
+ LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
+ // Get the value at a slot
+ LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue);
+ // Set the value at a slot
+ VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
+ // Free TLS memory block and make callback
+ VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
+
+ //***************************************************************************
+ // IExecutionEngine methods for locking
+ //***************************************************************************
+ CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags);
+ void STDMETHODCALLTYPE DestroyLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE AcquireLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE ReleaseLock(CRITSEC_COOKIE lock);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateAutoEvent(BOOL bInitialState);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateManualEvent(BOOL bInitialState);
+ void STDMETHODCALLTYPE CloseEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrSetEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrResetEvent(EVENT_COOKIE event);
+ DWORD STDMETHODCALLTYPE WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds);
+ SEMAPHORE_COOKIE STDMETHODCALLTYPE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax);
+ void STDMETHODCALLTYPE ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore);
+ DWORD STDMETHODCALLTYPE ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount);
+ MUTEX_COOKIE STDMETHODCALLTYPE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName);
+ void STDMETHODCALLTYPE ClrCloseMutex(MUTEX_COOKIE mutex);
+ BOOL STDMETHODCALLTYPE ClrReleaseMutex(MUTEX_COOKIE mutex);
+ DWORD STDMETHODCALLTYPE ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrAllocationDisallowed();
+ void STDMETHODCALLTYPE GetLastThrownObjectExceptionFromThread(void **ppvException);
+
+public:
+ IExecutionEngine *original_IEE;
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp
new file mode 100644
index 0000000000..d4efc33c69
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.cpp
@@ -0,0 +1,49 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "runtimedetails.h"
+#include "spmiutil.h"
+#include "methodcallsummarizer.h"
+#include "jithost.h"
+
+JitHost* g_ourJitHost;
+
+JitHost::JitHost(ICorJitHost* wrappedHost) : wrappedHost(wrappedHost), mcs(nullptr)
+{
+}
+
+void JitHost::setMethodCallSummarizer(MethodCallSummarizer* methodCallSummarizer)
+{
+ this->mcs = methodCallSummarizer;
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return wrappedHost->allocateMemory(size, usePageAllocator);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ return wrappedHost->freeMemory(block, usePageAllocator);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ mcs->AddCall("getIntConfigValue");
+ return wrappedHost->getIntConfigValue(key, defaultValue);
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ mcs->AddCall("getStringConfigValue");
+ return wrappedHost->getStringConfigValue(key);
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ mcs->AddCall("freeStringConfigValue");
+ wrappedHost->freeStringConfigValue(value);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/jithost.h b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.h
new file mode 100644
index 0000000000..06acf8bf91
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/jithost.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JITHOST
+#define _JITHOST
+
+class JitHost : public ICorJitHost
+{
+public:
+ JitHost(ICorJitHost* wrappedHost);
+
+ void setMethodCallSummarizer(MethodCallSummarizer* methodCallSummarizer);
+
+#include "icorjithostimpl.h"
+
+private:
+ ICorJitHost* wrappedHost;
+ MethodCallSummarizer* mcs;
+};
+
+extern JitHost* g_ourJitHost;
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp
new file mode 100644
index 0000000000..61f31e4237
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.cpp
@@ -0,0 +1,140 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "methodcallsummarizer.h"
+
+MethodCallSummarizer::MethodCallSummarizer(WCHAR *logPath)
+{
+ numNames = 0;
+ names = nullptr;
+ counts = nullptr;
+
+ WCHAR *ExecutableName = GetCommandLineW();
+ WCHAR *quote1 = NULL;
+
+ //if there are any quotes in filename convert them to spaces.
+ while ((quote1 = wcsstr(ExecutableName, W("\""))) != NULL)
+ *quote1 = W(' ');
+
+ //remove any illegal or annoying characters from file name by converting them to underscores
+ while ((quote1 = wcspbrk(ExecutableName, W("=<>:\"/\\|?! *.,"))) != NULL)
+ *quote1 = W('_');
+
+ const WCHAR *DataFileExtension = W(".csv");
+ size_t ExecutableNameLength = wcslen(ExecutableName);
+ size_t DataFileExtensionLength = wcslen(DataFileExtension);
+ size_t logPathLength = wcslen(logPath);
+
+ unsigned int randNumber = 0;
+ WCHAR RandNumberString[9];
+ RandNumberString[0] = L'\0';
+ size_t RandNumberLength = 0;
+
+ size_t dataFileNameLength = logPathLength + 1 + ExecutableNameLength + 1 + RandNumberLength + 1 + DataFileExtensionLength + 1;
+
+ const size_t MaxAcceptablePathLength = MAX_PATH - 20; // subtract 20 to leave buffer, for possible random number addition
+ if (dataFileNameLength >= MaxAcceptablePathLength)
+ {
+ // The path name is too long; creating the file will fail. This can happen because we use the command line,
+ // which for ngen includes lots of environment variables, for example.
+
+ // Assume (!) the extra space is all in the ExecutableName, so shorten that.
+ ExecutableNameLength -= dataFileNameLength - MaxAcceptablePathLength;
+
+ dataFileNameLength = MaxAcceptablePathLength;
+
+#ifdef FEATURE_PAL
+ PAL_Random(/* bStrong */ FALSE, &randNumber, sizeof(randNumber));
+#else // !FEATURE_PAL
+ rand_s(&randNumber);
+#endif // !FEATURE_PAL
+
+ RandNumberLength = 9; // 8 hex digits + null
+ swprintf_s(RandNumberString, RandNumberLength, W("%08X"), randNumber);
+
+ dataFileNameLength += RandNumberLength - 1;
+ }
+
+ dataFileName = new WCHAR[dataFileNameLength];
+ dataFileName[0] = 0;
+ wcsncat_s(dataFileName, dataFileNameLength, logPath, logPathLength);
+ wcsncat_s(dataFileName, dataFileNameLength, W("\\\0"), 1);
+ wcsncat_s(dataFileName, dataFileNameLength, ExecutableName, ExecutableNameLength);
+
+ if (RandNumberLength > 0)
+ {
+ wcsncat_s(dataFileName, dataFileNameLength, RandNumberString, RandNumberLength);
+ }
+
+ wcsncat_s(dataFileName, dataFileNameLength, DataFileExtension, DataFileExtensionLength);
+}
+
+//lots of ways will be faster.. this happens to be decently simple and good enough for the task at hand and nicely sorts the output.
+//in this approach the most commonly added items are at the top of the list... 60% landed in the first three slots in short runs
+void MethodCallSummarizer::AddCall(const char *name)
+{
+ //if we can find it already in our list, increment the count
+ for(int i=0;i<numNames;i++)
+ {
+ if(strcmp(name, names[i])==0)
+ {
+ counts[i]++;
+ for(i=1;i<numNames;i++)
+ if(counts[i]>counts[i-1])
+ {
+ unsigned int tempui = counts[i-1];
+ counts[i-1] = counts[i];
+ counts[i] = tempui;
+ char *tempc = names[i-1];
+ names[i-1] = names[i];
+ names[i] = tempc;
+ }
+ return;
+ }
+ }
+
+ //else we didn't find it, so add it
+ char **tnames = names;
+ unsigned int *tcounts = counts;
+
+ names = new char*[numNames+1];
+ if(tnames!=nullptr)
+ {
+ memcpy(names, tnames, numNames*sizeof(char*));
+ delete tnames;
+ }
+
+ size_t tlen = strlen(name);
+ names[numNames] = new char[tlen+1];
+ memcpy(names[numNames], name, tlen+1);
+
+ counts = new unsigned int[numNames+1];
+ if(tcounts!=nullptr)
+ {
+ memcpy(counts, tcounts, numNames*sizeof(unsigned int));
+ delete tcounts;
+ }
+ counts[numNames] = 1;
+
+ numNames++;
+}
+
+void MethodCallSummarizer::SaveTextFile()
+{
+ char buff[512];
+ DWORD bytesWritten = 0;
+ HANDLE hFile = CreateFileW(dataFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ DWORD len = (DWORD)sprintf_s(buff,512,"FunctionName,Count\n");
+ WriteFile(hFile, buff, len, &bytesWritten, NULL);
+
+ for(int i=0;i<numNames;i++)
+ {
+ len = sprintf_s(buff,512,"%s,%u\n", names[i], counts[i]);
+ WriteFile(hFile, buff, len, &bytesWritten, NULL);
+ }
+ CloseHandle(hFile);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h
new file mode 100644
index 0000000000..09eaaca46f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/methodcallsummarizer.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _MethodCallSummarizer
+#define _MethodCallSummarizer
+
+class MethodCallSummarizer
+{
+public:
+ MethodCallSummarizer(WCHAR *name);
+ void AddCall(const char *name);
+ void SaveTextFile();
+
+private:
+ char **names;
+ unsigned int *counts;
+ int numNames;
+ WCHAR *dataFileName;
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp
new file mode 100644
index 0000000000..ee47dd8c44
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.cpp
@@ -0,0 +1,231 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.cpp - thin shim for the jit
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "superpmi-shim-counter.h"
+#include "runtimedetails.h"
+#include "coreclrcallbacks.h"
+#include "icorjitcompiler.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+HMODULE g_hRealJit = 0; //We leak this currently (could do the proper shutdown in process_detach)
+WCHAR* g_realJitPath = nullptr; //We leak this (could do the proper shutdown in process_detach)
+WCHAR* g_logPath = nullptr; //Again, we leak this one too...
+char* g_logFilePath = nullptr; //We *don't* leak this, hooray!
+WCHAR* g_HomeDirectory = nullptr;
+WCHAR* g_DefaultRealJitPath = nullptr;
+
+void SetDefaultPaths()
+{
+ if (g_HomeDirectory == nullptr)
+ {
+ g_HomeDirectory = GetEnvironmentVariableWithDefaultW(W("HOME"), W("."));
+ }
+
+ if (g_DefaultRealJitPath == nullptr)
+ {
+ size_t len = wcslen(g_HomeDirectory) + 1 + wcslen(DEFAULT_REAL_JIT_NAME_W) + 1;
+ g_DefaultRealJitPath = new WCHAR[len];
+ wcscpy_s(g_DefaultRealJitPath, len, g_HomeDirectory);
+ wcscat_s(g_DefaultRealJitPath, len, DIRECTORY_SEPARATOR_STR_W);
+ wcscat_s(g_DefaultRealJitPath, len, DEFAULT_REAL_JIT_NAME_W);
+ }
+}
+
+void SetLibName()
+{
+ if (g_realJitPath == nullptr)
+ {
+ g_realJitPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimPath"), g_DefaultRealJitPath);
+ }
+}
+
+void SetLogPath()
+{
+ if (g_logPath == nullptr)
+ {
+ g_logPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimLogPath"), g_HomeDirectory);
+ }
+}
+
+// TODO: this only works for ANSI file paths...
+void SetLogFilePath()
+{
+ if (g_logFilePath == nullptr)
+ {
+ // If the environment variable isn't set, we don't enable file logging
+ g_logFilePath = GetEnvironmentVariableWithDefaultA("SuperPMIShimLogFilePath", nullptr);
+ }
+}
+
+extern "C"
+BOOL
+#ifndef FEATURE_PAL
+APIENTRY
+#endif // !FEATURE_PAL
+DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef FEATURE_PAL
+ if (0 != PAL_InitializeDLL())
+ {
+ fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+ SetLogFilePath();
+ Logger::OpenLogFile(g_logFilePath);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ Logger::Shutdown();
+
+ delete[] g_logFilePath;
+ g_logFilePath = nullptr;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+// Exported via def file
+extern "C"
+void __stdcall jitStartup(ICorJitHost* host)
+{
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if (g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if (g_hRealJit == 0)
+ {
+ LogError("jitStartup - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ // Get the required entrypoint
+ PjitStartup pnjitStartup = (PjitStartup)::GetProcAddress(g_hRealJit, "jitStartup");
+ if (pnjitStartup == nullptr)
+ {
+ // This portion of the interface is not used by the JIT under test.
+ g_ourJitHost = nullptr;
+ return;
+ }
+
+ g_ourJitHost = new JitHost(host);
+ pnjitStartup(g_ourJitHost);
+}
+
+//Exported via def file
+extern "C"
+ICorJitCompiler* __stdcall getJit()
+{
+ DWORD dwRetVal = 0;
+ PgetJit pngetJit;
+ interceptor_ICJC *pJitInstance = nullptr;
+ ICorJitCompiler *tICJI = nullptr;
+
+ SetDefaultPaths();
+ SetLibName();
+ SetLogPath();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return nullptr;
+ }
+ }
+
+ //get the required entrypoints
+ pngetJit = (PgetJit)::GetProcAddress(g_hRealJit, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("getJit() - GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return nullptr;
+ }
+
+ tICJI = pngetJit();
+ if(tICJI == nullptr)
+ {
+ LogError("getJit() - pngetJit gave us null");
+ return nullptr;
+ }
+
+ pJitInstance = new interceptor_ICJC();
+ pJitInstance->original_ICorJitCompiler = tICJI;
+ pJitInstance->mcs = new MethodCallSummarizer(g_logPath);
+ if (g_ourJitHost != nullptr)
+ {
+ g_ourJitHost->setMethodCallSummarizer(pJitInstance->mcs);
+ }
+ return pJitInstance;
+}
+
+//Exported via def file
+extern "C"
+void __stdcall sxsJitStartup(CoreClrCallbacks const & original_cccallbacks)
+{
+ PsxsJitStartup pnsxsJitStartup;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("sxsJitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ //get entry point
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(g_hRealJit, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("sxsJitStartup() - GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return;
+ }
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ original_CoreClrCallbacks = new CoreClrCallbacks();
+ original_CoreClrCallbacks->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ original_CoreClrCallbacks->m_pfnIEE = original_cccallbacks.m_pfnIEE;
+ original_CoreClrCallbacks->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ original_CoreClrCallbacks->m_pfnGetCLRFunction = original_cccallbacks.m_pfnGetCLRFunction;
+
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+
+ temp->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ pnsxsJitStartup(*temp);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def
new file mode 100644
index 0000000000..436434c3de
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.def
@@ -0,0 +1,5 @@
+LIBRARY
+EXPORTS
+ getJit
+ jitStartup
+ sxsJitStartup
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h
new file mode 100644
index 0000000000..2068a02775
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/superpmi-shim-counter.h
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.h - thin shim for the jit
+//----------------------------------------------------------
+#ifndef _SuperPMIShim
+#define _SuperPMIShim
+
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/.gitmirror b/src/ToolBox/superpmi/superpmi-shim-simple/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt b/src/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt
new file mode 100644
index 0000000000..354e46d097
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt
@@ -0,0 +1,72 @@
+project(superpmi-shim-simple)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+
+if(WIN32)
+ #use static crt
+ add_definitions(-MT)
+endif(WIN32)
+
+include_directories(.)
+include_directories(../superpmi-shared)
+
+set(SUPERPMI_SHIM_SIMPLE_SOURCES
+ coreclrcallbacks.cpp
+ jithost.cpp
+ icorjitcompiler.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ superpmi-shim-simple.cpp
+ ../superpmi-shared/callutils.cpp
+ ../superpmi-shared/compileresult.cpp
+ ../superpmi-shared/errorhandling.cpp
+ ../superpmi-shared/logging.cpp
+ ../superpmi-shared/mclist.cpp
+ ../superpmi-shared/methodcontext.cpp
+ ../superpmi-shared/methodcontextreader.cpp
+ ../superpmi-shared/simpletimer.cpp
+ ../superpmi-shared/spmiutil.cpp
+ ../superpmi-shared/tocfile.cpp
+ ../superpmi-shared/typeutils.cpp
+)
+
+add_precompiled_header(
+ standardpch.h
+ ../superpmi-shared/standardpch.cpp
+ SUPERPMI_SHIM_SIMPLE_SOURCES
+)
+
+if (WIN32)
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/superpmi-shim-simple.def ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-simple.def)
+
+ list(APPEND SUPERPMI_SHIM_SIMPLE_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-simple.def)
+endif (WIN32)
+
+add_library(superpmi-shim-simple
+ SHARED
+ ${SUPERPMI_SHIM_SIMPLE_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi-shim-simple
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi-shim-simple
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi-shim-simple.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi-shim-simple DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..cf35748672
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp
@@ -0,0 +1,58 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+typedef LPVOID (__stdcall * pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
+typedef BOOL (__stdcall * pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
+
+CoreClrCallbacks *original_CoreClrCallbacks = nullptr;
+pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
+pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ interceptor_IEE *iee = new interceptor_IEE();
+ iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+ return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ {
+ original_EEHeapAllocInProcessHeap =
+ (pfnEEHeapAllocInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapAllocInProcessHeap");
+ return (void*)EEHeapAllocInProcessHeap;
+ }
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ {
+ original_EEHeapFreeInProcessHeap =
+ (pfnEEHeapFreeInProcessHeap)original_CoreClrCallbacks->m_pfnGetCLRFunction("EEHeapFreeInProcessHeap");
+ return (void*)EEHeapFreeInProcessHeap;
+ }
+ return original_CoreClrCallbacks->m_pfnGetCLRFunction(functionName);
+}
+
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.h
new file mode 100644
index 0000000000..2e3a673f57
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.h
@@ -0,0 +1,20 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _CoreClrCallbacks
+#define _CoreClrCallbacks
+
+#include "runtimedetails.h"
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t();
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength);
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes);
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem);
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName);
+
+// Added to allow us to persist a copy of the original callbacks
+extern CoreClrCallbacks *original_CoreClrCallbacks;
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp
new file mode 100644
index 0000000000..f6fceb2029
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp
@@ -0,0 +1,59 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "icorjitcompiler.h"
+#include "icorjitinfo.h"
+
+interceptor_IEEMM *current_IEEMM = nullptr; //we want this to live beyond the scope of a single compileMethodCall
+
+CorJitResult __stdcall interceptor_ICJC::compileMethod (
+ ICorJitInfo *comp, /* IN */
+ struct CORINFO_METHOD_INFO *info, /* IN */
+ unsigned /* code:CorJitFlag */ flags, /* IN */
+ BYTE **nativeEntry, /* OUT */
+ ULONG *nativeSizeOfCode /* OUT */
+ )
+{
+ interceptor_ICJI our_ICorJitInfo;
+ our_ICorJitInfo.original_ICorJitInfo = comp;
+
+ if(current_IEEMM == nullptr)
+ current_IEEMM = new interceptor_IEEMM();
+
+ CorJitResult temp = original_ICorJitCompiler->compileMethod(&our_ICorJitInfo, info, flags, nativeEntry, nativeSizeOfCode);
+
+ return temp;
+}
+
+void interceptor_ICJC::clearCache()
+{
+ original_ICorJitCompiler->clearCache();
+}
+
+BOOL interceptor_ICJC::isCacheCleanupRequired()
+{
+ return original_ICorJitCompiler->isCacheCleanupRequired();
+}
+
+void interceptor_ICJC::ProcessShutdownWork(ICorStaticInfo* info)
+{
+ original_ICorJitCompiler->ProcessShutdownWork(info);
+}
+
+void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */)
+{
+ original_ICorJitCompiler->getVersionIdentifier(versionIdentifier);
+}
+
+unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags)
+{
+ return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags);
+}
+
+void interceptor_ICJC::setRealJit(ICorJitCompiler* realJitCompiler)
+{
+ original_ICorJitCompiler->setRealJit(realJitCompiler);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h
new file mode 100644
index 0000000000..a5eda6aa19
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitCompiler
+#define _ICorJitCompiler
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+
+class interceptor_ICJC : public ICorJitCompiler
+{
+
+#include "icorjitcompilerimpl.h"
+
+public:
+ // Added to help us track the original icjc and be able to easily indirect to it.
+ ICorJitCompiler *original_ICorJitCompiler;
+};
+
+extern interceptor_IEEMM *current_IEEMM; // we want a pointer to the memory manager to live beyond the scope of a single compileMethodCall (jit32 expects this)
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
new file mode 100644
index 0000000000..89b19d8754
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
@@ -0,0 +1,1726 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "icorjitinfo.h"
+#include "superpmi-shim-simple.h"
+#include "ieememorymanager.h"
+#include "icorjitcompiler.h"
+#include "spmiutil.h"
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ return original_ICorJitInfo->getMethodAttribs(ftn);
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void interceptor_ICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ original_ICorJitInfo->setMethodAttribs(ftn, attribs);
+}
+
+
+// Given a method descriptor ftnHnd, extract signature information into sigInfo
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+void interceptor_ICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ original_ICorJitInfo->getMethodSig(ftn, sig, memberParent);
+}
+
+
+ /*********************************************************************
+ * Note the following methods can only be used on functions known
+ * to be IL. This includes the method being compiled and any method
+ * that 'getMethodInfo' returns true for
+ *********************************************************************/
+
+ // return information about a method private to the implementation
+ // returns false if method is not IL, or is otherwise unavailable.
+ // This method is used to fetch data needed to inline functions
+ bool interceptor_ICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ return original_ICorJitInfo->getMethodInfo(ftn, info);
+}
+
+// Decides if you have any limitations for inlining. If everything's OK, it will return
+// INLINE_PASS and will fill out pRestrictions with a mask of restrictions the caller of this
+// function must respect. If caller passes pRestrictions = nullptr, if there are any restrictions
+// INLINE_FAIL will be returned
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+//
+// The inlined method need not be verified
+
+CorInfoInline interceptor_ICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ return original_ICorJitInfo->canInline(callerHnd, calleeHnd, pRestrictions);
+}
+
+// Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all
+// inlining results when it returns INLINE_FAIL and INLINE_NEVER. All other results are reported by the
+// JIT.
+void interceptor_ICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ original_ICorJitInfo->reportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
+}
+
+
+// Returns false if the call is across security boundaries thus we cannot tailcall
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+bool interceptor_ICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ return original_ICorJitInfo->canTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix);
+}
+
+// Reports whether or not a method can be tail called, and why.
+// canTailCall is responsible for reporting all results when it returns
+// false. All other results are reported by the JIT.
+void interceptor_ICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ original_ICorJitInfo->reportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void interceptor_ICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ original_ICorJitInfo->getEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->getMethodClass(method);
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->getMethodModule(method);
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void interceptor_ICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics interceptor_ICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ return original_ICorJitInfo->getIntrinsicID(method, pMustExpand);
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool interceptor_ICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ return original_ICorJitInfo->isInSIMDModule(classHnd);
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv interceptor_ICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->getUnmanagedCallConv(method);
+}
+
+// return if any marshaling is required for PInvoke methods. Note that
+// method == 0 => calli. The call site sig is only needed for the varargs or calli case
+BOOL interceptor_ICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ return original_ICorJitInfo->pInvokeMarshalingRequired(method, callSiteSig);
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL interceptor_ICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->satisfiesMethodConstraints(parent, method);
+}
+
+// Given a delegate target class, a target method parent class, a target method,
+// a delegate class, check if the method signature is compatible with the Invoke method of the delegate
+// (under the typical instantiation of any free type variables in the memberref signatures).
+BOOL interceptor_ICJI::isCompatibleDelegate(
+ CORINFO_CLASS_HANDLE objCls, /* type of the delegate target, if any */
+ CORINFO_CLASS_HANDLE methodParentCls, /* exact parent of the target method, if any */
+ CORINFO_METHOD_HANDLE method, /* (representative) target method, if any */
+ CORINFO_CLASS_HANDLE delegateCls, /* exact type of the delegate */
+ BOOL *pfIsOpenDelegate /* is the delegate open */
+ )
+{
+ return original_ICorJitInfo->isCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL interceptor_ICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ return original_ICorJitInfo->isDelegateCreationAllowed(delegateHnd, calleeHnd);
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification interceptor_ICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ return original_ICorJitInfo->isInstantiationOfVerifiedGeneric(method);
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void interceptor_ICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ original_ICorJitInfo->initConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ return original_ICorJitInfo->canSkipMethodVerification(ftnHandle);
+}
+
+// load and restore the method
+void interceptor_ICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ original_ICorJitInfo->methodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ return original_ICorJitInfo->mapMethodDeclToMethodImpl(method);
+}
+
+// Returns the global cookie for the /GS unsafe buffer checks
+// The cookie might be a constant value (JIT), or a handle to memory location (Ngen)
+void interceptor_ICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+
+// Resolve metadata token into runtime method handles.
+void interceptor_ICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ original_ICorJitInfo->resolveToken(pResolvedToken);
+}
+
+bool interceptor_ICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ return original_ICorJitInfo->tryResolveToken(pResolvedToken);
+}
+
+// Signature information about the call sig
+void interceptor_ICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ original_ICorJitInfo->findSig(module, sigTOK, context, sig);
+}
+
+// for Varargs, the signature at the call site may differ from
+// the signature at the definition. Thus we need a way of
+// fetching the call site information
+void interceptor_ICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ original_ICorJitInfo->findCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ return original_ICorJitInfo->getTokenTypeAsHandle(pResolvedToken);
+}
+
+// Returns true if the module does not require verification
+//
+// If fQuickCheckOnlyWithoutCommit=TRUE, the function only checks that the
+// module does not currently require verification in the current AppDomain.
+// This decision could change in the future, and so should not be cached.
+// If it is cached, it should only be used as a hint.
+// This is only used by ngen for calculating certain hints.
+//
+
+// Returns enum whether the module does not require verification
+// Also see ICorMethodInfo::canSkipMethodVerification();
+CorInfoCanSkipVerificationResult interceptor_ICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ return original_ICorJitInfo->canSkipVerification(module);
+}
+
+// Checks if the given metadata token is valid
+BOOL interceptor_ICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ return original_ICorJitInfo->isValidToken(module, metaTOK);
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL interceptor_ICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ return original_ICorJitInfo->isValidStringRef(module, metaTOK);
+}
+
+BOOL interceptor_ICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ return original_ICorJitInfo->shouldEnforceCallvirtRestriction(scope);
+}
+
+/**********************************************************************************/
+//
+// ICorClassInfo
+//
+/**********************************************************************************/
+
+// If the value class 'cls' is isomorphic to a primitive type it will
+// return that type, otherwise it will return CORINFO_TYPE_VALUECLASS
+CorInfoType interceptor_ICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->asCorInfoType(cls);
+}
+
+// for completeness
+const char* interceptor_ICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassName(cls);
+}
+
+
+// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
+// If fNamespace=TRUE, include the namespace/enclosing classes
+// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
+// If fAssembly=TRUE, suffix with a comma and the full assembly qualification
+// return size of representation
+int interceptor_ICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ return original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->isValueClass(cls);
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->canInlineTypeCheckWithObjectVTable(cls);
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD interceptor_ICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassAttribs(cls);
+}
+
+// Returns "TRUE" iff "cls" is a struct type such that return buffers used for returning a value
+// of this type must be stack-allocated. This will generally be true only if the struct
+// contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
+// an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
+// returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
+// buffers do not require GC write barriers.
+BOOL interceptor_ICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->isStructRequiringStackAllocRetBuf(cls);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE interceptor_ICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ return original_ICorJitInfo->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* interceptor_ICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ return original_ICorJitInfo->getAssemblyName(assem);
+}
+
+// Allocate and delete process-lifetime objects. Should only be
+// referred to from static fields, lest a leak occur.
+// Note that "LongLifetimeFree" does not execute destructors, if "obj"
+// is an array of a struct type with a destructor.
+void* interceptor_ICJI::LongLifetimeMalloc(size_t sz)
+{
+ return original_ICorJitInfo->LongLifetimeMalloc(sz);
+}
+
+void interceptor_ICJI::LongLifetimeFree(void* obj)
+{
+ original_ICorJitInfo->LongLifetimeFree(obj);
+}
+
+size_t interceptor_ICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection);
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned interceptor_ICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getClassSize(cls);
+}
+
+unsigned interceptor_ICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ return original_ICorJitInfo->getClassAlignmentRequirement(cls, fDoubleAlignHint);
+}
+
+// This is only called for Value classes. It returns a boolean array
+// in representing of 'cls' from a GC perspective. The class is
+// assumed to be an array of machine words
+// (of length // getClassSize(cls) / sizeof(void*)),
+// 'gcPtrs' is a poitner to an array of BYTEs of this length.
+// getClassGClayout fills in this array so that gcPtrs[i] is set
+// to one of the CorInfoGCType values which is the GC type of
+// the i-th machine word of an object of type 'cls'
+// returns the number of GC pointers in the array
+unsigned interceptor_ICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ return original_ICorJitInfo->getClassGClayout(cls, gcPtrs);
+}
+
+// returns the number of instance fields in a class
+unsigned interceptor_ICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ return original_ICorJitInfo->getClassNumInstanceFields(cls);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ return original_ICorJitInfo->getFieldInClass(clsHnd, num);
+}
+
+BOOL interceptor_ICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ return original_ICorJitInfo->checkMethodModifier(hMethod, modifier, fOptional);
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc interceptor_ICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ return original_ICorJitInfo->getNewHelper(pResolvedToken, callerHandle);
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc interceptor_ICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ return original_ICorJitInfo->getNewArrHelper(arrayCls);
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc interceptor_ICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ return original_ICorJitInfo->getCastingHelper(pResolvedToken, fThrowing);
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc interceptor_ICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ return original_ICorJitInfo->getSharedCCtorHelper(clsHnd);
+}
+
+CorInfoHelpFunc interceptor_ICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ return original_ICorJitInfo->getSecurityPrologHelper(ftn);
+}
+
+// This is not pretty. Boxing nullable<T> actually returns
+// a boxed<T> not a boxed Nullable<T>. This call allows the verifier
+// to call back to the EE on the 'box' instruction and get the transformed
+// type to use for verification.
+CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getTypeForBox(cls);
+}
+
+// returns the correct box helper for a particular class. Note
+// that if this returns CORINFO_HELP_BOX, the JIT can assume
+// 'standard' boxing (allocate object and copy), and optimize
+CorInfoHelpFunc interceptor_ICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getBoxHelper(cls);
+}
+
+// returns the unbox helper. If 'helperCopies' points to a true
+// value it means the JIT is requesting a helper that unboxes the
+// value into a particular location and thus has the signature
+// void unboxHelper(void* dest, CORINFO_CLASS_HANDLE cls, Object* obj)
+// Otherwise (it is null or points at a FALSE value) it is requesting
+// a helper that returns a poitner to the unboxed data
+// void* unboxHelper(CORINFO_CLASS_HANDLE cls, Object* obj)
+// The EE has the option of NOT returning the copy style helper
+// (But must be able to always honor the non-copy style helper)
+// The EE set 'helperCopies' on return to indicate what kind of
+// helper has been created.
+
+CorInfoHelpFunc interceptor_ICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getUnBoxHelper(cls);
+}
+
+bool interceptor_ICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ return original_ICorJitInfo->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+}
+
+void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* interceptor_ICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ return original_ICorJitInfo->getHelperName(funcNum);
+}
+
+// This function tries to initialize the class (run the class constructor).
+// this function returns whether the JIT must insert helper calls before
+// accessing static field or method.
+//
+// See code:ICorClassInfo#ClassConstruction.
+CorInfoInitClassResult interceptor_ICJI::initClass(
+ CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
+ // nullptr - inquire about cctor trigger in method prolog
+ CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
+ CORINFO_CONTEXT_HANDLE context, // Exact context of method
+ BOOL speculative // TRUE means don't actually run it
+ )
+{
+ return original_ICorJitInfo->initClass(field, method, context, speculative);
+}
+
+// This used to be called "loadClass". This records the fact
+// that the class must be loaded (including restored if necessary) before we execute the
+// code that we are currently generating. When jitting code
+// the function loads the class immediately. When zapping code
+// the zapper will if necessary use the call to record the fact that we have
+// to do a fixup/restore before running the method currently being generated.
+//
+// This is typically used to ensure value types are loaded before zapped
+// code that manipulates them is executed, so that the GC can access information
+// about those value types.
+void interceptor_ICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ original_ICorJitInfo->classMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE interceptor_ICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ return original_ICorJitInfo->getBuiltinClass(classId);
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType interceptor_ICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getTypeForPrimitiveValueClass(cls);
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL interceptor_ICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ return original_ICorJitInfo->canCast(child, parent);
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL interceptor_ICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ return original_ICorJitInfo->areTypesEquivalent(cls1, cls2);
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ return original_ICorJitInfo->mergeClasses(cls1, cls2);
+}
+
+// Given a class handle, returns the Parent type.
+// For COMObjectType, it returns Class Handle of System.Object.
+// Returns 0 if System.Object is passed in.
+CORINFO_CLASS_HANDLE interceptor_ICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getParentType(cls);
+}
+
+// Returns the CorInfoType of the "child type". If the child type is
+// not a primitive type, *clsRet will be set.
+// Given an Array of Type Foo, returns Foo.
+// Given BYREF Foo, returns Foo
+CorInfoType interceptor_ICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ return original_ICorJitInfo->getChildType(clsHnd, clsRet);
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL interceptor_ICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->satisfiesClassConstraints(cls);
+}
+
+// Check if this is a single dimensional array type
+BOOL interceptor_ICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->isSDArray(cls);
+}
+
+// Get the numbmer of dimensions in an array
+unsigned interceptor_ICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ return original_ICorJitInfo->getArrayRank(cls);
+}
+
+// Get static field data for an array
+void * interceptor_ICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ return original_ICorJitInfo->getArrayInitializationData(field, size);
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult interceptor_ICJI::canAccessClass(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper /* If canAccessMethod returns something other
+ than ALLOWED, then this is filled in. */
+ )
+{
+ return original_ICorJitInfo->canAccessClass(pResolvedToken, callerHandle, pAccessHelper);
+}
+
+/**********************************************************************************/
+//
+// ICorFieldInfo
+//
+/**********************************************************************************/
+
+// this function is for debugging only. It returns the field name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* interceptor_ICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ return original_ICorJitInfo->getFieldName(ftn, moduleName);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE interceptor_ICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ return original_ICorJitInfo->getFieldClass(field);
+}
+
+// Return the field's type, if it is CORINFO_TYPE_VALUECLASS 'structType' is set
+// the field's value class (if 'structType' == 0, then don't bother
+// the structure info).
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+CorInfoType interceptor_ICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ return original_ICorJitInfo->getFieldType(field, structType, memberParent);
+}
+
+// return the data member's instance offset
+unsigned interceptor_ICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ return original_ICorJitInfo->getFieldOffset(field);
+}
+
+// TODO: jit64 should be switched to the same plan as the i386 jits - use
+// getClassGClayout to figure out the need for writebarrier helper, and inline the copying.
+// The interpretted value class copy is slow. Once this happens, USE_WRITE_BARRIER_HELPERS
+bool interceptor_ICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ return original_ICorJitInfo->isWriteBarrierHelperRequired(field);
+}
+
+void interceptor_ICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ original_ICorJitInfo->getFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ // this method does exist in some forms of the jit interface... if trip into one we'll know about it
+ DebugBreakorAV(67);
+ return true;
+}
+
+/*********************************************************************************/
+//
+// ICorDebugInfo
+//
+/*********************************************************************************/
+
+// Query the EE to find out where interesting break points
+// in the code are. The native compiler will ensure that these places
+// have a corresponding break point in native code.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void interceptor_ICJI::getBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ unsigned int *cILOffsets, // [OUT] size of pILOffsets
+ DWORD **pILOffsets, // [OUT] IL offsets of interest
+ // jit MUST free with freeArray!
+ ICorDebugInfo::BoundaryTypes *implictBoundaries // [OUT] tell jit, all boundries of this type
+ )
+{
+ original_ICorJitInfo->getBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+}
+
+// Report back the mapping from IL to native code,
+// this map should include all boundaries that 'getBoundaries'
+// reported as interesting to the debugger.
+
+// Note that debugger (and profiler) is assuming that all of the
+// offsets form a contiguous block of memory, and that the
+// OffsetMapping is sorted in order of increasing native offset.
+void interceptor_ICJI::setBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cMap, // [IN] size of pMap
+ ICorDebugInfo::OffsetMapping *pMap // [IN] map including all points of interest.
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ original_ICorJitInfo->setBoundaries(ftn, cMap, pMap);
+}
+
+// Query the EE to find out the scope of local varables.
+// normally the JIT would trash variables after last use, but
+// under debugging, the JIT needs to keep them live over their
+// entire scope so that they can be inspected.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void interceptor_ICJI::getVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 *cVars, // [OUT] size of 'vars'
+ ICorDebugInfo::ILVarInfo **vars, // [OUT] scopes of variables of interest
+ // jit MUST free with freeArray!
+ bool *extendOthers // [OUT] it TRUE, then assume the scope
+ // of unmentioned vars is entire method
+ )
+{
+ original_ICorJitInfo->getVars(ftn, cVars, vars, extendOthers);
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+
+void interceptor_ICJI::setVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cVars, // [IN] size of 'vars'
+ ICorDebugInfo::NativeVarInfo *vars // [IN] map telling where local vars are stored at what points
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ original_ICorJitInfo->setVars(ftn, cVars, vars);
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * interceptor_ICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ return original_ICorJitInfo->allocateArray(cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void interceptor_ICJI::freeArray(
+ void *array
+ )
+{
+ original_ICorJitInfo->freeArray(array);
+}
+
+/*********************************************************************************/
+//
+// ICorArgInfo
+//
+/*********************************************************************************/
+
+// advance the pointer to the argument list.
+// a ptr of 0, is special and always means the first argument
+CORINFO_ARG_LIST_HANDLE interceptor_ICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ return original_ICorJitInfo->getArgNext(args);
+}
+
+// Get the type of a particular argument
+// CORINFO_TYPE_UNDEF is returned when there are no more arguments
+// If the type returned is a primitive type (or an enum) *vcTypeRet set to nullptr
+// otherwise it is set to the TypeHandle associted with the type
+// Enumerations will always look their underlying type (probably should fix this)
+// Otherwise vcTypeRet is the type as would be seen by the IL,
+// The return value is the type that is used for calling convention purposes
+// (Thus if the EE wants a value class to be passed like an int, then it will
+// return CORINFO_TYPE_INT
+CorInfoTypeWithMod interceptor_ICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ return original_ICorJitInfo->getArgType(sig, args, vcTypeRet);
+}
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ return original_ICorJitInfo->getArgClass(sig, args);
+}
+
+// Returns type of HFA for valuetype
+CorInfoType interceptor_ICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ return original_ICorJitInfo->getHFAType(hClass);
+}
+
+/*****************************************************************************
+* ICorErrorInfo contains methods to deal with SEH exceptions being thrown
+* from the corinfo interface. These methods may be called when an exception
+* with code EXCEPTION_COMPLUS is caught.
+*****************************************************************************/
+
+// Returns the HRESULT of the current exception
+HRESULT interceptor_ICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ return original_ICorJitInfo->GetErrorHRESULT(pExceptionPointers);
+}
+
+// Fetches the message of the current exception
+// Returns the size of the message (including terminating null). This can be
+// greater than bufferLength if the buffer is insufficient.
+ULONG interceptor_ICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ return original_ICorJitInfo->GetErrorMessage(buffer, bufferLength);
+}
+
+// returns EXCEPTION_EXECUTE_HANDLER if it is OK for the compile to handle the
+// exception, abort some work (like the inlining) and continue compilation
+// returns EXCEPTION_CONTINUE_SEARCH if exception must always be handled by the EE
+// things like ThreadStoppedException ...
+// returns EXCEPTION_CONTINUE_EXECUTION if exception is fixed up by the EE
+
+int interceptor_ICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ return original_ICorJitInfo->FilterException(pExceptionPointers);
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void interceptor_ICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ original_ICorJitInfo->HandleException(pExceptionPointers);
+}
+
+void interceptor_ICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ original_ICorJitInfo->ThrowExceptionForJitResult(result);
+}
+
+//Throws an exception defined by the given throw helper.
+void interceptor_ICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ original_ICorJitInfo->ThrowExceptionForHelper(throwHelper);
+}
+
+/*****************************************************************************
+ * ICorStaticInfo contains EE interface methods which return values that are
+ * constant from invocation to invocation. Thus they may be embedded in
+ * persisted information like statically generated code. (This is of course
+ * assuming that all code versions are identical each time.)
+ *****************************************************************************/
+
+// Return details about EE internal data structures
+void interceptor_ICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ original_ICorJitInfo->getEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR interceptor_ICJI::getJitTimeLogFilename()
+{
+ return original_ICorJitInfo->getJitTimeLogFilename();
+}
+
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef interceptor_ICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ return original_ICorJitInfo->getMethodDefFromMethod(hMethod);
+}
+
+// this function is for debugging only. It returns the method name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* interceptor_ICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ return original_ICorJitInfo->getMethodName(ftn, moduleName);
+}
+
+// this function is for debugging only. It returns a value that
+// is will always be the same for a given method. It is used
+// to implement the 'jitRange' functionality
+unsigned interceptor_ICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ return original_ICorJitInfo->getMethodHash(ftn);
+}
+
+// this function is for debugging only.
+size_t interceptor_ICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ return original_ICorJitInfo->findNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+}
+
+bool interceptor_ICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ return original_ICorJitInfo->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+}
+
+//Stuff on ICorDynamicInfo
+DWORD interceptor_ICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getThreadTLSIndex(ppIndirection);
+}
+
+const void * interceptor_ICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getInlinedCallFrameVptr(ppIndirection);
+}
+
+LONG * interceptor_ICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getAddrOfCaptureThreadGlobal(ppIndirection);
+}
+
+SIZE_T* interceptor_ICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ return original_ICorJitInfo->getAddrModuleDomainID(module);
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* interceptor_ICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getHelperFtn(ftnNum, ppIndirection);
+}
+
+// return a callable address of the function (native code). This function
+// may return a different value (depending on whether the method has
+// been JITed or not.
+void interceptor_ICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ original_ICorJitInfo->getFunctionEntryPoint(ftn, pResult, accessFlags);
+}
+
+// return a directly callable address. This can be used similarly to the
+// value returned by getFunctionEntryPoint() except that it is
+// guaranteed to be multi callable entrypoint.
+void interceptor_ICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* interceptor_ICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getMethodSync(ftn, ppIndirection);
+}
+
+// These entry points must be called if a handle is being embedded in
+// the code to be passed to a JIT helper function. (as opposed to just
+// being passed back into the ICorInfo interface.)
+
+// get slow lazy string literal helper to use (CORINFO_HELP_STRCNS*).
+// Returns CORINFO_HELP_UNDEF if lazy string literal helper cannot be used.
+CorInfoHelpFunc interceptor_ICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ return original_ICorJitInfo->getLazyStringLiteralHelper(handle);
+}
+
+CORINFO_MODULE_HANDLE interceptor_ICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedModuleHandle(handle, ppIndirection);
+}
+
+CORINFO_CLASS_HANDLE interceptor_ICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedClassHandle(handle, ppIndirection);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedMethodHandle(handle, ppIndirection);
+}
+
+CORINFO_FIELD_HANDLE interceptor_ICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->embedFieldHandle(handle, ppIndirection);
+}
+
+// Given a module scope (module), a method handle (context) and
+// a metadata token (metaTOK), fetch the handle
+// (type, field or method) associated with the token.
+// If this is not possible at compile-time (because the current method's
+// code is shared and the token contains generic parameters)
+// then indicate how the handle should be looked up at run-time.
+//
+void interceptor_ICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ original_ICorJitInfo->embedGenericHandle(pResolvedToken, fEmbedParent, pResult);
+}
+
+// Return information used to locate the exact enclosing type of the current method.
+// Used only to invoke .cctor method from code shared across generic instantiations
+// !needsRuntimeLookup statically known (enclosing type of method itself)
+// needsRuntimeLookup:
+// CORINFO_LOOKUP_THISOBJ use vtable pointer of 'this' param
+// CORINFO_LOOKUP_CLASSPARAM use vtable hidden param
+// CORINFO_LOOKUP_METHODPARAM use enclosing type of method-desc hidden param
+CORINFO_LOOKUP_KIND interceptor_ICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ return original_ICorJitInfo->getLocationOfThisType(context);
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* interceptor_ICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getPInvokeUnmanagedTarget(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* interceptor_ICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getAddressOfPInvokeFixup(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void interceptor_ICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ original_ICorJitInfo->getAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ return original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool interceptor_ICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ return original_ICorJitInfo->canGetCookieForPInvokeCalliSig(szMetaSig);
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE interceptor_ICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ return original_ICorJitInfo->getJustMyCodeHandle(method, ppIndirection);
+}
+
+// Gets a method handle that can be used to correlate profiling data.
+// This is the IP of a native method, or the address of the descriptor struct
+// for IL. Always guaranteed to be unique per process, and not to move. */
+void interceptor_ICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ original_ICorJitInfo->GetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void interceptor_ICJI::getCallInfo(
+ // Token info
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+
+ //Generics info
+ CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
+
+ //Security info
+ CORINFO_METHOD_HANDLE callerHandle,
+
+ //Jit info
+ CORINFO_CALLINFO_FLAGS flags,
+
+ //out params
+ CORINFO_CALL_INFO *pResult
+ )
+{
+ original_ICorJitInfo->getCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult);
+}
+
+BOOL interceptor_ICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+
+{
+ return original_ICorJitInfo->canAccessFamily(hCaller, hInstanceType);
+}
+// Returns TRUE if the Class Domain ID is the RID of the class (currently true for every class
+// except reflection emitted classes and generics)
+BOOL interceptor_ICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ return original_ICorJitInfo->isRIDClassDomainID(cls);
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned interceptor_ICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getClassDomainID(cls, ppIndirection);
+}
+
+
+// return the data's address (for static fields only)
+void* interceptor_ICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getFieldAddress(field, ppIndirection);
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getVarArgsHandle(pSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool interceptor_ICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ return original_ICorJitInfo->canGetVarArgsHandle(pSig);
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType interceptor_ICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ return original_ICorJitInfo->constructStringLiteral(module, metaTok, ppValue);
+}
+
+InfoAccessType interceptor_ICJI::emptyStringLiteral(
+ void **ppValue
+ )
+{
+ return original_ICorJitInfo->emptyStringLiteral(ppValue);
+}
+
+// (static fields only) given that 'field' refers to thread local store,
+// return the ID (TLS index), which is used to find the begining of the
+// TLS data area for the particular DLL 'field' is associated with.
+DWORD interceptor_ICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ return original_ICorJitInfo->getFieldThreadLocalStoreID(field, ppIndirection);
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void interceptor_ICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ original_ICorJitInfo->setOverride(pOverride, currentMethod);
+}
+
+// Adds an active dependency from the context method's module to the given module
+// This is internal callback for the EE. JIT should not call it directly.
+void interceptor_ICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ original_ICorJitInfo->addActiveDependency(moduleFrom, moduleTo);
+}
+
+CORINFO_METHOD_HANDLE interceptor_ICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ return original_ICorJitInfo->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+}
+
+void interceptor_ICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ original_ICorJitInfo->MethodCompileComplete(methHnd);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* interceptor_ICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ return original_ICorJitInfo->getTailCallCopyArgsThunk(pSig, flags);
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD interceptor_ICJI::getJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes)
+{
+ return original_ICorJitInfo->getJitFlags(jitFlags, sizeInBytes);
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully.
+bool interceptor_ICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ return original_ICorJitInfo->runWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* interceptor_ICJI::getMemoryManager()
+{
+ if(current_IEEMM->original_IEEMM == nullptr)
+ current_IEEMM->original_IEEMM = original_ICorJitInfo->getMemoryManager();
+
+ return current_IEEMM;
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void interceptor_ICJI::allocMem (
+ ULONG hotCodeSize, /* IN */
+ ULONG coldCodeSize, /* IN */
+ ULONG roDataSize, /* IN */
+ ULONG xcptnsCount, /* IN */
+ CorJitAllocMemFlag flag, /* IN */
+ void ** hotCodeBlock, /* OUT */
+ void ** coldCodeBlock, /* OUT */
+ void ** roDataBlock /* OUT */
+ )
+{
+ return original_ICorJitInfo->allocMem(hotCodeSize, coldCodeSize, roDataSize, xcptnsCount, flag, hotCodeBlock, coldCodeBlock, roDataBlock);
+}
+
+// Reserve memory for the method/funclet's unwind information.
+// Note that this must be called before allocMem. It should be
+// called once for the main method, once for every funclet, and
+// once for every block of cold code for which allocUnwindInfo
+// will be called.
+//
+// This is necessary because jitted code must allocate all the
+// memory needed for the unwindInfo at the allocMem call.
+// For prejitted code we split up the unwinding information into
+// separate sections .rdata and .pdata.
+//
+void interceptor_ICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ original_ICorJitInfo->reserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+}
+
+// Allocate and initialize the .rdata and .pdata for this method or
+// funclet, and get the block of memory needed for the machine-specific
+// unwind information (the info for crawling the stack frame).
+// Note that allocMem must be called first.
+//
+// Parameters:
+//
+// pHotCode main method code buffer, always filled in
+// pColdCode cold code buffer, only filled in if this is cold code,
+// null otherwise
+// startOffset start of code block, relative to appropriate code buffer
+// (e.g. pColdCode if cold, pHotCode if hot).
+// endOffset end of code block, relative to appropriate code buffer
+// unwindSize size of unwind info pointed to by pUnwindBlock
+// pUnwindBlock pointer to unwind info
+// funcKind type of funclet (main method code, handler, filter)
+//
+void interceptor_ICJI::allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ original_ICorJitInfo->allocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock, funcKind);
+}
+
+// Get a block of memory needed for the code manager information,
+// (the info for enumerating the GC pointers while crawling the
+// stack frame).
+// Note that allocMem must be called first
+void * interceptor_ICJI::allocGCInfo (
+ size_t size /* IN */
+ )
+{
+ return original_ICorJitInfo->allocGCInfo(size);
+}
+
+// only used on x64
+void interceptor_ICJI::yieldExecution()
+{
+ original_ICorJitInfo->yieldExecution();
+}
+
+// Indicate how many exception handler blocks are to be returned.
+// This is guaranteed to be called before any 'setEHinfo' call.
+// Note that allocMem must be called before this method can be called.
+void interceptor_ICJI::setEHcount (
+ unsigned cEH /* IN */
+ )
+{
+ original_ICorJitInfo->setEHcount(cEH);
+}
+
+// Set the values for one particular exception handler block.
+//
+// Handler regions should be lexically contiguous.
+// This is because FinallyIsUnwinding() uses lexicality to
+// determine if a "finally" clause is executing.
+void interceptor_ICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ original_ICorJitInfo->setEHinfo(EHnumber, clause);
+}
+
+// Level 1 -> fatalError, Level 2 -> Error, Level 3 -> Warning
+// Level 4 means happens 10 times in a run, level 5 means 100, level 6 means 1000 ...
+// returns non-zero if the logging succeeded
+BOOL interceptor_ICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ return original_ICorJitInfo->logMsg(level, fmt, args);
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int interceptor_ICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ return original_ICorJitInfo->doAssert(szFile, iLine, szExpr);
+}
+
+void interceptor_ICJI::reportFatalError(CorJitResult result)
+{
+ original_ICorJitInfo->reportFatalError(result);
+}
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT interceptor_ICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer
+ )
+{
+ return original_ICorJitInfo->allocBBProfileBuffer(count, profileBuffer);
+}
+
+// get profile information to be used for optimizing the current method. The format
+// of the buffer is the same as the format the JIT passes to allocBBProfileBuffer.
+HRESULT interceptor_ICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * numRuns
+ )
+{
+ return original_ICorJitInfo->getBBProfileData(ftnHnd, count, profileBuffer, numRuns);
+}
+
+// Associates a native call site, identified by its offset in the native code stream, with
+// the signature information and method handle the JIT used to lay out the call site. If
+// the call site has no signature information (e.g. a helper call) or has no method handle
+// (e.g. a CALLI P/Invoke), then null should be passed instead.
+void interceptor_ICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ original_ICorJitInfo->recordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void interceptor_ICJI::recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ original_ICorJitInfo->recordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD interceptor_ICJI::getRelocTypeHint(void * target)
+{
+ return original_ICorJitInfo->getRelocTypeHint(target);
+}
+
+// A callback to identify the range of address known to point to
+// compiler-generated native entry points that call back into
+// MSIL.
+void interceptor_ICJI::getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ )
+{
+ original_ICorJitInfo->getModuleNativeEntryPointRange(pStart, pEnd);
+}
+
+// For what machine does the VM expect the JIT to generate code? The VM
+// returns one of the IMAGE_FILE_MACHINE_* values. Note that if the VM
+// is cross-compiling (such as the case for crossgen), it will return a
+// different value than if it was compiling for the host architecture.
+//
+DWORD interceptor_ICJI::getExpectedTargetArchitecture()
+{
+ return original_ICorJitInfo->getExpectedTargetArchitecture();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h
new file mode 100644
index 0000000000..c266f036fb
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitInfo
+#define _ICorJitInfo
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+
+class interceptor_ICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+public:
+
+ //Added to help us track the original icji and be able to easily indirect
+ //to it. And a simple way to keep one memory manager instance per instance.
+ ICorJitInfo *original_ICorJitInfo;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp
new file mode 100644
index 0000000000..668b4b7728
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.cpp
@@ -0,0 +1,72 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "ieememorymanager.h"
+#include "superpmi-shim-simple.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEEMM->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::AddRef()
+{
+ return original_IEEMM->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEEMM::Release()
+{
+ return original_IEEMM->Release();
+}
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ return original_IEEMM->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return original_IEEMM->ClrVirtualFree(lpAddress, dwSize, dwFreeType);
+}
+SIZE_T STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return original_IEEMM->ClrVirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return original_IEEMM->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessHeap()
+{
+ return original_IEEMM->ClrGetProcessHeap();
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ return original_IEEMM->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ return original_IEEMM->ClrHeapDestroy(hHeap);
+}
+LPVOID STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return original_IEEMM->ClrHeapAlloc(hHeap, dwFlags, dwBytes);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return original_IEEMM->ClrHeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ return original_IEEMM->ClrHeapValidate(hHeap, dwFlags, lpMem);
+}
+HANDLE STDMETHODCALLTYPE interceptor_IEEMM::ClrGetProcessExecutableHeap()
+{
+ return original_IEEMM->ClrGetProcessExecutableHeap();
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h
new file mode 100644
index 0000000000..411c532b59
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/ieememorymanager.h
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IEEMemoryManager
+#define _IEEMemoryManager
+
+#include "runtimedetails.h"
+
+/*
+interface IEEMemoryManager : IUnknown
+{
+ LPVOID ClrVirtualAlloc(
+ [in] LPVOID lpAddress, // region to reserve or commit
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD flAllocationType, // type of allocation
+ [in] DWORD flProtect // type of access protection
+ )
+
+ BOOL ClrVirtualFree(
+ [in] LPVOID lpAddress, // address of region
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD dwFreeType // operation type
+ )
+
+ SIZE_T ClrVirtualQuery(
+ [in] const void* lpAddress, // address of region
+ [in] PMEMORY_BASIC_INFORMATION lpBuffer, // information buffer
+ [in] SIZE_T dwLength // size of buffer
+ )
+
+ BOOL ClrVirtualProtect(
+ [in] LPVOID lpAddress, // region of committed pages
+ [in] SIZE_T dwSize, // size of the region
+ [in] DWORD flNewProtect, // desired access protection
+ [in] DWORD* lpflOldProtect // old protection
+ )
+
+ HANDLE ClrGetProcessHeap()
+
+ HANDLE ClrHeapCreate(
+ [in] DWORD flOptions, // heap allocation attributes
+ [in] SIZE_T dwInitialSize, // initial heap size
+ [in] SIZE_T dwMaximumSize // maximum heap size
+ )
+
+ BOOL ClrHeapDestroy(
+ [in] HANDLE hHeap // handle to heap
+ )
+
+ LPVOID ClrHeapAlloc(
+ [in] HANDLE hHeap, // handle to private heap block
+ [in] DWORD dwFlags, // heap allocation control
+ [in] SIZE_T dwBytes // number of bytes to allocate
+ )
+
+ BOOL ClrHeapFree(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap free options
+ [in] LPVOID lpMem // pointer to memory
+ )
+
+ BOOL ClrHeapValidate(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap access options
+ [in] const void* lpMem // optional pointer to memory block
+ )
+
+ HANDLE ClrGetProcessExecutableHeap()
+
+}; // interface IEEMemoryManager
+
+*/
+
+class interceptor_IEEMM : public IEEMemoryManager
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IEEMemoryManager methods for locking
+ //***************************************************************************
+ LPVOID STDMETHODCALLTYPE ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
+ BOOL STDMETHODCALLTYPE ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
+ SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+ BOOL STDMETHODCALLTYPE ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessHeap();
+ HANDLE STDMETHODCALLTYPE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
+ BOOL STDMETHODCALLTYPE ClrHeapDestroy(HANDLE hHeap);
+ LPVOID STDMETHODCALLTYPE ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
+ BOOL STDMETHODCALLTYPE ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
+ BOOL STDMETHODCALLTYPE ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap();
+
+public:
+ // Added so we know where to make the real calls to.
+ IEEMemoryManager *original_IEEMM;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp
new file mode 100644
index 0000000000..a137266751
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "iexecutionengine.h"
+#include "superpmi-shim-simple.h"
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+HRESULT STDMETHODCALLTYPE interceptor_IEE::QueryInterface(REFIID id, void **pInterface)
+{
+ return original_IEE->QueryInterface(id, pInterface);
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::AddRef()
+{
+ return original_IEE->AddRef();
+}
+ULONG STDMETHODCALLTYPE interceptor_IEE::Release()
+{
+ return original_IEE->Release();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ original_IEE->TLS_AssociateCallback(slot, callback);
+}
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE interceptor_IEE::TLS_GetDataBlock()
+{
+ return original_IEE->TLS_GetDataBlock();
+}
+
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE interceptor_IEE::TLS_GetValue(DWORD slot)
+{
+ return original_IEE->TLS_GetValue(slot);
+}
+
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE interceptor_IEE::TLS_CheckValue(DWORD slot, LPVOID * pValue)
+{
+ return original_IEE->TLS_CheckValue(slot, pValue);
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ original_IEE->TLS_SetValue(slot, pData);
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE interceptor_IEE::TLS_ThreadDetaching()
+{
+ original_IEE->TLS_ThreadDetaching();
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+CRITSEC_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return original_IEE->CreateLock(szTag, level, flags);
+}
+void STDMETHODCALLTYPE interceptor_IEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->DestroyLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->AcquireLock(lock);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+ original_IEE->ReleaseLock(lock);
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateAutoEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateAutoEvent(bInitialState);
+}
+EVENT_COOKIE STDMETHODCALLTYPE interceptor_IEE::CreateManualEvent(BOOL bInitialState)
+{
+ return original_IEE->CreateManualEvent(bInitialState);
+}
+void STDMETHODCALLTYPE interceptor_IEE::CloseEvent(EVENT_COOKIE event)
+{
+ original_IEE->CloseEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrSetEvent(event);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ return original_IEE->ClrResetEvent(event);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->WaitForEvent(event, dwMilliseconds, bAlertable);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ return original_IEE->WaitForSingleObject(handle, dwMilliseconds);
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ return original_IEE->ClrCreateSemaphore(dwInitial, dwMax);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ original_IEE->ClrCloseSemaphore(semaphore);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ return original_IEE->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount);
+}
+MUTEX_COOKIE STDMETHODCALLTYPE interceptor_IEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ return original_IEE->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
+}
+void STDMETHODCALLTYPE interceptor_IEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ original_IEE->ClrCloseMutex(mutex);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ return original_IEE->ClrReleaseMutex(mutex);
+}
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ return original_IEE->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable);
+}
+
+DWORD STDMETHODCALLTYPE interceptor_IEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return original_IEE->ClrSleepEx(dwMilliseconds, bAlertable);
+}
+BOOL STDMETHODCALLTYPE interceptor_IEE::ClrAllocationDisallowed()
+{
+ return original_IEE->ClrAllocationDisallowed();
+}
+void STDMETHODCALLTYPE interceptor_IEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ original_IEE->GetLastThrownObjectExceptionFromThread(ppvException);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h
new file mode 100644
index 0000000000..dd3d7ee1fa
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/iexecutionengine.h
@@ -0,0 +1,150 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IExecutionEngine
+#define _IExecutionEngine
+
+#include "ieememorymanager.h"
+
+/*
+interface IExecutionEngine : IUnknown
+{
+ // Thread Local Storage is based on logical threads. The underlying
+ // implementation could be threads, fibers, or something more exotic.
+ // Slot numbers are predefined. This is not a general extensibility
+ // mechanism.
+
+ // Associate a callback function for releasing TLS on thread/fiber death.
+ // This can be NULL.
+ void TLS_AssociateCallback([in] DWORD slot, [in] PTLS_CALLBACK_FUNCTION callback)
+
+ // May be called once to get the master TLS block slot for fast Get/Set operations
+ DWORD TLS_GetMasterSlotIndex()
+
+ // Get the value at a slot
+ PVOID TLS_GetValue([in] DWORD slot)
+
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL TLS_CheckValue([in] DWORD slot, [out] PVOID * pValue)
+
+ // Set the value at a slot
+ void TLS_SetValue([in] DWORD slot, [in] PVOID pData)
+
+ // Free TLS memory block and make callback
+ void TLS_ThreadDetaching()
+
+ // Critical Sections are sometimes exposed to the host and therefore need to be
+ // reflected from all CLR DLLs to the EE.
+ //
+ // In addition, we always monitor interactions between the lock & the GC, based
+ // on the GC mode in which the lock is acquired and we restrict what operations
+ // are permitted while holding the lock based on this.
+ //
+ // Finally, we we rank all our locks to prevent deadlock across all the DLLs of
+ // the CLR. This is the level argument to CreateLock.
+ //
+ // All usage of these locks must be exception-safe. To achieve this, we suggest
+ // using Holders (see holder.h & crst.h). In fact, within the EE code cannot
+ // hold locks except by using exception-safe holders.
+
+ CRITSEC_COOKIE CreateLock([in] LPCSTR szTag, [in] LPCSTR level, [in] CrstFlags flags)
+
+ void DestroyLock([in] CRITSEC_COOKIE lock)
+
+ void AcquireLock([in] CRITSEC_COOKIE lock)
+
+ void ReleaseLock([in] CRITSEC_COOKIE lock)
+
+ EVENT_COOKIE CreateAutoEvent([in] BOOL bInitialState)
+ EVENT_COOKIE CreateManualEvent([in] BOOL bInitialState)
+ void CloseEvent([in] EVENT_COOKIE event)
+ BOOL ClrSetEvent([in] EVENT_COOKIE event)
+ BOOL ClrResetEvent([in] EVENT_COOKIE event)
+ DWORD WaitForEvent([in] EVENT_COOKIE event, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ DWORD WaitForSingleObject([in] HANDLE handle, [in] DWORD dwMilliseconds)
+
+ // OS header file defines CreateSemaphore.
+ SEMAPHORE_COOKIE ClrCreateSemaphore([in] DWORD dwInitial, [in] DWORD dwMax)
+ void ClrCloseSemaphore([in] SEMAPHORE_COOKIE semaphore)
+ DWORD ClrWaitForSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] LONG lReleaseCount, [in] LONG *lpPreviousCount)
+
+ MUTEX_COOKIE ClrCreateMutex([in]LPSECURITY_ATTRIBUTES lpMutexAttributes, [in]BOOL bInitialOwner, [in]LPCTSTR lpName)
+ DWORD ClrWaitForMutex([in] MUTEX_COOKIE mutex, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseMutex([in] MUTEX_COOKIE mutex)
+ void ClrCloseMutex([in] MUTEX_COOKIE mutex)
+
+ DWORD ClrSleepEx([in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+
+ BOOL ClrAllocationDisallowed()
+
+ void GetLastThrownObjectExceptionFromThread([out] void **ppvException)
+
+}; // interface IExecutionEngine
+*/
+
+class interceptor_IEE : public IExecutionEngine
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IExecutionEngine methods for TLS
+ //***************************************************************************
+ // Associate a callback for cleanup with a TLS slot
+ VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
+ // Get the TLS block for fast Get/Set operations
+ LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
+ // Get the value at a slot
+ LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue);
+ // Set the value at a slot
+ VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
+ // Free TLS memory block and make callback
+ VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
+
+ //***************************************************************************
+ // IExecutionEngine methods for locking
+ //***************************************************************************
+ CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags);
+ void STDMETHODCALLTYPE DestroyLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE AcquireLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE ReleaseLock(CRITSEC_COOKIE lock);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateAutoEvent(BOOL bInitialState);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateManualEvent(BOOL bInitialState);
+ void STDMETHODCALLTYPE CloseEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrSetEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrResetEvent(EVENT_COOKIE event);
+ DWORD STDMETHODCALLTYPE WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds);
+ SEMAPHORE_COOKIE STDMETHODCALLTYPE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax);
+ void STDMETHODCALLTYPE ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore);
+ DWORD STDMETHODCALLTYPE ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount);
+ MUTEX_COOKIE STDMETHODCALLTYPE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName);
+ void STDMETHODCALLTYPE ClrCloseMutex(MUTEX_COOKIE mutex);
+ BOOL STDMETHODCALLTYPE ClrReleaseMutex(MUTEX_COOKIE mutex);
+ DWORD STDMETHODCALLTYPE ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrAllocationDisallowed();
+ void STDMETHODCALLTYPE GetLastThrownObjectExceptionFromThread(void **ppvException);
+
+public:
+ // Added so we know where to make the real calls to.
+ IExecutionEngine *original_IEE;
+};
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp
new file mode 100644
index 0000000000..01bff37a01
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.cpp
@@ -0,0 +1,40 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "runtimedetails.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+JitHost* g_ourJitHost;
+
+JitHost::JitHost(ICorJitHost* wrappedHost) : wrappedHost(wrappedHost)
+{
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return wrappedHost->allocateMemory(size, usePageAllocator);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ return wrappedHost->freeMemory(block, usePageAllocator);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ return wrappedHost->getIntConfigValue(key, defaultValue);
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ return wrappedHost->getStringConfigValue(key);
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ wrappedHost->freeStringConfigValue(value);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/jithost.h b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.h
new file mode 100644
index 0000000000..7df1c581dc
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/jithost.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JITHOST
+#define _JITHOST
+
+class JitHost : public ICorJitHost
+{
+public:
+ JitHost(ICorJitHost* wrappedHost);
+
+#include "icorjithostimpl.h"
+
+private:
+ ICorJitHost* wrappedHost;
+};
+
+extern JitHost* g_ourJitHost;
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp
new file mode 100644
index 0000000000..2381d0fa38
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.cpp
@@ -0,0 +1,217 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.cpp - thin shim for the jit
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "superpmi-shim-simple.h"
+#include "runtimedetails.h"
+#include "coreclrcallbacks.h"
+#include "icorjitcompiler.h"
+#include "errorhandling.h"
+#include "logging.h"
+#include "spmiutil.h"
+#include "jithost.h"
+
+HMODULE g_hRealJit = 0; //We leak this currently (could do the proper shutdown in process_detach)
+WCHAR* g_realJitPath = nullptr; //We leak this (could do the proper shutdown in process_detach)
+char* g_logFilePath = nullptr; //We *don't* leak this, hooray!
+WCHAR* g_HomeDirectory = nullptr;
+WCHAR* g_DefaultRealJitPath = nullptr;
+
+void SetDefaultPaths()
+{
+ if (g_HomeDirectory == nullptr)
+ {
+ g_HomeDirectory = GetEnvironmentVariableWithDefaultW(W("HOME"), W("."));
+ }
+
+ if (g_DefaultRealJitPath == nullptr)
+ {
+ size_t len = wcslen(g_HomeDirectory) + 1 + wcslen(DEFAULT_REAL_JIT_NAME_W) + 1;
+ g_DefaultRealJitPath = new WCHAR[len];
+ wcscpy_s(g_DefaultRealJitPath, len, g_HomeDirectory);
+ wcscat_s(g_DefaultRealJitPath, len, DIRECTORY_SEPARATOR_STR_W);
+ wcscat_s(g_DefaultRealJitPath, len, DEFAULT_REAL_JIT_NAME_W);
+ }
+}
+
+void SetLibName()
+{
+ if (g_realJitPath == nullptr)
+ {
+ g_realJitPath = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimPath"), g_DefaultRealJitPath);
+ }
+}
+
+// TODO: this only works for ANSI file paths...
+void SetLogFilePath()
+{
+ if (g_logFilePath == nullptr)
+ {
+ // If the environment variable isn't set, we don't enable file logging
+ g_logFilePath = GetEnvironmentVariableWithDefaultA("SuperPMIShimLogFilePath", nullptr);
+ }
+}
+
+extern "C"
+BOOL
+#ifndef FEATURE_PAL
+APIENTRY
+#endif // !FEATURE_PAL
+DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef FEATURE_PAL
+ if (0 != PAL_InitializeDLL())
+ {
+ fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n");
+ exit(1);
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+ SetLogFilePath();
+ Logger::OpenLogFile(g_logFilePath);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ Logger::Shutdown();
+
+ delete[] g_logFilePath;
+ g_logFilePath = nullptr;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+// Exported via def file
+extern "C"
+void __stdcall jitStartup(ICorJitHost* host)
+{
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if (g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if (g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ // Get the required entrypoint
+ PjitStartup pnjitStartup = (PjitStartup)::GetProcAddress(g_hRealJit, "jitStartup");
+ if (pnjitStartup == nullptr)
+ {
+ // This portion of the interface is not used by the JIT under test.
+ g_ourJitHost = nullptr;
+ return;
+ }
+
+ g_ourJitHost = new JitHost(host);
+ pnjitStartup(g_ourJitHost);
+}
+
+
+//Exported via def file
+extern "C"
+ICorJitCompiler* __stdcall getJit()
+{
+ DWORD dwRetVal = 0;
+ PgetJit pngetJit;
+ interceptor_ICJC *pJitInstance = nullptr;
+ ICorJitCompiler *tICJI = nullptr;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("getJit() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return nullptr;
+ }
+ }
+
+ //get the required entrypoints
+ pngetJit = (PgetJit)::GetProcAddress(g_hRealJit, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("getJit() - GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return nullptr;
+ }
+
+ tICJI = pngetJit();
+ if(tICJI == nullptr)
+ {
+ LogError("getJit() - pngetJit gave us null");
+ return nullptr;
+ }
+
+ pJitInstance = new interceptor_ICJC();
+ pJitInstance->original_ICorJitCompiler = tICJI;
+ return pJitInstance;
+}
+
+//Exported via def file
+extern "C"
+void __stdcall sxsJitStartup(CoreClrCallbacks const & original_cccallbacks)
+{
+ PsxsJitStartup pnsxsJitStartup;
+
+ SetDefaultPaths();
+ SetLibName();
+
+ //Load Library
+ if(g_hRealJit == 0)
+ {
+ g_hRealJit = ::LoadLibraryW(g_realJitPath);
+ if(g_hRealJit == 0)
+ {
+ LogError("sxsJitStartup() - LoadLibrary failed to load '%ws' (0x%08x)", g_realJitPath, ::GetLastError());
+ return;
+ }
+ }
+
+ //get entry point
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(g_hRealJit, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("sxsJitStartup() - GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return;
+ }
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ original_CoreClrCallbacks = new CoreClrCallbacks();
+ original_CoreClrCallbacks->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ original_CoreClrCallbacks->m_pfnIEE = original_cccallbacks.m_pfnIEE;
+ original_CoreClrCallbacks->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ original_CoreClrCallbacks->m_pfnGetCLRFunction = original_cccallbacks.m_pfnGetCLRFunction;
+
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+
+ temp->m_hmodCoreCLR = original_cccallbacks.m_hmodCoreCLR;
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = original_cccallbacks.m_pfnGetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ pnsxsJitStartup(*temp);
+}
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def
new file mode 100644
index 0000000000..436434c3de
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.def
@@ -0,0 +1,5 @@
+LIBRARY
+EXPORTS
+ getJit
+ jitStartup
+ sxsJitStartup
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h
new file mode 100644
index 0000000000..2068a02775
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/superpmi-shim-simple.h
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// SuperPMI-Shim.h - thin shim for the jit
+//----------------------------------------------------------
+#ifndef _SuperPMIShim
+#define _SuperPMIShim
+
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/.gitmirror b/src/ToolBox/superpmi/superpmi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/CMakeLists.txt b/src/ToolBox/superpmi/superpmi/CMakeLists.txt
new file mode 100644
index 0000000000..8756fa0e32
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/CMakeLists.txt
@@ -0,0 +1,76 @@
+project(superpmi)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+
+if(WIN32)
+ #use static crt
+ add_definitions(-MT)
+endif(WIN32)
+
+include_directories(.)
+include_directories(../superpmi-shared)
+
+# When it is ready (the build works on all platforms, referencing the coredistools
+# package), define this:
+# add_definitions(-DUSE_COREDISTOOLS)
+
+set(SUPERPMI_SOURCES
+ commandline.cpp
+ coreclrcallbacks.cpp
+ cycletimer.cpp
+ icorjitinfo.cpp
+ ieememorymanager.cpp
+ iexecutionengine.cpp
+ jitdebugger.cpp
+ jitinstance.cpp
+ methodstatsemitter.cpp
+ neardiffer.cpp
+ parallelsuperpmi.cpp
+ superpmi.cpp
+ jithost.cpp
+ ../superpmi-shared/callutils.cpp
+ ../superpmi-shared/compileresult.cpp
+ ../superpmi-shared/errorhandling.cpp
+ ../superpmi-shared/logging.cpp
+ ../superpmi-shared/mclist.cpp
+ ../superpmi-shared/methodcontext.cpp
+ ../superpmi-shared/methodcontextreader.cpp
+ ../superpmi-shared/simpletimer.cpp
+ ../superpmi-shared/spmiutil.cpp
+ ../superpmi-shared/tocfile.cpp
+ ../superpmi-shared/typeutils.cpp
+)
+
+add_precompiled_header(
+ standardpch.h
+ ../superpmi-shared/standardpch.cpp
+ SUPERPMI_SOURCES
+)
+
+add_executable(superpmi
+ ${SUPERPMI_SOURCES}
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(superpmi
+ utilcodestaticnohost
+ mscorrc_debug
+ coreclrpal
+ palrt
+ )
+else()
+ target_link_libraries(superpmi
+ version.lib
+ advapi32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ )
+
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/superpmi.pdb DESTINATION PDB)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+install (TARGETS superpmi DESTINATION .)
diff --git a/src/ToolBox/superpmi/superpmi/commandline.cpp b/src/ToolBox/superpmi/superpmi/commandline.cpp
new file mode 100644
index 0000000000..56a2f32d1d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/commandline.cpp
@@ -0,0 +1,543 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.cpp - tiny very specific command line parser
+//----------------------------------------------------------
+
+#include "standardpch.h"
+#include "commandline.h"
+#include "superpmi.h"
+#include "mclist.h"
+#include "methodcontext.h"
+#include "logging.h"
+
+// NOTE: this is parsed by parallelsuperpmi.cpp::ProcessChildStdOut() to determine if an incorrect
+// argument usage error has occurred.
+const char* const g_SuperPMIUsageFirstLine = "SuperPMI is a JIT compiler testing tool.";
+
+void CommandLine::DumpHelp(const char* program)
+{
+ printf("%s\n", g_SuperPMIUsageFirstLine);
+ printf("\n");
+ printf("Usage: %s [options] jitname [jitname2] filename.mc\n", program);
+ printf(" jitname" PLATFORM_SHARED_LIB_SUFFIX_A " - path of jit to be tested\n");
+ printf(" jitname2" PLATFORM_SHARED_LIB_SUFFIX_A " - optional path of second jit to be tested\n");
+ printf(" filename.mc - load method contexts from filename.mc\n");
+ printf(" -j[it] Name - optionally -jit can be used to specify jits\n");
+ printf(" -l[oad] filename - optionally -load can be used to specify method contexts\n");
+ printf("\n");
+ printf("Options:\n");
+ printf("\n");
+ printf(" -boe\n");
+ printf(" Break on error return from compileMethod\n");
+ printf("\n");
+ printf(" -boa\n");
+ printf(" Break on assert from the JIT\n");
+ printf("\n");
+ printf(" -v[erbosity] messagetypes\n");
+ printf(" Controls which types of messages SuperPMI logs. Specify a string of\n");
+ printf(" characters representing message categories to enable, where:\n");
+ printf(" e - errors (internal fatal errors that are non-recoverable)\n");
+ printf(" w - warnings (internal conditions that are unusual, but not serious)\n");
+ printf(" m - missing (failures due to missing JIT-EE interface details)\n");
+ printf(" i - issues (issues found with the JIT, e.g. asm diffs, asserts)\n");
+ printf(" n - information (notifications/summaries, e.g. 'Loaded 5 Jitted 4 FailedCompile 1')\n");
+ printf(" v - verbose (status messages, e.g. 'Jit startup took '151.12ms')\n");
+ printf(" d - debug (lots of detailed output)\n");
+ printf(" a - all (enable all message types; overrides other enable message types)\n");
+ printf(" q - quiet (disable all output; overrides all others)\n");
+ printf(" e.g. '-v ew' only writes error and warning messages to the console.\n");
+ printf(" 'q' takes precedence over any other message type specified.\n");
+ printf(" Default set of messages enabled is 'ewminv'.\n");
+ printf("\n");
+ printf(" -w[riteLogFile] logfile\n");
+ printf(" Write log messages to the specified file.\n");
+ printf("\n");
+ printf(" -c[ompile] <indices>\n");
+ printf(" Compile only those method contexts whose indices are specified.\n");
+ printf(" Indices can be either a single index, comma separated values,\n");
+ printf(" a range, or the name of a .MCL file with newline delimited indices.\n");
+ printf(" e.g. -compile 20\n");
+ printf(" e.g. -compile 20,25,30,32\n");
+ printf(" e.g. -compile 10-99\n");
+ printf(" e.g. -compile 5,10-99,101,201-300\n");
+ printf(" e.g. -compile failed.mcl\n");
+ printf("\n");
+ printf(" -m[atchHash] <MD5 Hash>\n");
+ printf(" Compile only method context with specific MD5 hash\n");
+ printf("\n");
+ printf(" -e[mitMethodStats] <stats-types>\n");
+ printf(" Emit method statistics in CSV format to filename.mc.stats.\n");
+ printf(" Specify a string of characters representing statistics to emit, where:\n");
+ printf(" i - method IL code size\n");
+ printf(" a - method compiled ASM code size\n");
+ printf(" h - method hash to uniquely identify a method across MCH files\n");
+ printf(" n - method number inside the source MCH\n");
+ printf(" t - method throughput time\n");
+ printf(" * - all available method stats\n");
+ printf("\n");
+ printf(" -a[pplyDiff]\n");
+ printf(" Compare the compile result generated from the provided JIT with the\n");
+ printf(" compile result stored with the MC. If two JITs are provided, this\n");
+ printf(" compares the compile results generated by the two JITs.\n");
+ printf("\n");
+ printf(" -r[eproName] prefix\n");
+ printf(" Write out failing methods to prefix-n.mc\n");
+ printf("\n");
+ printf(" -f[ailingMCList] mclfilename\n");
+ printf(" Write out failing methods to mclfilename.\n");
+ printf(" If using -applyDiff and no -diffMCList is specified,\n");
+ printf(" comparison failures also get written to mclfilename.\n");
+ printf("\n");
+ printf(" -diffMCList diffMCLfilename\n");
+ printf(" Write out methods that differ between compilations to diffMCLfilename.\n");
+ printf(" This only works with -applyDiff.\n");
+ printf("\n");
+ printf(" -p[arallel] [workerCount]\n");
+ printf(" Run in parallel mode by spawning 'workerCount' processes to do processing.\n");
+ printf(" If 'workerCount' is not specified, the number of workers used is\n");
+ printf(" the number of processors on the machine.\n");
+ printf("\n");
+ printf(" -skipCleanup\n");
+ printf(" Skip deletion of temporary files created by child SuperPMI processes with -parallel.\n");
+ printf("\n");
+ printf(" -target <target>\n");
+ printf(" Used by the assembly differences calculator. This specifies the target\n");
+ printf(" architecture for cross-compilation. Currently allowed <target> value: arm64\n");
+ printf("\n");
+#ifdef USE_COREDISTOOLS
+ printf(" -coredistools\n");
+ printf(" Use disassembly tools from the CoreDisTools library\n");
+ printf("\n");
+#endif // USE_COREDISTOOLS
+ printf("Inputs are case sensitive.\n");
+ printf("\n");
+ printf("SuperPMI method contexts are stored in files with extension .MC, implying\n");
+ printf("a single method context, or .MCH, implying a set of method contexts. Either\n");
+ printf("extension works equivalently.\n");
+ printf("\n");
+ printf("Exit codes:\n");
+ printf("0 : success\n");
+ printf("-1 : general fatal error (e.g., failed to initialize, failed to read files)\n");
+ printf("-2 : JIT failed to initialize\n");
+ printf("1 : there were compilation failures\n");
+ printf("2 : there were assembly diffs\n");
+ printf("\n");
+ printf("Examples:\n");
+ printf(" %s " MAKEDLLNAME_A("clrjit") " test.mch\n", program);
+ printf(" ; compile all functions in test.mch using " MAKEDLLNAME_A("clrjit") "\n");
+ printf(" %s -p " MAKEDLLNAME_A("clrjit") " test.mch\n", program);
+ printf(" ; same as above, but use all available processors to compile in parallel\n");
+ printf(" %s -f fail.mcl " MAKEDLLNAME_A("clrjit") " test.mch\n", program);
+ printf(" ; if there are any failures, record their MC numbers in the file fail.mcl\n");
+}
+
+//Assumption: All inputs are initialized to default or real value. we'll just set the stuff we see on the command line.
+//Assumption: Single byte names are passed in.. mb stuff doesnt cause an obvious problem... but it might have issues...
+//Assumption: Values larger than 2^31 aren't expressible from the commandline.... (atoi) Unless you pass in negatives.. :-|
+bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
+{
+ size_t argLen = 0;
+ size_t tempLen = 0;
+ bool foundJit = false;
+ bool foundFile = false;
+
+ if (argc == 1) //Print help when no args are passed
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ for (int i = 1; i<argc; i++)
+ {
+ bool isASwitch = (argv[i][0] == '-');
+#ifndef FEATURE_PAL
+ if (argv[i][0] == '/') // Also accept "/" on Windows
+ {
+ isASwitch = true;
+ }
+#endif // !FEATURE_PAL
+
+ //Process a switch
+ if (isASwitch)
+ {
+ argLen = strlen(argv[i]);
+
+ if (argLen >1)
+ argLen--; //adjust for leading switch
+ else
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if ((_strnicmp(&argv[i][1], "help", argLen) == 0) ||
+ (_strnicmp(&argv[i][1], "HELP", argLen) == 0) ||
+ (_strnicmp(&argv[i][1], "?", argLen) == 0))
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+ else if ((_strnicmp(&argv[i][1], "load", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processMethodContext:
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ LogError("Arg '%s' is invalid, name of file missing.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ o->nameOfInputMethodContextFile = new char[tempLen + 1];
+ strcpy_s(o->nameOfInputMethodContextFile, tempLen + 1, argv[i]);
+ foundFile = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "jit", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ processJit:
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ LogError("Arg '%s' is invalid, name of jit missing.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ char *tempStr = new char[tempLen + 1];
+ strcpy_s(tempStr, tempLen + 1, argv[i]);
+ if (!foundJit)
+ {
+ o->nameOfJit = tempStr;
+ foundJit = true;
+ }
+ else
+ {
+ o->nameOfJit2 = tempStr;
+ }
+ }
+ else if ((_strnicmp(&argv[i][1], "reproName", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ tempLen = strlen(argv[i]);
+ if (tempLen == 0)
+ {
+ LogError("Arg '%s' is invalid, name of prefix missing.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ char *tempStr = new char[tempLen + 1];
+ strcpy_s(tempStr, tempLen + 1, argv[i]);
+ o->reproName = tempStr;
+ }
+ else if ((_strnicmp(&argv[i][1], "failingMCList", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->mclFilename = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "diffMCList", 10) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->diffMCLFilename = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "target", 6) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->targetArchitecture = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "boe", 3) == 0))
+ {
+ o->breakOnError = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "boa", 3) == 0))
+ {
+ o->breakOnAssert = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "verbosity", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ Logger::SetLogLevel(Logger::ParseLogLevelString(argv[i]));
+ }
+ else if ((_strnicmp(&argv[i][1], "writeLogFile", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->writeLogFile = argv[i];
+ Logger::OpenLogFile(argv[i]);
+ }
+ else if ((_strnicmp(&argv[i][1], "emitMethodStats", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->methodStatsTypes = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "applyDiff", argLen) == 0))
+ {
+ o->applyDiff = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "compile", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ bool isValidList = MCList::processArgAsMCL(argv[i], &o->indexCount, &o->indexes);
+ if (!isValidList)
+ {
+ LogError("Arg '%s' is invalid, needed method context list.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->hash != nullptr)
+ {
+ LogError("Cannot use both method context list and method context hash.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->offset > 0 && o->increment > 0)
+ {
+ LogError("Cannot use method context list in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->compileList = argv[i]; // Save this in case we need it for -parallel.
+ }
+#ifdef USE_COREDISTOOLS
+ else if ((_strnicmp(&argv[i][1], "coredistools", argLen) == 0)) {
+ o->useCoreDisTools = true;
+ }
+#endif // USE_COREDISTOOLS
+ else if ((_strnicmp(&argv[i][1], "matchHash", argLen) == 0))
+ {
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if (strlen(argv[i]) != (MD5_HASH_BUFFER_SIZE - 1))
+ {
+ LogError("Arg '%s' is invalid, needed a valid method context hash.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount > 0)
+ {
+ LogError("Cannot use both method context list and method context hash.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->offset > 0 && o->increment > 0)
+ {
+ LogError("Cannot use method context hash in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ o->hash = argv[i];
+ }
+ else if ((_strnicmp(&argv[i][1], "parallel", argLen) == 0))
+ {
+ o->parallel = true;
+
+ // Is there another argument?
+ if (i + 1 < argc)
+ {
+ // If so, does it look like a worker count?
+ bool isWorkerCount = true;
+ size_t nextlen = strlen(argv[i + 1]);
+ for (size_t j = 0; j < nextlen; j++)
+ {
+ if (!isdigit(argv[i + 1][j]))
+ {
+ isWorkerCount = false; // Doesn't look like a worker count; bail out and let someone else handle it.
+ break;
+ }
+ }
+ if (isWorkerCount)
+ {
+ ++i;
+ o->workerCount = atoi(argv[i]);
+
+ if (o->workerCount < 1)
+ {
+ LogError("Invalid workers count specified, workers count must be at least 1.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->workerCount > MAXIMUM_WAIT_OBJECTS)
+ {
+ LogError("Invalid workers count specified, workers count cannot be more than %d.", MAXIMUM_WAIT_OBJECTS);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ }
+ }
+ else if ((_stricmp(&argv[i][1], "skipCleanup") == 0))
+ {
+ o->skipCleanup = true;
+ }
+ else if ((_strnicmp(&argv[i][1], "stride", argLen) == 0))
+ {
+ // "-stride" is an internal switch used by -parallel. Usage is:
+ //
+ // -stride offset increment
+ //
+ // It compiles methods in this series until end-of-file:
+ // offset, offset+increment, offset+2*increment, offset+3*increment, ...
+
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->offset = atoi(argv[i]);
+
+ if (++i >= argc)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ o->increment = atoi(argv[i]);
+
+ if (o->offset < 1 || o->increment < 1)
+ {
+ LogError("Incorrect offset/increment specified for -stride. Offset and increment both must be > 0.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->indexCount > 0)
+ {
+ LogError("Cannot use method context list in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->hash != nullptr)
+ {
+ LogError("Cannot use method context hash in parallel mode.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ else
+ {
+ LogError("Unknown switch '%s' passed as argument.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ //Process an input filename
+ //String comparisons on file extensions must be case-insensitive since we run on Windows
+ else
+ {
+ char *lastdot = strrchr(argv[i], '.');
+ if (lastdot == nullptr)
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if (_stricmp(lastdot, PLATFORM_SHARED_LIB_SUFFIX_A) == 0)
+ goto processJit;
+ else if (_stricmp(lastdot, ".mc") == 0)
+ goto processMethodContext;
+ else if (_stricmp(lastdot, ".mch") == 0)
+ goto processMethodContext;
+ else if (_stricmp(lastdot, ".mct") == 0)
+ goto processMethodContext;
+ else
+ {
+ LogError("Unknown file type passed as argument, '%s'.", argv[i]);
+ DumpHelp(argv[0]);
+ return false;
+ }
+ }
+ }
+
+ // Do some argument validation.
+
+ if (o->nameOfJit == nullptr)
+ {
+ LogError("Missing name of a Jit.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->nameOfInputMethodContextFile == nullptr)
+ {
+ LogError("Missing name of an input file.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->diffMCLFilename != nullptr && !o->applyDiff)
+ {
+ LogError("-diffMCList specified without -applyDiff.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->targetArchitecture != nullptr && (0 != _stricmp(o->targetArchitecture, "arm64")))
+ {
+ LogError("Illegal target architecture specified with -target (only arm64 is supported).");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ if (o->skipCleanup && !o->parallel)
+ {
+ LogError("-skipCleanup requires -parallel.");
+ DumpHelp(argv[0]);
+ return false;
+ }
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi/commandline.h b/src/ToolBox/superpmi/superpmi/commandline.h
new file mode 100644
index 0000000000..051eb6def2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/commandline.h
@@ -0,0 +1,74 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// CommandLine.h - tiny very specific command line parser
+//----------------------------------------------------------
+#ifndef _CommandLine
+#define _CommandLine
+
+class CommandLine
+{
+public:
+
+ class Options
+ {
+ public:
+ Options() :
+ nameOfJit(nullptr),
+ nameOfJit2(nullptr),
+ nameOfInputMethodContextFile(nullptr),
+ writeLogFile(nullptr),
+ reproName(nullptr),
+ breakOnError(false),
+ breakOnAssert(false),
+ applyDiff(false),
+ parallel(false),
+ useCoreDisTools(false),
+ skipCleanup(false),
+ workerCount(-1),
+ indexCount(-1),
+ indexes(nullptr),
+ hash(nullptr),
+ methodStatsTypes(nullptr),
+ mclFilename(nullptr),
+ diffMCLFilename(nullptr),
+ targetArchitecture(nullptr),
+ compileList(nullptr),
+ offset(-1),
+ increment(-1)
+ {
+ }
+
+ char* nameOfJit;
+ char* nameOfJit2;
+ char* nameOfInputMethodContextFile;
+ char* writeLogFile;
+ char* reproName;
+ bool breakOnError;
+ bool breakOnAssert;
+ bool applyDiff;
+ bool parallel; // User specified to use /parallel mode.
+ bool useCoreDisTools; // Use CoreDisTools library instead of Msvcdis
+ bool skipCleanup; // In /parallel mode, do we skip cleanup of temporary files? Used for debugging /parallel.
+ int workerCount; // Number of workers to use for /parallel mode. -1 (or 1) means don't use parallel mode.
+ int indexCount; // If indexCount is -1 and hash points to nullptr it means compile all.
+ int* indexes;
+ char* hash;
+ char* methodStatsTypes;
+ char* mclFilename;
+ char* diffMCLFilename;
+ char* targetArchitecture;
+ char* compileList;
+ int offset;
+ int increment;
+ };
+
+ static bool Parse(int argc, char* argv[], /* OUT */ Options* o);
+
+private:
+ static void DumpHelp(const char* program);
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp
new file mode 100644
index 0000000000..7a66d20940
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp
@@ -0,0 +1,69 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "spmiutil.h"
+#include "coreclrcallbacks.h"
+#include "iexecutionengine.h"
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
+{
+ MyIEE *iee = InitIExecutionEngine();
+ return iee;
+}
+
+/*#pragma warning( suppress :4996 ) //deprecated
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength)
+{
+ DebugBreakorAV(131);
+ return 0;
+}
+*/
+
+HANDLE ourHeap = nullptr;
+
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ if(ourHeap==nullptr)
+ ourHeap = HeapCreate(0, 4096 ,0);
+ if(ourHeap==nullptr)
+ {
+ LogError("HeapCreate Failed");
+ __debugbreak();
+ return nullptr;
+ }
+ LPVOID result = HeapAlloc(ourHeap, dwFlags, dwBytes);
+// LogDebug("EEHeapAllocInProcessHeap %p %u %u", result, dwFlags, dwBytes);
+ return result;
+}
+
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem)
+{
+// return true;
+ return HeapFree(ourHeap, dwFlags, lpMem);
+}
+
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
+{
+ if(strcmp(functionName, "EEHeapAllocInProcessHeap")==0)
+ return (void*)EEHeapAllocInProcessHeap;
+ if(strcmp(functionName, "EEHeapFreeInProcessHeap")==0)
+ return (void*)EEHeapFreeInProcessHeap;
+ DebugBreakorAV(132);
+ return nullptr;
+}
+
+CoreClrCallbacks *InitCoreClrCallbacks()
+{
+ CoreClrCallbacks *temp = new CoreClrCallbacks();
+ ::ZeroMemory(temp, sizeof(CoreClrCallbacks));
+
+ temp->m_hmodCoreCLR = (HINSTANCE)(size_t)0xbadbad01; // any non-null value seems okay...
+ temp->m_pfnIEE = IEE_t;
+ temp->m_pfnGetCORSystemDirectory = nullptr;//GetCORSystemDirectory;
+ temp->m_pfnGetCLRFunction = GetCLRFunction;
+
+ return temp;
+}
diff --git a/src/ToolBox/superpmi/superpmi/coreclrcallbacks.h b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.h
new file mode 100644
index 0000000000..cb3815525e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _CoreClrCallbacks
+#define _CoreClrCallbacks
+
+#include "runtimedetails.h"
+
+IExecutionEngine* STDMETHODCALLTYPE IEE_t();
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength);
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap (DWORD dwFlags, SIZE_T dwBytes);
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap (DWORD dwFlags, LPVOID lpMem);
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName);
+CoreClrCallbacks *InitCoreClrCallbacks();
+
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/cycletimer.cpp b/src/ToolBox/superpmi/superpmi/cycletimer.cpp
new file mode 100644
index 0000000000..de0e19b935
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/cycletimer.cpp
@@ -0,0 +1,60 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "cycletimer.h"
+
+CycleTimer::CycleTimer()
+{
+ start = 0;
+ stop = 0;
+ overhead = QueryOverhead();
+}
+
+CycleTimer::~CycleTimer()
+{
+}
+
+void CycleTimer::Start()
+{
+ BOOL retVal = QueryThreadCycleTime(GetCurrentThread(), &start);
+
+ if(retVal == FALSE)
+ {
+ LogError("CycleTimer::Start unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+void CycleTimer::Stop()
+{
+ BOOL retVal = QueryThreadCycleTime(GetCurrentThread(), &stop);
+
+ if(retVal == FALSE)
+ {
+ LogError("CycleTimer::Stop unable to QPC. error was 0x%08x", ::GetLastError());
+ ::__debugbreak();
+ }
+}
+
+unsigned __int64 CycleTimer::GetCycles()
+{
+ return stop - start - overhead;
+}
+
+unsigned __int64 CycleTimer::QueryOverhead()
+{
+ unsigned __int64 tot = 0;
+ unsigned __int64 startCycles;
+ unsigned __int64 endCycles;
+ const int N = 1000;
+ for (int i = 0; i < N; i++)
+ {
+ QueryThreadCycleTime(GetCurrentThread(), &startCycles);
+ QueryThreadCycleTime(GetCurrentThread(), &endCycles);
+ tot += (endCycles-startCycles);
+ }
+ return tot/N;
+} \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/cycletimer.h b/src/ToolBox/superpmi/superpmi/cycletimer.h
new file mode 100644
index 0000000000..9ad855501a
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/cycletimer.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _CycleTimer
+#define _CycleTimer
+
+#include "errorhandling.h"
+
+class CycleTimer
+{
+public:
+ CycleTimer();
+ ~CycleTimer();
+
+ void Start();
+ void Stop();
+ unsigned __int64 GetCycles();
+ unsigned __int64 QueryOverhead();
+
+private:
+ // Cycles
+ unsigned __int64 start;
+ unsigned __int64 stop;
+ unsigned __int64 overhead;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/filecache.cpp b/src/ToolBox/superpmi/superpmi/filecache.cpp
new file mode 100644
index 0000000000..dcd783a09b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/filecache.cpp
@@ -0,0 +1,150 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// TODO-Cleanup: this class is unused
+
+#include "standardpch.h"
+#include "filecache.h"
+
+#define FileCacheSize 0xFFFFFF //needs to be bigger than the biggest read request.
+
+HANDLE FileCache::cachedHandle;
+bool FileCache::openAsCache;
+BYTE *FileCache::rawBuff;
+unsigned int FileCache::offset;
+unsigned int FileCache::length;
+__int64 FileCache::fileoffset;
+
+HANDLE
+FileCache::CreateFileA(
+ _In_ LPCSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ )
+{
+ openAsCache = false;
+ rawBuff = nullptr;
+ offset = 0;
+ length = 0;
+
+ if((dwShareMode&CACHE_THIS_FILE)==CACHE_THIS_FILE)
+ {
+ dwShareMode ^= CACHE_THIS_FILE;
+ openAsCache = true;
+ }
+ HANDLE temp = ::CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+ if(openAsCache)
+ cachedHandle = temp; //yes yes.. this is unsafe.. but one accessor now is okay. bswhack
+ return temp;
+}
+
+HANDLE
+FileCache::CreateFileW(
+ _In_ LPCWSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ )
+{
+ openAsCache = false;
+ rawBuff = nullptr;
+ offset = 0;
+ length = 0;
+ return ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+}
+
+
+//Somewhat sloppy quick copy... we don't treat lpNumberOfBytesRead etc correctly
+BOOL
+FileCache::ReadFile(
+ _In_ HANDLE hFile,
+ _Out_writes_bytes_to_opt_(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToRead,
+ _Out_opt_ LPDWORD lpNumberOfBytesRead,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ )
+{
+ if(!openAsCache)
+ return ::ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
+ else
+ {
+ if(rawBuff == nullptr)
+ {
+ rawBuff = new BYTE[FileCacheSize];
+ length = FileCacheSize;
+ offset = FileCacheSize;
+ }
+ if(nNumberOfBytesToRead > FileCacheSize)
+ {
+ printf("ERROR: nNumberOfBytesToRead exceeds FileCacheSize %u > %u\n", nNumberOfBytesToRead, FileCacheSize);
+ __debugbreak();
+ }
+ if((offset+nNumberOfBytesToRead) > length)
+ {
+ memmove(rawBuff, &rawBuff[offset], length-offset); //Use memmove since we have overlapping regions more than half the time
+ ::ReadFile(hFile, &rawBuff[length-offset], offset, lpNumberOfBytesRead, lpOverlapped);
+ if(*lpNumberOfBytesRead ==0)
+ __debugbreak();
+ length -= offset-(*lpNumberOfBytesRead);
+ LARGE_INTEGER DataTemp;
+ LARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ ::SetFilePointerEx(hFile, zero, &DataTemp, FILE_CURRENT);
+ fileoffset = DataTemp.QuadPart;
+
+ offset = 0;
+ }
+ memcpy(lpBuffer, &rawBuff[offset], nNumberOfBytesToRead);
+ offset+=nNumberOfBytesToRead;
+ if(offset > FileCacheSize)
+ __debugbreak();
+ return true;
+ }
+}
+
+BOOL
+FileCache::WriteFile(
+ _In_ HANDLE hFile,
+ _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToWrite,
+ _Out_opt_ LPDWORD lpNumberOfBytesWritten,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ )
+{
+ if(!openAsCache)
+ return ::WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
+ else
+ {
+ printf("ERROR: We only support one file open via the cache.\n");
+ __debugbreak();
+ return false;
+ }
+}
+
+BOOL
+FileCache::CloseHandle(
+ _In_ HANDLE hObject
+ )
+{
+ if(!openAsCache)
+ return ::CloseHandle(hObject);
+ else
+ {
+ if(rawBuff!=nullptr)
+ delete []rawBuff;
+ return ::CloseHandle(hObject);
+ }
+}
+__int64 FileCache::GetFilePos(HANDLE hFile)
+{
+ return (fileoffset - (__int64)length) + (__int64)offset;
+}
diff --git a/src/ToolBox/superpmi/superpmi/filecache.h b/src/ToolBox/superpmi/superpmi/filecache.h
new file mode 100644
index 0000000000..51df5edde9
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/filecache.h
@@ -0,0 +1,78 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// TODO-Cleanup: this class is unused
+
+//----------------------------------------------------------
+// FileCache.h - very simple read ahead/ReadFile abstraction
+//----------------------------------------------------------
+#ifndef _FileCache
+#define _FileCache
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define CACHE_THIS_FILE 0xFF
+
+class FileCache
+{
+public:
+
+static HANDLE
+CreateFileA(
+ _In_ LPCSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ );
+
+static HANDLE
+CreateFileW(
+ _In_ LPCWSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ );
+
+static BOOL
+ReadFile(
+ _In_ HANDLE hFile,
+ _Out_writes_bytes_to_opt_(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToRead,
+ _Out_opt_ LPDWORD lpNumberOfBytesRead,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ );
+
+static BOOL
+WriteFile(
+ _In_ HANDLE hFile,
+ _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
+ _In_ DWORD nNumberOfBytesToWrite,
+ _Out_opt_ LPDWORD lpNumberOfBytesWritten,
+ _Inout_opt_ LPOVERLAPPED lpOverlapped
+ );
+
+static BOOL
+CloseHandle(
+ _In_ HANDLE hObject
+ );
+
+static __int64 GetFilePos(HANDLE hFile);
+
+private:
+ static HANDLE cachedHandle;
+ static bool openAsCache;
+ static BYTE *rawBuff;
+ static unsigned int offset;
+ static unsigned int length;
+ static __int64 fileoffset;
+};
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
new file mode 100644
index 0000000000..41b0195a6d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
@@ -0,0 +1,2044 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "icorjitinfo.h"
+#include "jitdebugger.h"
+#include "spmiutil.h"
+
+ICorJitInfo *pICJI = nullptr;
+
+ICorJitInfo* InitICorJitInfo(JitInstance *jitInstance)
+{
+ MyICJI *icji = new MyICJI();
+ icji->jitInstance = jitInstance;
+ pICJI = icji;
+ return icji;
+}
+
+//Stuff on ICorStaticInfo
+/**********************************************************************************/
+//
+// ICorMethodInfo
+//
+/**********************************************************************************/
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD MyICJI::getMethodAttribs (CORINFO_METHOD_HANDLE ftn /* IN */)
+{
+ jitInstance->mc->cr->AddCall("getMethodAttribs");
+ return jitInstance->mc->repGetMethodAttribs(ftn);
+}
+
+// sets private JIT flags, which can be, retrieved using getAttrib.
+void MyICJI::setMethodAttribs (CORINFO_METHOD_HANDLE ftn,/* IN */
+ CorInfoMethodRuntimeFlags attribs/* IN */)
+{
+ jitInstance->mc->cr->AddCall("setMethodAttribs");
+ jitInstance->mc->cr->recSetMethodAttribs(ftn, attribs);
+}
+
+
+// Given a method descriptor ftnHnd, extract signature information into sigInfo
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+void MyICJI::getMethodSig (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_SIG_INFO *sig, /* OUT */
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodSig");
+ jitInstance->mc->repGetMethodSig(ftn, sig, memberParent);
+}
+
+
+ /*********************************************************************
+ * Note the following methods can only be used on functions known
+ * to be IL. This includes the method being compiled and any method
+ * that 'getMethodInfo' returns true for
+ *********************************************************************/
+
+ // return information about a method private to the implementation
+ // returns false if method is not IL, or is otherwise unavailable.
+ // This method is used to fetch data needed to inline functions
+ bool MyICJI::getMethodInfo (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_METHOD_INFO* info /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodInfo");
+ DWORD exceptionCode = 0;
+ bool value = jitInstance->mc->repGetMethodInfo(ftn, info, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return value;
+}
+
+// Decides if you have any limitations for inlining. If everything's OK, it will return
+// INLINE_PASS and will fill out pRestrictions with a mask of restrictions the caller of this
+// function must respect. If caller passes pRestrictions = nullptr, if there are any restrictions
+// INLINE_FAIL will be returned
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+//
+// The inlined method need not be verified
+
+CorInfoInline MyICJI::canInline (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE calleeHnd, /* IN */
+ DWORD* pRestrictions /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("canInline");
+
+ DWORD exceptionCode = 0;
+ CorInfoInline result = jitInstance->mc->repCanInline(callerHnd, calleeHnd, pRestrictions, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return result;
+}
+
+// Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all
+// inlining results when it returns INLINE_FAIL and INLINE_NEVER. All other results are reported by the
+// JIT.
+void MyICJI::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd,
+ CorInfoInline inlineResult,
+ const char * reason)
+{
+ jitInstance->mc->cr->AddCall("reportInliningDecision");
+ jitInstance->mc->cr->recReportInliningDecision(inlinerHnd, inlineeHnd, inlineResult, reason);
+}
+
+
+// Returns false if the call is across security boundaries thus we cannot tailcall
+//
+// The callerHnd must be the immediate caller (i.e. when we have a chain of inlined calls)
+bool MyICJI::canTailCall (
+ CORINFO_METHOD_HANDLE callerHnd, /* IN */
+ CORINFO_METHOD_HANDLE declaredCalleeHnd, /* IN */
+ CORINFO_METHOD_HANDLE exactCalleeHnd, /* IN */
+ bool fIsTailPrefix /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("canTailCall");
+ return jitInstance->mc->repCanTailCall(callerHnd, declaredCalleeHnd, exactCalleeHnd, fIsTailPrefix);
+}
+
+// Reports whether or not a method can be tail called, and why.
+// canTailCall is responsible for reporting all results when it returns
+// false. All other results are reported by the JIT.
+void MyICJI::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd,
+ CORINFO_METHOD_HANDLE calleeHnd,
+ bool fIsTailPrefix,
+ CorInfoTailCall tailCallResult,
+ const char * reason)
+{
+ jitInstance->mc->cr->AddCall("reportTailCallDecision");
+ jitInstance->mc->cr->recReportTailCallDecision(callerHnd, calleeHnd, fIsTailPrefix, tailCallResult, reason);
+}
+
+// get individual exception handler
+void MyICJI::getEHinfo(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ unsigned EHnumber, /* IN */
+ CORINFO_EH_CLAUSE* clause /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getEHinfo");
+ jitInstance->mc->repGetEHinfo(ftn, EHnumber, clause);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE MyICJI::getMethodClass (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodClass");
+ return jitInstance->mc->repGetMethodClass(method);
+}
+
+// return module it belongs to
+CORINFO_MODULE_HANDLE MyICJI::getMethodModule (
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodModule");
+ LogError("Hit unimplemented getMethodModule");
+ DebugBreakorAV(7);
+ return 0;
+}
+
+// This function returns the offset of the specified method in the
+// vtable of it's owning class or interface.
+void MyICJI::getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+ unsigned* offsetAfterIndirection /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodVTableOffset");
+ jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+}
+
+// If a method's attributes have (getMethodAttribs) CORINFO_FLG_INTRINSIC set,
+// getIntrinsicID() returns the intrinsic ID.
+CorInfoIntrinsics MyICJI::getIntrinsicID(
+ CORINFO_METHOD_HANDLE method,
+ bool* pMustExpand /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getIntrinsicID");
+ return jitInstance->mc->repGetIntrinsicID(method, pMustExpand);
+}
+
+// Is the given module the System.Numerics.Vectors module?
+bool MyICJI::isInSIMDModule(
+ CORINFO_CLASS_HANDLE classHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("isInSIMDModule");
+ return jitInstance->mc->repIsInSIMDModule(classHnd) ? true : false;
+}
+
+// return the unmanaged calling convention for a PInvoke
+CorInfoUnmanagedCallConv MyICJI::getUnmanagedCallConv(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("getUnmanagedCallConv");
+ return jitInstance->mc->repGetUnmanagedCallConv(method);
+}
+
+// return if any marshaling is required for PInvoke methods. Note that
+// method == 0 => calli. The call site sig is only needed for the varargs or calli case
+BOOL MyICJI::pInvokeMarshalingRequired(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_SIG_INFO* callSiteSig
+ )
+{
+ jitInstance->mc->cr->AddCall("pInvokeMarshalingRequired");
+ return jitInstance->mc->repPInvokeMarshalingRequired(method, callSiteSig);
+}
+
+// Check constraints on method type arguments (only).
+// The parent class should be checked separately using satisfiesClassConstraints(parent).
+BOOL MyICJI::satisfiesMethodConstraints(
+ CORINFO_CLASS_HANDLE parent, // the exact parent of the method
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("satisfiesMethodConstraints");
+ return jitInstance->mc->repSatisfiesMethodConstraints(parent, method);
+}
+
+// Given a delegate target class, a target method parent class, a target method,
+// a delegate class, check if the method signature is compatible with the Invoke method of the delegate
+// (under the typical instantiation of any free type variables in the memberref signatures).
+BOOL MyICJI::isCompatibleDelegate(
+ CORINFO_CLASS_HANDLE objCls, /* type of the delegate target, if any */
+ CORINFO_CLASS_HANDLE methodParentCls, /* exact parent of the target method, if any */
+ CORINFO_METHOD_HANDLE method, /* (representative) target method, if any */
+ CORINFO_CLASS_HANDLE delegateCls, /* exact type of the delegate */
+ BOOL *pfIsOpenDelegate /* is the delegate open */
+ )
+{
+ jitInstance->mc->cr->AddCall("isCompatibleDelegate");
+ return jitInstance->mc->repIsCompatibleDelegate(objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate);
+}
+
+// Determines whether the delegate creation obeys security transparency rules
+BOOL MyICJI::isDelegateCreationAllowed (
+ CORINFO_CLASS_HANDLE delegateHnd,
+ CORINFO_METHOD_HANDLE calleeHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("isDelegateCreationAllowed");
+ return jitInstance->mc->repIsDelegateCreationAllowed(delegateHnd, calleeHnd);
+}
+
+
+// Indicates if the method is an instance of the generic
+// method that passes (or has passed) verification
+CorInfoInstantiationVerification MyICJI::isInstantiationOfVerifiedGeneric (
+ CORINFO_METHOD_HANDLE method /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("isInstantiationOfVerifiedGeneric");
+ return jitInstance->mc->repIsInstantiationOfVerifiedGeneric(method);
+}
+
+// Loads the constraints on a typical method definition, detecting cycles;
+// for use in verification.
+void MyICJI::initConstraintsForVerification(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ BOOL *pfHasCircularClassConstraints, /* OUT */
+ BOOL *pfHasCircularMethodConstraint /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("initConstraintsForVerification");
+ jitInstance->mc->repInitConstraintsForVerification(method, pfHasCircularClassConstraints, pfHasCircularMethodConstraint);
+}
+
+// Returns enum whether the method does not require verification
+// Also see ICorModuleInfo::canSkipVerification
+CorInfoCanSkipVerificationResult MyICJI::canSkipMethodVerification (
+ CORINFO_METHOD_HANDLE ftnHandle
+ )
+{
+ jitInstance->mc->cr->AddCall("canSkipMethodVerification");
+ return jitInstance->mc->repCanSkipMethodVerification(ftnHandle, FALSE);
+}
+
+// load and restore the method
+void MyICJI::methodMustBeLoadedBeforeCodeIsRun(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("methodMustBeLoadedBeforeCodeIsRun");
+ jitInstance->mc->cr->recMethodMustBeLoadedBeforeCodeIsRun(method);
+}
+
+CORINFO_METHOD_HANDLE MyICJI::mapMethodDeclToMethodImpl(
+ CORINFO_METHOD_HANDLE method
+ )
+{
+ jitInstance->mc->cr->AddCall("mapMethodDeclToMethodImpl");
+ LogError("Hit unimplemented mapMethodDeclToMethodImpl");
+ DebugBreakorAV(17);
+ return 0;
+}
+
+// Returns the global cookie for the /GS unsafe buffer checks
+// The cookie might be a constant value (JIT), or a handle to memory location (Ngen)
+void MyICJI::getGSCookie(
+ GSCookie * pCookieVal, // OUT
+ GSCookie ** ppCookieVal // OUT
+ )
+{
+ jitInstance->mc->cr->AddCall("getGSCookie");
+ jitInstance->mc->repGetGSCookie(pCookieVal, ppCookieVal);
+}
+
+/**********************************************************************************/
+//
+// ICorModuleInfo
+//
+/**********************************************************************************/
+
+// Resolve metadata token into runtime method handles.
+void MyICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ DWORD exceptionCode = 0;
+ jitInstance->mc->cr->AddCall("resolveToken");
+ jitInstance->mc->repResolveToken(pResolvedToken, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+}
+
+// Resolve metadata token into runtime method handles.
+bool MyICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
+{
+ jitInstance->mc->cr->AddCall("tryResolveToken");
+ return jitInstance->mc->repTryResolveToken(pResolvedToken);
+}
+
+// Signature information about the call sig
+void MyICJI::findSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned sigTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("findSig");
+ jitInstance->mc->repFindSig(module, sigTOK, context, sig);
+}
+
+// for Varargs, the signature at the call site may differ from
+// the signature at the definition. Thus we need a way of
+// fetching the call site information
+void MyICJI::findCallSiteSig (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned methTOK, /* IN */
+ CORINFO_CONTEXT_HANDLE context, /* IN */
+ CORINFO_SIG_INFO *sig /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("findCallSiteSig");
+ jitInstance->mc->repFindCallSiteSig(module, methTOK, context, sig);
+}
+
+CORINFO_CLASS_HANDLE MyICJI::getTokenTypeAsHandle (
+ CORINFO_RESOLVED_TOKEN * pResolvedToken /* IN */)
+{
+ jitInstance->mc->cr->AddCall("getTokenTypeAsHandle");
+ return jitInstance->mc->repGetTokenTypeAsHandle(pResolvedToken);
+}
+
+// Returns true if the module does not require verification
+//
+// If fQuickCheckOnlyWithoutCommit=TRUE, the function only checks that the
+// module does not currently require verification in the current AppDomain.
+// This decision could change in the future, and so should not be cached.
+// If it is cached, it should only be used as a hint.
+// This is only used by ngen for calculating certain hints.
+//
+
+// Returns enum whether the module does not require verification
+// Also see ICorMethodInfo::canSkipMethodVerification();
+CorInfoCanSkipVerificationResult MyICJI::canSkipVerification (
+ CORINFO_MODULE_HANDLE module /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("canSkipVerification");
+ LogError("Hit unimplemented canSkipVerification");
+ DebugBreakorAV(22);
+ return CORINFO_VERIFICATION_CANNOT_SKIP;
+}
+
+// Checks if the given metadata token is valid
+BOOL MyICJI::isValidToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("isValidToken");
+ return jitInstance->mc->repIsValidToken(module, metaTOK);
+}
+
+// Checks if the given metadata token is valid StringRef
+BOOL MyICJI::isValidStringRef (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ unsigned metaTOK /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("isValidStringRef");
+ return jitInstance->mc->repIsValidStringRef(module, metaTOK);
+}
+
+BOOL MyICJI::shouldEnforceCallvirtRestriction(
+ CORINFO_MODULE_HANDLE scope
+ )
+{
+ jitInstance->mc->cr->AddCall("shouldEnforceCallvirtRestriction");
+ return jitInstance->mc->repShouldEnforceCallvirtRestriction(scope);
+}
+
+/**********************************************************************************/
+//
+// ICorClassInfo
+//
+/**********************************************************************************/
+
+// If the value class 'cls' is isomorphic to a primitive type it will
+// return that type, otherwise it will return CORINFO_TYPE_VALUECLASS
+CorInfoType MyICJI::asCorInfoType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("asCorInfoType");
+ return jitInstance->mc->repAsCorInfoType(cls);
+}
+
+// for completeness
+const char* MyICJI::getClassName (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassName");
+ const char* result = jitInstance->mc->repGetClassName(cls);
+ return result;
+}
+
+
+// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
+// If fNamespace=TRUE, include the namespace/enclosing classes
+// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
+// If fAssembly=TRUE, suffix with a comma and the full assembly qualification
+// return size of representation
+int MyICJI::appendClassName(
+ __deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fNamespace,
+ BOOL fFullInst,
+ BOOL fAssembly
+ )
+{
+ jitInstance->mc->cr->AddCall("appendClassName");
+ const WCHAR* result = jitInstance->mc->repAppendClassName(cls, fNamespace, fFullInst, fAssembly);
+ int nLen = 0;
+ if (ppBuf != nullptr && result != nullptr)
+ {
+ nLen = (int)wcslen(result);
+ if (*pnBufLen > nLen)
+ {
+ wcscpy_s(*ppBuf, *pnBufLen, result );
+ (*ppBuf) += nLen;
+ (*pnBufLen) -= nLen;
+ }
+ }
+ return nLen;
+}
+
+// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
+BOOL MyICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("isValueClass");
+ return jitInstance->mc->repIsValueClass(cls);
+}
+
+// If this method returns true, JIT will do optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType()
+BOOL MyICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("canInlineTypeCheckWithObjectVTable");
+ return jitInstance->mc->repCanInlineTypeCheckWithObjectVTable(cls);
+}
+
+// return flags (defined above, CORINFO_FLG_PUBLIC ...)
+DWORD MyICJI::getClassAttribs (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassAttribs");
+ return jitInstance->mc->repGetClassAttribs(cls);
+}
+
+// Returns "TRUE" iff "cls" is a struct type such that return buffers used for returning a value
+// of this type must be stack-allocated. This will generally be true only if the struct
+// contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
+// an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
+// returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
+// buffers do not require GC write barriers.
+BOOL MyICJI::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("isStructRequiringStackAllocRetBuf");
+ return jitInstance->mc->repIsStructRequiringStackAllocRetBuf(cls);
+}
+
+CORINFO_MODULE_HANDLE MyICJI::getClassModule (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassModule");
+ LogError("Hit unimplemented getClassModule");
+ DebugBreakorAV(28);
+ return (CORINFO_MODULE_HANDLE)0;
+ //jitInstance->mc->getClassModule(cls);
+}
+
+// Returns the assembly that contains the module "mod".
+CORINFO_ASSEMBLY_HANDLE MyICJI::getModuleAssembly (
+ CORINFO_MODULE_HANDLE mod
+ )
+{
+ jitInstance->mc->cr->AddCall("getModuleAssembly");
+ LogError("Hit unimplemented getModuleAssembly");
+ DebugBreakorAV(28);
+ return (CORINFO_ASSEMBLY_HANDLE)0;
+
+// return jitInstance->mc->getModuleAssembly(mod);
+}
+
+// Returns the name of the assembly "assem".
+const char* MyICJI::getAssemblyName (
+ CORINFO_ASSEMBLY_HANDLE assem
+ )
+{
+ jitInstance->mc->cr->AddCall("getAssemblyName");
+ LogError("Hit unimplemented getAssemblyName");
+ DebugBreakorAV(28);
+ return nullptr;
+// return jitInstance->mc->getAssemblyName(assem);
+}
+
+// Allocate and delete process-lifetime objects. Should only be
+// referred to from static fields, lest a leak occur.
+// Note that "LongLifetimeFree" does not execute destructors, if "obj"
+// is an array of a struct type with a destructor.
+void* MyICJI::LongLifetimeMalloc(size_t sz)
+{
+ jitInstance->mc->cr->AddCall("LongLifetimeMalloc");
+ LogError("Hit unimplemented LongLifetimeMalloc");
+ DebugBreakorAV(32);
+ return 0;
+}
+
+void MyICJI::LongLifetimeFree(void* obj)
+{
+ jitInstance->mc->cr->AddCall("LongLifetimeFree");
+ LogError("Hit unimplemented LongLifetimeFree");
+ DebugBreakorAV(33);
+}
+
+size_t MyICJI::getClassModuleIdForStatics (
+ CORINFO_CLASS_HANDLE cls,
+ CORINFO_MODULE_HANDLE *pModule,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassModuleIdForStatics");
+ return jitInstance->mc->repGetClassModuleIdForStatics(cls, pModule, ppIndirection);
+}
+
+// return the number of bytes needed by an instance of the class
+unsigned MyICJI::getClassSize (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassSize");
+ return jitInstance->mc->repGetClassSize(cls);
+}
+
+unsigned MyICJI::getClassAlignmentRequirement (
+ CORINFO_CLASS_HANDLE cls,
+ BOOL fDoubleAlignHint
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassAlignmentRequirement");
+ return jitInstance->mc->repGetClassAlignmentRequirement(cls, fDoubleAlignHint);
+}
+
+// This is only called for Value classes. It returns a boolean array
+// in representing of 'cls' from a GC perspective. The class is
+// assumed to be an array of machine words
+// (of length // getClassSize(cls) / sizeof(void*)),
+// 'gcPtrs' is a poitner to an array of BYTEs of this length.
+// getClassGClayout fills in this array so that gcPtrs[i] is set
+// to one of the CorInfoGCType values which is the GC type of
+// the i-th machine word of an object of type 'cls'
+// returns the number of GC pointers in the array
+unsigned MyICJI::getClassGClayout (
+ CORINFO_CLASS_HANDLE cls, /* IN */
+ BYTE *gcPtrs /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassGClayout");
+ return jitInstance->mc->repGetClassGClayout(cls, gcPtrs);
+}
+
+// returns the number of instance fields in a class
+unsigned MyICJI::getClassNumInstanceFields (
+ CORINFO_CLASS_HANDLE cls /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassNumInstanceFields");
+ return jitInstance->mc->repGetClassNumInstanceFields(cls);
+}
+
+CORINFO_FIELD_HANDLE MyICJI::getFieldInClass(
+ CORINFO_CLASS_HANDLE clsHnd,
+ INT num
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldInClass");
+ return jitInstance->mc->repGetFieldInClass(clsHnd, num);
+}
+
+BOOL MyICJI::checkMethodModifier(
+ CORINFO_METHOD_HANDLE hMethod,
+ LPCSTR modifier,
+ BOOL fOptional
+ )
+{
+ jitInstance->mc->cr->AddCall("checkMethodModifier");
+ BOOL result = jitInstance->mc->repCheckMethodModifier(hMethod, modifier, fOptional);
+ return result;
+}
+
+// returns the "NEW" helper optimized for "newCls."
+CorInfoHelpFunc MyICJI::getNewHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle
+ )
+{
+ jitInstance->mc->cr->AddCall("getNewHelper");
+ return jitInstance->mc->repGetNewHelper(pResolvedToken, callerHandle);
+}
+
+// returns the newArr (1-Dim array) helper optimized for "arrayCls."
+CorInfoHelpFunc MyICJI::getNewArrHelper(
+ CORINFO_CLASS_HANDLE arrayCls
+ )
+{
+ jitInstance->mc->cr->AddCall("getNewArrHelper");
+ return jitInstance->mc->repGetNewArrHelper(arrayCls);
+}
+
+// returns the optimized "IsInstanceOf" or "ChkCast" helper
+CorInfoHelpFunc MyICJI::getCastingHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ bool fThrowing
+ )
+{
+ jitInstance->mc->cr->AddCall("getCastingHelper");
+ return jitInstance->mc->repGetCastingHelper(pResolvedToken, fThrowing);
+}
+
+// returns helper to trigger static constructor
+CorInfoHelpFunc MyICJI::getSharedCCtorHelper(
+ CORINFO_CLASS_HANDLE clsHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("getSharedCCtorHelper");
+ return jitInstance->mc->repGetSharedCCtorHelper(clsHnd);
+}
+
+CorInfoHelpFunc MyICJI::getSecurityPrologHelper(
+ CORINFO_METHOD_HANDLE ftn
+ )
+{
+ jitInstance->mc->cr->AddCall("getSecurityPrologHelper");
+ return jitInstance->mc->repGetSecurityPrologHelper(ftn);
+}
+
+// This is not pretty. Boxing nullable<T> actually returns
+// a boxed<T> not a boxed Nullable<T>. This call allows the verifier
+// to call back to the EE on the 'box' instruction and get the transformed
+// type to use for verification.
+CORINFO_CLASS_HANDLE MyICJI::getTypeForBox(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getTypeForBox");
+ return jitInstance->mc->repGetTypeForBox(cls);
+}
+
+// returns the correct box helper for a particular class. Note
+// that if this returns CORINFO_HELP_BOX, the JIT can assume
+// 'standard' boxing (allocate object and copy), and optimize
+CorInfoHelpFunc MyICJI::getBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getBoxHelper");
+ return jitInstance->mc->repGetBoxHelper(cls);
+}
+
+// returns the unbox helper. If 'helperCopies' points to a true
+// value it means the JIT is requesting a helper that unboxes the
+// value into a particular location and thus has the signature
+// void unboxHelper(void* dest, CORINFO_CLASS_HANDLE cls, Object* obj)
+// Otherwise (it is null or points at a FALSE value) it is requesting
+// a helper that returns a poitner to the unboxed data
+// void* unboxHelper(CORINFO_CLASS_HANDLE cls, Object* obj)
+// The EE has the option of NOT returning the copy style helper
+// (But must be able to always honor the non-copy style helper)
+// The EE set 'helperCopies' on return to indicate what kind of
+// helper has been created.
+
+CorInfoHelpFunc MyICJI::getUnBoxHelper(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getUnBoxHelper");
+ CorInfoHelpFunc result = jitInstance->mc->repGetUnBoxHelper(cls);
+ return result;
+}
+
+bool MyICJI::getReadyToRunHelper(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_LOOKUP_KIND * pGenericLookupKind,
+ CorInfoHelpFunc id,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ jitInstance->mc->cr->AddCall("getReadyToRunHelper");
+ return jitInstance->mc->repGetReadyToRunHelper(pResolvedToken, pGenericLookupKind, id, pLookup);
+}
+
+void MyICJI::getReadyToRunDelegateCtorHelper(
+ CORINFO_RESOLVED_TOKEN * pTargetMethod,
+ CORINFO_CLASS_HANDLE delegateType,
+ CORINFO_CONST_LOOKUP * pLookup
+ )
+{
+ jitInstance->mc->cr->AddCall("getReadyToRunDelegateCtorHelper");
+ jitInstance->mc->repGetReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
+}
+
+const char* MyICJI::getHelperName(
+ CorInfoHelpFunc funcNum
+ )
+{
+ jitInstance->mc->cr->AddCall("getHelperName");
+ return jitInstance->mc->repGetHelperName(funcNum);
+}
+
+// This function tries to initialize the class (run the class constructor).
+// this function returns whether the JIT must insert helper calls before
+// accessing static field or method.
+//
+// See code:ICorClassInfo#ClassConstruction.
+CorInfoInitClassResult MyICJI::initClass(
+ CORINFO_FIELD_HANDLE field, // Non-nullptr - inquire about cctor trigger before static field access
+ // nullptr - inquire about cctor trigger in method prolog
+ CORINFO_METHOD_HANDLE method, // Method referencing the field or prolog
+ CORINFO_CONTEXT_HANDLE context, // Exact context of method
+ BOOL speculative // TRUE means don't actually run it
+ )
+{
+ jitInstance->mc->cr->AddCall("initClass");
+ return jitInstance->mc->repInitClass(field, method, context, speculative);
+}
+
+// This used to be called "loadClass". This records the fact
+// that the class must be loaded (including restored if necessary) before we execute the
+// code that we are currently generating. When jitting code
+// the function loads the class immediately. When zapping code
+// the zapper will if necessary use the call to record the fact that we have
+// to do a fixup/restore before running the method currently being generated.
+//
+// This is typically used to ensure value types are loaded before zapped
+// code that manipulates them is executed, so that the GC can access information
+// about those value types.
+void MyICJI::classMustBeLoadedBeforeCodeIsRun(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("classMustBeLoadedBeforeCodeIsRun");
+ jitInstance->mc->cr->recClassMustBeLoadedBeforeCodeIsRun(cls);
+}
+
+// returns the class handle for the special builtin classes
+CORINFO_CLASS_HANDLE MyICJI::getBuiltinClass (
+ CorInfoClassId classId
+ )
+{
+ jitInstance->mc->cr->AddCall("getBuiltinClass");
+ return jitInstance->mc->repGetBuiltinClass(classId);
+}
+
+// "System.Int32" ==> CORINFO_TYPE_INT..
+CorInfoType MyICJI::getTypeForPrimitiveValueClass(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getTypeForPrimitiveValueClass");
+ return jitInstance->mc->repGetTypeForPrimitiveValueClass(cls);
+}
+
+// TRUE if child is a subtype of parent
+// if parent is an interface, then does child implement / extend parent
+BOOL MyICJI::canCast(
+ CORINFO_CLASS_HANDLE child, // subtype (extends parent)
+ CORINFO_CLASS_HANDLE parent // base type
+ )
+{
+ jitInstance->mc->cr->AddCall("canCast");
+ return jitInstance->mc->repCanCast(child, parent);
+}
+
+// TRUE if cls1 and cls2 are considered equivalent types.
+BOOL MyICJI::areTypesEquivalent(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ jitInstance->mc->cr->AddCall("areTypesEquivalent");
+ return jitInstance->mc->repAreTypesEquivalent(cls1, cls2);
+}
+
+// returns is the intersection of cls1 and cls2.
+CORINFO_CLASS_HANDLE MyICJI::mergeClasses(
+ CORINFO_CLASS_HANDLE cls1,
+ CORINFO_CLASS_HANDLE cls2
+ )
+{
+ jitInstance->mc->cr->AddCall("mergeClasses");
+ return jitInstance->mc->repMergeClasses(cls1, cls2);
+}
+
+// Given a class handle, returns the Parent type.
+// For COMObjectType, it returns Class Handle of System.Object.
+// Returns 0 if System.Object is passed in.
+CORINFO_CLASS_HANDLE MyICJI::getParentType (
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getParentType");
+ return jitInstance->mc->repGetParentType(cls);
+}
+
+// Returns the CorInfoType of the "child type". If the child type is
+// not a primitive type, *clsRet will be set.
+// Given an Array of Type Foo, returns Foo.
+// Given BYREF Foo, returns Foo
+CorInfoType MyICJI::getChildType (
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_CLASS_HANDLE *clsRet
+ )
+{
+ jitInstance->mc->cr->AddCall("getChildType");
+ return jitInstance->mc->repGetChildType(clsHnd, clsRet);
+}
+
+// Check constraints on type arguments of this class and parent classes
+BOOL MyICJI::satisfiesClassConstraints(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("satisfiesClassConstraints");
+ return jitInstance->mc->repSatisfiesClassConstraints(cls);
+}
+
+// Check if this is a single dimensional array type
+BOOL MyICJI::isSDArray(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("isSDArray");
+ return jitInstance->mc->repIsSDArray(cls);
+}
+
+// Get the numbmer of dimensions in an array
+unsigned MyICJI::getArrayRank(
+ CORINFO_CLASS_HANDLE cls
+ )
+{
+ jitInstance->mc->cr->AddCall("getArrayRank");
+ return jitInstance->mc->repGetArrayRank(cls);
+}
+
+// Get static field data for an array
+void * MyICJI::getArrayInitializationData(
+ CORINFO_FIELD_HANDLE field,
+ DWORD size
+ )
+{
+ jitInstance->mc->cr->AddCall("getArrayInitializationData");
+ return jitInstance->mc->repGetArrayInitializationData(field, size);
+}
+
+// Check Visibility rules.
+CorInfoIsAccessAllowedResult MyICJI::canAccessClass(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_HELPER_DESC *pAccessHelper /* If canAccessMethod returns something other
+ than ALLOWED, then this is filled in. */
+ )
+{
+ jitInstance->mc->cr->AddCall("canAccessClass");
+ return jitInstance->mc->repCanAccessClass(pResolvedToken, callerHandle, pAccessHelper);
+}
+
+/**********************************************************************************/
+//
+// ICorFieldInfo
+//
+/**********************************************************************************/
+
+// this function is for debugging only. It returns the field name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* MyICJI::getFieldName (
+ CORINFO_FIELD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldName");
+ return jitInstance->mc->repGetFieldName(ftn, moduleName);
+}
+
+// return class it belongs to
+CORINFO_CLASS_HANDLE MyICJI::getFieldClass (
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldClass");
+ return jitInstance->mc->repGetFieldClass(field);
+}
+
+// Return the field's type, if it is CORINFO_TYPE_VALUECLASS 'structType' is set
+// the field's value class (if 'structType' == 0, then don't bother
+// the structure info).
+//
+// 'memberParent' is typically only set when verifying. It should be the
+// result of calling getMemberParent.
+CorInfoType MyICJI::getFieldType(
+ CORINFO_FIELD_HANDLE field,
+ CORINFO_CLASS_HANDLE *structType,
+ CORINFO_CLASS_HANDLE memberParent/* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldType");
+ return jitInstance->mc->repGetFieldType(field, structType, memberParent);
+}
+
+// return the data member's instance offset
+unsigned MyICJI::getFieldOffset(
+ CORINFO_FIELD_HANDLE field
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldOffset");
+ return jitInstance->mc->repGetFieldOffset(field);
+}
+
+// TODO: jit64 should be switched to the same plan as the i386 jits - use
+// getClassGClayout to figure out the need for writebarrier helper, and inline the copying.
+// The interpretted value class copy is slow. Once this happens, USE_WRITE_BARRIER_HELPERS
+bool MyICJI::isWriteBarrierHelperRequired(
+ CORINFO_FIELD_HANDLE field)
+{
+ jitInstance->mc->cr->AddCall("isWriteBarrierHelperRequired");
+ bool result = jitInstance->mc->repIsWriteBarrierHelperRequired(field);
+ return result;
+}
+
+void MyICJI::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ CORINFO_METHOD_HANDLE callerHandle,
+ CORINFO_ACCESS_FLAGS flags,
+ CORINFO_FIELD_INFO *pResult
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldInfo");
+ jitInstance->mc->repGetFieldInfo(pResolvedToken, callerHandle, flags, pResult);
+}
+
+// Returns true iff "fldHnd" represents a static field.
+bool MyICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
+{
+ jitInstance->mc->cr->AddCall("isFieldStatic");
+ return jitInstance->mc->repIsFieldStatic(fldHnd);
+}
+
+/*********************************************************************************/
+//
+// ICorDebugInfo
+//
+/*********************************************************************************/
+
+// Query the EE to find out where interesting break points
+// in the code are. The native compiler will ensure that these places
+// have a corresponding break point in native code.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void MyICJI::getBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ unsigned int *cILOffsets, // [OUT] size of pILOffsets
+ DWORD **pILOffsets, // [OUT] IL offsets of interest
+ // jit MUST free with freeArray!
+ ICorDebugInfo::BoundaryTypes *implictBoundaries // [OUT] tell jit, all boundries of this type
+ )
+{
+ jitInstance->mc->cr->AddCall("getBoundaries");
+ jitInstance->mc->repGetBoundaries(ftn, cILOffsets, pILOffsets, implictBoundaries);
+
+ //The JIT will want to call freearray on the array we pass back, so move the data into a form that complies with this
+ if(*cILOffsets > 0)
+ {
+ DWORD *realOffsets = (DWORD*)allocateArray(*cILOffsets*sizeof(ICorDebugInfo::BoundaryTypes));
+ memcpy(realOffsets, *pILOffsets, *cILOffsets*sizeof(ICorDebugInfo::BoundaryTypes));
+ *pILOffsets = realOffsets;
+ }
+ else
+ *pILOffsets = 0;
+}
+
+// Report back the mapping from IL to native code,
+// this map should include all boundaries that 'getBoundaries'
+// reported as interesting to the debugger.
+
+// Note that debugger (and profiler) is assuming that all of the
+// offsets form a contiguous block of memory, and that the
+// OffsetMapping is sorted in order of increasing native offset.
+void MyICJI::setBoundaries(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cMap, // [IN] size of pMap
+ ICorDebugInfo::OffsetMapping *pMap // [IN] map including all points of interest.
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ jitInstance->mc->cr->AddCall("setBoundaries");
+ jitInstance->mc->cr->recSetBoundaries(ftn, cMap, pMap);
+
+ freeArray(pMap);//see note in recSetBoundaries... we own this array and own destroying it.
+}
+
+// Query the EE to find out the scope of local varables.
+// normally the JIT would trash variables after last use, but
+// under debugging, the JIT needs to keep them live over their
+// entire scope so that they can be inspected.
+//
+// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will
+// be used only as a hint and the native compiler should not change its
+// code generation.
+void MyICJI::getVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 *cVars, // [OUT] size of 'vars'
+ ICorDebugInfo::ILVarInfo **vars, // [OUT] scopes of variables of interest
+ // jit MUST free with freeArray!
+ bool *extendOthers // [OUT] it TRUE, then assume the scope
+ // of unmentioned vars is entire method
+ )
+{
+ jitInstance->mc->cr->AddCall("getVars");
+ jitInstance->mc->repGetVars(ftn, cVars, vars, extendOthers);
+
+ //The JIT will want to call freearray on the array we pass back, so move the data into a form that complies with this
+ if(*cVars > 0)
+ {
+ ICorDebugInfo::ILVarInfo *realOffsets = (ICorDebugInfo::ILVarInfo*)allocateArray(*cVars*sizeof(ICorDebugInfo::ILVarInfo));
+ memcpy(realOffsets, *vars, *cVars*sizeof(ICorDebugInfo::ILVarInfo));
+ *vars = realOffsets;
+ }
+ else
+ *vars = nullptr;
+}
+
+// Report back to the EE the location of every variable.
+// note that the JIT might split lifetimes into different
+// locations etc.
+
+void MyICJI::setVars(
+ CORINFO_METHOD_HANDLE ftn, // [IN] method of interest
+ ULONG32 cVars, // [IN] size of 'vars'
+ ICorDebugInfo::NativeVarInfo *vars // [IN] map telling where local vars are stored at what points
+ // jit allocated with allocateArray, EE frees
+ )
+{
+ jitInstance->mc->cr->AddCall("setVars");
+ jitInstance->mc->cr->recSetVars(ftn, cVars, vars);
+ freeArray(vars);//See note in recSetVars... we own destroying this array
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * MyICJI::allocateArray(
+ ULONG cBytes
+ )
+{
+ return jitInstance->allocateArray(cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void MyICJI::freeArray(
+ void *array
+ )
+{
+ jitInstance->freeArray(array);
+}
+
+/*********************************************************************************/
+//
+// ICorArgInfo
+//
+/*********************************************************************************/
+
+// advance the pointer to the argument list.
+// a ptr of 0, is special and always means the first argument
+CORINFO_ARG_LIST_HANDLE MyICJI::getArgNext (
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getArgNext");
+ return jitInstance->mc->repGetArgNext(args);
+}
+
+// Get the type of a particular argument
+// CORINFO_TYPE_UNDEF is returned when there are no more arguments
+// If the type returned is a primitive type (or an enum) *vcTypeRet set to nullptr
+// otherwise it is set to the TypeHandle associted with the type
+// Enumerations will always look their underlying type (probably should fix this)
+// Otherwise vcTypeRet is the type as would be seen by the IL,
+// The return value is the type that is used for calling convention purposes
+// (Thus if the EE wants a value class to be passed like an int, then it will
+// return CORINFO_TYPE_INT
+CorInfoTypeWithMod MyICJI::getArgType (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args, /* IN */
+ CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */
+ )
+{
+ DWORD exceptionCode = 0;
+ jitInstance->mc->cr->AddCall("getArgType");
+ CorInfoTypeWithMod value = jitInstance->mc->repGetArgType(sig, args, vcTypeRet, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return value;
+}
+
+// If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it
+CORINFO_CLASS_HANDLE MyICJI::getArgClass (
+ CORINFO_SIG_INFO* sig, /* IN */
+ CORINFO_ARG_LIST_HANDLE args /* IN */
+ )
+{
+ DWORD exceptionCode = 0;
+ jitInstance->mc->cr->AddCall("getArgClass");
+ CORINFO_CLASS_HANDLE value = jitInstance->mc->repGetArgClass(sig, args, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+ return value;
+}
+
+// Returns type of HFA for valuetype
+CorInfoType MyICJI::getHFAType (
+ CORINFO_CLASS_HANDLE hClass
+ )
+{
+ jitInstance->mc->cr->AddCall("getHFAType");
+ LogError("Hit unimplemented getHFAType");
+ DebugBreakorAV(75);
+ return (CorInfoType)0;
+}
+
+/*****************************************************************************
+* ICorErrorInfo contains methods to deal with SEH exceptions being thrown
+* from the corinfo interface. These methods may be called when an exception
+* with code EXCEPTION_COMPLUS is caught.
+*****************************************************************************/
+
+// Returns the HRESULT of the current exception
+HRESULT MyICJI::GetErrorHRESULT(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ jitInstance->mc->cr->AddCall("GetErrorHRESULT");
+ LogError("Hit unimplemented GetErrorHRESULT");
+ DebugBreakorAV(76);
+ return 0;
+}
+
+// Fetches the message of the current exception
+// Returns the size of the message (including terminating null). This can be
+// greater than bufferLength if the buffer is insufficient.
+ULONG MyICJI::GetErrorMessage(
+ __inout_ecount(bufferLength) LPWSTR buffer,
+ ULONG bufferLength
+ )
+{
+ jitInstance->mc->cr->AddCall("GetErrorMessage");
+ LogError("Hit unimplemented GetErrorMessage");
+ DebugBreakorAV(77);
+ return 0;
+}
+
+// returns EXCEPTION_EXECUTE_HANDLER if it is OK for the compile to handle the
+// exception, abort some work (like the inlining) and continue compilation
+// returns EXCEPTION_CONTINUE_SEARCH if exception must always be handled by the EE
+// things like ThreadStoppedException ...
+// returns EXCEPTION_CONTINUE_EXECUTION if exception is fixed up by the EE
+
+int MyICJI::FilterException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ jitInstance->mc->cr->AddCall("FilterException");
+ int result = jitInstance->mc->repFilterException(pExceptionPointers);
+ return result;
+}
+
+// Cleans up internal EE tracking when an exception is caught.
+void MyICJI::HandleException(
+ struct _EXCEPTION_POINTERS *pExceptionPointers
+ )
+{
+ jitInstance->mc->cr->AddCall("HandleException");
+}
+
+void MyICJI::ThrowExceptionForJitResult(
+ HRESULT result)
+{
+ jitInstance->mc->cr->AddCall("ThrowExceptionForJitResult");
+ LogError("Hit unimplemented ThrowExceptionForJitResult");
+ DebugBreakorAV(80);
+}
+
+//Throws an exception defined by the given throw helper.
+void MyICJI::ThrowExceptionForHelper(
+ const CORINFO_HELPER_DESC * throwHelper)
+{
+ jitInstance->mc->cr->AddCall("ThrowExceptionForHelper");
+ LogError("Hit unimplemented ThrowExceptionForHelper");
+ DebugBreakorAV(81);
+}
+
+/*****************************************************************************
+ * ICorStaticInfo contains EE interface methods which return values that are
+ * constant from invocation to invocation. Thus they may be embedded in
+ * persisted information like statically generated code. (This is of course
+ * assuming that all code versions are identical each time.)
+ *****************************************************************************/
+
+// Return details about EE internal data structures
+void MyICJI::getEEInfo(
+ CORINFO_EE_INFO *pEEInfoOut
+ )
+{
+ jitInstance->mc->cr->AddCall("getEEInfo");
+ jitInstance->mc->repGetEEInfo(pEEInfoOut);
+}
+
+// Returns name of the JIT timer log
+LPCWSTR MyICJI::getJitTimeLogFilename()
+{
+ jitInstance->mc->cr->AddCall("getJitTimeLogFilename");
+ //we have the ability to replay this, but we treat it in this case as EE context
+// return jitInstance->eec->jitTimeLogFilename;
+
+ // We want to be able to set COMPLUS_JitTimeLogFile when replaying, to collect JIT
+ // statistics. So, just do a getenv() call. This isn't quite as thorough as
+ // the normal CLR config value functions (which also check the registry), and we've
+ // also hard-coded the variable name here instead of using:
+ // CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitTimeLogFile);
+ // like in the VM, but it works for our purposes.
+ return GetEnvironmentVariableWithDefaultW(W("COMPlus_JitTimeLogFile"));
+}
+
+ /*********************************************************************************/
+ //
+ // Diagnostic methods
+ //
+ /*********************************************************************************/
+
+// this function is for debugging only. Returns method token.
+// Returns mdMethodDefNil for dynamic methods.
+mdMethodDef MyICJI::getMethodDefFromMethod(
+ CORINFO_METHOD_HANDLE hMethod
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodDefFromMethod");
+ mdMethodDef result = jitInstance->mc->repGetMethodDefFromMethod(hMethod);
+ return result;
+}
+
+// this function is for debugging only. It returns the method name
+// and if 'moduleName' is non-null, it sets it to something that will
+// says which method (a class name, or a module name)
+const char* MyICJI::getMethodName (
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **moduleName /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodName");
+ return jitInstance->mc->repGetMethodName(ftn, moduleName);
+}
+
+// this function is for debugging only. It returns a value that
+// is will always be the same for a given method. It is used
+// to implement the 'jitRange' functionality
+unsigned MyICJI::getMethodHash (
+ CORINFO_METHOD_HANDLE ftn /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodHash");
+ return jitInstance->mc->repGetMethodHash(ftn);
+}
+
+// this function is for debugging only.
+size_t MyICJI::findNameOfToken (
+ CORINFO_MODULE_HANDLE module, /* IN */
+ mdToken metaTOK, /* IN */
+ __out_ecount (FQNameCapacity) char * szFQName, /* OUT */
+ size_t FQNameCapacity /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("findNameOfToken");
+ return jitInstance->mc->repFindNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
+}
+
+bool MyICJI::getSystemVAmd64PassStructInRegisterDescriptor(
+ /* IN */ CORINFO_CLASS_HANDLE structHnd,
+ /* OUT */ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr
+ )
+{
+ jitInstance->mc->cr->AddCall("getSystemVAmd64PassStructInRegisterDescriptor");
+ return jitInstance->mc->repGetSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
+}
+
+//Stuff on ICorDynamicInfo
+DWORD MyICJI::getThreadTLSIndex(
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getThreadTLSIndex");
+ return jitInstance->mc->repGetThreadTLSIndex(ppIndirection);
+}
+
+const void * MyICJI::getInlinedCallFrameVptr(
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getInlinedCallFrameVptr");
+ return jitInstance->mc->repGetInlinedCallFrameVptr(ppIndirection);
+}
+
+LONG * MyICJI::getAddrOfCaptureThreadGlobal(
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getAddrOfCaptureThreadGlobal");
+ return jitInstance->mc->repGetAddrOfCaptureThreadGlobal(ppIndirection);
+}
+
+SIZE_T* MyICJI::getAddrModuleDomainID(CORINFO_MODULE_HANDLE module)
+{
+ jitInstance->mc->cr->AddCall("getAddrModuleDomainID");
+ LogError("Hit unimplemented getAddrModuleDomainID");
+ DebugBreakorAV(88);
+ return 0;
+}
+
+// return the native entry point to an EE helper (see CorInfoHelpFunc)
+void* MyICJI::getHelperFtn (
+ CorInfoHelpFunc ftnNum,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getHelperFtn");
+ return jitInstance->mc->repGetHelperFtn(ftnNum, ppIndirection);
+}
+
+// return a callable address of the function (native code). This function
+// may return a different value (depending on whether the method has
+// been JITed or not.
+void MyICJI::getFunctionEntryPoint(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ CORINFO_CONST_LOOKUP * pResult, /* OUT */
+ CORINFO_ACCESS_FLAGS accessFlags)
+{
+ jitInstance->mc->cr->AddCall("getFunctionEntryPoint");
+ jitInstance->mc->repGetFunctionEntryPoint(ftn, pResult, accessFlags);
+}
+
+// return a directly callable address. This can be used similarly to the
+// value returned by getFunctionEntryPoint() except that it is
+// guaranteed to be multi callable entrypoint.
+void MyICJI::getFunctionFixedEntryPoint(
+ CORINFO_METHOD_HANDLE ftn,
+ CORINFO_CONST_LOOKUP * pResult)
+{
+ jitInstance->mc->cr->AddCall("getFunctionFixedEntryPoint");
+ jitInstance->mc->repGetFunctionFixedEntryPoint(ftn, pResult);
+}
+
+// get the synchronization handle that is passed to monXstatic function
+void* MyICJI::getMethodSync(
+ CORINFO_METHOD_HANDLE ftn,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodSync");
+ return jitInstance->mc->repGetMethodSync(ftn, ppIndirection);
+}
+
+// These entry points must be called if a handle is being embedded in
+// the code to be passed to a JIT helper function. (as opposed to just
+// being passed back into the ICorInfo interface.)
+
+// get slow lazy string literal helper to use (CORINFO_HELP_STRCNS*).
+// Returns CORINFO_HELP_UNDEF if lazy string literal helper cannot be used.
+CorInfoHelpFunc MyICJI::getLazyStringLiteralHelper(
+ CORINFO_MODULE_HANDLE handle
+ )
+{
+ jitInstance->mc->cr->AddCall("getLazyStringLiteralHelper");
+ return jitInstance->mc->repGetLazyStringLiteralHelper(handle);
+}
+
+
+CORINFO_MODULE_HANDLE MyICJI::embedModuleHandle(
+ CORINFO_MODULE_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedModuleHandle");
+ return jitInstance->mc->repEmbedModuleHandle(handle, ppIndirection);
+}
+
+CORINFO_CLASS_HANDLE MyICJI::embedClassHandle(
+ CORINFO_CLASS_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedClassHandle");
+ return jitInstance->mc->repEmbedClassHandle(handle, ppIndirection);
+}
+
+CORINFO_METHOD_HANDLE MyICJI::embedMethodHandle(
+ CORINFO_METHOD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedMethodHandle");
+ return jitInstance->mc->repEmbedMethodHandle(handle, ppIndirection);
+}
+
+CORINFO_FIELD_HANDLE MyICJI::embedFieldHandle(
+ CORINFO_FIELD_HANDLE handle,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("embedFieldHandle");
+ return jitInstance->mc->repEmbedFieldHandle(handle, ppIndirection);
+}
+
+// Given a module scope (module), a method handle (context) and
+// a metadata token (metaTOK), fetch the handle
+// (type, field or method) associated with the token.
+// If this is not possible at compile-time (because the current method's
+// code is shared and the token contains generic parameters)
+// then indicate how the handle should be looked up at run-time.
+//
+void MyICJI::embedGenericHandle(
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+ BOOL fEmbedParent, // TRUE - embeds parent type handle of the field/method handle
+ CORINFO_GENERICHANDLE_RESULT * pResult)
+{
+ jitInstance->mc->cr->AddCall("embedGenericHandle");
+ jitInstance->mc->repEmbedGenericHandle(pResolvedToken, fEmbedParent, pResult);
+}
+
+// Return information used to locate the exact enclosing type of the current method.
+// Used only to invoke .cctor method from code shared across generic instantiations
+// !needsRuntimeLookup statically known (enclosing type of method itself)
+// needsRuntimeLookup:
+// CORINFO_LOOKUP_THISOBJ use vtable pointer of 'this' param
+// CORINFO_LOOKUP_CLASSPARAM use vtable hidden param
+// CORINFO_LOOKUP_METHODPARAM use enclosing type of method-desc hidden param
+CORINFO_LOOKUP_KIND MyICJI::getLocationOfThisType(
+ CORINFO_METHOD_HANDLE context
+ )
+{
+ jitInstance->mc->cr->AddCall("getLocationOfThisType");
+ return jitInstance->mc->repGetLocationOfThisType(context);
+}
+
+// return the unmanaged target *if method has already been prelinked.*
+void* MyICJI::getPInvokeUnmanagedTarget(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getPInvokeUnmanagedTarget");
+ void *result = jitInstance->mc->repGetPInvokeUnmanagedTarget(method, ppIndirection);
+ return result;
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void* MyICJI::getAddressOfPInvokeFixup(
+ CORINFO_METHOD_HANDLE method,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getAddressOfPInvokeFixup");
+ return jitInstance->mc->repGetAddressOfPInvokeFixup(method, ppIndirection);
+}
+
+// return address of fixup area for late-bound PInvoke calls.
+void MyICJI::getAddressOfPInvokeTarget(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_CONST_LOOKUP *pLookup
+ )
+{
+ jitInstance->mc->cr->AddCall("getAddressOfPInvokeTarget");
+ jitInstance->mc->repGetAddressOfPInvokeTarget(method, pLookup);
+}
+
+// Generate a cookie based on the signature that would needs to be passed
+// to CORINFO_HELP_PINVOKE_CALLI
+LPVOID MyICJI::GetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig,
+ void ** ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("GetCookieForPInvokeCalliSig");
+ return jitInstance->mc->repGetCookieForPInvokeCalliSig(szMetaSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool MyICJI::canGetCookieForPInvokeCalliSig(
+ CORINFO_SIG_INFO* szMetaSig
+ )
+{
+ jitInstance->mc->cr->AddCall("canGetCookieForPInvokeCalliSig");
+ return jitInstance->mc->repCanGetCookieForPInvokeCalliSig(szMetaSig);
+}
+
+// Gets a handle that is checked to see if the current method is
+// included in "JustMyCode"
+CORINFO_JUST_MY_CODE_HANDLE MyICJI::getJustMyCodeHandle(
+ CORINFO_METHOD_HANDLE method,
+ CORINFO_JUST_MY_CODE_HANDLE**ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getJustMyCodeHandle");
+ return jitInstance->mc->repGetJustMyCodeHandle(method, ppIndirection);
+}
+
+// Gets a method handle that can be used to correlate profiling data.
+// This is the IP of a native method, or the address of the descriptor struct
+// for IL. Always guaranteed to be unique per process, and not to move. */
+void MyICJI::GetProfilingHandle(
+ BOOL *pbHookFunction,
+ void **pProfilerHandle,
+ BOOL *pbIndirectedHandles
+ )
+{
+ jitInstance->mc->cr->AddCall("GetProfilingHandle");
+ jitInstance->mc->repGetProfilingHandle(pbHookFunction, pProfilerHandle, pbIndirectedHandles);
+}
+
+// Returns instructions on how to make the call. See code:CORINFO_CALL_INFO for possible return values.
+void MyICJI::getCallInfo(
+ // Token info
+ CORINFO_RESOLVED_TOKEN * pResolvedToken,
+
+ //Generics info
+ CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
+
+ //Security info
+ CORINFO_METHOD_HANDLE callerHandle,
+
+ //Jit info
+ CORINFO_CALLINFO_FLAGS flags,
+
+ //out params
+ CORINFO_CALL_INFO *pResult
+ )
+{
+ jitInstance->mc->cr->AddCall("getCallInfo");
+ DWORD exceptionCode = 0;
+ jitInstance->mc->repGetCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult, &exceptionCode);
+ if(exceptionCode != 0)
+ ThrowException(exceptionCode);
+}
+
+BOOL MyICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller,
+ CORINFO_CLASS_HANDLE hInstanceType)
+
+{
+ jitInstance->mc->cr->AddCall("canAccessFamily");
+ return jitInstance->mc->repCanAccessFamily(hCaller, hInstanceType);
+}
+// Returns TRUE if the Class Domain ID is the RID of the class (currently true for every class
+// except reflection emitted classes and generics)
+BOOL MyICJI::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls)
+{
+ jitInstance->mc->cr->AddCall("isRIDClassDomainID");
+ LogError("Hit unimplemented isRIDClassDomainID");
+ DebugBreakorAV(107);
+ return false;
+}
+
+// returns the class's domain ID for accessing shared statics
+unsigned MyICJI::getClassDomainID (
+ CORINFO_CLASS_HANDLE cls,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getClassDomainID");
+ return jitInstance->mc->repGetClassDomainID(cls, ppIndirection);
+}
+
+
+// return the data's address (for static fields only)
+void* MyICJI::getFieldAddress(
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldAddress");
+ return jitInstance->mc->repGetFieldAddress(field, ppIndirection);
+}
+
+// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
+CORINFO_VARARGS_HANDLE MyICJI::getVarArgsHandle(
+ CORINFO_SIG_INFO *pSig,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getVarArgsHandle");
+ return jitInstance->mc->repGetVarArgsHandle(pSig, ppIndirection);
+}
+
+// returns true if a VM cookie can be generated for it (might be false due to cross-module
+// inlining, in which case the inlining should be aborted)
+bool MyICJI::canGetVarArgsHandle(
+ CORINFO_SIG_INFO *pSig
+ )
+{
+ jitInstance->mc->cr->AddCall("canGetVarArgsHandle");
+ return jitInstance->mc->repCanGetVarArgsHandle(pSig);
+}
+
+// Allocate a string literal on the heap and return a handle to it
+InfoAccessType MyICJI::constructStringLiteral(
+ CORINFO_MODULE_HANDLE module,
+ mdToken metaTok,
+ void **ppValue
+ )
+{
+ jitInstance->mc->cr->AddCall("constructStringLiteral");
+ return jitInstance->mc->repConstructStringLiteral(module, metaTok, ppValue);
+}
+
+InfoAccessType MyICJI::emptyStringLiteral(
+ void **ppValue
+ )
+{
+ jitInstance->mc->cr->AddCall("emptyStringLiteral");
+ return jitInstance->mc->repEmptyStringLiteral(ppValue);
+}
+
+// (static fields only) given that 'field' refers to thread local store,
+// return the ID (TLS index), which is used to find the begining of the
+// TLS data area for the particular DLL 'field' is associated with.
+DWORD MyICJI::getFieldThreadLocalStoreID (
+ CORINFO_FIELD_HANDLE field,
+ void **ppIndirection
+ )
+{
+ jitInstance->mc->cr->AddCall("getFieldThreadLocalStoreID");
+ return jitInstance->mc->repGetFieldThreadLocalStoreID(field, ppIndirection);
+}
+
+// Sets another object to intercept calls to "self" and current method being compiled
+void MyICJI::setOverride(
+ ICorDynamicInfo *pOverride,
+ CORINFO_METHOD_HANDLE currentMethod
+ )
+{
+ jitInstance->mc->cr->AddCall("setOverride");
+ LogError("Hit unimplemented setOverride");
+ DebugBreakorAV(115);
+}
+
+// Adds an active dependency from the context method's module to the given module
+// This is internal callback for the EE. JIT should not call it directly.
+void MyICJI::addActiveDependency(
+ CORINFO_MODULE_HANDLE moduleFrom,
+ CORINFO_MODULE_HANDLE moduleTo
+ )
+{
+ jitInstance->mc->cr->AddCall("addActiveDependency");
+ LogError("Hit unimplemented addActiveDependency");
+ DebugBreakorAV(116);
+}
+
+CORINFO_METHOD_HANDLE MyICJI::GetDelegateCtor(
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_CLASS_HANDLE clsHnd,
+ CORINFO_METHOD_HANDLE targetMethodHnd,
+ DelegateCtorArgs * pCtorData
+ )
+{
+ jitInstance->mc->cr->AddCall("GetDelegateCtor");
+ return jitInstance->mc->repGetDelegateCtor(methHnd, clsHnd, targetMethodHnd, pCtorData);
+}
+
+void MyICJI::MethodCompileComplete(
+ CORINFO_METHOD_HANDLE methHnd
+ )
+{
+ jitInstance->mc->cr->AddCall("MethodCompileComplete");
+ LogError("Hit unimplemented MethodCompileComplete");
+ DebugBreakorAV(118);
+}
+
+// return a thunk that will copy the arguments for the given signature.
+void* MyICJI::getTailCallCopyArgsThunk (
+ CORINFO_SIG_INFO *pSig,
+ CorInfoHelperTailCallSpecialHandling flags
+ )
+{
+ jitInstance->mc->cr->AddCall("getTailCallCopyArgsThunk");
+ return jitInstance->mc->repGetTailCallCopyArgsThunk(pSig, flags);
+}
+
+//Stuff directly on ICorJitInfo
+
+// Returns extended flags for a particular compilation instance.
+DWORD MyICJI::getJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes)
+{
+ jitInstance->mc->cr->AddCall("getJitFlags");
+ return jitInstance->mc->repGetJitFlags(jitFlags, sizeInBytes);
+}
+
+// Runs the given function with the given parameter under an error trap
+// and returns true if the function completes successfully. We fake this
+// up a bit for SuperPMI and simply catch all exceptions.
+bool MyICJI::runWithErrorTrap(void (*function)(void*), void *param)
+{
+ return RunWithErrorTrap(function, param);
+}
+
+// return memory manager that the JIT can use to allocate a regular memory
+IEEMemoryManager* MyICJI::getMemoryManager()
+{
+ return InitIEEMemoryManager(jitInstance);
+}
+
+// get a block of memory for the code, readonly data, and read-write data
+void MyICJI::allocMem (
+ ULONG hotCodeSize, /* IN */
+ ULONG coldCodeSize, /* IN */
+ ULONG roDataSize, /* IN */
+ ULONG xcptnsCount, /* IN */
+ CorJitAllocMemFlag flag, /* IN */
+ void ** hotCodeBlock, /* OUT */
+ void ** coldCodeBlock, /* OUT */
+ void ** roDataBlock /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("allocMem");
+ //TODO-Cleanup: investigate if we need to check roDataBlock as well. Could hot block size be ever 0?
+ *hotCodeBlock = HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,hotCodeSize);
+ if (coldCodeSize>0)
+ *coldCodeBlock = HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,coldCodeSize);
+ else
+ *coldCodeBlock = nullptr;
+ *roDataBlock = HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,roDataSize);
+ jitInstance->mc->cr->recAllocMem(hotCodeSize, coldCodeSize, roDataSize, xcptnsCount, flag, hotCodeBlock, coldCodeBlock, roDataBlock);
+}
+
+// Reserve memory for the method/funclet's unwind information.
+// Note that this must be called before allocMem. It should be
+// called once for the main method, once for every funclet, and
+// once for every block of cold code for which allocUnwindInfo
+// will be called.
+//
+// This is necessary because jitted code must allocate all the
+// memory needed for the unwindInfo at the allocMem call.
+// For prejitted code we split up the unwinding information into
+// separate sections .rdata and .pdata.
+//
+void MyICJI::reserveUnwindInfo (
+ BOOL isFunclet, /* IN */
+ BOOL isColdCode, /* IN */
+ ULONG unwindSize /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("reserveUnwindInfo");
+ jitInstance->mc->cr->recReserveUnwindInfo(isFunclet, isColdCode, unwindSize);
+}
+
+// Allocate and initialize the .rdata and .pdata for this method or
+// funclet, and get the block of memory needed for the machine-specific
+// unwind information (the info for crawling the stack frame).
+// Note that allocMem must be called first.
+//
+// Parameters:
+//
+// pHotCode main method code buffer, always filled in
+// pColdCode cold code buffer, only filled in if this is cold code,
+// null otherwise
+// startOffset start of code block, relative to appropriate code buffer
+// (e.g. pColdCode if cold, pHotCode if hot).
+// endOffset end of code block, relative to appropriate code buffer
+// unwindSize size of unwind info pointed to by pUnwindBlock
+// pUnwindBlock pointer to unwind info
+// funcKind type of funclet (main method code, handler, filter)
+//
+void MyICJI::allocUnwindInfo (
+ BYTE * pHotCode, /* IN */
+ BYTE * pColdCode, /* IN */
+ ULONG startOffset, /* IN */
+ ULONG endOffset, /* IN */
+ ULONG unwindSize, /* IN */
+ BYTE * pUnwindBlock, /* IN */
+ CorJitFuncKind funcKind /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("allocUnwindInfo");
+ jitInstance->mc->cr->recAllocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock, funcKind);
+}
+
+// Get a block of memory needed for the code manager information,
+// (the info for enumerating the GC pointers while crawling the
+// stack frame).
+// Note that allocMem must be called first
+void * MyICJI::allocGCInfo (
+ size_t size /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("allocGCInfo");
+ void *temp = (unsigned char*)HeapAlloc(jitInstance->mc->cr->getCodeHeap(),0,size);
+ jitInstance->mc->cr->recAllocGCInfo(size, temp);
+
+ return temp;
+}
+
+// Only used on x64.
+void MyICJI::yieldExecution()
+{
+ jitInstance->mc->cr->AddCall("yieldExecution");
+}
+
+// Indicate how many exception handler blocks are to be returned.
+// This is guaranteed to be called before any 'setEHinfo' call.
+// Note that allocMem must be called before this method can be called.
+void MyICJI::setEHcount (
+ unsigned cEH /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("setEHcount");
+ jitInstance->mc->cr->recSetEHcount(cEH);
+}
+
+// Set the values for one particular exception handler block.
+//
+// Handler regions should be lexically contiguous.
+// This is because FinallyIsUnwinding() uses lexicality to
+// determine if a "finally" clause is executing.
+void MyICJI::setEHinfo (
+ unsigned EHnumber, /* IN */
+ const CORINFO_EH_CLAUSE *clause /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("setEHinfo");
+ jitInstance->mc->cr->recSetEHinfo(EHnumber, clause);
+}
+
+// Level 1 -> fatalError, Level 2 -> Error, Level 3 -> Warning
+// Level 4 means happens 10 times in a run, level 5 means 100, level 6 means 1000 ...
+// returns non-zero if the logging succeeded
+BOOL MyICJI::logMsg(unsigned level, const char* fmt, va_list args)
+{
+ jitInstance->mc->cr->AddCall("logMsg");
+
+// if(level<=2)
+// {
+ //jitInstance->mc->cr->recMessageLog(fmt, args);
+ //DebugBreakorAV(0x99);
+ //}
+ jitInstance->mc->cr->recMessageLog(fmt, args);
+ return 0;
+}
+
+// do an assert. will return true if the code should retry (DebugBreak)
+// returns false, if the assert should be igored.
+int MyICJI::doAssert(const char* szFile, int iLine, const char* szExpr)
+{
+ jitInstance->mc->cr->AddCall("doAssert");
+ char buff[16 * 1024];
+ sprintf_s(buff, sizeof(buff), "%s (%d) - %s", szFile, iLine, szExpr);
+
+ LogIssue(ISSUE_ASSERT, "%s", buff);
+ jitInstance->mc->cr->recMessageLog(buff);
+
+ // Under "/boa", ask the user if they want to attach a debugger. If they do, the debugger will be attached,
+ // then we'll call DebugBreakorAV(), which will issue a __debugbreak() and actually cause
+ // us to stop in the debugger.
+ if (breakOnDebugBreakorAV)
+ {
+ DbgBreakCheck(szFile, iLine, szExpr);
+ }
+
+ DebugBreakorAV(0x7b);
+ return 0;
+}
+
+void MyICJI::reportFatalError(CorJitResult result)
+{
+ jitInstance->mc->cr->AddCall("reportFatalError");
+ jitInstance->mc->cr->recReportFatalError(result);
+}
+
+// allocate a basic block profile buffer where execution counts will be stored
+// for jitted basic blocks.
+HRESULT MyICJI::allocBBProfileBuffer (
+ ULONG count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer
+ )
+{
+ jitInstance->mc->cr->AddCall("allocBBProfileBuffer");
+ return jitInstance->mc->cr->repAllocBBProfileBuffer(count, profileBuffer);
+}
+
+// get profile information to be used for optimizing the current method. The format
+// of the buffer is the same as the format the JIT passes to allocBBProfileBuffer.
+HRESULT MyICJI::getBBProfileData(
+ CORINFO_METHOD_HANDLE ftnHnd,
+ ULONG * count, // The number of basic blocks that we have
+ ProfileBuffer ** profileBuffer,
+ ULONG * numRuns
+ )
+{
+ jitInstance->mc->cr->AddCall("getBBProfileData");
+ return jitInstance->mc->repGetBBProfileData(ftnHnd, count, profileBuffer, numRuns);
+}
+
+// Associates a native call site, identified by its offset in the native code stream, with
+// the signature information and method handle the JIT used to lay out the call site. If
+// the call site has no signature information (e.g. a helper call) or has no method handle
+// (e.g. a CALLI P/Invoke), then null should be passed instead.
+void MyICJI::recordCallSite(
+ ULONG instrOffset, /* IN */
+ CORINFO_SIG_INFO * callSig, /* IN */
+ CORINFO_METHOD_HANDLE methodHandle /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("recordCallSite");
+ jitInstance->mc->cr->repRecordCallSite(instrOffset, callSig, methodHandle);
+}
+
+// A relocation is recorded if we are pre-jitting.
+// A jump thunk may be inserted if we are jitting
+void MyICJI::recordRelocation(
+ void * location, /* IN */
+ void * target, /* IN */
+ WORD fRelocType, /* IN */
+ WORD slotNum, /* IN */
+ INT32 addlDelta /* IN */
+ )
+{
+ jitInstance->mc->cr->AddCall("recordRelocation");
+ jitInstance->mc->cr->repRecordRelocation(location, target, fRelocType, slotNum, addlDelta);
+}
+
+WORD MyICJI::getRelocTypeHint(void * target)
+{
+ jitInstance->mc->cr->AddCall("getRelocTypeHint");
+ WORD result = jitInstance->mc->repGetRelocTypeHint(target);
+ return result;
+}
+
+// A callback to identify the range of address known to point to
+// compiler-generated native entry points that call back into
+// MSIL.
+void MyICJI::getModuleNativeEntryPointRange(
+ void ** pStart, /* OUT */
+ void ** pEnd /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getModuleNativeEntryPointRange");
+ LogError("Hit unimplemented getModuleNativeEntryPointRange");
+ DebugBreakorAV(128);
+}
+
+// For what machine does the VM expect the JIT to generate code? The VM
+// returns one of the IMAGE_FILE_MACHINE_* values. Note that if the VM
+// is cross-compiling (such as the case for crossgen), it will return a
+// different value than if it was compiling for the host architecture.
+//
+DWORD MyICJI::getExpectedTargetArchitecture()
+{
+#if defined(_TARGET_X86_)
+ return IMAGE_FILE_MACHINE_I386;
+#elif defined(_TARGET_AMD64_)
+ return IMAGE_FILE_MACHINE_AMD64;
+#elif defined(_TARGET_ARM_)
+ return IMAGE_FILE_MACHINE_ARMNT;
+#elif defined(_TARGET_ARM64_)
+ return IMAGE_FILE_MACHINE_ARM64;
+#else
+ return IMAGE_FILE_MACHINE_UNKNOWN;
+#endif
+}
diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.h b/src/ToolBox/superpmi/superpmi/icorjitinfo.h
new file mode 100644
index 0000000000..2182db3be6
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _ICorJitInfo
+#define _ICorJitInfo
+
+#include "runtimedetails.h"
+#include "ieememorymanager.h"
+
+extern ICorJitInfo *pICJI;
+
+class MyICJI : public ICorJitInfo
+{
+
+#include "icorjitinfoimpl.h"
+
+public:
+
+ //Added extras... todo add padding to detect corruption?
+ JitInstance *jitInstance;
+};
+
+ICorJitInfo* InitICorJitInfo(JitInstance *jitInstance);
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/ieememorymanager.cpp b/src/ToolBox/superpmi/superpmi/ieememorymanager.cpp
new file mode 100644
index 0000000000..8bb95b1f4d
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/ieememorymanager.cpp
@@ -0,0 +1,110 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "spmiutil.h"
+#include "ieememorymanager.h"
+
+IEEMemoryManager *pIEEMM = nullptr;
+HANDLE processHeap = INVALID_HANDLE_VALUE;
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+
+HRESULT STDMETHODCALLTYPE MyIEEMM::QueryInterface(REFIID id, void **pInterface)
+{
+ DebugBreakorAV(133);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEEMM::AddRef()
+{
+ DebugBreakorAV(134);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEEMM::Release()
+{
+ DebugBreakorAV(135);
+ return 0;
+}
+
+HANDLE virtHeap = INVALID_HANDLE_VALUE;
+
+//***************************************************************************
+// IEEMemoryManager methods for locking
+//***************************************************************************
+LPVOID STDMETHODCALLTYPE MyIEEMM::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ if(virtHeap == INVALID_HANDLE_VALUE)
+ virtHeap = HeapCreate(0, 0xFFFF, 0);
+ if(virtHeap != INVALID_HANDLE_VALUE)
+ return HeapAlloc(virtHeap, HEAP_ZERO_MEMORY, dwSize);
+ return nullptr;
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return HeapFree(virtHeap, 0, lpAddress);
+}
+SIZE_T STDMETHODCALLTYPE MyIEEMM::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ DebugBreakorAV(136);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ DebugBreakorAV(137);
+ return 0;
+}
+HANDLE STDMETHODCALLTYPE MyIEEMM::ClrGetProcessHeap()
+{
+ DebugBreakorAV(138);
+ return 0;
+}
+HANDLE STDMETHODCALLTYPE MyIEEMM::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ DebugBreakorAV(139);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrHeapDestroy(HANDLE hHeap)
+{
+ DebugBreakorAV(140);
+ return 0;
+}
+LPVOID STDMETHODCALLTYPE MyIEEMM::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+ return HeapAlloc(hHeap,dwFlags,dwBytes);
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return HeapFree(hHeap, dwFlags, lpMem);
+}
+BOOL STDMETHODCALLTYPE MyIEEMM::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ DebugBreakorAV(141);
+ return 0;
+}
+HANDLE STDMETHODCALLTYPE MyIEEMM::ClrGetProcessExecutableHeap()
+{
+ if (processHeap == INVALID_HANDLE_VALUE)
+ {
+ DWORD flOptions = 0;
+#ifndef FEATURE_PAL // TODO-Review: PAL doesn't have HEAP_CREATE_ENABLE_EXECUTE. Is this ok?
+ flOptions = HEAP_CREATE_ENABLE_EXECUTE;
+#endif // !FEATURE_PAL
+ processHeap = HeapCreate(flOptions, 10000, 0);
+ }
+ return processHeap;
+}
+
+IEEMemoryManager *InitIEEMemoryManager(JitInstance *jitInstance)
+{
+ if(pIEEMM==nullptr)
+ {
+ MyIEEMM *ieemm = new MyIEEMM();
+ ieemm->jitInstance = jitInstance;
+ pIEEMM = ieemm;
+ }
+ return pIEEMM;
+}
diff --git a/src/ToolBox/superpmi/superpmi/ieememorymanager.h b/src/ToolBox/superpmi/superpmi/ieememorymanager.h
new file mode 100644
index 0000000000..90763bc233
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/ieememorymanager.h
@@ -0,0 +1,113 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IEEMemoryManager
+#define _IEEMemoryManager
+
+#include "runtimedetails.h"
+#include "jitinstance.h"
+
+/*
+interface IEEMemoryManager : IUnknown
+{
+ LPVOID ClrVirtualAlloc(
+ [in] LPVOID lpAddress, // region to reserve or commit
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD flAllocationType, // type of allocation
+ [in] DWORD flProtect // type of access protection
+ )
+
+ BOOL ClrVirtualFree(
+ [in] LPVOID lpAddress, // address of region
+ [in] SIZE_T dwSize, // size of region
+ [in] DWORD dwFreeType // operation type
+ )
+
+ SIZE_T ClrVirtualQuery(
+ [in] const void* lpAddress, // address of region
+ [in] PMEMORY_BASIC_INFORMATION lpBuffer, // information buffer
+ [in] SIZE_T dwLength // size of buffer
+ )
+
+ BOOL ClrVirtualProtect(
+ [in] LPVOID lpAddress, // region of committed pages
+ [in] SIZE_T dwSize, // size of the region
+ [in] DWORD flNewProtect, // desired access protection
+ [in] DWORD* lpflOldProtect // old protection
+ )
+
+ HANDLE ClrGetProcessHeap()
+
+ HANDLE ClrHeapCreate(
+ [in] DWORD flOptions, // heap allocation attributes
+ [in] SIZE_T dwInitialSize, // initial heap size
+ [in] SIZE_T dwMaximumSize // maximum heap size
+ )
+
+ BOOL ClrHeapDestroy(
+ [in] HANDLE hHeap // handle to heap
+ )
+
+ LPVOID ClrHeapAlloc(
+ [in] HANDLE hHeap, // handle to private heap block
+ [in] DWORD dwFlags, // heap allocation control
+ [in] SIZE_T dwBytes // number of bytes to allocate
+ )
+
+ BOOL ClrHeapFree(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap free options
+ [in] LPVOID lpMem // pointer to memory
+ )
+
+ BOOL ClrHeapValidate(
+ [in] HANDLE hHeap, // handle to heap
+ [in] DWORD dwFlags, // heap access options
+ [in] const void* lpMem // optional pointer to memory block
+ )
+
+ HANDLE ClrGetProcessExecutableHeap()
+
+}; // interface IEEMemoryManager
+
+*/
+extern HANDLE virtHeap;
+extern IEEMemoryManager *pIEEMM;
+extern HANDLE processHeap;
+class MyIEEMM : public IEEMemoryManager
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IEEMemoryManager methods for locking
+ //***************************************************************************
+ LPVOID STDMETHODCALLTYPE ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
+ BOOL STDMETHODCALLTYPE ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
+ SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+ BOOL STDMETHODCALLTYPE ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessHeap();
+ HANDLE STDMETHODCALLTYPE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
+ BOOL STDMETHODCALLTYPE ClrHeapDestroy(HANDLE hHeap);
+ LPVOID STDMETHODCALLTYPE ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
+ BOOL STDMETHODCALLTYPE ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
+ BOOL STDMETHODCALLTYPE ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap();
+
+public:
+ //Added extras... todo add padding to detect corruption?
+ JitInstance *jitInstance;
+};
+
+IEEMemoryManager *InitIEEMemoryManager(JitInstance *jitInstance);
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/iexecutionengine.cpp b/src/ToolBox/superpmi/superpmi/iexecutionengine.cpp
new file mode 100644
index 0000000000..db9e121d8e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/iexecutionengine.cpp
@@ -0,0 +1,213 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "spmiutil.h"
+#include "iexecutionengine.h"
+
+LPVOID TLS_Slots[MAX_PREDEFINED_TLS_SLOT];
+class MyIEE;
+IExecutionEngine *pIEE = nullptr;
+
+//***************************************************************************
+// IUnknown methods
+//***************************************************************************
+
+HRESULT STDMETHODCALLTYPE MyIEE::QueryInterface(REFIID id, void **pInterface)
+{
+ //TODO-Cleanup: check the rid
+ *pInterface = InitIEEMemoryManager(nullptr);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEE::AddRef()
+{
+ DebugBreakorAV(142);
+ return 0;
+}
+ULONG STDMETHODCALLTYPE MyIEE::Release()
+{
+ DebugBreakorAV(143);
+ return 0;
+}
+
+//***************************************************************************
+// IExecutionEngine methods for TLS
+//***************************************************************************
+
+DWORD TlsIndex = 42;
+
+// Associate a callback for cleanup with a TLS slot
+VOID STDMETHODCALLTYPE MyIEE::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ //TODO-Cleanup: figure an appropriate realish value for this
+}
+
+// Get the TLS block for fast Get/Set operations
+LPVOID* STDMETHODCALLTYPE MyIEE::TLS_GetDataBlock()
+{
+ //We were previously allocating a TlsIndex with
+ //the master slot index set to a nullptr
+ //so in the new version we just return nullptr
+ //and it seems to be working for now
+ return nullptr;
+}
+
+// Get the value at a slot
+LPVOID STDMETHODCALLTYPE MyIEE::TLS_GetValue(DWORD slot)
+{
+ /* if(slot>MAX_PREDEFINED_TLS_SLOT)
+ __debugbreak();
+ void *thing = TlsGetValue(TlsIndex);
+
+ // if(slot == 0x9)
+ //return 0; //trick out the contract system to be as off as possible.
+ //TODO-Cleanup: does anything beyond contracts care? This seems like a pretty thin mock.
+ */
+ return TLS_Slots[slot];
+}
+
+// Get the value at a slot, return FALSE if TLS info block doesn't exist
+BOOL STDMETHODCALLTYPE MyIEE::TLS_CheckValue(DWORD slot, LPVOID * pValue)
+{
+ DebugBreakorAV(144);
+ //TODO-Cleanup: does anything beyond contracts care? This seems like a pretty thin mock.
+ return true;
+}
+// Set the value at a slot
+VOID STDMETHODCALLTYPE MyIEE::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ if (slot > MAX_PREDEFINED_TLS_SLOT)
+ {
+ DebugBreakorAV(143);
+ return;
+ }
+ void *thing = TlsGetValue(TlsIndex);//TODO-Cleanup: this seems odd.. explain?
+
+ //TODO-Cleanup: does anything beyond contracts care? This seems like a pretty thin mock.
+ TLS_Slots[slot] = pData;
+}
+// Free TLS memory block and make callback
+VOID STDMETHODCALLTYPE MyIEE::TLS_ThreadDetaching()
+{
+ DebugBreakorAV(145);
+}
+
+//***************************************************************************
+// IExecutionEngine methods for locking
+//***************************************************************************
+
+CRITSEC_COOKIE STDMETHODCALLTYPE MyIEE::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ return (CRITSEC_COOKIE)(size_t)0xbad01241;
+}
+void STDMETHODCALLTYPE MyIEE::DestroyLock(CRITSEC_COOKIE lock)
+{
+ DebugBreakorAV(146);
+}
+void STDMETHODCALLTYPE MyIEE::AcquireLock(CRITSEC_COOKIE lock)
+{
+}
+void STDMETHODCALLTYPE MyIEE::ReleaseLock(CRITSEC_COOKIE lock)
+{
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE MyIEE::CreateAutoEvent(BOOL bInitialState)
+{
+ DebugBreakorAV(147);
+ return 0;
+}
+EVENT_COOKIE STDMETHODCALLTYPE MyIEE::CreateManualEvent(BOOL bInitialState)
+{
+ DebugBreakorAV(148);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::CloseEvent(EVENT_COOKIE event)
+{
+ DebugBreakorAV(149);
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrSetEvent(EVENT_COOKIE event)
+{
+ DebugBreakorAV(150);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrResetEvent(EVENT_COOKIE event)
+{
+ DebugBreakorAV(151);
+ return 0;
+}
+DWORD STDMETHODCALLTYPE MyIEE::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ DebugBreakorAV(152);
+ return 0;
+}
+DWORD STDMETHODCALLTYPE MyIEE::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ DebugBreakorAV(153);
+ return 0;
+}
+SEMAPHORE_COOKIE STDMETHODCALLTYPE MyIEE::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ DebugBreakorAV(154);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ DebugBreakorAV(155);
+}
+DWORD STDMETHODCALLTYPE MyIEE::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ DebugBreakorAV(156);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ DebugBreakorAV(157);
+ return 0;
+}
+MUTEX_COOKIE STDMETHODCALLTYPE MyIEE::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ DebugBreakorAV(158);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ DebugBreakorAV(159);
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ DebugBreakorAV(160);
+ return 0;
+}
+DWORD STDMETHODCALLTYPE MyIEE::ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ DebugBreakorAV(161);
+ return 0;
+}
+
+DWORD STDMETHODCALLTYPE MyIEE::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ DebugBreakorAV(162);
+ return 0;
+}
+BOOL STDMETHODCALLTYPE MyIEE::ClrAllocationDisallowed()
+{
+ DebugBreakorAV(163);
+ return 0;
+}
+void STDMETHODCALLTYPE MyIEE::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ DebugBreakorAV(164);
+}
+
+MyIEE *InitIExecutionEngine()
+{
+ MyIEE *iee = new MyIEE();
+ pIEE = iee;
+ return iee;
+}
diff --git a/src/ToolBox/superpmi/superpmi/iexecutionengine.h b/src/ToolBox/superpmi/superpmi/iexecutionengine.h
new file mode 100644
index 0000000000..f1a76b9a4f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/iexecutionengine.h
@@ -0,0 +1,151 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _IExecutionEngine
+#define _IExecutionEngine
+
+#include "ieememorymanager.h"
+
+/*
+interface IExecutionEngine : IUnknown
+{
+ // Thread Local Storage is based on logical threads. The underlying
+ // implementation could be threads, fibers, or something more exotic.
+ // Slot numbers are predefined. This is not a general extensibility
+ // mechanism.
+
+ // Associate a callback function for releasing TLS on thread/fiber death.
+ // This can be NULL.
+ void TLS_AssociateCallback([in] DWORD slot, [in] PTLS_CALLBACK_FUNCTION callback)
+
+ // May be called once to get the master TLS block slot for fast Get/Set operations
+ DWORD TLS_GetMasterSlotIndex()
+
+ // Get the value at a slot
+ PVOID TLS_GetValue([in] DWORD slot)
+
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL TLS_CheckValue([in] DWORD slot, [out] PVOID * pValue)
+
+ // Set the value at a slot
+ void TLS_SetValue([in] DWORD slot, [in] PVOID pData)
+
+ // Free TLS memory block and make callback
+ void TLS_ThreadDetaching()
+
+ // Critical Sections are sometimes exposed to the host and therefore need to be
+ // reflected from all CLR DLLs to the EE.
+ //
+ // In addition, we always monitor interactions between the lock & the GC, based
+ // on the GC mode in which the lock is acquired and we restrict what operations
+ // are permitted while holding the lock based on this.
+ //
+ // Finally, we we rank all our locks to prevent deadlock across all the DLLs of
+ // the CLR. This is the level argument to CreateLock.
+ //
+ // All usage of these locks must be exception-safe. To achieve this, we suggest
+ // using Holders (see holder.h & crst.h). In fact, within the EE code cannot
+ // hold locks except by using exception-safe holders.
+
+ CRITSEC_COOKIE CreateLock([in] LPCSTR szTag, [in] LPCSTR level, [in] CrstFlags flags)
+
+ void DestroyLock([in] CRITSEC_COOKIE lock)
+
+ void AcquireLock([in] CRITSEC_COOKIE lock)
+
+ void ReleaseLock([in] CRITSEC_COOKIE lock)
+
+ EVENT_COOKIE CreateAutoEvent([in] BOOL bInitialState)
+ EVENT_COOKIE CreateManualEvent([in] BOOL bInitialState)
+ void CloseEvent([in] EVENT_COOKIE event)
+ BOOL ClrSetEvent([in] EVENT_COOKIE event)
+ BOOL ClrResetEvent([in] EVENT_COOKIE event)
+ DWORD WaitForEvent([in] EVENT_COOKIE event, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ DWORD WaitForSingleObject([in] HANDLE handle, [in] DWORD dwMilliseconds)
+
+ // OS header file defines CreateSemaphore.
+ SEMAPHORE_COOKIE ClrCreateSemaphore([in] DWORD dwInitial, [in] DWORD dwMax)
+ void ClrCloseSemaphore([in] SEMAPHORE_COOKIE semaphore)
+ DWORD ClrWaitForSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseSemaphore([in] SEMAPHORE_COOKIE semaphore, [in] LONG lReleaseCount, [in] LONG *lpPreviousCount)
+
+ MUTEX_COOKIE ClrCreateMutex([in]LPSECURITY_ATTRIBUTES lpMutexAttributes, [in]BOOL bInitialOwner, [in]LPCTSTR lpName)
+ DWORD ClrWaitForMutex([in] MUTEX_COOKIE mutex, [in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+ BOOL ClrReleaseMutex([in] MUTEX_COOKIE mutex)
+ void ClrCloseMutex([in] MUTEX_COOKIE mutex)
+
+ DWORD ClrSleepEx([in] DWORD dwMilliseconds, [in] BOOL bAlertable)
+
+ BOOL ClrAllocationDisallowed()
+
+ void GetLastThrownObjectExceptionFromThread([out] void **ppvException)
+
+}; // interface IExecutionEngine
+*/
+
+extern LPVOID TLS_Slots[MAX_PREDEFINED_TLS_SLOT];
+extern IExecutionEngine *pIEE;
+
+class MyIEE : public IExecutionEngine
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IExecutionEngine methods for TLS
+ //***************************************************************************
+ // Associate a callback for cleanup with a TLS slot
+ VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
+
+ // Get the TLS block for fast Get/Set operations
+ LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
+ // Get the value at a slot
+ LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue);
+ // Set the value at a slot
+ VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
+ // Free TLS memory block and make callback
+ VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
+
+ //***************************************************************************
+ // IExecutionEngine methods for locking
+ //***************************************************************************
+ CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags);
+ void STDMETHODCALLTYPE DestroyLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE AcquireLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE ReleaseLock(CRITSEC_COOKIE lock);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateAutoEvent(BOOL bInitialState);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateManualEvent(BOOL bInitialState);
+ void STDMETHODCALLTYPE CloseEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrSetEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrResetEvent(EVENT_COOKIE event);
+ DWORD STDMETHODCALLTYPE WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds);
+ SEMAPHORE_COOKIE STDMETHODCALLTYPE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax);
+ void STDMETHODCALLTYPE ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore);
+ DWORD STDMETHODCALLTYPE ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount);
+ MUTEX_COOKIE STDMETHODCALLTYPE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName);
+ void STDMETHODCALLTYPE ClrCloseMutex(MUTEX_COOKIE mutex);
+ BOOL STDMETHODCALLTYPE ClrReleaseMutex(MUTEX_COOKIE mutex);
+ DWORD STDMETHODCALLTYPE ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrAllocationDisallowed();
+ void STDMETHODCALLTYPE GetLastThrownObjectExceptionFromThread(void **ppvException);
+};
+
+MyIEE *InitIExecutionEngine();
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/jitdebugger.cpp b/src/ToolBox/superpmi/superpmi/jitdebugger.cpp
new file mode 100644
index 0000000000..867664e82f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitdebugger.cpp
@@ -0,0 +1,459 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//*****************************************************************************
+// JitDebugger.cpp
+//
+// Code to help with invoking the just-in-time debugger
+//
+//*****************************************************************************
+
+#include "standardpch.h"
+#include "runtimedetails.h"
+#include "logging.h"
+#include "jitdebugger.h"
+
+// JIT debugging is broken due to utilcode changes to support LongFile. We need to re-copy
+// or adjust the implementation of the below functions so they link properly.
+#if 0
+#ifndef FEATURE_PAL // No just-in-time debugger under PAL
+#define FEATURE_JIT_DEBUGGING
+#endif // !FEATURE_PAL
+#endif // 0
+
+#ifndef FEATURE_JIT_DEBUGGING
+
+int DbgBreakCheck(
+ const char* szFile,
+ int iLine,
+ const char* szExpr)
+{
+ LogError("SuperPMI: Assert Failure (PID %d, Thread %d/%x)\n"
+ "%s\n"
+ "\n"
+ "%s, Line: %d\n",
+ GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId(),
+ szExpr, szFile, iLine);
+
+ return 1;
+}
+
+#else // FEATURE_JIT_DEBUGGING
+
+// Some definitions to make this code look more like the CLR utilcode versions it was stolen from.
+#define WszCreateEvent CreateEventW
+#define WszGetModuleFileName GetModuleFileNameW
+
+#ifdef WszRegOpenKeyEx
+#undef WszRegOpenKeyEx
+#define WszRegOpenKeyEx RegOpenKeyExW
+#endif
+
+#ifndef _WIN64
+//------------------------------------------------------------------------------
+// Returns TRUE if we are running on a 64-bit OS in WoW, FALSE otherwise.
+BOOL RunningInWow64()
+{
+ static int s_Wow64Process;
+
+ if (s_Wow64Process == 0)
+ {
+ BOOL fWow64Process = FALSE;
+
+ if (!IsWow64Process(GetCurrentProcess(), &fWow64Process))
+ fWow64Process = FALSE;
+
+ s_Wow64Process = fWow64Process ? 1 : -1;
+ }
+
+ return (s_Wow64Process == 1) ? TRUE : FALSE;
+}
+#endif
+
+//------------------------------------------------------------------------------
+//
+// GetRegistryLongValue - Reads a configuration LONG value from the registry.
+//
+// Parameters
+// hKeyParent -- Parent key
+// szKey -- key to open
+// szName -- name of the value
+// pValue -- put value here, if found
+// fReadNonVirtualizedKey -- whether to read 64-bit hive on WOW64
+//
+// Returns
+// TRUE -- If the value was found and read
+// FALSE -- The value was not found, could not be read, or was not DWORD
+//
+// Exceptions
+// None
+//------------------------------------------------------------------------------
+BOOL GetRegistryLongValue(HKEY hKeyParent,
+ LPCWSTR szKey,
+ LPCWSTR szName,
+ long *pValue,
+ BOOL fReadNonVirtualizedKey)
+{
+ DWORD ret; // Return value from registry operation.
+ HKEY hkey; // Registry key.
+ long iValue; // The value to read.
+ DWORD iType; // Type of value to get.
+ DWORD iSize; // Size of buffer.
+ REGSAM samDesired = KEY_READ; // Desired access rights to the key
+
+ if (fReadNonVirtualizedKey)
+ {
+ if (RunningInWow64())
+ {
+ samDesired |= KEY_WOW64_64KEY;
+ }
+ }
+
+ ret = WszRegOpenKeyEx(hKeyParent, szKey, 0, samDesired, &hkey);
+
+ // If we opened the key, see if there is a value.
+ if (ret == ERROR_SUCCESS)
+ {
+ iType = REG_DWORD;
+ iSize = sizeof(long);
+ ret = RegQueryValueExW(hkey, szName, NULL, &iType, reinterpret_cast<BYTE*>(&iValue), &iSize);
+
+ if (ret == ERROR_SUCCESS && iType == REG_DWORD && iSize == sizeof(long))
+ { // We successfully read a DWORD value.
+ *pValue = iValue;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+} // GetRegistryLongValue
+
+//----------------------------------------------------------------------------
+//
+// GetCurrentModuleFileName - Retrieve the current module's filename
+//
+// Arguments:
+// pBuffer - output string buffer
+// pcchBuffer - the number of characters of the string buffer
+//
+// Return Value:
+// S_OK on success, else detailed error code.
+//
+// Note:
+//
+//----------------------------------------------------------------------------
+HRESULT GetCurrentModuleFileName(__out_ecount(*pcchBuffer) LPWSTR pBuffer, __inout DWORD *pcchBuffer)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if ((pBuffer == NULL) || (pcchBuffer == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Get the appname to look up in the exclusion or inclusion list.
+ WCHAR appPath[MAX_PATH + 2];
+
+ DWORD ret = WszGetModuleFileName(NULL, appPath, NumItems(appPath));
+
+ if ((ret == NumItems(appPath)) || (ret == 0))
+ {
+ // The module file name exceeded maxpath, or GetModuleFileName failed.
+ return E_UNEXPECTED;
+ }
+
+ // Pick off the part after the path.
+ WCHAR* appName = wcsrchr(appPath, L'\\');
+
+ // If no backslash, use the whole name; if there is a backslash, skip it.
+ appName = appName ? appName+1 : appPath;
+
+ if (*pcchBuffer < wcslen(appName))
+ {
+ *pcchBuffer = static_cast<DWORD>(wcslen(appName)) + 1;
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ wcscpy_s(pBuffer, *pcchBuffer, appName);
+ return S_OK;
+}
+
+
+//----------------------------------------------------------------------------
+//
+// IsCurrentModuleFileNameInAutoExclusionList - decide if the current module's filename
+// is in the AutoExclusionList list
+//
+// Arguments:
+// None
+//
+// Return Value:
+// TRUE or FALSE
+//
+// Note:
+// This function cannot be used in out of process scenarios like DAC because it
+// looks at current module's filename. In OOP we want to use target process's
+// module's filename.
+//
+//----------------------------------------------------------------------------
+BOOL IsCurrentModuleFileNameInAutoExclusionList()
+{
+ HKEY hKeyHolder;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\AutoExclusionList"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerAutoExclusionListKey, 0, KEY_READ, &hKeyHolder);
+
+ if (ret != ERROR_SUCCESS)
+ {
+ // there's not even an AutoExclusionList hive
+ return FALSE;
+ }
+
+ WCHAR wszAppName[MAX_PATH];
+ DWORD cchAppName = NumItems(wszAppName);
+
+ // Get the appname to look up in the exclusion or inclusion list.
+ if (GetCurrentModuleFileName(wszAppName, &cchAppName) != S_OK)
+ {
+ // Assume it is not on the exclusion list if we cannot find the module's filename.
+ return FALSE;
+ }
+
+ // Look in AutoExclusionList key for appName get the size of any value stored there.
+ DWORD value, valueType, valueSize = sizeof(value);
+ ret = RegQueryValueExW(hKeyHolder, wszAppName, 0, &valueType, reinterpret_cast<BYTE*>(&value), &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_DWORD) && (value == 1))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+} // IsCurrentModuleFileNameInAutoExclusionList
+
+
+
+//*****************************************************************************
+// Retrieve information regarding what registered default debugger
+//*****************************************************************************
+void GetDebuggerSettingInfo(LPWSTR wszDebuggerString, DWORD cchDebuggerString, BOOL *pfAuto)
+{
+ HRESULT hr = GetDebuggerSettingInfoWorker(wszDebuggerString, &cchDebuggerString, pfAuto);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ // error!
+ }
+} // GetDebuggerSettingInfo
+
+//---------------------------------------------------------------------------------------
+//
+// GetDebuggerSettingInfoWorker - retrieve information regarding what registered default debugger
+//
+// Arguments:
+// * wszDebuggerString - [out] the string buffer to store the registered debugger launch
+// string
+// * pcchDebuggerString - [in, out] the size of string buffer in characters
+// * pfAuto - [in] the flag to indicate whether the debugger neeeds to be launched
+// automatically
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+// Notes:
+// * wszDebuggerString can be NULL. When wszDebuggerString is NULL, pcchDebuggerString should
+// * point to a DWORD of zero. pcchDebuggerString cannot be NULL, and the DWORD pointed by
+// * pcchDebuggerString will store the used or required string buffer size in characters.
+HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto)
+{
+ if ((pcchDebuggerString == NULL) || ((wszDebuggerString == NULL) && (*pcchDebuggerString != 0)))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Initialize the output values before we start.
+ if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
+ {
+ *wszDebuggerString = L'\0';
+ }
+
+ if (pfAuto != NULL)
+ {
+ *pfAuto = FALSE;
+ }
+
+ HKEY hKey;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerKey, 0, KEY_READ, &hKey);
+
+ if (ret != ERROR_SUCCESS)
+ { // Wow, there's not even an AeDebug hive, so no native debugger, no auto.
+ return S_OK;
+ }
+
+ // Look in AeDebug key for "Debugger"; get the size of any value stored there.
+ DWORD valueType, valueSize;
+ ret = RegQueryValueExW(hKey, kUnmanagedDebuggerValue, 0, &valueType, 0, &valueSize);
+
+ if ((wszDebuggerString == NULL) || (*pcchDebuggerString < valueSize / sizeof(WCHAR)))
+ {
+ *pcchDebuggerString = valueSize / sizeof(WCHAR) + 1;
+ RegCloseKey(hKey);
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ *pcchDebuggerString = valueSize / sizeof(WCHAR);
+
+ // The size of an empty string with the null terminator is 2.
+ BOOL fIsDebuggerStringEmptry = valueSize <= 2 ? TRUE : FALSE;
+
+ if ((ret != ERROR_SUCCESS) || (valueType != REG_SZ) || fIsDebuggerStringEmptry)
+ {
+ RegCloseKey(hKey);
+ return S_OK;
+ }
+
+ ret = RegQueryValueExW(hKey, kUnmanagedDebuggerValue, NULL, NULL, reinterpret_cast< LPBYTE >(wszDebuggerString), &valueSize);
+ if (ret != ERROR_SUCCESS)
+ {
+ *wszDebuggerString = L'\0';
+ RegCloseKey(hKey);
+ return S_OK;
+ }
+
+ if (pfAuto != NULL)
+ {
+ BOOL fAuto = FALSE;
+
+ // Get the appname to look up in DebugApplications key.
+ WCHAR wzAppName[MAX_PATH];
+ DWORD cchAppName = NumItems(wzAppName);
+ long iValue;
+
+ // Check DebugApplications setting
+ if ((SUCCEEDED(GetCurrentModuleFileName(wzAppName, &cchAppName))) &&
+ (
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsKey, wzAppName, &iValue, TRUE)
+ ) &&
+ (iValue == 1))
+ {
+ fAuto = TRUE;
+ }
+ else
+ {
+ // Look in AeDebug key for "Auto"; get the size of any value stored there.
+ ret = RegQueryValueExW(hKey, kUnmanagedDebuggerAutoValue, 0, &valueType, 0, &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_SZ) && (valueSize / sizeof(WCHAR) < MAX_PATH))
+ {
+ WCHAR wzAutoKey[MAX_PATH];
+ valueSize = NumItems(wzAutoKey) * sizeof(WCHAR);
+ RegQueryValueExW(hKey, kUnmanagedDebuggerAutoValue, NULL, NULL, reinterpret_cast< LPBYTE >(wzAutoKey), &valueSize);
+
+ // The OS's behavior is to consider Auto to be FALSE unless the first character is set
+ // to 1. They don't take into consideration the following characters. Also if the value
+ // isn't present they assume an Auto value of FALSE.
+ if ((wzAutoKey[0] == L'1') && !IsCurrentModuleFileNameInAutoExclusionList())
+ {
+ fAuto = TRUE;
+ }
+ }
+ }
+
+ *pfAuto = fAuto;
+ }
+
+ RegCloseKey(hKey);
+ return S_OK;
+} // GetDebuggerSettingInfoWorker
+
+BOOL LaunchJITDebugger()
+{
+ BOOL fSuccess = FALSE;
+
+ WCHAR debugger[1000];
+ GetDebuggerSettingInfo(debugger, NumItems(debugger), NULL);
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ // We can leave this event as it is since it is inherited by a child process.
+ // We will block one scheduler, but the process is asking a user if they want to attach debugger.
+ HANDLE eventHandle = WszCreateEvent(&sa, TRUE, FALSE, NULL);
+ if (eventHandle == NULL) {
+ return FALSE;
+ }
+
+ WCHAR cmdLine[1000];
+ swprintf_s(cmdLine, debugger, GetCurrentProcessId(), eventHandle);
+
+ STARTUPINFOW StartupInfo;
+ memset(&StartupInfo, 0, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(StartupInfo);
+ StartupInfo.lpDesktop = L"Winsta0\\Default";
+
+ PROCESS_INFORMATION ProcessInformation;
+ if (CreateProcessW(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
+ {
+ WaitForSingleObject(eventHandle, INFINITE);
+ fSuccess = TRUE;
+ }
+
+ CloseHandle(eventHandle);
+
+ return fSuccess;
+}
+
+
+// See if we should invoke the just-in-time debugger on an assert.
+int DbgBreakCheck(
+ const char* szFile,
+ int iLine,
+ const char* szExpr)
+{
+ char dialogText[1000];
+ char dialogTitle[1000];
+
+ sprintf_s(dialogText, sizeof(dialogText), "%s\n\n%s, Line: %d\n\nAbort - Kill program\nRetry - Debug\nIgnore - Keep running\n",
+ szExpr, szFile, iLine);
+ sprintf_s(dialogTitle, sizeof(dialogTitle), "SuperPMI: Assert Failure (PID %d, Thread %d/%x) ",
+ GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId());
+
+ // Tell user there was an error.
+ int ret = MessageBoxA(NULL, dialogText, dialogTitle, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION | MB_TOPMOST);
+
+ switch(ret)
+ {
+ case IDABORT:
+ TerminateProcess(GetCurrentProcess(), 1);
+ break;
+
+ // Tell caller to break at the correct loction.
+ case IDRETRY:
+
+ if (IsDebuggerPresent())
+ {
+ SetErrorMode(0);
+ }
+ else
+ {
+ LaunchJITDebugger();
+ }
+
+ return 1;
+
+ case IDIGNORE:
+ // nothing to do
+ break;
+ }
+
+ return 0;
+}
+
+#endif // FEATURE_JIT_DEBUGGING
diff --git a/src/ToolBox/superpmi/superpmi/jitdebugger.h b/src/ToolBox/superpmi/superpmi/jitdebugger.h
new file mode 100644
index 0000000000..eee2318f8f
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitdebugger.h
@@ -0,0 +1,33 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JitDebugger
+#define _JitDebugger
+
+extern bool breakOnDebugBreakorAV; // It's kind of awful that I'm making this global, but it was kind of awful that it was file-global already.
+
+//
+// Functions to support just-in-time debugging.
+//
+
+BOOL GetRegistryLongValue(HKEY hKeyParent, // Parent key.
+ LPCWSTR szKey, // Key name to look at.
+ LPCWSTR szName, // Name of value to get.
+ long *pValue, // Put value here, if found.
+ BOOL fReadNonVirtualizedKey); // Whether to read 64-bit hive on WOW64
+
+HRESULT GetCurrentModuleFileName(__out_ecount(*pcchBuffer) LPWSTR pBuffer, __inout DWORD *pcchBuffer);
+
+#ifndef _WIN64
+BOOL RunningInWow64();
+#endif
+
+BOOL IsCurrentModuleFileNameInAutoExclusionList();
+HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto);
+void GetDebuggerSettingInfo(LPWSTR wszDebuggerString, DWORD cchDebuggerString, BOOL *pfAuto);
+
+int DbgBreakCheck(const char* szFile, int iLine, const char* szExpr);
+
+#endif // !_JitDebugger
diff --git a/src/ToolBox/superpmi/superpmi/jithost.cpp b/src/ToolBox/superpmi/superpmi/jithost.cpp
new file mode 100644
index 0000000000..cb61e48de2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jithost.cpp
@@ -0,0 +1,120 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "superpmi.h"
+#include "jitinstance.h"
+#include "icorjitinfo.h"
+#include "jithost.h"
+
+// Look for 'key' as an environment variable named COMPlus_<key>. The returned value
+// is nullptr if it is not found, or a string if found. If not nullptr, the returned
+// value must be freed with jitInstance.freeLongLivedArray(value).
+wchar_t* GetCOMPlusVariable(const wchar_t* key, JitInstance& jitInstance)
+{
+ static const wchar_t Prefix[] = W("COMPlus_");
+ static const size_t PrefixLen = (sizeof(Prefix) / sizeof(Prefix[0])) - 1;
+
+ // Prepend "COMPlus_" to the provided key
+ size_t keyLen = wcslen(key);
+ size_t keyBufferLen = keyLen + PrefixLen + 1;
+ wchar_t* keyBuffer = reinterpret_cast<wchar_t*>(jitInstance.allocateArray(static_cast<ULONG>(sizeof(wchar_t) * keyBufferLen)));
+ wcscpy_s(keyBuffer, keyBufferLen, Prefix);
+ wcscpy_s(&keyBuffer[PrefixLen], keyLen + 1, key);
+
+ // Look up the environment variable
+ DWORD valueLen = GetEnvironmentVariableW(keyBuffer, nullptr, 0);
+ if (valueLen == 0)
+ {
+ jitInstance.freeArray(keyBuffer);
+ return nullptr;
+ }
+
+ // Note this value must live as long as the jit instance does.
+ wchar_t* value = reinterpret_cast<wchar_t*>(jitInstance.allocateLongLivedArray(sizeof(wchar_t) * valueLen));
+ DWORD newValueLen = GetEnvironmentVariableW(keyBuffer, value, valueLen);
+
+ jitInstance.freeArray(keyBuffer);
+ if (valueLen < newValueLen)
+ {
+ jitInstance.freeLongLivedArray(value);
+ return nullptr;
+ }
+
+ return value;
+}
+
+JitHost::JitHost(JitInstance& jitInstance) : jitInstance(jitInstance)
+{
+}
+
+void* JitHost::allocateMemory(size_t size, bool usePageAllocator)
+{
+ return InitIEEMemoryManager(&jitInstance)->ClrVirtualAlloc(nullptr, size, 0, 0);
+}
+
+void JitHost::freeMemory(void* block, bool usePageAllocator)
+{
+ InitIEEMemoryManager(&jitInstance)->ClrVirtualFree(block, 0, 0);
+}
+
+int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
+{
+ jitInstance.mc->cr->AddCall("getIntConfigValue");
+ int result = jitInstance.mc->repGetIntConfigValue(key, defaultValue);
+
+ if (result != defaultValue)
+ {
+ return result;
+ }
+
+ // Look for special case keys.
+ if (wcscmp(key, W("SuperPMIMethodContextNumber")) == 0)
+ {
+ return jitInstance.mc->index;
+ }
+
+ // If the result is the default value, probe the environment for
+ // a COMPlus variable with the same name.
+ wchar_t* complus = GetCOMPlusVariable(key, jitInstance);
+ if (complus == nullptr)
+ {
+ return defaultValue;
+ }
+
+ // Parse the value as a hex integer.
+ wchar_t* endPtr;
+ result = static_cast<int>(wcstoul(complus, &endPtr, 16));
+ bool succeeded = (errno != ERANGE) && (endPtr != complus);
+ jitInstance.freeLongLivedArray(complus);
+
+ return succeeded ? result : defaultValue;
+}
+
+const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
+{
+ jitInstance.mc->cr->AddCall("getStringConfigValue");
+ const wchar_t *result = jitInstance.mc->repGetStringConfigValue(key);
+
+ if (result != nullptr)
+ {
+ // Now we need to dup it, so you can call freeStringConfigValue() on what we return.
+ size_t resultLenInChars = wcslen(result) + 1;
+ wchar_t *dupResult = (wchar_t*)jitInstance.allocateLongLivedArray((ULONG)(sizeof(wchar_t) * resultLenInChars));
+ wcscpy_s(dupResult, resultLenInChars, result);
+
+ return dupResult;
+ }
+
+ // If the result is the default value, probe the environment for
+ // a COMPlus variable with the same name.
+ return GetCOMPlusVariable(key, jitInstance);
+}
+
+void JitHost::freeStringConfigValue(const wchar_t* value)
+{
+ jitInstance.mc->cr->AddCall("freeStringConfigValue");
+ jitInstance.freeLongLivedArray((void*)value);
+}
diff --git a/src/ToolBox/superpmi/superpmi/jithost.h b/src/ToolBox/superpmi/superpmi/jithost.h
new file mode 100644
index 0000000000..60181cd907
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jithost.h
@@ -0,0 +1,20 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JITHOST
+#define _JITHOST
+
+class JitHost : public ICorJitHost
+{
+public:
+ JitHost(JitInstance& jitInstance);
+
+#include "icorjithostimpl.h"
+
+private:
+ JitInstance& jitInstance;
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/jitinstance.cpp b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
new file mode 100644
index 0000000000..5003e91f96
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
@@ -0,0 +1,436 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "superpmi.h"
+#include "jitinstance.h"
+#include "coreclrcallbacks.h"
+#include "icorjitinfo.h"
+#include "jithost.h"
+#include "errorhandling.h"
+#include "spmiutil.h"
+
+JitInstance *JitInstance::InitJit(char *nameOfJit, bool breakOnAssert, SimpleTimer *st1, MethodContext* firstContext)
+{
+ JitInstance *jit = new JitInstance();
+ if (jit == nullptr)
+ {
+ LogError("Failed to allocate a JitInstance");
+ return nullptr;
+ }
+
+ if (st1 != nullptr)
+ st1->Start();
+ HRESULT hr = jit->StartUp(nameOfJit, false, breakOnAssert, firstContext);
+ if (st1 != nullptr)
+ st1->Stop();
+ if (hr != S_OK)
+ {
+ LogError("Startup of JIT(%s) failed %d", nameOfJit, hr);
+ return nullptr;
+ }
+ if (st1 != nullptr)
+ LogVerbose("Jit startup took %fms", st1->GetMilliseconds());
+ return jit;
+}
+
+HRESULT JitInstance::StartUp(char * PathToJit, bool copyJit, bool parambreakOnDebugBreakorAV, MethodContext* firstContext)
+{
+ //startup jit
+ DWORD dwRetVal = 0;
+ UINT uRetVal = 0;
+ BOOL bRetVal = FALSE;
+
+ breakOnDebugBreakorAV = parambreakOnDebugBreakorAV;
+
+ char pFullPathName[MAX_PATH];
+ char lpTempPathBuffer[MAX_PATH];
+ char szTempFileName[MAX_PATH];
+
+//Get an allocator instance
+//Note: we do this to keep cleanup somewhat simple...
+ ourHeap = ::HeapCreate(0,0,0);
+ if(ourHeap == nullptr)
+ {
+ LogError("Failed to get a new heap (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//find the full jit path
+ dwRetVal = ::GetFullPathNameA(PathToJit, MAX_PATH, pFullPathName, nullptr);
+ if (dwRetVal == 0)
+ {
+ LogError("GetFullPathName failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//Store the full path to the jit
+ PathToOriginalJit = (char *)::HeapAlloc(ourHeap, 0, MAX_PATH);
+ if(PathToOriginalJit == nullptr)
+ {
+ LogError("1st HeapAlloc failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ ::strcpy_s(PathToOriginalJit, MAX_PATH, pFullPathName);
+
+ if(copyJit)
+ {
+ //Get a temp file location
+ dwRetVal = ::GetTempPathA(MAX_PATH, lpTempPathBuffer);
+ if (dwRetVal == 0)
+ {
+ LogError("GetTempPath failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ if (dwRetVal > MAX_PATH)
+ {
+ LogError("GetTempPath returned a path that was larger than MAX_PATH");
+ return E_FAIL;
+ }
+ //Get a temp filename
+ uRetVal = ::GetTempFileNameA(lpTempPathBuffer, "Jit", 0, szTempFileName);
+ if (uRetVal == 0)
+ {
+ LogError("GetTempFileName failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ dwRetVal = (DWORD)::strlen(szTempFileName);
+
+ //Store the full path to the temp jit
+ PathToTempJit = (char *)::HeapAlloc(ourHeap, 0, MAX_PATH);
+ if(PathToTempJit == nullptr)
+ {
+ LogError("2nd HeapAlloc failed 0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ ::strcpy_s(PathToTempJit, MAX_PATH, szTempFileName);
+
+ //Copy Temp File
+ bRetVal = ::CopyFileA(PathToOriginalJit, PathToTempJit, FALSE);
+ if (bRetVal == FALSE)
+ {
+ LogError("CopyFile failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ }
+ else
+ PathToTempJit = PathToOriginalJit;
+
+#ifndef FEATURE_PAL // No file version APIs in the PAL
+ //Do a quick version check
+ DWORD dwHandle = 0;
+ DWORD fviSize = GetFileVersionInfoSizeA(PathToTempJit, &dwHandle);
+
+ if ((fviSize != 0)&&(dwHandle==0))
+ {
+ unsigned char *fviData = new unsigned char[fviSize];
+ if (GetFileVersionInfoA(PathToTempJit, dwHandle, fviSize, fviData))
+ {
+ UINT size = 0;
+ VS_FIXEDFILEINFO *verInfo = nullptr;
+ if (VerQueryValueA(fviData, "\\", (LPVOID*)&verInfo, &size))
+ {
+ if (size)
+ {
+ if (verInfo->dwSignature == 0xfeef04bd)
+ LogDebug("'%s' is version %u.%u.%u.%u", PathToTempJit,
+ (verInfo->dwFileVersionMS)>>16, (verInfo->dwFileVersionMS)&0xFFFF,
+ (verInfo->dwFileVersionLS)>>16, (verInfo->dwFileVersionLS)&0xFFFF);
+ }
+ }
+ }
+ delete[] fviData;
+ }
+#endif // !FEATURE_PAL
+
+//Load Library
+ hLib = ::LoadLibraryA(PathToTempJit);
+ if(hLib == 0)
+ {
+ LogError("LoadLibrary failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//get entry points
+ pngetJit = (PgetJit)::GetProcAddress(hLib, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return -1;
+ }
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(hLib, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return -1;
+ }
+ pnjitStartup = (PjitStartup)::GetProcAddress(hLib, "jitStartup");
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ CoreClrCallbacks *cccallbacks = InitCoreClrCallbacks();
+ pnsxsJitStartup(*cccallbacks);
+
+ // Setup ICorJitHost and call jitStartup if necessary
+ if (pnjitStartup != nullptr)
+ {
+ mc = firstContext;
+ jitHost = new JitHost(*this);
+ pnjitStartup(jitHost);
+ }
+
+ pJitInstance = pngetJit();
+ if(pJitInstance == nullptr)
+ {
+ LogError("pngetJit gave us null");
+ return -1;
+ }
+
+ // Check the JIT version identifier.
+
+ GUID versionId;
+ memset(&versionId, 0, sizeof(GUID));
+ pJitInstance->getVersionIdentifier(&versionId);
+
+ if (memcmp(&versionId, &JITEEVersionIdentifier, sizeof(GUID)) != 0)
+ {
+ // Mismatched version ID. Fail the load.
+ pJitInstance = NULL;
+
+ LogError("Jit Compiler has wrong version identifier");
+ return -1;
+ }
+
+
+ icji = InitICorJitInfo(this);
+
+ return S_OK;
+}
+
+bool JitInstance::reLoad(MethodContext* firstContext)
+{
+ FreeLibrary(hLib);
+
+//Load Library
+ hLib = ::LoadLibraryA(PathToTempJit);
+ if(hLib == 0)
+ {
+ LogError("LoadLibrary failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+
+//get entry points
+ pngetJit = (PgetJit)::GetProcAddress(hLib, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(hLib, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ pnjitStartup = (PjitStartup)::GetProcAddress(hLib, "jitStartup");
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ CoreClrCallbacks *cccallbacks = InitCoreClrCallbacks();
+ pnsxsJitStartup(*cccallbacks);
+
+ // Setup ICorJitHost and call jitStartup if necessary
+ if (pnjitStartup != nullptr)
+ {
+ mc = firstContext;
+ jitHost = new JitHost(*this);
+ pnjitStartup(jitHost);
+ }
+
+ pJitInstance = pngetJit();
+ if(pJitInstance == nullptr)
+ {
+ LogError("pngetJit gave us null");
+ return false;
+ }
+
+ icji = InitICorJitInfo(this);
+
+ return true;
+}
+
+JitInstance::Result JitInstance::CompileMethod(MethodContext *MethodToCompile, int mcIndex, bool collectThroughput)
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException
+ {
+ JitInstance* pThis;
+ JitInstance::Result result;
+ CORINFO_METHOD_INFO info;
+ unsigned flags;
+ int mcIndex;
+ bool collectThroughput;
+ } param;
+ param.pThis = this;
+ param.result = RESULT_SUCCESS; // assume success
+ param.flags = 0;
+ param.mcIndex = mcIndex;
+ param.collectThroughput = collectThroughput;
+
+ //store to instance field our raw values, so we can figure things out a bit later...
+ mc = MethodToCompile;
+
+ times[0] = 0;
+ times[1] = 0;
+
+ mc->repEnvironmentSet(); //Sets envvars
+ stj.Start();
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ BYTE *NEntryBlock = nullptr;
+ ULONG NCodeSizeBlock = 0;
+
+ pParam->pThis->mc->repCompileMethod(&pParam->info, &pParam->flags);
+ if (pParam->collectThroughput)
+ {
+ pParam->pThis->lt.Start();
+ }
+ CorJitResult temp = pParam->pThis->pJitInstance->compileMethod(pParam->pThis->icji, &pParam->info, pParam->flags, &NEntryBlock, &NCodeSizeBlock);
+ if (pParam->collectThroughput)
+ {
+ pParam->pThis->lt.Stop();
+ pParam->pThis->times[0] = pParam->pThis->lt.GetCycles();
+ }
+ if ((SpmiTargetArchitecture == SPMI_TARGET_ARCHITECTURE_ARM64) && (temp == CORJIT_SKIPPED))
+ {
+ // For altjit, treat SKIPPED as OK
+ temp = CORJIT_OK;
+ }
+ if (temp == CORJIT_OK)
+ {
+ //capture the results of compilation
+ pParam->pThis->mc->cr->recCompileMethod(&NEntryBlock, &NCodeSizeBlock, temp);
+ pParam->pThis->mc->cr->recAllocMemCapture();
+ pParam->pThis->mc->cr->recAllocGCInfoCapture();
+
+ pParam->pThis->mc->cr->recMessageLog("Successful Compile");
+ }
+ else
+ {
+ LogDebug("compileMethod failed with result %d", temp);
+ pParam->result = RESULT_ERROR;
+ }
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop)
+ {
+ SpmiException e(&param.exceptionPointers);
+
+ if (e.GetCode() == EXCEPTIONCODE_MC)
+ {
+ char *message = e.GetExceptionMessage();
+ LogMissing("Method context %d failed to replay: %s", mcIndex, message);
+ e.DeleteMessage();
+ param.result = RESULT_MISSING;
+ }
+ else
+ {
+ e.ShowAndDeleteMessage();
+ param.result = RESULT_ERROR;
+ }
+ }
+ PAL_ENDTRY
+
+ stj.Stop();
+ if (collectThroughput)
+ {
+ // If we get here, we know it compiles
+ timeResult(param.info, param.flags);
+ }
+ mc->repEnvironmentUnset(); //Unsets envvars
+
+ mc->cr->secondsToCompile = stj.GetSeconds();
+
+ return param.result;
+}
+
+void JitInstance::timeResult(CORINFO_METHOD_INFO info, unsigned flags)
+{
+ BYTE *NEntryBlock = nullptr;
+ ULONG NCodeSizeBlock = 0;
+
+ int sampleSize = 10;
+ // Save 2 smallest times. To help reduce noise, we will look at the closest pair of these.
+ unsigned __int64 time;
+
+ for (int i = 0; i < sampleSize; i++)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ lt.Start();
+ pJitInstance->compileMethod(icji, &info, flags, &NEntryBlock, &NCodeSizeBlock);
+ lt.Stop();
+ time = lt.GetCycles();
+ if (times[1] == 0)
+ {
+ if (time < times[0])
+ {
+ times[1] = times[0];
+ times[0] = time;
+ }
+ else
+ times[1] = time;
+ }
+ else if (time < times[1])
+ {
+ if (time < times[0])
+ {
+ times[1] = times[0];
+ times[0] = time;
+ }
+ else
+ times[1] = time;
+ }
+ }
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * JitInstance::allocateArray(
+ ULONG cBytes
+ )
+{
+ mc->cr->AddCall("allocateArray");
+ return HeapAlloc(mc->cr->getCodeHeap(),0,cBytes);
+}
+
+// Used to allocate memory that needs to live as long as the jit
+// instance does.
+void * JitInstance::allocateLongLivedArray(
+ ULONG cBytes
+ )
+{
+ return HeapAlloc(ourHeap,0,cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void JitInstance::freeArray(
+ void *array
+ )
+{
+ mc->cr->AddCall("freeArray");
+ HeapFree(mc->cr->getCodeHeap(),0,array);
+}
+
+// Used to free memory allocated by JitInstance::allocateLongLivedArray.
+void JitInstance::freeLongLivedArray(
+ void *array
+ )
+{
+ HeapFree(ourHeap,0,array);
+}
diff --git a/src/ToolBox/superpmi/superpmi/jitinstance.h b/src/ToolBox/superpmi/superpmi/jitinstance.h
new file mode 100644
index 0000000000..c85c2f5bee
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitinstance.h
@@ -0,0 +1,57 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _JitInstance
+#define _JitInstance
+
+#include "superpmi.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "cycletimer.h"
+
+class JitInstance
+{
+private:
+ char *PathToOriginalJit;
+ char *PathToTempJit;
+ HANDLE ourHeap;
+ HMODULE hLib;
+ PgetJit pngetJit;
+ PjitStartup pnjitStartup;
+ PsxsJitStartup pnsxsJitStartup;
+ ICorJitHost *jitHost;
+ ICorJitInfo *icji;
+ SimpleTimer stj;
+
+ JitInstance() {};
+ void timeResult(CORINFO_METHOD_INFO info, unsigned flags);
+
+public:
+ enum Result
+ {
+ RESULT_ERROR,
+ RESULT_SUCCESS,
+ RESULT_MISSING
+ };
+ CycleTimer lt;
+ MethodContext *mc;
+ ULONGLONG times[2];
+ ICorJitCompiler *pJitInstance;
+
+ // Allocate and initialize the jit provided
+ static JitInstance *InitJit(char *nameOfJit, bool breakOnAssert, SimpleTimer *st1, MethodContext* firstContext);
+
+ HRESULT StartUp(char *PathToJit, bool copyJit, bool breakOnDebugBreakorAV, MethodContext* firstContext);
+ bool reLoad(MethodContext* firstContext);
+
+ Result CompileMethod(MethodContext *MethodToCompile, int mcIndex, bool collectThroughput);
+
+ void* allocateArray(ULONG size);
+ void* allocateLongLivedArray(ULONG size);
+ void freeArray(void* array);
+ void freeLongLivedArray(void* array);
+};
+
+#endif
diff --git a/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp b/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp
new file mode 100644
index 0000000000..0a43f02dd9
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp
@@ -0,0 +1,126 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//-----------------------------------------------------------------------------
+// MethodStatsEmitter.cpp - Emits useful method stats for compiled methods for analysis
+//-----------------------------------------------------------------------------
+
+#include "standardpch.h"
+#include "methodstatsemitter.h"
+#include "logging.h"
+
+MethodStatsEmitter::MethodStatsEmitter(char *nameOfInput)
+{
+ char filename[MAX_PATH + 1];
+ sprintf_s(filename, MAX_PATH + 1, "%s.stats", nameOfInput);
+
+ hStatsFile = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hStatsFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", filename, GetLastError());
+ }
+}
+
+MethodStatsEmitter::~MethodStatsEmitter()
+{
+ if (hStatsFile != INVALID_HANDLE_VALUE)
+ {
+ if (CloseHandle(hStatsFile) == 0)
+ {
+ LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
+ }
+ }
+}
+
+void MethodStatsEmitter::Emit(int methodNumber, MethodContext *mc, ULONGLONG firstTime, ULONGLONG secondTime)
+{
+ if (hStatsFile != INVALID_HANDLE_VALUE)
+ {
+ //Print the CSV header row
+ char rowData[2048];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'h') != NULL || strchr(statsTypes, 'H') != NULL)
+ {
+ //Obtain the method Hash
+ char md5Hash[MD5_HASH_BUFFER_SIZE];
+ if (mc->dumpMethodMD5HashToBuffer(md5Hash, MD5_HASH_BUFFER_SIZE) != MD5_HASH_BUFFER_SIZE)
+ md5Hash[0] = 0;
+
+ charCount += sprintf(rowData + charCount, "%s,", md5Hash);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL)
+ {
+ charCount += sprintf(rowData + charCount, "%d,", methodNumber);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL)
+ {
+ //Obtain the IL code size for this method
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ charCount += sprintf(rowData + charCount, "%d,", info.ILCodeSize);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL)
+ {
+ //Obtain the compiled method ASM size
+ BYTE *temp;
+ DWORD codeSize;
+ CorJitResult result;
+ if (mc->cr->CompileMethod != nullptr)
+ mc->cr->repCompileMethod(&temp, &codeSize, &result);
+ else
+ codeSize = 0;//this is likely a thin mc
+
+ charCount += sprintf(rowData + charCount, "%d,", codeSize);
+ }
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL)
+ {
+ charCount += sprintf(rowData + charCount, "%llu,%llu,", firstTime, secondTime);
+ }
+
+ //get rid of the final ',' and replace it with a '\n'
+ rowData[charCount - 1] = '\n';
+
+ if (!WriteFile(hStatsFile, rowData, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
+ {
+ LogError("Failed to write row header '%s'. GetLastError()=%u", rowData, GetLastError());
+ }
+ }
+}
+
+void MethodStatsEmitter::SetStatsTypes(char *types)
+{
+ statsTypes = types;
+
+ if (hStatsFile != INVALID_HANDLE_VALUE)
+ {
+ //Print the CSV header row
+ char rowHeader[1024];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'h') != NULL || strchr(statsTypes, 'H') != NULL)
+ charCount += sprintf(rowHeader + charCount, "HASH,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL)
+ charCount += sprintf(rowHeader + charCount, "METHOD_NUMBER,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL)
+ charCount += sprintf(rowHeader + charCount, "IL_CODE_SIZE,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL)
+ charCount += sprintf(rowHeader + charCount, "ASM_CODE_SIZE,");
+ if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL)
+ charCount += sprintf(rowHeader + charCount, "Time1,Time2,");
+
+ //get rid of the final ',' and replace it with a '\n'
+ rowHeader[charCount - 1] = '\n';
+
+ if (!WriteFile(hStatsFile, rowHeader, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
+ {
+ LogError("Failed to write row header '%s'. GetLastError()=%u", rowHeader, GetLastError());
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/methodstatsemitter.h b/src/ToolBox/superpmi/superpmi/methodstatsemitter.h
new file mode 100644
index 0000000000..fb651b04b2
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/methodstatsemitter.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//-----------------------------------------------------------------------------
+// MethodStatsEmitter.h - Emits useful method stats for compiled methods for analysis
+//-----------------------------------------------------------------------------
+#ifndef _MethodStatsEmitter
+#define _MethodStatsEmitter
+
+#include "methodcontext.h"
+#include "jitinstance.h"
+
+class MethodStatsEmitter
+{
+
+private:
+ char *statsTypes;
+ HANDLE hStatsFile;
+
+public:
+ MethodStatsEmitter(char *nameOfInput);
+ ~MethodStatsEmitter();
+
+ void Emit(int methodNumber, MethodContext *mc, ULONGLONG firstTime, ULONGLONG secondTime);
+ void SetStatsTypes(char *types);
+};
+#endif \ No newline at end of file
diff --git a/src/ToolBox/superpmi/superpmi/neardiffer.cpp b/src/ToolBox/superpmi/superpmi/neardiffer.cpp
new file mode 100644
index 0000000000..5b2e3b1b57
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/neardiffer.cpp
@@ -0,0 +1,1031 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// nearDiffer.cpp - differ that handles code that is very similar
+//----------------------------------------------------------
+
+#include "standardpch.h"
+
+#ifdef USE_COREDISTOOLS
+#include "coredistools.h"
+#endif // USE_COREDISTOOLS
+
+#include "logging.h"
+#include "neardiffer.h"
+
+#ifdef USE_COREDISTOOLS
+
+//
+// Helper functions to print messages from CoreDisTools Library
+// The file/linenumber information is from this helper itself,
+// since we are only linking with the CoreDisTools library.
+//
+static void LogFromCoreDisToolsHelper(LogLevel level, const char *msg, va_list argList)
+{
+ Logger::LogVprintf(__func__, __FILE__, __LINE__, level, argList, msg);
+}
+
+#define LOGGER(L) \
+static void Log##L(const char *msg, ...) \
+{\
+ va_list argList; \
+ va_start(argList, msg); \
+ LogFromCoreDisToolsHelper (LOGLEVEL_##L, msg, argList); \
+ va_end(argList); \
+}
+
+LOGGER(VERBOSE)
+LOGGER(ERROR)
+LOGGER(WARNING)
+
+const PrintControl CorPrinter= { LogERROR, LogWARNING, LogVERBOSE, LogVERBOSE };
+
+#endif // USE_COREDISTOOLS
+
+//
+// The NearDiff Disassembler Initialization
+//
+void NearDiffer::InitAsmDiff()
+{
+#ifdef USE_COREDISTOOLS
+ if (UseCoreDisTools)
+ {
+ corAsmDiff = NewDiffer(Target_Host, &CorPrinter, NearDiffer::compareOffsets);
+ }
+#endif // USE_COREDISTOOLS
+}
+
+//
+// The NearDiff destructor
+//
+NearDiffer::~NearDiffer()
+{
+#ifdef USE_COREDISTOOLS
+ if (corAsmDiff != nullptr)
+ {
+ FinishDiff(corAsmDiff);
+ }
+#endif // USE_COREDISTOOLS
+}
+
+// At a high level, the near differ takes in a method context and two compile results, performs
+// some simple fixups, and then compares the main artifacts of the compile result (i.e. generated
+// code, GC info, EH info, debug info, etc.) for equality. In order to be fast, the fixups and
+// definitions of "equality" are minimal; for example, the GC info check just does a simple memcmp.
+//
+// The entrypoint into the near differ is nearDiffer::compare; its doc comments will have more
+// details on what it does. That function in turn fans out to various other components. For asm
+// diffing, the main function of interest will be nearDiffer::compareCodeSection.
+//
+// Most of the diffing logic is architecture-independent, with the following exceptions:
+//
+// - The MSDIS instance must be created with knowledge of the architecture it is working with.
+// - The heuristics to compare different literal operand values has some architecture-specific
+// assumptions.
+// - The code stream is fixed up using relocations recorded during compilation time. The logic
+// for applying these should, in theory, be architecture independent, but depending on how
+// the runtime implements this from platform to platform, there might be subtle differences here.
+//
+
+#ifdef USE_MSVCDIS
+
+DIS* NearDiffer::GetMsVcDis()
+{
+ DIS *disasm;
+
+#ifdef _TARGET_AMD64_
+ if ((TargetArchitecture != nullptr) && (0 == _stricmp(TargetArchitecture, "arm64")))
+ {
+ disasm = DIS::PdisNew(DIS::distArm64);
+ }
+ else
+ {
+ disasm = DIS::PdisNew(DIS::distX8664);
+ }
+#elif defined(_TARGET_X86_)
+ disasm = DIS::PdisNew(DIS::distX86);
+#endif
+
+ return disasm;
+}
+
+#endif // USE_MSVCDIS
+
+//
+// Simple, quick-and-dirty disassembler. If NearDiffer::compareCodeSection finds that two code
+// streams differ, it will call this to dump the two differing code blocks to the log. The dump
+// is logged under the verbose logging level.
+//
+// The output format is in MSDIS's disassembly format.
+//
+// Arguments:
+// block - A pointer to the code block to diassemble.
+// blocksize - The size of the code block to disassemble.
+// originalAddr - The original base address of the code block.
+//
+void NearDiffer::DumpCodeBlock(unsigned char *block, ULONG blocksize, void *originalAddr)
+{
+#ifdef USE_MSVCDIS
+ DIS *disasm = GetMsVcDis();
+ size_t offset = 0;
+ std::string codeBlock;
+
+ while (offset < blocksize)
+ {
+ DIS::INSTRUCTION instr;
+ DIS::OPERAND ops[3];
+
+ size_t instrSize = disasm->CbDisassemble((DIS::ADDR)originalAddr + offset, (void *)(block + offset), 15);
+ if(instrSize==0)
+ {
+ LogWarning("Zero sized instruction");
+ break;
+ }
+ disasm->FDecode(&instr, ops, 3);
+
+ wchar_t instrMnemonicWide[64]; // I never know how much to allocate...
+ disasm->CchFormatInstr(instrMnemonicWide, 64);
+ char instrMnemonic[128];
+ size_t count;
+ wcstombs_s(&count, instrMnemonic, 128, instrMnemonicWide, 64);
+
+ const size_t minInstrBytes = 7;
+ size_t instrBytes = max(instrSize, minInstrBytes);
+ size_t buffSize = _snprintf(nullptr, 0, "%p %s\n", (void*)((size_t)originalAddr+offset), instrMnemonic) + 3 * instrBytes + 1;
+ char *buff = new char[buffSize];
+ int written = 0;
+ written += sprintf_s(buff, buffSize, "%p ", (void*)((size_t)originalAddr+offset));
+ for (size_t i = 0; i < instrBytes; i++)
+ {
+ if (i < instrSize)
+ {
+ written += sprintf_s(buff + written, buffSize - written, "%02X ", *(const uint8_t*)(block + offset + i));
+ }
+ else
+ {
+ written += sprintf_s(buff + written, buffSize - written, " ");
+ }
+ }
+ written += sprintf_s(buff + written, buffSize - written, "%s\n", instrMnemonic);
+ codeBlock += buff;
+ delete[] buff;
+ offset += instrSize;
+ }
+ LogVerbose("Code dump:\n%s", codeBlock.c_str());
+ delete disasm;
+#else // !USE_MSVCDIS
+ LogVerbose("No disassembler");
+#endif // !USE_MSVCDIS
+}
+
+//
+// Struct to capture the information required by offset comparator.
+//
+struct DiffData
+{
+ // Common Data
+ CompileResult *cr;
+
+ // Details of the first block
+ size_t blocksize1;
+ size_t datablock1;
+ size_t datablockSize1;
+ size_t originalBlock1;
+ size_t originalDataBlock1;
+ size_t otherCodeBlock1;
+ size_t otherCodeBlockSize1;
+
+ // Details of the second block
+ size_t blocksize2;
+ size_t datablock2;
+ size_t datablockSize2;
+ size_t originalBlock2;
+ size_t originalDataBlock2;
+ size_t otherCodeBlock2;
+ size_t otherCodeBlockSize2;
+};
+
+//
+// NearDiff Offset Comparator.
+// Determine whether two syntactically different constants are
+// semantically equivalent, using certain heuristics.
+//
+bool NearDiffer::compareOffsets(const void *payload,
+ size_t blockOffset,
+ size_t instrLen,
+ uint64_t offset1,
+ uint64_t offset2)
+{
+ // The trivial case
+ if (offset1 == offset2)
+ {
+ return true;
+ }
+
+ const DiffData *data = (const DiffData *)payload;
+ size_t ip1 = data->originalBlock1 + blockOffset;
+ size_t ip2 = data->originalBlock2 + blockOffset;
+ size_t ipRelOffset1 = ip1 + instrLen + (size_t)offset1;
+ size_t ipRelOffset2 = ip2 + instrLen + (size_t)offset2;
+
+ // Case where we have a call into flat address -- the most common case.
+ size_t gOffset1 = ipRelOffset1;
+ size_t gOffset2 = ipRelOffset2;
+ if ((DWORD)gOffset1 == (DWORD)gOffset2) //make sure the lower 32bits match (best we can do in the current replay form)
+ return true;
+
+ //Case where we have an offset into the read only section (e.g. loading a float value)
+ size_t roOffset1a = (size_t)offset1 - data->originalDataBlock1;
+ size_t roOffset2a = (size_t)offset2 - data->originalDataBlock2;
+ if ((roOffset1a == roOffset2a) && (roOffset1a < data->datablockSize1)) //Confirm its an offset that fits inside our RoRegion
+ return true;
+
+ // This case is written to catch IP-relative offsets to the RO data-section
+ // For example:
+ //
+ size_t roOffset1b = ipRelOffset1 - data->originalDataBlock1;
+ size_t roOffset2b = ipRelOffset2 - data->originalDataBlock2;
+ if ((roOffset1b == roOffset2b) && (roOffset1b < data->datablockSize1)) //Confirm its an offset that fits inside our RoRegion
+ return true;
+
+ //Case where we push an address to our own code section.
+ size_t gOffset1a = (size_t)offset1 - data->originalBlock1;
+ size_t gOffset2a = (size_t)offset2 - data->originalBlock2;
+ if ((gOffset1a == gOffset2a) && (gOffset1a < data->blocksize1)) //Confirm its in our code region
+ return true;
+
+ //Case where we push an address in the other codeblock.
+ size_t gOffset1b = (size_t)offset1 - data->otherCodeBlock1;
+ size_t gOffset2b = (size_t)offset2 - data->otherCodeBlock2;
+ if ((gOffset1b == gOffset2b) && (gOffset1b < data->otherCodeBlockSize1)) //Confirm it's in the other code region
+ return true;
+
+ //Case where we have an offset into the hot codeblock from the cold code block (why?)
+ size_t ocOffset1 = ipRelOffset1 - data->otherCodeBlock1;
+ size_t ocOffset2 = ipRelOffset2 - data->otherCodeBlock2;
+ if (ocOffset1 == ocOffset2) //Would be nice to check to see if it fits in the other code block
+ return true;
+
+ //VSD calling case.
+ size_t Offset1 = (ipRelOffset1 - 8);
+ if (data->cr->CallTargetTypes->GetIndex((DWORDLONG)Offset1) != (DWORD)-1)
+ {
+ // This logging is too noisy, so disable it.
+ //LogVerbose("Found VSD callsite, did softer compare than ideal");
+ return true;
+ }
+
+ //x86 VSD calling cases.
+ size_t Offset1b = (size_t)offset1 - 4;
+ size_t Offset2b = (size_t)offset2;
+ if (data->cr->CallTargetTypes->GetIndex((DWORDLONG)Offset1b) != (DWORD)-1)
+ {
+ // This logging is too noisy, so disable it.
+ //LogVerbose("Found VSD callsite, did softer compare than ideal");
+ return true;
+ }
+ if (data->cr->CallTargetTypes->GetIndex((DWORDLONG)Offset2b) != (DWORD)-1)
+ {
+ // This logging is too noisy, so disable it.
+ //LogVerbose("Found VSD callsite, did softer compare than ideal");
+ return true;
+ }
+
+ //Case might be a field address that we handed out to handle inlined values being loaded into
+ //a register as an immediate value (and where the address is encoded as an indirect immediate load)
+ size_t realTargetAddr = (size_t)data->cr->searchAddressMap((void*)gOffset2);
+ if (realTargetAddr == gOffset1)
+ return true;
+
+ //Case might be a field address that we handed out to handle inlined values being loaded into
+ //a register as an immediate value (and where the address is encoded and loaded by immediate into a register)
+ realTargetAddr = (size_t)data->cr->searchAddressMap((void*)offset2);
+ if (realTargetAddr == offset1)
+ return true;
+ if (realTargetAddr == 0x424242)//this offset matches what we got back from a getTailCallCopyArgsThunk
+ return true;
+
+ realTargetAddr = (size_t)data->cr->searchAddressMap((void*)(gOffset2));
+ if (realTargetAddr != -1) //we know this was passed out as a bbloc
+ return true;
+
+ return false;
+}
+
+//
+// Compares two code sections for syntactic equality. This is the core of the asm diffing logic.
+//
+// This mostly relies on MSDIS's decoded representation of an instruction to compare for equality.
+// That is, using MSDIS's internal IR, this goes through the code stream and compares, instruction
+// by instruction, op code and operand values for equality.
+//
+// Obviously, just blindly comparing operand values will raise a lot of false alarms. In order to
+// compensate for phenomena like literal pointer addresses in the code stream changing, this applies
+// some heuristics on mismatching operand values to try to normalize them a little bit. Essentially,
+// if operand values don't match, they are re-interpreted as various relative deltas from known base
+// addresses. For example, a common case is a pointer into the read-only data section. One of the
+// heuristics subtracts both operand values from the base address of the read-only data section and
+// checks to see if they are the same distance away from their respective read-only base addresses.
+//
+// Notes:
+// - The core syntactic comparison is platform agnostic; we compare op codes and operand values
+// using MSDIS's architecture-independent IR (i.e. the data structures defined in msvcdis.h).
+// Only the disassembler instance itself is initialized differently based on the target arch-
+// itecture.
+// - That being said, the heuristics themselves are not guaranteed to be platform agnostic. For
+// instance, there is a case that applies only to x86 VSD calls. When porting the near differ
+// to new platforms, these special cases should be examined and ported with care.
+//
+// Arguments:
+// mc - The method context of the method to diff. Unused.
+// cr1 - The first compile result to compare. Unused.
+// cr2 - The second compile result to compare. Unused.
+// block1 - A pointer to the first code block to diassemble.
+// blocksize1 - The size of the first code block to compare.
+// datablock1 - A pointer to the first read-only data block to compare. Unused.
+// datablockSize1 - The size of the first read-only data block to compare.
+// originalBlock1 - The original base address of the first code block.
+// originalDataBlock1 - The original base address of the first read-only data block.
+// otherCodeBlock1 - The original base address of the first cold code block. Note that this is
+// just an address; we don't need the cold code buffer.
+// otherCodeBlockSize1- The size of the first cold code block.
+// block2 - A pointer to the second code block to diassemble.
+// blocksize2 - The size of the second code block to compare.
+// datablock2 - A pointer to the second read-only data block to compare.
+// datablockSize2 - The size of the second read-only data block to compare.
+// originalBlock2 - The original base address of the second code block.
+// originalDataBlock2 - The original base address of the second read-only data block.
+// otherCodeBlock2 - The original base address of the second cold code block. Note that this is
+// just an address; we don't need the cold code buffer.
+// otherCodeBlockSize2- The size of the second cold code block.
+//
+// Return Value:
+// True if the code sections are syntactically identical; false otherwise.
+//
+
+bool NearDiffer::compareCodeSection(
+ MethodContext *mc,
+ CompileResult *cr1,
+ CompileResult *cr2,
+ unsigned char *block1,
+ ULONG blocksize1,
+ unsigned char *datablock1,
+ ULONG datablockSize1,
+ void *originalBlock1,
+ void *originalDataBlock1,
+ void *otherCodeBlock1,
+ ULONG otherCodeBlockSize1,
+ unsigned char *block2,
+ ULONG blocksize2,
+ unsigned char *datablock2,
+ ULONG datablockSize2,
+ void *originalBlock2,
+ void *originalDataBlock2,
+ void *otherCodeBlock2,
+ ULONG otherCodeBlockSize2)
+{
+ DiffData data =
+ {
+ cr2,
+
+ // Details of the first block
+ (size_t)blocksize1,
+ (size_t)datablock1,
+ (size_t)datablockSize1,
+ (size_t)originalBlock1,
+ (size_t)originalDataBlock1,
+ (size_t)otherCodeBlock1,
+ (size_t)otherCodeBlockSize1,
+
+ // Details of the second block
+ (size_t)blocksize2,
+ (size_t)datablock2,
+ (size_t)datablockSize2,
+ (size_t)originalBlock2,
+ (size_t)originalDataBlock2,
+ (size_t)otherCodeBlock2,
+ (size_t)otherCodeBlockSize2
+ };
+
+#ifdef USE_COREDISTOOLS
+ if (UseCoreDisTools)
+ {
+ bool areSame = NearDiffCodeBlocks(corAsmDiff, &data,
+ (const uint8_t *)originalBlock1, block1, blocksize1,
+ (const uint8_t *)originalBlock2, block2, blocksize2);
+
+ if (!areSame)
+ {
+ DumpDiffBlocks(corAsmDiff, (const uint8_t *) originalBlock1,
+ block1, blocksize1, (const uint8_t *) originalBlock2,
+ block2, blocksize2);
+ }
+
+ return areSame;
+ }
+#endif // USE_COREDISTOOLS
+
+#ifdef USE_MSVCDIS
+ bool haveSeenRet = false;
+ DIS *disasm_1 = GetMsVcDis();
+ DIS *disasm_2 = GetMsVcDis();
+
+ size_t offset = 0;
+
+ if (blocksize1 != blocksize2)
+ {
+ LogVerbose("Code sizes don't match %u != %u", blocksize1, blocksize2);
+ goto DumpDetails;
+ }
+
+ while (offset < blocksize1)
+ {
+ DIS::INSTRUCTION instr_1;
+ DIS::INSTRUCTION instr_2;
+ const int MaxOperandCount = 5;
+ DIS::OPERAND ops_1[MaxOperandCount];
+ DIS::OPERAND ops_2[MaxOperandCount];
+
+ // Zero out the locals, just in case.
+ memset(&instr_1, 0, sizeof(instr_1));
+ memset(&instr_2, 0, sizeof(instr_2));
+ memset(&ops_1, 0, sizeof(ops_1));
+ memset(&ops_2, 0, sizeof(ops_2));
+
+ size_t instrSize_1 = disasm_1->CbDisassemble((DIS::ADDR)originalBlock1 + offset, (void *)(block1 + offset), 15);
+ size_t instrSize_2 = disasm_2->CbDisassemble((DIS::ADDR)originalBlock2 + offset, (void *)(block2 + offset), 15);
+
+ if (instrSize_1 != instrSize_2)
+ {
+ LogVerbose("Different instruction sizes %llu %llu", instrSize_1, instrSize_2);
+ goto DumpDetails;
+ }
+ if (instrSize_1 == 0)
+ {
+ if (haveSeenRet)
+ {
+ // This logging is pretty noisy, so disable it.
+ //LogVerbose("instruction size of zero after seeing a ret (soft issue?).");
+ break;
+ }
+ LogWarning("instruction size of zero.");
+ goto DumpDetails;
+ }
+
+ bool FDecodeError = false;
+ if (!disasm_1->FDecode(&instr_1, ops_1, MaxOperandCount))
+ {
+ LogWarning("FDecode of instr_1 returned false.");
+ FDecodeError = true;
+ }
+ if (!disasm_2->FDecode(&instr_2, ops_2, MaxOperandCount))
+ {
+ LogWarning("FDecode of instr_2 returned false.");
+ FDecodeError = true;
+ }
+
+ wchar_t instrMnemonic_1[64]; // I never know how much to allocate...
+ disasm_1->CchFormatInstr(instrMnemonic_1, 64);
+ wchar_t instrMnemonic_2[64]; // I never know how much to allocate...
+ disasm_2->CchFormatInstr(instrMnemonic_2, 64);
+ if (wcscmp(instrMnemonic_1, L"ret") == 0)
+ haveSeenRet = true;
+ if (wcscmp(instrMnemonic_1, L"rep ret") == 0)
+ haveSeenRet = true;
+
+ // First, check to see if these instructions are actually identical.
+ // This is done 1) to avoid the detailed comparison of the fields of instr_1
+ // and instr_2 if they are identical, and 2) because in the event that
+ // there are bugs or unimplemented instructions in FDecode, we don't want
+ // to count them as diffs if they are bitwise identical.
+
+ if (memcmp((block1 + offset), (block2 + offset), instrSize_1) != 0)
+ {
+ if (FDecodeError)
+ {
+ LogWarning("FDecode returned false.");
+ goto DumpDetails;
+ }
+
+ if (instr_1.opa != instr_2.opa)
+ {
+ LogVerbose("different opa %d %d", instr_1.opa, instr_2.opa);
+ goto DumpDetails;
+ }
+ if (instr_1.coperand != instr_2.coperand)
+ {
+ LogVerbose("different coperand %u %u", (unsigned int)instr_1.coperand, (unsigned int)instr_2.coperand);
+ goto DumpDetails;
+ }
+ if (instr_1.dwModifiers != instr_2.dwModifiers)
+ {
+ LogVerbose("different dwModifiers %u %u", instr_1.dwModifiers, instr_2.dwModifiers);
+ goto DumpDetails;
+ }
+
+ for (size_t i = 0; i < instr_1.coperand; i++)
+ {
+ if (ops_1[i].cb != ops_2[i].cb)
+ {
+ LogVerbose("different cb %llu %llu", ops_1[i].cb, ops_2[i].cb);
+ goto DumpDetails;
+ }
+ if (ops_1[i].imcls != ops_2[i].imcls)
+ {
+ LogVerbose("different imcls %d %d", ops_1[i].imcls, ops_2[i].imcls);
+ goto DumpDetails;
+ }
+ if (ops_1[i].opcls != ops_2[i].opcls)
+ {
+ LogVerbose("different opcls %d %d", ops_1[i].opcls, ops_2[i].opcls);
+ goto DumpDetails;
+ }
+ if (ops_1[i].rega1 != ops_2[i].rega1)
+ {
+ LogVerbose("different rega1 %d %d", ops_1[i].rega1, ops_2[i].rega1);
+ goto DumpDetails;
+ }
+ if (ops_1[i].rega2 != ops_2[i].rega2)
+ {
+ LogVerbose("different rega2 %d %d", ops_1[i].rega2, ops_2[i].rega2);
+ goto DumpDetails;
+ }
+ if (ops_1[i].rega3 != ops_2[i].rega3)
+ {
+ LogVerbose("different rega3 %d %d", ops_1[i].rega3, ops_2[i].rega3);
+ goto DumpDetails;
+ }
+ if (ops_1[i].wScale != ops_2[i].wScale)
+ {
+ LogVerbose("different wScale %u %u", ops_1[i].wScale, ops_2[i].wScale);
+ goto DumpDetails;
+ }
+
+ //
+ // These are special.. we can often reason out exactly why these values
+ // are different using heuristics.
+ //
+ // Why is Instruction size passed as zero?
+ // Ans: Because the implementation of areOffsetsEquivalent() uses
+ // the instruction size to compute absolute offsets in the case of
+ // PC-relative addressing, and MSVCDis already reports the
+ // absolute offsets! For example:
+ // 0F 2E 05 67 00 9A FD ucomiss xmm0, dword ptr[FFFFFFFFFD9A006Eh]
+ //
+
+ if (compareOffsets(&data, offset, 0, ops_1[i].dwl, ops_2[i].dwl))
+ {
+ continue;
+ }
+ else
+ {
+ size_t gOffset1 = (size_t)originalBlock1 + offset + (size_t)ops_1[i].dwl;
+ size_t gOffset2 = (size_t)originalBlock2 + offset + (size_t)ops_2[i].dwl;
+
+ LogVerbose("operand %d dwl is different", i);
+#ifdef _TARGET_AMD64_
+ LogVerbose("gOffset1 %016llX", gOffset1);
+ LogVerbose("gOffset2 %016llX", gOffset2);
+ LogVerbose("gOffset1 - gOffset2 %016llX", gOffset1 - gOffset2);
+#elif defined(_TARGET_X86_)
+ LogVerbose("gOffset1 %08X", gOffset1);
+ LogVerbose("gOffset2 %08X", gOffset2);
+ LogVerbose("gOffset1 - gOffset2 %08X", gOffset1 - gOffset2);
+#endif
+ LogVerbose("dwl1 %016llX", ops_1[i].dwl);
+ LogVerbose("dwl2 %016llX", ops_2[i].dwl);
+ goto DumpDetails;
+ }
+ }
+ }
+ offset += instrSize_1;
+ }
+ delete disasm_1;
+ delete disasm_2;
+ return true;
+
+DumpDetails:
+ LogVerbose("block1 %p", block1);
+ LogVerbose("block2 %p", block2);
+ LogVerbose("originalBlock1 [%p,%p)", originalBlock1, (const uint8_t *)originalBlock1 + blocksize1);
+ LogVerbose("originalBlock2 [%p,%p)", originalBlock2, (const uint8_t *)originalBlock2 + blocksize2);
+ LogVerbose("blocksize1 %08X", blocksize1);
+ LogVerbose("blocksize2 %08X", blocksize2);
+ LogVerbose("dataBlock1 [%p,%p)", originalDataBlock1, (const uint8_t *)originalDataBlock1 + datablockSize1);
+ LogVerbose("dataBlock2 [%p,%p)", originalDataBlock2, (const uint8_t *)originalDataBlock2 + datablockSize2);
+ LogVerbose("datablockSize1 %08X", datablockSize1);
+ LogVerbose("datablockSize2 %08X", datablockSize2);
+ LogVerbose("otherCodeBlock1 [%p,%p)", otherCodeBlock1, (const uint8_t *)otherCodeBlock1 + otherCodeBlockSize1);
+ LogVerbose("otherCodeBlock2 [%p,%p)", otherCodeBlock2, (const uint8_t *)otherCodeBlock2 + otherCodeBlockSize2);
+ LogVerbose("otherCodeBlockSize1 %08X", otherCodeBlockSize1);
+ LogVerbose("otherCodeBlockSize2 %08X", otherCodeBlockSize2);
+
+#ifdef _TARGET_AMD64_
+ LogVerbose("offset %016llX", offset);
+ LogVerbose("addr1 %016llX", (size_t)originalBlock1 + offset);
+ LogVerbose("addr2 %016llX", (size_t)originalBlock2 + offset);
+#elif defined(_TARGET_X86_)
+ LogVerbose("offset %08X", offset);
+ LogVerbose("addr1 %08X", (size_t)originalBlock1 + offset);
+ LogVerbose("addr2 %08X", (size_t)originalBlock2 + offset);
+#endif
+
+ LogVerbose("Block1:");
+ DumpCodeBlock(block1, blocksize1, originalBlock1);
+ LogVerbose("Block2:");
+ DumpCodeBlock(block2, blocksize2, originalBlock2);
+
+ if (disasm_1 != nullptr)
+ delete disasm_1;
+ if (disasm_2 != nullptr)
+ delete disasm_2;
+ return false;
+#else // !USE_MSVCDIS
+ return false; // No disassembler; assume there are differences
+#endif // !USE_MSVCDIS
+}
+
+//
+// Compares two read-only data sections for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+// block1 - A pointer to the first code block to diassemble.
+// blocksize1 - The size of the first code block to compare.
+// originalDataBlock1 - The original base address of the first read-only data block.
+// block2 - A pointer to the second code block to diassemble.
+// blocksize2 - The size of the second code block to compare.
+// originalDataBlock2 - The original base address of the second read-only data block.
+//
+// Return Value:
+// True if the read-only data sections are identical; false otherwise.
+//
+bool NearDiffer::compareReadOnlyDataBlock(MethodContext *mc, CompileResult *cr1, CompileResult *cr2,
+ unsigned char *block1, ULONG blocksize1, void *originalDataBlock1,
+ unsigned char *block2, ULONG blocksize2, void *originalDataBlock2)
+{
+ //no rodata
+ if(blocksize1==0 && blocksize2==0)
+ return true;
+
+ if(blocksize1!=blocksize2)
+ {
+ LogVerbose("compareReadOnlyDataBlock found non-matching sizes %u %u", blocksize1, blocksize2);
+ return false;
+ }
+
+ //TODO-Cleanup: The values on the datablock seem to wobble. Need further investigation to evaluate a good near comparison for these
+ return true;
+}
+
+//
+// Compares two EH info blocks for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the EH info blocks are identical; false otherwise.
+//
+bool NearDiffer::compareEHInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ ULONG cEHSize_1;
+ ULONG ehFlags_1;
+ ULONG tryOffset_1;
+ ULONG tryLength_1;
+ ULONG handlerOffset_1;
+ ULONG handlerLength_1;
+ ULONG classToken_1;
+
+ ULONG cEHSize_2;
+ ULONG ehFlags_2;
+ ULONG tryOffset_2;
+ ULONG tryLength_2;
+ ULONG handlerOffset_2;
+ ULONG handlerLength_2;
+ ULONG classToken_2;
+
+
+ cEHSize_1 = cr1->repSetEHcount();
+ cEHSize_2 = cr2->repSetEHcount();
+
+ //no exception
+ if(cEHSize_1==0 && cEHSize_2==0)
+ return true;
+
+ if(cEHSize_1!=cEHSize_2)
+ {
+ LogVerbose("compareEHInfo found non-matching sizes %u %u", cEHSize_1, cEHSize_2);
+ return false;
+ }
+
+ for(unsigned int i=0;i<cEHSize_1;i++)
+ {
+ cr1->repSetEHinfo(i, &ehFlags_1, &tryOffset_1, &tryLength_1, &handlerOffset_1, &handlerLength_1, &classToken_1);
+ cr2->repSetEHinfo(i, &ehFlags_2, &tryOffset_2, &tryLength_2, &handlerOffset_2, &handlerLength_2, &classToken_2);
+ if(ehFlags_1!=ehFlags_2)
+ {
+ LogVerbose("EH flags don't match %u != %u", ehFlags_1, ehFlags_2);
+ return false;
+ }
+ if((tryOffset_1!=tryOffset_2) || (tryLength_1!=tryLength_2))
+ {
+ LogVerbose("EH try information don't match, offset: %u %u, length: %u %u", tryOffset_1, tryOffset_2, tryLength_1, tryLength_2);
+ return false;
+ }
+ if((handlerOffset_1!=handlerOffset_2) || (handlerLength_1!=handlerLength_2))
+ {
+ LogVerbose("EH handler information don't match, offset: %u %u, length: %u %u", handlerOffset_1, handlerOffset_2, handlerLength_1, handlerLength_2);
+ return false;
+ }
+ if(classToken_1!=classToken_2)
+ {
+ LogVerbose("EH class tokens don't match %u!=%u", classToken_1, classToken_2);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//
+// Compares two GC info blocks for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the GC info blocks are identical; false otherwise.
+//
+bool NearDiffer::compareGCInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ void *gcInfo1;
+ size_t gcInfo1Size;
+ void *gcInfo2;
+ size_t gcInfo2Size;
+
+ cr1->repAllocGCInfo(&gcInfo1Size, &gcInfo1);
+ cr2->repAllocGCInfo(&gcInfo2Size, &gcInfo2);
+
+ if (gcInfo1Size != gcInfo2Size)
+ {
+ LogVerbose("Reported GCInfo sizes don't match: %u != %u", (unsigned int)gcInfo1Size, (unsigned int)gcInfo2Size);
+ return false;
+ }
+
+ if (memcmp(gcInfo1, gcInfo2, gcInfo1Size) != 0)
+ {
+ LogVerbose("GCInfo doesn't match.");
+ return false;
+ }
+
+ return true;
+}
+
+//
+// Compares two sets of native var info for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the native var info is identical; false otherwise.
+//
+bool NearDiffer::compareVars(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ CORINFO_METHOD_HANDLE ftn_1;
+ ULONG32 cVars_1;
+ ICorDebugInfo::NativeVarInfo *vars_1;
+
+ CORINFO_METHOD_HANDLE ftn_2;
+ ULONG32 cVars_2;
+ ICorDebugInfo::NativeVarInfo *vars_2;
+
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ bool set1 = cr1->repSetVars(&ftn_1, &cVars_1, &vars_1);
+ bool set2 = cr2->repSetVars(&ftn_2, &cVars_2, &vars_2);
+ if((set1==false)&&(set2==false))
+ return true; // we don't have boundaries for either of these.
+ if(((set1==true)&&(set2==false))||((set1==false)&&(set2==true)))
+ {
+ LogVerbose("missing matching vars sets");
+ return false;
+ }
+
+ //no vars
+ if(cVars_1==0 && cVars_2==0)
+ {
+ return true;
+ }
+
+ if(ftn_1!=ftn_2)
+ {
+ //We would like to find out this situation
+ __debugbreak();
+ LogVerbose("compareVars found non-matching CORINFO_METHOD_HANDLE %p %p", ftn_1, ftn_2);
+ return false;
+ }
+ if(ftn_1!=info.ftn)
+ {
+ LogVerbose("compareVars found issues with the CORINFO_METHOD_HANDLE %p %p", ftn_1, info.ftn);
+ return false;
+ }
+
+ if(cVars_1!=cVars_2)
+ {
+ LogVerbose("compareVars found non-matching var count %u %u", cVars_1, cVars_2);
+ return false;
+ }
+
+ //TODO-Cleanup: The values on the NativeVarInfo array seem to wobble. Need further investigation to evaluate a good near comparison for these
+ //for(unsigned int i=0;i<cVars_1;i++)
+ //{
+ // if(vars_1[i].startOffset!=vars_2[i].startOffset)
+ // {
+ // LogVerbose("compareVars found non-matching startOffsets %u %u for var: %u", vars_1[i].startOffset, vars_2[i].startOffset, i);
+ // return false;
+ // }
+ //}
+
+ return true;
+}
+
+//
+// Compares two sets of native offset mappings for equality.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the native offset mappings are identical; false otherwise.
+//
+bool NearDiffer::compareBoundaries(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ CORINFO_METHOD_HANDLE ftn_1;
+ ULONG32 cMap_1;
+ ICorDebugInfo::OffsetMapping *map_1;
+
+ CORINFO_METHOD_HANDLE ftn_2;
+ ULONG32 cMap_2;
+ ICorDebugInfo::OffsetMapping *map_2;
+
+ CORINFO_METHOD_INFO info;
+ unsigned flags = 0;
+ mc->repCompileMethod(&info, &flags);
+
+ bool set1 = cr1->repSetBoundaries(&ftn_1, &cMap_1, &map_1);
+ bool set2 = cr2->repSetBoundaries(&ftn_2, &cMap_2, &map_2);
+ if((set1==false)&&(set2==false))
+ return true; // we don't have boundaries for either of these.
+ if(((set1==true)&&(set2==false))||((set1==false)&&(set2==true)))
+ {
+ LogVerbose("missing matching boundary sets");
+ return false;
+ }
+
+ if(ftn_1!=ftn_2)
+ {
+ LogVerbose("compareBoundaries found non-matching CORINFO_METHOD_HANDLE %p %p", ftn_1, ftn_2);
+ return false;
+ }
+
+ //no maps
+ if(cMap_1==0 && cMap_2==0)
+ return true;
+
+ if(cMap_1!=cMap_2)
+ {
+ LogVerbose("compareBoundaries found non-matching var count %u %u", cMap_1, cMap_2);
+ return false;
+ }
+
+ for(unsigned int i=0;i<cMap_1;i++)
+ {
+ if(map_1[i].ilOffset!=map_2[i].ilOffset)
+ {
+ LogVerbose("compareBoundaries found non-matching ilOffset %u %u for map: %u", map_1[i].ilOffset, map_2[i].ilOffset, i);
+ return false;
+ }
+ if(map_1[i].nativeOffset!=map_2[i].nativeOffset)
+ {
+ LogVerbose("compareBoundaries found non-matching nativeOffset %u %u for map: %u", map_1[i].nativeOffset, map_2[i].nativeOffset, i);
+ return false;
+ }
+ if(map_1[i].source!=map_2[i].source)
+ {
+ LogVerbose("compareBoundaries found non-matching source %u %u for map: %u", (unsigned int)map_1[i].source, (unsigned int)map_2[i].source, i);
+ return false;
+ }
+ }
+
+
+ return true;
+}
+
+//
+// Compares two compiled versions of a method for equality. This is the main driver for the various
+// components of near diffing.
+//
+// Before starting the diffing process, this applies some fixups to the code stream based on relocations
+// recorded during compilation, using the original base address that was used when compiling the method.
+//
+// Arguments:
+// mc - The method context of the method to diff.
+// cr1 - The first compile result to compare.
+// cr2 - The second compile result to compare.
+//
+// Return Value:
+// True if the compile results are identical; false otherwise.
+//
+bool NearDiffer::compare(MethodContext *mc, CompileResult *cr1, CompileResult *cr2)
+{
+ ULONG hotCodeSize_1;
+ ULONG coldCodeSize_1;
+ ULONG roDataSize_1;
+ ULONG xcptnsCount_1;
+ CorJitAllocMemFlag flag_1;
+ unsigned char *hotCodeBlock_1;
+ unsigned char *coldCodeBlock_1;
+ unsigned char *roDataBlock_1;
+ void *orig_hotCodeBlock_1;
+ void *orig_coldCodeBlock_1;
+ void *orig_roDataBlock_1;
+
+ ULONG hotCodeSize_2;
+ ULONG coldCodeSize_2;
+ ULONG roDataSize_2;
+ ULONG xcptnsCount_2;
+ CorJitAllocMemFlag flag_2;
+ unsigned char *hotCodeBlock_2;
+ unsigned char *coldCodeBlock_2;
+ unsigned char *roDataBlock_2;
+ void *orig_hotCodeBlock_2;
+ void *orig_coldCodeBlock_2;
+ void *orig_roDataBlock_2;
+
+ cr1->repAllocMem(&hotCodeSize_1, &coldCodeSize_1, &roDataSize_1, &xcptnsCount_1, &flag_1,
+ &hotCodeBlock_1, &coldCodeBlock_1, &roDataBlock_1, &orig_hotCodeBlock_1, &orig_coldCodeBlock_1, &orig_roDataBlock_1);
+ cr2->repAllocMem(&hotCodeSize_2, &coldCodeSize_2, &roDataSize_2, &xcptnsCount_2, &flag_2,
+ &hotCodeBlock_2, &coldCodeBlock_2, &roDataBlock_2, &orig_hotCodeBlock_2, &orig_coldCodeBlock_2, &orig_roDataBlock_2);
+
+ LogDebug("HCS1 %d CCS1 %d RDS1 %d xcpnt1 %d flag1 %08X, HCB %p CCB %p RDB %p ohcb %p occb %p odb %p",
+ hotCodeSize_1, coldCodeSize_1, roDataSize_1, xcptnsCount_1, flag_1,
+ hotCodeBlock_1, coldCodeBlock_1, roDataBlock_1,
+ orig_hotCodeBlock_1, orig_coldCodeBlock_1, orig_roDataBlock_1);
+ LogDebug("HCS2 %d CCS2 %d RDS2 %d xcpnt2 %d flag2 %08X, HCB %p CCB %p RDB %p ohcb %p occb %p odb %p",
+ hotCodeSize_2, coldCodeSize_2, roDataSize_2, xcptnsCount_2, flag_2,
+ hotCodeBlock_2, coldCodeBlock_2, roDataBlock_2,
+ orig_hotCodeBlock_2, orig_coldCodeBlock_2, orig_roDataBlock_2);
+
+ cr1->applyRelocs(hotCodeBlock_1, hotCodeSize_1, orig_hotCodeBlock_1);
+ cr2->applyRelocs(hotCodeBlock_2, hotCodeSize_2, orig_hotCodeBlock_2);
+ cr1->applyRelocs(coldCodeBlock_1, coldCodeSize_1, orig_coldCodeBlock_1);
+ cr2->applyRelocs(coldCodeBlock_2, coldCodeSize_2, orig_coldCodeBlock_2);
+ cr1->applyRelocs(roDataBlock_1, roDataSize_1, orig_roDataBlock_1);
+ cr2->applyRelocs(roDataBlock_2, roDataSize_2, orig_roDataBlock_2);
+
+ if(!compareCodeSection(mc, cr1, cr2,
+ hotCodeBlock_1, hotCodeSize_1, roDataBlock_1, roDataSize_1, orig_hotCodeBlock_1, orig_roDataBlock_1, orig_coldCodeBlock_1, coldCodeSize_1,
+ hotCodeBlock_2, hotCodeSize_2, roDataBlock_2, roDataSize_2, orig_hotCodeBlock_2, orig_roDataBlock_2, orig_coldCodeBlock_2, coldCodeSize_2))
+ return false;
+
+ if(!compareCodeSection(mc, cr1, cr2,
+ coldCodeBlock_1, coldCodeSize_1, roDataBlock_1, roDataSize_1, orig_coldCodeBlock_1, orig_roDataBlock_1, orig_hotCodeBlock_1, hotCodeSize_1,
+ coldCodeBlock_2, coldCodeSize_2, roDataBlock_2, roDataSize_2, orig_coldCodeBlock_2, orig_roDataBlock_2, orig_hotCodeBlock_2, hotCodeSize_2))
+ return false;
+
+ if(!compareReadOnlyDataBlock(mc, cr1, cr2,
+ roDataBlock_1, roDataSize_1, orig_roDataBlock_1,
+ roDataBlock_2, roDataSize_2, orig_roDataBlock_2))
+ return false;
+
+ if(!compareEHInfo(mc, cr1, cr2))
+ return false;
+
+ if (!compareGCInfo(mc, cr1, cr2))
+ return false;
+
+ if (!compareVars(mc, cr1, cr2))
+ return false;
+
+ if(!compareBoundaries(mc, cr1, cr2))
+ return false;
+
+ return true;
+}
diff --git a/src/ToolBox/superpmi/superpmi/neardiffer.h b/src/ToolBox/superpmi/superpmi/neardiffer.h
new file mode 100644
index 0000000000..e3ffe1c790
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/neardiffer.h
@@ -0,0 +1,86 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------
+// nearDiffer.h - differ that handles code that is very similar
+//----------------------------------------------------------
+#ifndef _nearDiffer
+#define _nearDiffer
+
+#include "methodcontext.h"
+#include "compileresult.h"
+
+class NearDiffer
+{
+public:
+
+ NearDiffer(const char *targetArch, bool useCorDisTools)
+ : TargetArchitecture(targetArch)
+ , UseCoreDisTools(useCorDisTools)
+#ifdef USE_COREDISTOOLS
+ , corAsmDiff(nullptr)
+#endif // USE_COREDISTOOLS
+ {
+ }
+
+ ~NearDiffer();
+
+ void InitAsmDiff();
+
+ bool compare(MethodContext *mc, CompileResult *cr1,CompileResult *cr2);
+
+ const char* TargetArchitecture;
+ const bool UseCoreDisTools;
+
+private:
+
+ void DumpCodeBlock(unsigned char *block, ULONG blocksize, void *originalAddr);
+
+ bool compareCodeSection(
+ MethodContext *mc,
+ CompileResult *cr1,
+ CompileResult *cr2,
+ unsigned char *block1,
+ ULONG blocksize1,
+ unsigned char *datablock1,
+ ULONG datablockSize1,
+ void *originalBlock1,
+ void *originalDataBlock1,
+ void *otherCodeBlock1,
+ ULONG otherCodeBlockSize1,
+ unsigned char *block2,
+ ULONG blocksize2,
+ unsigned char *datablock2,
+ ULONG datablockSize2,
+ void *originalBlock2,
+ void *originalDataBlock2,
+ void *otherCodeBlock2,
+ ULONG otherCodeBlockSize2);
+
+ bool compareReadOnlyDataBlock(MethodContext *mc, CompileResult *cr1, CompileResult *cr2,
+ unsigned char *block1, ULONG blocksize1, void *originalDataBlock1,
+ unsigned char *block2, ULONG blocksize2, void *originalDataBlock2);
+ bool compareEHInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+ bool compareGCInfo(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+ bool compareVars(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+ bool compareBoundaries(MethodContext *mc, CompileResult *cr1, CompileResult *cr2);
+
+ static bool compareOffsets(const void *payload,
+ size_t blockOffset,
+ size_t instrLen,
+ uint64_t offset1,
+ uint64_t offset2);
+
+#ifdef USE_COREDISTOOLS
+ CorAsmDiff *corAsmDiff;
+#endif // USE_COREDISTOOLS
+
+#ifdef USE_MSVCDIS
+ DIS* GetMsVcDis();
+#endif // USE_MSVCDIS
+
+};
+
+#endif // _nearDiffer
diff --git a/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp
new file mode 100644
index 0000000000..8c5232315e
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp
@@ -0,0 +1,587 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "superpmi.h"
+#include "simpletimer.h"
+#include "mclist.h"
+#include "commandline.h"
+#include "errorhandling.h"
+
+#define MAX_LOG_LINE_SIZE 0x1000 //4 KB
+
+bool closeRequested = false; // global variable to communicate CTRL+C between threads.
+
+bool StartProcess(char *commandLine, HANDLE hStdOutput, HANDLE hStdError, HANDLE *hProcess)
+{
+ LogDebug("StartProcess commandLine=%s", commandLine);
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = hStdOutput;
+ si.hStdError = hStdError;
+
+ ZeroMemory(&pi, sizeof(pi));
+
+ // Start the child process.
+ if (!CreateProcess(NULL, // No module name (use command line)
+ commandLine, // Command line
+ NULL, // Process handle not inheritable
+ NULL, // Thread handle not inheritable
+ TRUE, // Set handle inheritance to TRUE (required to use STARTF_USESTDHANDLES)
+ 0, // No creation flags
+ NULL, // Use parent's environment block
+ NULL, // Use parent's starting directory
+ &si, // Pointer to STARTUPINFO structure
+ &pi)) // Pointer to PROCESS_INFORMATION structure
+ {
+ LogError("CreateProcess failed (%d). CommandLine: %s", GetLastError(), commandLine);
+ *hProcess = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ *hProcess = pi.hProcess;
+ return true;
+}
+
+void ReadMCLToArray(char *mclFilename, int **arr, int *count)
+{
+ *count = 0;
+ *arr = nullptr;
+ char* buff = nullptr;
+
+ HANDLE hFile = CreateFileA(mclFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Unable to open '%s'. GetLastError()=%u", mclFilename, GetLastError());
+ goto Cleanup;
+ }
+
+ LARGE_INTEGER DataTemp;
+ if (GetFileSizeEx(hFile, &DataTemp) == 0)
+ {
+ LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
+ goto Cleanup;
+ }
+
+ if (DataTemp.QuadPart > MAXMCLFILESIZE)
+ {
+ LogError("Size %d exceeds max size of %d", DataTemp.QuadPart, MAXMCLFILESIZE);
+ goto Cleanup;
+ }
+
+ int sz;
+ sz = (int)(DataTemp.QuadPart);
+
+ buff = new char[sz];
+ DWORD bytesRead;
+ if (ReadFile(hFile, buff, sz, &bytesRead, nullptr) == 0)
+ {
+ LogError("ReadFile failed. GetLastError()=%u", GetLastError());
+ goto Cleanup;
+ }
+
+ for (int i = 0; i < sz; i++)
+ {
+ if (buff[i] == 0x0d)
+ (*count)++;
+ }
+
+ if (*count <= 0)
+ return;
+
+ *arr = new int[*count];
+ for (int j = 0, arrIndex = 0; j < sz; )
+ {
+ //seek the first number on the line
+ while (!isdigit((unsigned char)buff[j]))
+ j++;
+ //read in the number
+ (*arr)[arrIndex++] = atoi(&buff[j]);
+ //seek to the start of next line
+ while ((j < sz) && (buff[j] != 0x0a))
+ j++;
+ j++;
+ }
+
+Cleanup:
+ if (buff != nullptr)
+ delete[] buff;
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+}
+
+bool WriteArrayToMCL(char *mclFilename, int *arr, int count)
+{
+ HANDLE hMCLFile = CreateFileA(mclFilename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ bool result = true;
+
+ if (hMCLFile == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output file '%s'. GetLastError()=%u", mclFilename, GetLastError());
+ result = false;
+ goto Cleanup;
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ char strMethodIndex[12];
+ DWORD charCount = 0;
+ DWORD bytesWritten = 0;
+
+ charCount = sprintf(strMethodIndex, "%d\r\n", arr[i]);
+
+ if (!WriteFile(hMCLFile, strMethodIndex, charCount, &bytesWritten, nullptr) || (bytesWritten != charCount))
+ {
+ LogError("Failed to write method index '%d'. GetLastError()=%u", strMethodIndex, GetLastError());
+ result = false;
+ goto Cleanup;
+ }
+ }
+
+Cleanup:
+ if (hMCLFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hMCLFile);
+
+ return result;
+}
+
+void ProcessChildStdErr(char *stderrFilename)
+{
+ char buff[MAX_LOG_LINE_SIZE];
+
+ FILE *fp = fopen(stderrFilename, "r");
+
+ if (fp == NULL)
+ {
+ LogError("Unable to open '%s'.", stderrFilename);
+ goto Cleanup;
+ }
+
+ while (fgets(buff, MAX_LOG_LINE_SIZE, fp) != NULL)
+ {
+ //get rid of the '\n' at the end of line
+ size_t buffLen = strlen(buff);
+ if (buff[buffLen - 1] == '\n')
+ buff[buffLen - 1] = 0;
+
+ if (strncmp(buff, "ERROR: ", 7) == 0)
+ LogError("%s", &buff[7]); //log as Error and remove the "ERROR: " in front
+ else if (strncmp(buff, "WARNING: ", 9) == 0)
+ LogWarning("%s", &buff[9]); //log as Warning and remove the "WARNING: " in front
+ else if (strlen(buff) > 0)
+ LogWarning("%s", buff); //unknown output, log it as a warning
+ }
+
+Cleanup:
+ if (fp != NULL)
+ fclose(fp);
+}
+
+void ProcessChildStdOut(const CommandLine::Options& o, char *stdoutFilename, int *loaded, int *jitted, int *failed, int *diffs, bool *usageError)
+{
+ char buff[MAX_LOG_LINE_SIZE];
+
+ FILE *fp = fopen(stdoutFilename, "r");
+
+ if (fp == NULL)
+ {
+ LogError("Unable to open '%s'.", stdoutFilename);
+ goto Cleanup;
+ }
+
+ while (fgets(buff, MAX_LOG_LINE_SIZE, fp) != NULL)
+ {
+ //get rid of the '\n' at the end of line
+ size_t buffLen = strlen(buff);
+ if (buff[buffLen - 1] == '\n')
+ buff[buffLen - 1] = 0;
+
+ if (strncmp(buff, "MISSING: ", 9) == 0)
+ LogMissing("%s", &buff[9]); //log as Missing and remove the "MISSING: " in front
+ else if (strncmp(buff, "ISSUE: ", 7) == 0)
+ {
+ if (strncmp(&buff[7], "<ASM_DIFF> ", 11) == 0)
+ LogIssue(ISSUE_ASM_DIFF, "%s", &buff[18]); //log as Issue and remove the "ISSUE: <ASM_DIFF>" in front
+ else if (strncmp(&buff[7], "<ASSERT> ", 9) == 0)
+ LogIssue(ISSUE_ASSERT, "%s", &buff[16]); //log as Issue and remove the "ISSUE: <ASSERT>" in front
+ }
+ else if (strncmp(buff, g_SuperPMIUsageFirstLine, strlen(g_SuperPMIUsageFirstLine)) == 0)
+ {
+ *usageError = true; //Signals that we had a SuperPMI command line usage error
+
+ //Read the entire stdout file and printf it
+ printf("%s", buff);
+ while (fgets(buff, MAX_LOG_LINE_SIZE, fp) != NULL)
+ {
+ printf("%s", buff);
+ }
+ break;
+ }
+ else if (strncmp(buff, g_AllFormatStringFixedPrefix, strlen(g_AllFormatStringFixedPrefix)) == 0)
+ {
+ if (o.applyDiff)
+ {
+ int temp1 = 0, temp2 = 0, temp3 = 0, temp4 = 0;
+ int converted = sscanf(buff, g_AsmDiffsSummaryFormatString, &temp1, &temp2, &temp3, &temp4);
+ if (converted != 4)
+ {
+ LogError("Couldn't parse status message: \"%s\"", buff);
+ }
+ else
+ {
+ *loaded += temp1;
+ *jitted += temp2;
+ *failed += temp3;
+ *diffs += temp4;
+ }
+ }
+ else
+ {
+ int temp1 = 0, temp2 = 0, temp3 = 0;
+ int converted = sscanf(buff, g_SummaryFormatString, &temp1, &temp2, &temp3);
+ if (converted != 3)
+ {
+ LogError("Couldn't parse status message: \"%s\"", buff);
+ }
+ else
+ {
+ *loaded += temp1;
+ *jitted += temp2;
+ *failed += temp3;
+ *diffs = -1;
+ }
+ }
+ }
+ }
+
+Cleanup:
+ if (fp != NULL)
+ fclose(fp);
+}
+
+#ifndef FEATURE_PAL // TODO-Porting: handle Ctrl-C signals gracefully on Unix
+BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
+{
+ //Since the child SuperPMI.exe processes share the same console
+ //We don't need to kill them individually as they also receive the Ctrl-C
+
+ closeRequested = true; //set a flag to indicate we need to quit
+ return TRUE;
+}
+#endif // !FEATURE_PAL
+
+int __cdecl compareInt(const void *arg1, const void *arg2)
+{
+ return (*(const int *)arg1) - (*(const int *)arg2);
+}
+
+// 'arrWorkerMCLPath' is an array of strings of size 'workerCount'.
+void MergeWorkerMCLs(char *mclFilename, char **arrWorkerMCLPath, int workerCount)
+{
+ int **MCL = new int*[workerCount], *MCLCount = new int[workerCount], totalCount = 0;
+
+ for (int i = 0; i < workerCount; i++)
+ {
+ //Read the next partial MCL file
+ ReadMCLToArray(arrWorkerMCLPath[i], &MCL[i], &MCLCount[i]);
+
+ totalCount += MCLCount[i];
+ }
+
+ int *mergedMCL = new int[totalCount];
+ int index = 0;
+
+ for (int i = 0; i < workerCount; i++)
+ {
+ for (int j = 0; j < MCLCount[i]; j++)
+ mergedMCL[index++] = MCL[i][j];
+ }
+
+ qsort(mergedMCL, totalCount, sizeof(int), compareInt);
+
+ //Write the merged MCL array back to disk
+ if (!WriteArrayToMCL(mclFilename, mergedMCL, totalCount))
+ LogError("Unable to write to MCL file %s.", mclFilename);
+}
+
+// From the arguments that we parsed, construct the arguments to pass to the child processes.
+#define MAX_CMDLINE_SIZE 0x1000 //4 KB
+char* ConstructChildProcessArgs(const CommandLine::Options& o)
+{
+ int bytesWritten = 0;
+ char* spmiArgs = new char[MAX_CMDLINE_SIZE];
+ *spmiArgs = '\0';
+
+ // We don't pass through /parallel, /skipCleanup, /verbosity, /failingMCList, or /diffMCList. Everything else we need to reconstruct and pass through.
+
+#define ADDSTRING(s) if (s != nullptr) { bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " %s", s); }
+#define ADDARG_BOOL(b,arg) if (b) { bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " %s", arg); }
+#define ADDARG_STRING(s,arg) if (s != nullptr) { bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " %s %s", arg, s); }
+
+ ADDARG_BOOL(o.breakOnError, "-boe");
+ ADDARG_BOOL(o.breakOnAssert, "-boa");
+ ADDARG_BOOL(o.applyDiff, "-applyDiff");
+ ADDARG_STRING(o.reproName, "-reproName");
+ ADDARG_STRING(o.writeLogFile, "-writeLogFile");
+ ADDARG_STRING(o.methodStatsTypes, "-emitMethodStats");
+ ADDARG_STRING(o.reproName, "-reproName");
+ ADDARG_STRING(o.hash, "-matchHash");
+ ADDARG_STRING(o.targetArchitecture, "-target");
+ ADDARG_STRING(o.compileList, "-compile");
+
+ ADDSTRING(o.nameOfJit);
+ ADDSTRING(o.nameOfJit2);
+ ADDSTRING(o.nameOfInputMethodContextFile);
+
+#undef ADDSTRING
+#undef ADDARG_BOOL
+#undef ADDARG_STRING
+
+ return spmiArgs;
+}
+
+int doParallelSuperPMI(CommandLine::Options& o)
+{
+ HRESULT hr = E_FAIL;
+ SimpleTimer st;
+ st.Start();
+
+#ifndef FEATURE_PAL // TODO-Porting: handle Ctrl-C signals gracefully on Unix
+ //Register a ConsoleCtrlHandler
+ if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
+ {
+ LogError("Failed to set control handler.");
+ return 1;
+ }
+#endif // !FEATURE_PAL
+
+ char tempPath[MAX_PATH];
+ if (!GetTempPath(MAX_PATH, tempPath))
+ {
+ LogError("Failed to get path to temp folder.");
+ return 1;
+ }
+
+ if (o.workerCount <= 0)
+ {
+ //Use the default value which is the number of processors on the machine.
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+
+ o.workerCount = sysinfo.dwNumberOfProcessors;
+
+ //If we ever execute on a machine which has more than MAXIMUM_WAIT_OBJECTS(64) CPU cores
+ //we still can't spawn more than the max supported by WaitForMultipleObjects()
+ if (o.workerCount > MAXIMUM_WAIT_OBJECTS)
+ o.workerCount = MAXIMUM_WAIT_OBJECTS;
+ }
+
+ // Obtain the folder path of the current executable, which we will use to spawn ourself.
+ char* spmiFilename = new char[MAX_PATH];
+ if (!GetModuleFileName(NULL, spmiFilename, MAX_PATH))
+ {
+ LogError("Failed to get current exe path.");
+ return 1;
+ }
+
+ char* spmiArgs = ConstructChildProcessArgs(o);
+
+ // TODO: merge all this output to a single call to LogVerbose to avoid all the newlines.
+ LogVerbose("Using child (%s) with args (%s)", spmiFilename, spmiArgs);
+ if (o.mclFilename != nullptr)
+ LogVerbose(" failingMCList=%s", o.mclFilename);
+ if (o.diffMCLFilename != nullptr)
+ LogVerbose(" diffMCLFilename=%s", o.diffMCLFilename);
+ LogVerbose(" workerCount=%d, skipCleanup=%d.", o.workerCount, o.skipCleanup);
+
+ HANDLE *hProcesses = new HANDLE[o.workerCount];
+ HANDLE *hStdOutput = new HANDLE[o.workerCount];
+ HANDLE *hStdError = new HANDLE[o.workerCount];
+
+ char** arrFailingMCListPath = new char*[o.workerCount];
+ char** arrDiffMCListPath = new char*[o.workerCount];
+ char** arrStdOutputPath = new char*[o.workerCount];
+ char** arrStdErrorPath = new char*[o.workerCount];
+
+ // Add a random number to the temporary file names to allow multiple parallel SuperPMI to happen at once.
+ unsigned int randNumber = 0;
+#ifdef FEATURE_PAL
+ PAL_Random(/* bStrong */ FALSE, &randNumber, sizeof(randNumber));
+#else // !FEATURE_PAL
+ rand_s(&randNumber);
+#endif // !FEATURE_PAL
+
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ if (o.mclFilename != nullptr)
+ {
+ arrFailingMCListPath[i] = new char[MAX_PATH];
+ sprintf_s(arrFailingMCListPath[i], MAX_PATH, "%sParallelSuperPMI-%u-%d.mcl", tempPath, randNumber, i);
+ }
+ else
+ {
+ arrFailingMCListPath[i] = nullptr;
+ }
+
+ if (o.diffMCLFilename != nullptr)
+ {
+ arrDiffMCListPath[i] = new char[MAX_PATH];
+ sprintf_s(arrDiffMCListPath[i], MAX_PATH, "%sParallelSuperPMI-Diff-%u-%d.mcl", tempPath, randNumber, i);
+ }
+ else
+ {
+ arrDiffMCListPath[i] = nullptr;
+ }
+
+ arrStdOutputPath[i] = new char[MAX_PATH];
+ arrStdErrorPath[i] = new char[MAX_PATH];
+
+ sprintf_s(arrStdOutputPath[i], MAX_PATH, "%sParallelSuperPMI-stdout-%u-%d.txt", tempPath, randNumber, i);
+ sprintf_s(arrStdErrorPath[i], MAX_PATH, "%sParallelSuperPMI-stderr-%u-%d.txt", tempPath, randNumber, i);
+ }
+
+ char cmdLine[MAX_CMDLINE_SIZE];
+ cmdLine[0] = '\0';
+ int bytesWritten;
+
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ bytesWritten = sprintf_s(cmdLine, MAX_CMDLINE_SIZE, "%s -stride %d %d", spmiFilename, i + 1, o.workerCount);
+
+ if (o.mclFilename != nullptr)
+ {
+ bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -failingMCList %s", arrFailingMCListPath[i]);
+ }
+
+ if (o.diffMCLFilename != nullptr)
+ {
+ bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -diffMCList %s", arrDiffMCListPath[i]);
+ }
+
+ bytesWritten += sprintf_s(cmdLine + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -v ewmin %s", spmiArgs);
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE; // Let newly created stdout/stderr handles be inherited.
+
+ LogDebug("stdout %i=%s", i, arrStdOutputPath[i]);
+ hStdOutput[i] = CreateFileA(arrStdOutputPath[i], GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hStdOutput[i] == INVALID_HANDLE_VALUE)
+ {
+ LogError("Unable to open '%s'. GetLastError()=%u", arrStdOutputPath[i], GetLastError());
+ return -1;
+ }
+
+ LogDebug("stderr %i=%s", i, arrStdErrorPath[i]);
+ hStdError[i] = CreateFileA(arrStdErrorPath[i], GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hStdError[i] == INVALID_HANDLE_VALUE)
+ {
+ LogError("Unable to open '%s'. GetLastError()=%u", arrStdErrorPath[i], GetLastError());
+ return -1;
+ }
+
+ //Create a SuperPMI worker process and redirect its output to file
+ if (!StartProcess(cmdLine, hStdOutput[i], hStdError[i], &hProcesses[i]))
+ {
+ return -1;
+ }
+ }
+
+ WaitForMultipleObjects(o.workerCount, hProcesses, true, INFINITE);
+
+ // Close stdout/stderr
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ CloseHandle(hStdOutput[i]);
+ CloseHandle(hStdError[i]);
+ }
+
+ DWORD exitCode = 0; // 0 == assume success
+
+ if (!closeRequested)
+ {
+ // Figure out the error code to use. We use the largest magnitude error code of the children.
+ // Mainly, if any child returns non-zero, we want to return non-zero, to indicate failure.
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ DWORD exitCodeTmp;
+ BOOL ok = GetExitCodeProcess(hProcesses[i], &exitCodeTmp);
+ if (ok && (exitCodeTmp > exitCode))
+ {
+ exitCode = exitCodeTmp;
+ }
+ }
+
+ bool usageError = false; //variable to flag if we hit a usage error in SuperPMI
+
+ int loaded = 0, jitted = 0, failed = 0, diffs = 0;
+
+ //Read the stderr files and log them as errors
+ //Read the stdout files and parse them for counts and log any MISSING or ISSUE errors
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ ProcessChildStdErr(arrStdErrorPath[i]);
+ ProcessChildStdOut(o, arrStdOutputPath[i], &loaded, &jitted, &failed, &diffs, &usageError);
+ if (usageError)
+ break;
+ }
+
+ if (o.mclFilename != nullptr && !usageError)
+ {
+ //Concat the resulting .mcl files
+ MergeWorkerMCLs(o.mclFilename, arrFailingMCListPath, o.workerCount);
+ }
+
+ if (o.diffMCLFilename != nullptr && !usageError)
+ {
+ //Concat the resulting diff .mcl files
+ MergeWorkerMCLs(o.diffMCLFilename, arrDiffMCListPath, o.workerCount);
+ }
+
+ if (!usageError)
+ {
+ if (o.applyDiff)
+ {
+ LogInfo(g_AsmDiffsSummaryFormatString, loaded, jitted, failed, diffs);
+ }
+ else
+ {
+ LogInfo(g_SummaryFormatString, loaded, jitted, failed);
+ }
+ }
+
+ st.Stop();
+ LogVerbose("Total time: %fms", st.GetMilliseconds());
+ }
+
+ if (!o.skipCleanup)
+ {
+ // Delete all temporary files generated
+ for (int i = 0; i < o.workerCount; i++)
+ {
+ if (arrFailingMCListPath[i] != nullptr)
+ {
+ DeleteFile(arrFailingMCListPath[i]);
+ }
+ if (arrDiffMCListPath[i] != nullptr)
+ {
+ DeleteFile(arrDiffMCListPath[i]);
+ }
+ DeleteFile(arrStdOutputPath[i]);
+ DeleteFile(arrStdErrorPath[i]);
+ }
+ }
+
+ return (int)exitCode;
+}
diff --git a/src/ToolBox/superpmi/superpmi/superpmi.cpp b/src/ToolBox/superpmi/superpmi/superpmi.cpp
new file mode 100644
index 0000000000..ce352070f8
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/superpmi.cpp
@@ -0,0 +1,564 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+
+#ifdef USE_COREDISTOOLS
+#include "coredistools.h"
+#endif // USE_COREDISTOOLS
+
+#include "commandline.h"
+#include "superpmi.h"
+#include "jitinstance.h"
+#include "neardiffer.h"
+#include "simpletimer.h"
+#include "methodcontext.h"
+#include "methodcontextreader.h"
+#include "mclist.h"
+#include "methodstatsemitter.h"
+
+extern int doParallelSuperPMI(CommandLine::Options& o);
+
+// NOTE: these output status strings are parsed by parallelsuperpmi.cpp::ProcessChildStdOut().
+// There must be a single, fixed prefix common to all strings, to ease the determination of when
+// to parse the string fully.
+const char* const g_AllFormatStringFixedPrefix = "Loaded ";
+const char* const g_SummaryFormatString = "Loaded %d Jitted %d FailedCompile %d";
+const char* const g_AsmDiffsSummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Diffs %d";
+
+//#define SuperPMI_ChewMemory 0x7FFFFFFF //Amount of address space to consume on startup
+
+SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture;
+
+void SetSuperPmiTargetArchitecture(const char* targetArchitecture)
+{
+#ifdef _TARGET_AMD64_
+ if ((targetArchitecture != nullptr) && (0 == _stricmp(targetArchitecture, "arm64")))
+ {
+ SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_ARM64;
+ }
+ else
+ {
+ SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_AMD64;
+ }
+#elif defined(_TARGET_X86_)
+ SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_X86;
+#endif
+}
+
+// This function uses PAL_TRY, so it can't be in the a function that requires object unwinding. Extracting it out here
+// avoids compiler error.
+//
+void InvokeNearDiffer(
+ NearDiffer* nearDiffer,
+ CommandLine::Options* o,
+ MethodContext** mc,
+ CompileResult** crl,
+ int* matchCount,
+ MethodContextReader** reader,
+ MCList* failingMCL,
+ MCList* diffMCL
+ )
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException
+ {
+ NearDiffer* nearDiffer;
+ CommandLine::Options* o;
+ MethodContext** mc;
+ CompileResult** crl;
+ int* matchCount;
+ MethodContextReader** reader;
+ MCList* failingMCL;
+ MCList* diffMCL;
+ } param;
+ param.nearDiffer = nearDiffer;
+ param.o = o;
+ param.mc = mc;
+ param.crl = crl;
+ param.matchCount = matchCount;
+ param.reader = reader;
+ param.failingMCL = failingMCL;
+ param.diffMCL = diffMCL;
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ if (pParam->nearDiffer->compare(*pParam->mc, *pParam->crl, (*pParam->mc)->cr))
+ {
+ (*pParam->matchCount)++;
+ }
+ else
+ {
+ LogIssue(ISSUE_ASM_DIFF,
+ "main method %d of size %d differs", (*pParam->reader)->GetMethodContextIndex(), (*pParam->mc)->methodSize);
+
+ //This is a difference in ASM outputs from Jit1 & Jit2 and not a playback failure
+ //We will add this MC to the diffMCList if one is requested
+ //Otherwise this will end up in failingMCList
+ if ((*pParam->o).diffMCLFilename != nullptr)
+ (*pParam->diffMCL).AddMethodToMCL((*pParam->reader)->GetMethodContextIndex());
+ else if ((*pParam->o).mclFilename != nullptr)
+ (*pParam->failingMCL).AddMethodToMCL((*pParam->reader)->GetMethodContextIndex());
+ }
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop)
+ {
+ SpmiException e(&param.exceptionPointers);
+
+ LogError("main method %d of size %d failed to load and compile correctly. EnvCnt=%d",
+ (*reader)->GetMethodContextIndex(), (*mc)->methodSize, (*mc)->repEnvironmentGetCount());
+ e.ShowAndDeleteMessage();
+ if ((*o).mclFilename != nullptr)
+ (*failingMCL).AddMethodToMCL((*reader)->GetMethodContextIndex());
+ }
+ PAL_ENDTRY
+}
+
+// Run superpmi. The return value is as follows:
+// 0 : success
+// -1 : general fatal error (e.g., failed to initialize, failed to read files)
+// -2 : JIT failed to initialize
+// 1 : there were compilation failures
+// 2 : there were asm diffs
+int __cdecl main(int argc, char* argv[])
+{
+#ifdef FEATURE_PAL
+ if (0 != PAL_Initialize(argc, argv))
+ {
+ fprintf(stderr, "Error: Fail to PAL_Initialize\n");
+ return -1;
+ }
+#endif // FEATURE_PAL
+
+ Logger::Initialize();
+
+ SimpleTimer st1;
+ SimpleTimer st2;
+ SimpleTimer st3;
+ SimpleTimer st4;
+ st2.Start();
+ JitInstance::Result res, res2;
+ HRESULT hr = E_FAIL;
+ MethodContext *mc = nullptr;
+ JitInstance *jit = nullptr, *jit2 = nullptr;
+ MethodStatsEmitter *methodStatsEmitter = nullptr;
+
+#ifdef SuperPMI_ChewMemory
+ //Chew up the base 2gb of memory on x86... helpful in finding any places where classhandles etc are de-ref'd
+ SYSTEM_INFO sSysInfo;
+ GetSystemInfo(&sSysInfo);
+
+ LPVOID lpvAddr;
+#undef VirtualAlloc
+ do
+ {
+ lpvAddr = VirtualAlloc(NULL, sSysInfo.dwPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+ } while ((size_t)lpvAddr < SuperPMI_ChewMemory);
+#endif
+
+ bool collectThroughput = false;
+ MCList failingMCL, diffMCL;
+
+ CommandLine::Options o;
+ if (!CommandLine::Parse(argc, argv, &o))
+ {
+ return -1;
+ }
+
+ if (o.parallel)
+ {
+ return doParallelSuperPMI(o);
+ }
+
+ SetSuperPmiTargetArchitecture(o.targetArchitecture);
+
+ if (o.methodStatsTypes != NULL && (strchr(o.methodStatsTypes, '*') != NULL || strchr(o.methodStatsTypes, 't') != NULL || strchr(o.methodStatsTypes, 'T') != NULL))
+ {
+ collectThroughput = true;
+ }
+
+ LogVerbose("Using jit(%s) with input (%s)", o.nameOfJit, o.nameOfInputMethodContextFile);
+ std::string indexesStr = " indexCount=";
+ indexesStr += std::to_string(o.indexCount);
+ indexesStr += " (";
+ for (int i = 0; i < o.indexCount; i++)
+ {
+ indexesStr += std::to_string(o.indexes[i]);
+ if (i < (o.indexCount - 1))
+ indexesStr += ",";
+ }
+ indexesStr += ")";
+ LogVerbose(indexesStr.c_str());
+
+ if (o.methodStatsTypes != nullptr)
+ LogVerbose(" EmitMethodStats-Types=%s", o.methodStatsTypes);
+
+ if (o.hash != nullptr)
+ LogVerbose(" MD5Hash=%s", o.hash);
+
+ if (o.mclFilename != nullptr)
+ LogVerbose(" failingMCList=%s", o.mclFilename);
+
+ if (o.offset > 0 && o.increment > 0)
+ LogVerbose(" offset=%d increment=%d", o.offset, o.increment);
+
+ if (o.methodStatsTypes != nullptr)
+ {
+ methodStatsEmitter = new MethodStatsEmitter(o.nameOfInputMethodContextFile);
+ methodStatsEmitter->SetStatsTypes(o.methodStatsTypes);
+ }
+
+ if (o.mclFilename != nullptr)
+ {
+ failingMCL.InitializeMCL(o.mclFilename);
+ }
+ if (o.diffMCLFilename != nullptr)
+ {
+ diffMCL.InitializeMCL(o.diffMCLFilename);
+ }
+
+ // The method context reader handles skipping any unrequested method contexts
+ // Used in conjunction with an MCI file, it does a lot less work...
+ MethodContextReader *reader = new MethodContextReader(o.nameOfInputMethodContextFile, o.indexes, o.indexCount, o.hash, o.offset, o.increment);
+ if (!reader->isValid())
+ {
+ return -1;
+ }
+
+ int loadedCount = 0;
+ int jittedCount = 0;
+ int matchCount = 0;
+ int failCount = 0;
+ int index = 0;
+
+ st1.Start();
+ NearDiffer nearDiffer(o.targetArchitecture, o.useCoreDisTools);
+
+ if (o.applyDiff)
+ {
+ nearDiffer.InitAsmDiff();
+ }
+
+ while (true)
+ {
+ MethodContextBuffer mcb = reader->GetNextMethodContext();
+ if (mcb.Error())
+ {
+ return -1;
+ }
+ else if (mcb.allDone())
+ {
+ LogDebug("Done processing method contexts");
+ break;
+ }
+ if ((loadedCount % 500 == 0) && (loadedCount > 0))
+ {
+ st1.Stop();
+ if (o.applyDiff)
+ {
+ LogVerbose(" %2.1f%% - Loaded %d Jitted %d Matching %d FailedCompile %d at %d per second",
+ reader->PercentComplete(),
+ loadedCount,
+ jittedCount,
+ matchCount,
+ failCount,
+ (int)((double)500 / st1.GetSeconds()));
+ }
+ else
+ {
+ LogVerbose(" %2.1f%% - Loaded %d Jitted %d FailedCompile %d at %d per second",
+ reader->PercentComplete(),
+ loadedCount,
+ jittedCount,
+ failCount,
+ (int)((double)500 / st1.GetSeconds()));
+ }
+ st1.Start();
+ }
+
+ // Now read the data into a MethodContext. This could throw if the method context data is corrupt.
+
+ loadedCount++;
+ if (!MethodContext::Initialize(loadedCount, mcb.buff, mcb.size, &mc))
+ return -1;
+
+ if (jit == nullptr)
+ {
+ SimpleTimer st4;
+
+ jit = JitInstance::InitJit(o.nameOfJit, o.breakOnAssert, &st4, mc);
+ if (jit == nullptr)
+ {
+ // InitJit already printed a failure message
+ return -2;
+ }
+
+ if (o.nameOfJit2 != nullptr)
+ {
+ jit2 = JitInstance::InitJit(o.nameOfJit2, o.breakOnAssert, &st4, mc);
+ if (jit2 == nullptr)
+ {
+ // InitJit already printed a failure message
+ return -2;
+ }
+ }
+ }
+
+ // I needed to reason about what crl contains at any point in time
+ // Here is my guess based on reading the code so far
+ // crl initially contains the CompileResult from the MCH file
+ // However if we have a second jit it has the CompileResult from Jit1
+ CompileResult *crl = mc->cr;
+
+ mc->cr = new CompileResult();
+ mc->originalCR = crl;
+
+ jittedCount++;
+ st3.Start();
+ res = jit->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput);
+ st3.Stop();
+ LogDebug("Method %d compiled in %fms, result %d", reader->GetMethodContextIndex(), st3.GetMilliseconds(), res);
+
+ if ((res == JitInstance::RESULT_SUCCESS) && Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG))
+ {
+ mc->cr->dumpToConsole(); // Dump the compile results if doing debug logging
+ }
+
+ if (o.nameOfJit2 != nullptr)
+ {
+ // Lets get the results for the 2nd JIT
+ // We will save the first JIT's CR to save space for the 2nd JIT CR
+ // Note that the recorded CR is still stored in MC->originalCR
+ crl = mc->cr;
+ mc->cr = new CompileResult();
+
+ st4.Start();
+ res2 = jit2->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput);
+ st4.Stop();
+ LogDebug("Method %d compiled by JIT2 in %fms, result %d", reader->GetMethodContextIndex(), st4.GetMilliseconds(), res2);
+
+ if ((res2 == JitInstance::RESULT_SUCCESS) && Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG))
+ {
+ mc->cr->dumpToConsole(); // Dump the compile results if doing debug logging
+ }
+
+ if (res2 == JitInstance::RESULT_ERROR)
+ {
+ LogError("JIT2 main method %d of size %d failed to load and compile correctly. EnvCnt=%d",
+ reader->GetMethodContextIndex(), mc->methodSize, mc->repEnvironmentGetCount());
+ }
+
+ // Methods that don't compile due to missing JIT-EE information
+ // should still be added to the failing MC list.
+ // However, we will not add this MC# if JIT1 also failed, Else there will be duplicate logging
+ if ((res == JitInstance::RESULT_SUCCESS) &&
+ (res2 != JitInstance::RESULT_SUCCESS) &&
+ (o.mclFilename != nullptr))
+ {
+ failingMCL.AddMethodToMCL(reader->GetMethodContextIndex());
+ }
+ }
+
+ if (res == JitInstance::RESULT_SUCCESS)
+ {
+ if (collectThroughput)
+ {
+ if (o.nameOfJit2 != nullptr && res2 == JitInstance::RESULT_SUCCESS)
+ {
+ //TODO-Bug?: bug in getting the lowest cycle time??
+ ULONGLONG dif1, dif2, dif3, dif4;
+ dif1 = (jit->times[0] - jit2->times[0]) * (jit->times[0] - jit2->times[0]);
+ dif2 = (jit->times[0] - jit2->times[1]) * (jit->times[0] - jit2->times[1]);
+ dif3 = (jit->times[1] - jit2->times[0]) * (jit->times[1] - jit2->times[0]);
+ dif4 = (jit->times[1] - jit2->times[1]) * (jit->times[1] - jit2->times[1]);
+
+ if (dif1 < dif2)
+ {
+ if (dif3 < dif4)
+ {
+ if (dif1 < dif3)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ }
+ else
+ {
+ if (dif1 < dif4)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ }
+ }
+ else
+ {
+ if (dif3 < dif4)
+ {
+ if (dif2 < dif3)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[0];
+ }
+ }
+ else
+ {
+ if (dif2 < dif4)
+ {
+ crl->clockCyclesToCompile = jit->times[0];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ else
+ {
+ crl->clockCyclesToCompile = jit->times[1];
+ mc->cr->clockCyclesToCompile = jit2->times[1];
+ }
+ }
+ }
+
+ if (methodStatsEmitter != nullptr)
+ {
+ methodStatsEmitter->Emit(reader->GetMethodContextIndex(), mc, crl->clockCyclesToCompile, mc->cr->clockCyclesToCompile);
+ }
+ }
+ else
+ {
+ if (jit->times[0] > jit->times[1])
+ mc->cr->clockCyclesToCompile = jit->times[1];
+ else
+ mc->cr->clockCyclesToCompile = jit->times[0];
+ if (methodStatsEmitter != nullptr)
+ {
+ methodStatsEmitter->Emit(reader->GetMethodContextIndex(), mc, mc->cr->clockCyclesToCompile, 0);
+ }
+ }
+ }
+
+ if (!collectThroughput && methodStatsEmitter != nullptr)
+ {
+ //We have a separate call to Emit for collectThroughput
+ methodStatsEmitter->Emit(reader->GetMethodContextIndex(), mc, -1, -1);
+ }
+
+ if (o.applyDiff)
+ {
+ // We need at least two compile results to diff: they can either both come from JIT
+ // invocations, or one can be loaded from the method context file.
+
+ // We need to check both CompileResults to ensure we have a valid CR
+ if (crl->AllocMem == nullptr || mc->cr->AllocMem == nullptr)
+ {
+ LogError("method %d is missing a compileResult, cannot do diffing", reader->GetMethodContextIndex());
+
+ // If we are here this means that either we have 2 Jits and the second Jit failed to compile
+ // Or we have single Jit and the MethodContext doesn't have an originalCR
+ // In both cases we don't need to add this to the MCList again
+ }
+ else
+ {
+ InvokeNearDiffer(&nearDiffer, &o, &mc, &crl, &matchCount, &reader, &failingMCL, &diffMCL);
+ }
+ }
+ }
+ else
+ {
+ failCount++;
+
+ if (o.mclFilename != nullptr)
+ failingMCL.AddMethodToMCL(reader->GetMethodContextIndex());
+
+ // The following only apply specifically to failures caused by errors (as opposed
+ // to, for instance, failures caused by missing JIT-EE details).
+ if (res == JitInstance::RESULT_ERROR)
+ {
+ LogError("main method %d of size %d failed to load and compile correctly. EnvCnt=%d", reader->GetMethodContextIndex(), mc->methodSize, mc->repEnvironmentGetCount());
+ if ((o.reproName != nullptr) && (o.indexCount == -1))
+ {
+ char buff[500];
+ sprintf_s(buff, 500, "%s-%d.mc", o.reproName, reader->GetMethodContextIndex());
+ HANDLE hFileOut = CreateFileA(buff, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hFileOut == INVALID_HANDLE_VALUE)
+ {
+ LogError("Failed to open output '%s'. GetLastError()=%u", buff, GetLastError());
+ return -1;
+ }
+ mc->saveToFile(hFileOut);
+ if (CloseHandle(hFileOut) == 0)
+ {
+ LogError("CloseHandle for output file failed. GetLastError()=%u", GetLastError());
+ return -1;
+ }
+ LogInfo("Wrote out repro to '%s'", buff);
+ }
+ if (o.breakOnError)
+ {
+ if (o.indexCount == -1)
+ LogInfo("HINT: to repro add '/c %d' to cmdline", reader->GetMethodContextIndex());
+ __debugbreak();
+ }
+ }
+ }
+
+ delete crl;
+ delete mc;
+ }
+ delete reader;
+
+ int result = 0;
+
+ // NOTE: these output status strings are parsed by parallelsuperpmi.cpp::ProcessChildStdOut().
+ if (o.applyDiff)
+ {
+ LogInfo(g_AsmDiffsSummaryFormatString, loadedCount, jittedCount, failCount, jittedCount - failCount - matchCount);
+
+ if (matchCount != jittedCount)
+ {
+ result = 2;
+ }
+ }
+ else
+ {
+ LogInfo(g_SummaryFormatString, loadedCount, jittedCount, failCount);
+ }
+
+ // Failure to JIT overrides diffs for the error code.
+ if (failCount > 0)
+ {
+ result = 1;
+ }
+
+ st2.Stop();
+ LogVerbose("Total time: %fms", st2.GetMilliseconds());
+
+ if (methodStatsEmitter != nullptr)
+ {
+ delete methodStatsEmitter;
+ }
+
+ if (o.mclFilename != nullptr)
+ {
+ failingMCL.CloseMCL();
+ }
+ if (o.diffMCLFilename != nullptr)
+ {
+ diffMCL.CloseMCL();
+ }
+ Logger::Shutdown();
+ return result;
+}
diff --git a/src/ToolBox/superpmi/superpmi/superpmi.h b/src/ToolBox/superpmi/superpmi/superpmi.h
new file mode 100644
index 0000000000..d5b7bdaa2b
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/superpmi.h
@@ -0,0 +1,27 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#ifndef _SuperPMI
+#define _SuperPMI
+
+#include "errorhandling.h"
+
+enum SPMI_TARGET_ARCHITECTURE
+{
+ SPMI_TARGET_ARCHITECTURE_X86,
+ SPMI_TARGET_ARCHITECTURE_AMD64,
+ SPMI_TARGET_ARCHITECTURE_ARM64
+};
+
+extern SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture;
+extern void SetSuperPmiTargetArchitecture(const char* targetArchitecture);
+
+extern const char* const g_SuperPMIUsageFirstLine;
+
+extern const char* const g_AllFormatStringFixedPrefix;
+extern const char* const g_SummaryFormatString;
+extern const char* const g_AsmDiffsSummaryFormatString;
+
+#endif