Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SOS.Symbol.Package", "src\SOS\SOS.Package\SOS.Symbol.Package.csproj", "{410394E0-7F4F-42D5-B5FA-30956F44ACBC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{03479E19-3F18-49A6-910A-F5041E27E7C0}"
+ ProjectSection(SolutionItems) = preProject
+ src\tests\eventpipe\EventPipe.UnitTests.csproj = src\tests\eventpipe\EventPipe.UnitTests.csproj
+ EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetTrace.UnitTests", "src\tests\dotnet-trace\DotnetTrace.UnitTests.csproj", "{AEDCCF5B-5AD0-4D64-BF73-5CF468E07D22}"
EndProject
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{41BDFD6D-D165-4D67-BEF6-4E539040D30A}"
ProjectSection(SolutionItems) = preProject
- src\inc\arrayholder.h = src\inc\arrayholder.h
- src\inc\bitvector.h = src\inc\bitvector.h
- src\inc\check.h = src\inc\check.h
- src\inc\check.inl = src\inc\check.inl
- src\inc\clrdata.idl = src\inc\clrdata.idl
- src\inc\clrhost.h = src\inc\clrhost.h
- src\inc\clrinternal.idl = src\inc\clrinternal.idl
- src\inc\clrnt.h = src\inc\clrnt.h
- src\inc\clrprivappxhosting.idl = src\inc\clrprivappxhosting.idl
- src\inc\clrprivbinding.idl = src\inc\clrprivbinding.idl
- src\inc\clrprivhosting.idl = src\inc\clrprivhosting.idl
- src\inc\clrprivruntimebinders.idl = src\inc\clrprivruntimebinders.idl
- src\inc\clrtypes.h = src\inc\clrtypes.h
- src\inc\CMakeLists.txt = src\inc\CMakeLists.txt
- src\inc\contract.h = src\inc\contract.h
- src\inc\contract.inl = src\inc\contract.inl
- src\inc\cor.h = src\inc\cor.h
- src\inc\cordebug.idl = src\inc\cordebug.idl
- src\inc\coreclrhost.h = src\inc\coreclrhost.h
- src\inc\corexcep.h = src\inc\corexcep.h
- src\inc\corhdr.h = src\inc\corhdr.h
- src\inc\corhlpr.cpp = src\inc\corhlpr.cpp
- src\inc\corhlpr.h = src\inc\corhlpr.h
- src\inc\corhlprpriv.h = src\inc\corhlprpriv.h
- src\inc\corjitflags.h = src\inc\corjitflags.h
- src\inc\corprof.idl = src\inc\corprof.idl
- src\inc\corpub.idl = src\inc\corpub.idl
- src\inc\corsym.idl = src\inc\corsym.idl
- src\inc\cortypeinfo.h = src\inc\cortypeinfo.h
- src\inc\crosscomp.h = src\inc\crosscomp.h
- src\inc\crsttypes.h = src\inc\crsttypes.h
- src\inc\crtwrap.h = src\inc\crtwrap.h
- src\inc\daccess.h = src\inc\daccess.h
- src\inc\dacprivate.h = src\inc\dacprivate.h
- src\inc\dbgenginemetrics.h = src\inc\dbgenginemetrics.h
- src\inc\dbgportable.h = src\inc\dbgportable.h
- src\inc\dbgtargetcontext.h = src\inc\dbgtargetcontext.h
- src\inc\dbgutil.h = src\inc\dbgutil.h
- src\inc\debugmacros.h = src\inc\debugmacros.h
- src\inc\debugmacrosext.h = src\inc\debugmacrosext.h
- src\inc\debugreturn.h = src\inc\debugreturn.h
- src\inc\entrypoints.h = src\inc\entrypoints.h
- src\inc\ex.h = src\inc\ex.h
- src\inc\fstring.h = src\inc\fstring.h
- src\inc\fusion.idl = src\inc\fusion.idl
- src\inc\gcdecoder.cpp = src\inc\gcdecoder.cpp
- src\inc\gcdesc.h = src\inc\gcdesc.h
- src\inc\gcdump.h = src\inc\gcdump.h
- src\inc\gcinfo.h = src\inc\gcinfo.h
- src\inc\gcinfodecoder.h = src\inc\gcinfodecoder.h
- src\inc\gcinfodumper.h = src\inc\gcinfodumper.h
- src\inc\gcinfotypes.h = src\inc\gcinfotypes.h
- src\inc\gcmsg.inl = src\inc\gcmsg.inl
- src\inc\genericstackprobe.h = src\inc\genericstackprobe.h
- src\inc\getproductversionnumber.h = src\inc\getproductversionnumber.h
- src\inc\hillclimbing.h = src\inc\hillclimbing.h
- src\inc\holder.h = src\inc\holder.h
- src\inc\iallocator.h = src\inc\iallocator.h
- src\inc\iterator.h = src\inc\iterator.h
- src\inc\livedatatarget.h = src\inc\livedatatarget.h
- src\inc\log.h = src\inc\log.h
- src\inc\loglf.h = src\inc\loglf.h
- src\inc\longfilepathwrappers.h = src\inc\longfilepathwrappers.h
- src\inc\mdcommon.h = src\inc\mdcommon.h
- src\inc\memoryrange.h = src\inc\memoryrange.h
- src\inc\metadata.h = src\inc\metadata.h
- src\inc\metahost.idl = src\inc\metahost.idl
- src\inc\MSCOREE.IDL = src\inc\MSCOREE.IDL
- src\inc\mscorsvc.idl = src\inc\mscorsvc.idl
- src\inc\msodw.h = src\inc\msodw.h
- src\inc\new.hpp = src\inc\new.hpp
- src\inc\nibblemapmacros.h = src\inc\nibblemapmacros.h
- src\inc\nsutilpriv.h = src\inc\nsutilpriv.h
- src\inc\opcode.def = src\inc\opcode.def
- src\inc\openum.h = src\inc\openum.h
- src\inc\ostype.h = src\inc\ostype.h
- src\inc\palclr.h = src\inc\palclr.h
- src\inc\palclr_win.h = src\inc\palclr_win.h
- src\inc\pedecoder.h = src\inc\pedecoder.h
- src\inc\pedecoder.inl = src\inc\pedecoder.inl
- src\inc\predeftlsslot.h = src\inc\predeftlsslot.h
- src\inc\random.h = src\inc\random.h
- src\inc\readytorun.h = src\inc\readytorun.h
- src\inc\readytoruninstructionset.h = src\inc\readytoruninstructionset.h
- src\inc\regdisp.h = src\inc\regdisp.h
- src\inc\registrywrapper.h = src\inc\registrywrapper.h
- src\inc\releaseholder.h = src\inc\releaseholder.h
- src\inc\resource.h = src\inc\resource.h
- src\inc\runtimeinfo.h = src\inc\runtimeinfo.h
- src\inc\safemath.h = src\inc\safemath.h
- src\inc\safewrap.h = src\inc\safewrap.h
- src\inc\sbuffer.h = src\inc\sbuffer.h
- src\inc\sbuffer.inl = src\inc\sbuffer.inl
- src\inc\securityutil.h = src\inc\securityutil.h
- src\inc\securitywrapper.h = src\inc\securitywrapper.h
- src\inc\sigparser.h = src\inc\sigparser.h
- src\inc\sospriv.idl = src\inc\sospriv.idl
- src\inc\sstring.h = src\inc\sstring.h
- src\inc\sstring.inl = src\inc\sstring.inl
- src\inc\stacktrace.h = src\inc\stacktrace.h
- src\inc\static_assert.h = src\inc\static_assert.h
- src\inc\staticcontract.h = src\inc\staticcontract.h
- src\inc\stdmacros.h = src\inc\stdmacros.h
- src\inc\stresslog.h = src\inc\stresslog.h
- src\inc\switches.h = src\inc\switches.h
- src\inc\tls.h = src\inc\tls.h
- src\inc\unreachable.h = src\inc\unreachable.h
- src\inc\utilcode.h = src\inc\utilcode.h
- src\inc\volatile.h = src\inc\volatile.h
- src\inc\warningcontrol.h = src\inc\warningcontrol.h
- src\inc\win64unwind.h = src\inc\win64unwind.h
- src\inc\winwrap.h = src\inc\winwrap.h
- src\inc\xclrdata.idl = src\inc\xclrdata.idl
- src\inc\xcordebug.idl = src\inc\xcordebug.idl
- src\inc\yieldprocessornormalized.h = src\inc\yieldprocessornormalized.h
+ src\shared\inc\arrayholder.h = src\shared\inc\arrayholder.h
+ src\shared\inc\bitvector.h = src\shared\inc\bitvector.h
+ src\shared\inc\check.h = src\shared\inc\check.h
+ src\shared\inc\check.inl = src\shared\inc\check.inl
+ src\shared\inc\clrdata.idl = src\shared\inc\clrdata.idl
+ src\shared\inc\clrhost.h = src\shared\inc\clrhost.h
+ src\shared\inc\clrinternal.idl = src\shared\inc\clrinternal.idl
+ src\shared\inc\clrnt.h = src\shared\inc\clrnt.h
+ src\shared\inc\clrprivappxhosting.idl = src\shared\inc\clrprivappxhosting.idl
+ src\shared\inc\clrprivbinding.idl = src\shared\inc\clrprivbinding.idl
+ src\shared\inc\clrprivhosting.idl = src\shared\inc\clrprivhosting.idl
+ src\shared\inc\clrprivruntimebinders.idl = src\shared\inc\clrprivruntimebinders.idl
+ src\shared\inc\clrtypes.h = src\shared\inc\clrtypes.h
+ src\shared\inc\CMakeLists.txt = src\shared\inc\CMakeLists.txt
+ src\shared\inc\contract.h = src\shared\inc\contract.h
+ src\shared\inc\contract.inl = src\shared\inc\contract.inl
+ src\shared\inc\cor.h = src\shared\inc\cor.h
+ src\shared\inc\cordebug.idl = src\shared\inc\cordebug.idl
+ src\shared\inc\coreclrhost.h = src\shared\inc\coreclrhost.h
+ src\shared\inc\corexcep.h = src\shared\inc\corexcep.h
+ src\shared\inc\corhdr.h = src\shared\inc\corhdr.h
+ src\shared\inc\corhlpr.cpp = src\shared\inc\corhlpr.cpp
+ src\shared\inc\corhlpr.h = src\shared\inc\corhlpr.h
+ src\shared\inc\corhlprpriv.h = src\shared\inc\corhlprpriv.h
+ src\shared\inc\corjitflags.h = src\shared\inc\corjitflags.h
+ src\shared\inc\corprof.idl = src\shared\inc\corprof.idl
+ src\shared\inc\corpub.idl = src\shared\inc\corpub.idl
+ src\shared\inc\corsym.idl = src\shared\inc\corsym.idl
+ src\shared\inc\cortypeinfo.h = src\shared\inc\cortypeinfo.h
+ src\shared\inc\crosscomp.h = src\shared\inc\crosscomp.h
+ src\shared\inc\crsttypes.h = src\shared\inc\crsttypes.h
+ src\shared\inc\crtwrap.h = src\shared\inc\crtwrap.h
+ src\shared\inc\daccess.h = src\shared\inc\daccess.h
+ src\shared\inc\dacprivate.h = src\shared\inc\dacprivate.h
+ src\shared\inc\dbgenginemetrics.h = src\shared\inc\dbgenginemetrics.h
+ src\shared\inc\dbgportable.h = src\shared\inc\dbgportable.h
+ src\shared\inc\dbgtargetcontext.h = src\shared\inc\dbgtargetcontext.h
+ src\shared\inc\dbgutil.h = src\shared\inc\dbgutil.h
+ src\shared\inc\debugmacros.h = src\shared\inc\debugmacros.h
+ src\shared\inc\debugmacrosext.h = src\shared\inc\debugmacrosext.h
+ src\shared\inc\debugreturn.h = src\shared\inc\debugreturn.h
+ src\shared\inc\entrypoints.h = src\shared\inc\entrypoints.h
+ src\shared\inc\ex.h = src\shared\inc\ex.h
+ src\shared\inc\fstring.h = src\shared\inc\fstring.h
+ src\shared\inc\fusion.idl = src\shared\inc\fusion.idl
+ src\shared\inc\gcdecoder.cpp = src\shared\inc\gcdecoder.cpp
+ src\shared\inc\gcdesc.h = src\shared\inc\gcdesc.h
+ src\shared\inc\gcdump.h = src\shared\inc\gcdump.h
+ src\shared\inc\gcinfo.h = src\shared\inc\gcinfo.h
+ src\shared\inc\gcinfodecoder.h = src\shared\inc\gcinfodecoder.h
+ src\shared\inc\gcinfodumper.h = src\shared\inc\gcinfodumper.h
+ src\shared\inc\gcinfotypes.h = src\shared\inc\gcinfotypes.h
+ src\shared\inc\gcmsg.inl = src\shared\inc\gcmsg.inl
+ src\shared\inc\genericstackprobe.h = src\shared\inc\genericstackprobe.h
+ src\shared\inc\getproductversionnumber.h = src\shared\inc\getproductversionnumber.h
+ src\shared\inc\hillclimbing.h = src\shared\inc\hillclimbing.h
+ src\shared\inc\holder.h = src\shared\inc\holder.h
+ src\shared\inc\iallocator.h = src\shared\inc\iallocator.h
+ src\shared\inc\iterator.h = src\shared\inc\iterator.h
+ src\shared\inc\livedatatarget.h = src\shared\inc\livedatatarget.h
+ src\shared\inc\log.h = src\shared\inc\log.h
+ src\shared\inc\loglf.h = src\shared\inc\loglf.h
+ src\shared\inc\longfilepathwrappers.h = src\shared\inc\longfilepathwrappers.h
+ src\shared\inc\mdcommon.h = src\shared\inc\mdcommon.h
+ src\shared\inc\memoryrange.h = src\shared\inc\memoryrange.h
+ src\shared\inc\metadata.h = src\shared\inc\metadata.h
+ src\shared\inc\metahost.idl = src\shared\inc\metahost.idl
+ src\shared\inc\MSCOREE.IDL = src\shared\inc\MSCOREE.IDL
+ src\shared\inc\mscorsvc.idl = src\shared\inc\mscorsvc.idl
+ src\shared\inc\msodw.h = src\shared\inc\msodw.h
+ src\shared\inc\new.hpp = src\shared\inc\new.hpp
+ src\shared\inc\nibblemapmacros.h = src\shared\inc\nibblemapmacros.h
+ src\shared\inc\nsutilpriv.h = src\shared\inc\nsutilpriv.h
+ src\shared\inc\opcode.def = src\shared\inc\opcode.def
+ src\shared\inc\openum.h = src\shared\inc\openum.h
+ src\shared\inc\ostype.h = src\shared\inc\ostype.h
+ src\shared\inc\palclr.h = src\shared\inc\palclr.h
+ src\shared\inc\palclr_win.h = src\shared\inc\palclr_win.h
+ src\shared\inc\pedecoder.h = src\shared\inc\pedecoder.h
+ src\shared\inc\pedecoder.inl = src\shared\inc\pedecoder.inl
+ src\shared\inc\predeftlsslot.h = src\shared\inc\predeftlsslot.h
+ src\shared\inc\random.h = src\shared\inc\random.h
+ src\shared\inc\readytorun.h = src\shared\inc\readytorun.h
+ src\shared\inc\readytoruninstructionset.h = src\shared\inc\readytoruninstructionset.h
+ src\shared\inc\regdisp.h = src\shared\inc\regdisp.h
+ src\shared\inc\registrywrapper.h = src\shared\inc\registrywrapper.h
+ src\shared\inc\releaseholder.h = src\shared\inc\releaseholder.h
+ src\shared\inc\resource.h = src\shared\inc\resource.h
+ src\shared\inc\runtimeinfo.h = src\shared\inc\runtimeinfo.h
+ src\shared\inc\safemath.h = src\shared\inc\safemath.h
+ src\shared\inc\safewrap.h = src\shared\inc\safewrap.h
+ src\shared\inc\sbuffer.h = src\shared\inc\sbuffer.h
+ src\shared\inc\sbuffer.inl = src\shared\inc\sbuffer.inl
+ src\shared\inc\securityutil.h = src\shared\inc\securityutil.h
+ src\shared\inc\securitywrapper.h = src\shared\inc\securitywrapper.h
+ src\shared\inc\sigparser.h = src\shared\inc\sigparser.h
+ src\shared\inc\sospriv.idl = src\shared\inc\sospriv.idl
+ src\shared\inc\sstring.h = src\shared\inc\sstring.h
+ src\shared\inc\sstring.inl = src\shared\inc\sstring.inl
+ src\shared\inc\stacktrace.h = src\shared\inc\stacktrace.h
+ src\shared\inc\static_assert.h = src\shared\inc\static_assert.h
+ src\shared\inc\staticcontract.h = src\shared\inc\staticcontract.h
+ src\shared\inc\stdmacros.h = src\shared\inc\stdmacros.h
+ src\shared\inc\stresslog.h = src\shared\inc\stresslog.h
+ src\shared\inc\switches.h = src\shared\inc\switches.h
+ src\shared\inc\tls.h = src\shared\inc\tls.h
+ src\shared\inc\unreachable.h = src\shared\inc\unreachable.h
+ src\shared\inc\utilcode.h = src\shared\inc\utilcode.h
+ src\shared\inc\volatile.h = src\shared\inc\volatile.h
+ src\shared\inc\warningcontrol.h = src\shared\inc\warningcontrol.h
+ src\shared\inc\win64unwind.h = src\shared\inc\win64unwind.h
+ src\shared\inc\winwrap.h = src\shared\inc\winwrap.h
+ src\shared\inc\xclrdata.idl = src\shared\inc\xclrdata.idl
+ src\shared\inc\xcordebug.idl = src\shared\inc\xcordebug.idl
+ src\shared\inc\yieldprocessornormalized.h = src\shared\inc\yieldprocessornormalized.h
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "clr_std", "clr_std", "{33239640-6F4B-4DA4-A780-2F5B26D57EAD}"
ProjectSection(SolutionItems) = preProject
- src\inc\clr_std\algorithm = src\inc\clr_std\algorithm
- src\inc\clr_std\string = src\inc\clr_std\string
- src\inc\clr_std\type_traits = src\inc\clr_std\type_traits
- src\inc\clr_std\utility = src\inc\clr_std\utility
- src\inc\clr_std\vector = src\inc\clr_std\vector
+ src\shared\inc\clr_std\algorithm = src\shared\inc\clr_std\algorithm
+ src\shared\inc\clr_std\string = src\shared\inc\clr_std\string
+ src\shared\inc\clr_std\type_traits = src\shared\inc\clr_std\type_traits
+ src\shared\inc\clr_std\utility = src\shared\inc\clr_std\utility
+ src\shared\inc\clr_std\vector = src\shared\inc\clr_std\vector
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "llvm", "llvm", "{06730767-421B-465F-BB63-A3A07D72D7A2}"
ProjectSection(SolutionItems) = preProject
- src\inc\llvm\Dwarf.def = src\inc\llvm\Dwarf.def
- src\inc\llvm\Dwarf.h = src\inc\llvm\Dwarf.h
- src\inc\llvm\ELF.h = src\inc\llvm\ELF.h
+ src\shared\inc\llvm\Dwarf.def = src\shared\inc\llvm\Dwarf.def
+ src\shared\inc\llvm\Dwarf.h = src\shared\inc\llvm\Dwarf.h
+ src\shared\inc\llvm\ELF.h = src\shared\inc\llvm\ELF.h
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.Monitoring.UnitTests", "src\tests\Microsoft.Diagnostics.Monitoring\Microsoft.Diagnostics.Monitoring.UnitTests.csproj", "{6419BA04-6F1A-4D2F-8DE4-5C359E0364A3}"
src\shared\minipal\utils.h = src\shared\minipal\utils.h
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbgShim.UnitTests", "src\tests\DbgShim.UnitTests\DbgShim.UnitTests.csproj", "{DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|Any CPU = Checked|Any CPU
{8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|Any CPU.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|ARM.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|ARM.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|ARM64.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|ARM64.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|x64.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|x64.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|x86.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Checked|x86.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|ARM.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|x64.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Debug|x86.Build.0 = Debug|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|ARM.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|ARM.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|ARM64.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|x64.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|x64.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|x86.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.Release|x86.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{20EBC3C4-917C-402D-B778-9A6E3742BF5A} = {7852EDE4-93DF-4DB1-8A86-C521703811AF}
{8C35FEF8-1101-38F6-ACD0-462A1EA53A7D} = {7852EDE4-93DF-4DB1-8A86-C521703811AF}
{795B7A50-1B1F-406E-94E0-F9B35104EF22} = {7852EDE4-93DF-4DB1-8A86-C521703811AF}
+ {DD60B7C4-BECC-4C7D-A53B-FCDD4C92B728} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0}
<!-- All Rights Reserved. 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. -->
-<Project>
+<Project Sdk="Microsoft.Build.NoTargets">
<!--
$(BuildArch) - architecture to test (x64, x86, arm, arm64). Defaults to x64.
$(PrivateBuildPath) - if non-empty, path to private runtime build to copy/test
$(MicrosoftNETCoreApp50Version) $(MicrosoftAspNetCoreApp50Version) - 5.0 version
$(MicrosoftNETCoreApp31Version) $(MicrosoftAspNetCoreApp31Version) - 3.1 version
+ $(SingleFileRuntimeVersion) - The version of the runtime used to build single-file apps
+
From Arcade:
$(RepoRoot) - the root of the diagnostics repo
<PropertyGroup>
<BuildArch Condition="'$(BuildArch)' == ''">$(Platform)</BuildArch>
- <BuildArch Condition="'$(BuildArch)' == ''">x64</BuildArch>
+ <BuildArch Condition="'$(BuildArch)' == 'AnyCpu'">$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant)</BuildArch>
<PrivateBuildTesting>false</PrivateBuildTesting>
<PrivateBuildTesting Condition="'$(PrivateBuildPath)' != ''">true</PrivateBuildTesting>
+ <DotnetRuntimeVersion Condition="'$(DotnetRuntimeVersion)' == ''">default</DotnetRuntimeVersion>
<InternalReleaseTesting>false</InternalReleaseTesting>
<InternalReleaseTesting Condition="'$(DotnetRuntimeVersion)' != 'default'">true</InternalReleaseTesting>
<ExtraInstallArgs>-runtimesourcefeed '$(RuntimeSourceFeed)' -runtimesourcefeedkey '$(RuntimeSourceFeedKey)'</ExtraInstallArgs>
-->
<Target Name="InstallTestRuntimes"
- BeforeTargets="RunTests"
DependsOnTargets="CleanupVersionManifest;InstallRuntimesWindows;InstallRuntimesUnix;CopyPrivateBuild;WriteTestVersionManifest;" />
<!--
<RuntimeVersionLatest>$(RuntimeVersionLatest)</RuntimeVersionLatest>
<AspNetCoreVersionLatest>$(AspNetCoreVersionLatest)</AspNetCoreVersionLatest>
+
+ <SingleFileRuntimeVersion>$(SingleFileRuntimeVersion)</SingleFileRuntimeVersion>
</Configuration>
]]>
</TestConfigFileLines>
<SkipPackagePublishingVersionChecks>true</SkipPackagePublishingVersionChecks>
</PropertyGroup>
<PropertyGroup>
+ <!-- The SDK runtime version used to build single-file apps (currently hardcoded) -->
+ <SingleFileRuntimeVersion>6.0.3</SingleFileRuntimeVersion>
<!-- Latest symstore version updated by darc -->
<MicrosoftSymbolStoreVersion>1.0.317101</MicrosoftSymbolStoreVersion>
<!-- Runtime versions to test -->
<?xml version="1.0" encoding="utf-8"?>
<Project>
- <Import Project="Sdk.targets" Sdk="Microsoft.DotNet.Arcade.Sdk" />
-
- <!-- Work around https://github.com/dotnet/sourcelink/issues/572
+ <Import Project="Sdk.targets" Sdk="Microsoft.DotNet.Arcade.Sdk" />
+
+ <!-- Work around https://github.com/dotnet/sourcelink/issues/572
Remove once we build using an SDK that contains https://github.com/dotnet/sdk/pull/10613 -->
<PropertyGroup>
- <TargetFrameworkMonikerAssemblyAttributesPath>$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))</TargetFrameworkMonikerAssemblyAttributesPath>
+ <TargetFrameworkMonikerAssemblyAttributesPath>$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))</TargetFrameworkMonikerAssemblyAttributesPath>
</PropertyGroup>
<ItemGroup>
- <EmbeddedFiles Include="$(GeneratedAssemblyInfoFile)"/>
+ <EmbeddedFiles Include="$(GeneratedAssemblyInfoFile)"/>
</ItemGroup>
<!-- We need this for the binplacing for testing assets.
This should be removed at some point as it's brittle (harcodes versions and creates native-managed coupling). -->
<Target Name="_PublishPackageReferences"
- AfterTargets="PostBuildEvent"
- Condition="$(NeedsPublishing) == 'true'"
- DependsOnTargets="$(_BeforePublishNoBuildTargets);$(_CorePublishTargets)" />
+ AfterTargets="PostBuildEvent"
+ Condition="$(NeedsPublishing) == 'true'"
+ DependsOnTargets="$(_BeforePublishNoBuildTargets);$(_CorePublishTargets)" />
+
+ <Target Name="InvokeInstallRuntimes"
+ Condition="'$(IsUnitTestProject)' == 'true'"
+ BeforeTargets="RunTests;VSTest">
+ <MSBuild Projects="$(RepositoryEngineeringDir)InstallRuntimes.proj" Targets="InstallTestRuntimes" />
+ </Target>
</Project>
}
catch (ArgumentException ex)
{
- Trace.TraceError($"Module.Version FAILURE: '{versionToParse}' '{versionString}' {ex}");
+ Trace.TraceError($"Module.GetVersion FAILURE: '{versionToParse}' '{versionString}' {ex}");
}
}
}
+ else
+ {
+ Trace.TraceInformation($"Module.GetVersion no version string");
+ }
}
return versionData;
{
buildId = elfFile.BuildID;
}
+ else
+ {
+ Trace.TraceError($"GetBuildId: invalid ELF file {address:X16}");
+ }
}
else if (Target.OperatingSystem == OSPlatform.OSX)
{
{
buildId = machOFile.Uuid;
}
+ else
+ {
+ Trace.TraceError($"GetBuildId: invalid MachO file {address:X16}");
+ }
}
}
catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
/// <returns>version string or null</returns>
protected string GetVersionString(ulong address)
{
- Stream stream = RawMemoryService.CreateMemoryStream();
+ Stream stream = MemoryService.CreateMemoryStream();
try
{
if (Target.OperatingSystem == OSPlatform.Linux)
{
foreach (ELFProgramHeader programHeader in elfFile.Segments.Select((segment) => segment.Header))
{
- uint flags = RawMemoryService.PointerSize == 8 ? programHeader.Flags : programHeader.Flags32;
+ uint flags = MemoryService.PointerSize == 8 ? programHeader.Flags : programHeader.Flags32;
if (programHeader.Type == ELFProgramHeaderType.Load &&
(flags & (uint)ELFProgramHeaderAttributes.Writable) != 0)
{
}
}
}
+ Trace.TraceInformation($"GetVersionString: not found in ELF file {address:X16}");
+ }
+ else
+ {
+ Trace.TraceError($"GetVersionString: invalid ELF file {address:X16}");
}
}
else if (Target.OperatingSystem == OSPlatform.OSX)
}
}
}
+ Trace.TraceInformation($"GetVersionString: not found in MachO file {address:X16}");
+ }
+ else
+ {
+ Trace.TraceError($"GetVersionString: invalid MachO file {address:X16}");
}
}
else
Regex regex = ModuleName is not null ? new Regex(ModuleName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
foreach (ClrModule module in Runtime.EnumerateModules())
{
- if (regex is null || regex.IsMatch(Path.GetFileName(module.Name)))
+ if (regex is null || !string.IsNullOrEmpty(module.Name) && regex.IsMatch(Path.GetFileName(module.Name)))
{
if (Verbose)
{
foreach (IModule module in ModuleService.EnumerateModules().OrderBy((m) => m.ModuleIndex))
{
totalSize += module.ImageSize;
- if (regex is null || regex.IsMatch(Path.GetFileName(module.FileName)))
+ if (regex is null || !string.IsNullOrEmpty(module.FileName) && regex.IsMatch(Path.GetFileName(module.FileName)))
{
if (Verbose)
{
// See the LICENSE file in the project root for more information.
using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime;
+using System;
+using System.Collections.Immutable;
using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
namespace Microsoft.Diagnostics.ExtensionCommands
{
[Command(Name = "runtimes", Help = "List the runtimes in the target or change the default runtime.")]
- public class RuntimesCommand: CommandBase
+ public class RuntimesCommand : CommandBase
{
public IRuntimeService RuntimeService { get; set; }
public IContextService ContextService { get; set; }
- [Option(Name = "-netfx", Help = "Switches to the desktop .NET Framework if exists.")]
+ public ITarget Target { get; set; }
+
+ [Option(Name = "--netfx", Aliases = new string[] { "-netfx", "-f" }, Help = "Switches to the desktop .NET Framework if exists.")]
public bool NetFx { get; set; }
- [Option(Name = "-netcore", Help = "Switches to the .NET Core runtime if exists.")]
+ [Option(Name = "--netcore", Aliases = new string[] { "-netcore", "-c" }, Help = "Switches to the .NET Core or .NET 5+ runtime if exists.")]
public bool NetCore { get; set; }
public override void Invoke()
string current = displayStar ? (runtime == ContextService.GetCurrentRuntime() ? "*" : " ") : "";
Write(current);
Write(runtime.ToString());
+ ClrInfo clrInfo = runtime.Services.GetService<ClrInfo>();
+ if (clrInfo is not null)
+ {
+ unsafe
+ {
+ if (clrInfo.SingleFileRuntimeInfo.HasValue)
+ {
+ RuntimeInfo runtimeInfo = clrInfo.SingleFileRuntimeInfo.Value;
+ WriteLine(" Signature: {0}", Encoding.ASCII.GetString(runtimeInfo.Signature, RuntimeInfo.SignatureValueLength - 1));
+ WriteLine(" Version: {0}", runtimeInfo.Version);
+ if (Target.OperatingSystem == OSPlatform.Windows)
+ {
+ WriteLine(" Runtime: {0}", GetWindowsIndex(runtimeInfo.RuntimeModuleIndex));
+ WriteLine(" DBI: {0}", GetWindowsIndex(runtimeInfo.DbiModuleIndex));
+ WriteLine(" DAC: {0}", GetWindowsIndex(runtimeInfo.DacModuleIndex));
+ }
+ else
+ {
+ WriteLine(" Runtime: {0}", GetUnixIndex(runtimeInfo.RuntimeModuleIndex));
+ WriteLine(" DBI: {0}", GetUnixIndex(runtimeInfo.DbiModuleIndex));
+ WriteLine(" DAC: {0}", GetUnixIndex(runtimeInfo.DacModuleIndex));
+ }
+ }
+ }
+ }
}
}
}
+
+ private unsafe string GetWindowsIndex(byte* index)
+ {
+ uint timeStamp = BitConverter.ToUInt32(new ReadOnlySpan<byte>(index + sizeof(byte), sizeof(uint)).ToArray(), 0);
+ uint fileSize = BitConverter.ToUInt32(new ReadOnlySpan<byte>(index + sizeof(byte) + sizeof(uint), sizeof(uint)).ToArray(), 0);
+ return string.Format("TimeStamp {0:X8} FileSize {1:X8}", timeStamp, fileSize);
+ }
+
+ private unsafe string GetUnixIndex(byte* index)
+ {
+ var buildId = new ReadOnlySpan<byte>(index + sizeof(byte), index[0]).ToArray().ToImmutableArray();
+ return string.Format("BuildId {0}", ToHex(buildId));
+ }
+
+ private string ToHex(ImmutableArray<byte> array) => string.Concat(array.Select((b) => b.ToString("x2")));
}
}
--- /dev/null
+// 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;
+
+namespace Microsoft.Diagnostics.TestHelpers
+{
+ public sealed class CharToLineConverter
+ {
+ readonly Action<string> m_callback;
+ readonly StringBuilder m_text = new StringBuilder();
+
+ public CharToLineConverter(Action<string> callback)
+ {
+ m_callback = callback;
+ }
+
+ public void Input(byte[] buffer, int offset, int count)
+ {
+ for (int i = 0; i < count; i++) {
+ char c = (char)buffer[offset + i];
+ if (c == '\r') {
+ continue;
+ }
+ if (c == '\n') {
+ Flush();
+ }
+ else if (c == '\t' || (c >= (char)0x20 && c <= (char)127)) {
+ m_text.Append(c);
+ }
+ }
+ }
+
+ public void Input(string text)
+ {
+ foreach (char c in text) {
+ if (c == '\r') {
+ continue;
+ }
+ if (c == '\n') {
+ Flush();
+ }
+ else if (c == '\t' || (c >= (char)0x20 && c <= (char)127)) {
+ m_text.Append(c);
+ }
+ }
+ }
+
+ public void Flush()
+ {
+ m_callback(m_text.ToString());
+ m_text.Clear();
+ }
+ }
+}
public void WriteLine(string message)
{
Console.WriteLine(message);
+ Console.Out.Flush();
}
public void WriteLine(string format, params object[] args)
{
Console.WriteLine(format, args);
+ Console.Out.Flush();
}
}
}
\ No newline at end of file
// See the LICENSE file in the project root for more information.
using System;
+using System.Collections;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
ProcessRunner runner = new ProcessRunner(DotNetToolPath, args).
WithEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0").
WithEnvironmentVariable("DOTNET_ROOT", Path.GetDirectoryName(DotNetToolPath)).
+ WithEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", Path.GetDirectoryName(DotNetToolPath)).
+ WithEnvironmentVariable("DOTNET_INSTALL_DIR", Path.GetDirectoryName(DotNetToolPath)).
+ RemoveEnvironmentVariable("MSBuildSDKsPath").
WithWorkingDirectory(DebuggeeSolutionDirPath).
WithLog(output).
WithTimeout(TimeSpan.FromMinutes(10)). // restore can be painfully slow
output.WriteLine("Launching {0} {1}", DotNetToolPath, dotnetArgs);
ProcessRunner runner = new ProcessRunner(DotNetToolPath, dotnetArgs).
- WithWorkingDirectory(DebuggeeProjectDirPath).
- WithLog(output).
- WithTimeout(TimeSpan.FromMinutes(10)). // a mac CI build of the modules debuggee is painfully slow :(
- WithExpectedExitCode(0);
+ WithEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0").
+ WithEnvironmentVariable("DOTNET_ROOT", Path.GetDirectoryName(DotNetToolPath)).
+ WithEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", Path.GetDirectoryName(DotNetToolPath)).
+ WithEnvironmentVariable("DOTNET_INSTALL_DIR", Path.GetDirectoryName(DotNetToolPath)).
+ RemoveEnvironmentVariable("MSBuildSDKsPath").
+ WithWorkingDirectory(DebuggeeProjectDirPath).
+ WithLog(output).
+ WithTimeout(TimeSpan.FromMinutes(10)). // a mac CI build of the modules debuggee is painfully slow :(
+ WithExpectedExitCode(0);
if (OS.Kind != OSKind.Windows && Environment.GetEnvironmentVariable("HOME") == null)
{
--- /dev/null
+using System;
+using System.Diagnostics;
+using Xunit.Abstractions;
+
+namespace Microsoft.Diagnostics.TestHelpers
+{
+ public class LoggingListener : TraceListener
+ {
+ private readonly CharToLineConverter _converter;
+
+ public static void EnableListener(ITestOutputHelper output, string name)
+ {
+ if (Trace.Listeners[name] == null)
+ {
+ Trace.Listeners.Add(new LoggingListener(output, name));
+ Trace.AutoFlush = true;
+ }
+ }
+
+ public static void EnableConsoleListener(string name)
+ {
+ if (Trace.Listeners[name] == null)
+ {
+ Trace.Listeners.Add(new LoggingListener(name));
+ Trace.AutoFlush = true;
+ }
+ }
+
+ private LoggingListener(ITestOutputHelper output, string name)
+ : base(name)
+ {
+ _converter = new CharToLineConverter((text) => {
+ output.WriteLine(text);
+ });
+ }
+
+ private LoggingListener(string name)
+ : base(name)
+ {
+ _converter = new CharToLineConverter((text) => {
+ Console.WriteLine(text);
+ });
+ }
+
+ public override void Write(string message)
+ {
+ _converter.Input(message);
+ }
+
+ public override void WriteLine(string message)
+ {
+ _converter.Input(message + Environment.NewLine);
+ }
+ }
+}
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="Microsoft.DotNet.RemoteExecutor" Version="$(MicrosoftDotNetRemoteExecutorVersion)" />
<PackageReference Include="xunit" Version="$(XUnitVersion)" />
<PackageReference Include="xunit.abstractions" Version="$(XUnitAbstractionsVersion)" />
</ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(MSBuildThisFileDirectory)..\Microsoft.Diagnostics.DebugServices.Implementation\Microsoft.Diagnostics.DebugServices.Implementation.csproj" />
+ <ProjectReference Include="$(MSBuildThisFileDirectory)..\Microsoft.Diagnostics.DebugServices\Microsoft.Diagnostics.DebugServices.csproj" />
+ <ProjectReference Include="$(MSBuildThisFileDirectory)..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj" />
+ </ItemGroup>
</Project>
get { lock (_lock) { return _replayCommand; } }
}
+ public ProcessRunner RemoveEnvironmentVariable(string key)
+ {
+ lock (_lock)
+ {
+ _p.StartInfo.Environment.Remove(key);
+ }
+ return this;
+ }
+
public ProcessRunner WithEnvironmentVariable(string key, string value)
{
lock (_lock)
// can be thrown.
try
{
- p.Kill();
+ p.Kill(entireProcessTree: true);
}
catch (InvalidOperationException) { }
}
--- /dev/null
+// 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 Microsoft.Diagnostics.NETCore.Client;
+using Microsoft.DotNet.RemoteExecutor;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+namespace Microsoft.Diagnostics.TestHelpers
+{
+ public static class RemoteExecutorHelper
+ {
+ public static async Task<int> RemoteInvoke(ITestOutputHelper output, TestConfiguration config, TimeSpan timeout, string dumpPath, Func<string, Task<int>> method)
+ {
+ RemoteInvokeOptions options = new()
+ {
+ StartInfo = new ProcessStartInfo() { RedirectStandardOutput = true, RedirectStandardError = true }
+ };
+ // The remoteInvokeHandle is NOT disposed (i.e. with a using) here because the RemoteExecutor dispose code uses an older (1.x) version
+ // of clrmd that conflicts with the 2.0 version diagnostics is using and throws the exception:
+ //
+ // "Method not found: 'Microsoft.Diagnostics.Runtime.DataTarget Microsoft.Diagnostics.Runtime.DataTarget.AttachToProcess(Int32, UInt32)'."
+ //
+ // When RemoteExecutor is fixed the "using" can be added and the GC.SuppressFinalize be removed.
+ RemoteInvokeHandle remoteInvokeHandle = RemoteExecutor.Invoke(method, config.Serialize(), options);
+ GC.SuppressFinalize(remoteInvokeHandle);
+ try
+ {
+ Task stdOutputTask = WriteStreamToOutput(remoteInvokeHandle.Process.StandardOutput, output);
+ Task stdErrorTask = WriteStreamToOutput(remoteInvokeHandle.Process.StandardError, output);
+ Task outputTasks = Task.WhenAll(stdErrorTask, stdOutputTask);
+
+ Task processExit = Task.Factory.StartNew(() => remoteInvokeHandle.Process.WaitForExit(), TaskCreationOptions.LongRunning);
+ Task timeoutTask = Task.Delay(timeout);
+ Task completedTask = await Task.WhenAny(outputTasks, processExit, timeoutTask);
+ if (completedTask == timeoutTask)
+ {
+ if (!string.IsNullOrEmpty(dumpPath))
+ {
+ output.WriteLine($"RemoteExecutorHelper.RemoteInvoke timed out: writing dump to {dumpPath}");
+ DiagnosticsClient client = new(remoteInvokeHandle.Process.Id);
+ try
+ {
+ await client.WriteDumpAsync(DumpType.WithHeap, dumpPath, WriteDumpFlags.None, CancellationToken.None);
+ }
+ catch (Exception ex) when (ex is ArgumentException || ex is UnsupportedCommandException || ex is ServerErrorException)
+ {
+ output.WriteLine($"RemoteExecutorHelper.RemoteInvoke: writing dump FAILED {ex}");
+ }
+ }
+ throw new XunitException("RemoteExecutorHelper.RemoteInvoke timed out");
+ }
+ else
+ {
+ return remoteInvokeHandle.ExitCode;
+ }
+ }
+ finally
+ {
+ if (remoteInvokeHandle.Process != null)
+ {
+ try
+ {
+ output.WriteLine($"RemoteExecutorHelper.RemoteInvoke: killing process {remoteInvokeHandle.Process.Id}");
+ remoteInvokeHandle.Process.Kill(entireProcessTree: true);
+ }
+ catch
+ {
+ }
+ remoteInvokeHandle.Process.Dispose();
+ remoteInvokeHandle.Process = null;
+ }
+ }
+ }
+
+ public static async Task RemoteInvoke(ITestOutputHelper output, Action testCase)
+ {
+ var options = new RemoteInvokeOptions()
+ {
+ StartInfo = new ProcessStartInfo() { RedirectStandardOutput = true, RedirectStandardError = true }
+ };
+ using RemoteInvokeHandle remoteInvokeHandle = RemoteExecutor.Invoke(testCase, options);
+
+ Task stdOutputTask = WriteStreamToOutput(remoteInvokeHandle.Process.StandardOutput, output);
+ Task stdErrorTask = WriteStreamToOutput(remoteInvokeHandle.Process.StandardError, output);
+ await Task.WhenAll(stdErrorTask, stdOutputTask);
+ }
+
+ private static Task WriteStreamToOutput(StreamReader reader, ITestOutputHelper output)
+ {
+ return Task.Factory.StartNew(creationOptions: TaskCreationOptions.LongRunning, function: async () => {
+ try
+ {
+ while (!reader.EndOfStream)
+ {
+ string line = await reader.ReadLineAsync();
+ output.WriteLine(line);
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ output.WriteLine("Failed to collect remote process's output");
+ }
+ });
+ }
+ }
+}
["TempPath"] = Path.GetTempPath(),
["WorkingDir"] = GetInitialWorkingDir(),
["OS"] = OS.Kind.ToString(),
+ ["IsAlpine"] = OS.IsAlpine.ToString().ToLowerInvariant(),
+ ["TargetRid"] = GetRid(),
["TargetArchitecture"] = OS.TargetArchitecture.ToString().ToLowerInvariant(),
["NuGetPackageCacheDir"] = nugetPackages
};
Configurations = configs.Select(c => new TestConfiguration(c)).ToList();
}
+ static string GetRid()
+ {
+ string os = OS.Kind switch
+ {
+ OSKind.Linux => OS.IsAlpine ? "linux-musl" : "linux",
+ OSKind.OSX => "osx",
+ OSKind.Windows => "win",
+ _ => throw new PlatformNotSupportedException(),
+ };
+ string architecture = OS.TargetArchitecture.ToString().ToLowerInvariant();
+ return $"{os}-{architecture}";
+ }
+
Dictionary<string, string>[] ParseConfigFile(string path, Dictionary<string, string>[] templates)
{
XDocument doc = XDocument.Load(path);
_configStringView = GetStringViewWithVersion(RuntimeFrameworkVersion);
}
+ public string Serialize()
+ {
+ List<XElement> nodes = new();
+ foreach (KeyValuePair<string, string> keyvalue in _settings)
+ {
+ nodes.Add(new XElement(keyvalue.Key, keyvalue.Value));
+ }
+ XElement root = new("Configuration", nodes.ToArray());
+ TextWriter writer = new StringWriter();
+ root.Save(writer);
+ return writer.ToString();
+ }
+
+ public static TestConfiguration Deserialize(string xml)
+ {
+ XElement root = XElement.Parse(xml);
+ Dictionary<string, string> settings = new();
+ foreach (XElement child in root.Elements())
+ {
+ settings.Add(child.Name.LocalName, child.Value);
+ }
+ return new TestConfiguration(settings);
+ }
+
private string GetTruncatedRuntimeFrameworkVersion()
{
string version = RuntimeFrameworkVersion;
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Xml.Linq;
+using Xunit;
+
+namespace Microsoft.Diagnostics.TestHelpers
+{
+ public class TestDataReader
+ {
+ /// <summary>
+ /// Test data value
+ /// </summary>
+ public class Value
+ {
+ private readonly object _value;
+
+ internal Value(string valueString)
+ {
+ _value = valueString;
+ }
+
+ internal Value(ImmutableArray<ImmutableDictionary<string, Value>> values)
+ {
+ _value = values;
+ }
+
+ /// <summary>
+ /// Returns true if sub values
+ /// </summary>
+ public bool IsSubValue => _value is ImmutableArray<ImmutableDictionary<string, Value>>;
+
+ /// <summary>
+ /// Return the sub values for nested test data
+ /// </summary>
+ public ImmutableArray<ImmutableDictionary<string, Value>> Values
+ {
+ get { return _value is ImmutableArray<ImmutableDictionary<string, Value>> values ? values : ImmutableArray<ImmutableDictionary<string, Value>>.Empty; }
+ }
+
+ /// <summary>
+ /// Get the test data value as type T
+ /// </summary>
+ public T GetValue<T>()
+ {
+ return (T)GetValue(typeof(T));
+ }
+
+ /// <summary>
+ /// Get the test data value as "type"
+ /// </summary>
+ public object GetValue(Type type)
+ {
+ object value = _value;
+ if (value is string valueString)
+ {
+ GetValue(type, valueString, ref value);
+ }
+ return value;
+ }
+
+ /// <summary>
+ /// Convert test data string to value
+ /// </summary>
+ /// <param name="type">type to convert to</param>
+ /// <param name="valueString">test data string</param>
+ /// <param name="result">resulting object</param>
+ public static void GetValue(Type type, string valueString, ref object result)
+ {
+ valueString = valueString.Trim();
+ if (type == typeof(string))
+ {
+ result = valueString ?? "";
+ }
+ else if (type == typeof(bool))
+ {
+ switch (valueString.ToLowerInvariant())
+ {
+ case "true":
+ result = true;
+ break;
+ case "false":
+ result = false;
+ break;
+ }
+ }
+ else if (type.IsEnum)
+ {
+ result = Enum.Parse(type, valueString);
+ }
+ else if (type.IsPrimitive)
+ {
+ NumberStyles style = valueString.StartsWith("0x") ? NumberStyles.HexNumber : NumberStyles.Integer;
+ if (ulong.TryParse(valueString.Replace("0x", ""), style, CultureInfo.InvariantCulture, out ulong integerValue))
+ {
+ result = Convert.ChangeType(integerValue, type);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Test data file version
+ /// </summary>
+ public readonly Version Version;
+
+ /// <summary>
+ /// Target test data
+ /// </summary>
+ public readonly ImmutableDictionary<string, Value> Target;
+
+ /// <summary>
+ /// Shortcut to the module test data
+ /// </summary>
+ public readonly ImmutableArray<ImmutableDictionary<string, Value>> Modules;
+
+ /// <summary>
+ /// Shortcut to the thread test data
+ /// </summary>
+ public readonly ImmutableArray<ImmutableDictionary<string, Value>> Threads;
+
+ /// <summary>
+ /// Shortcut to the runtime test data
+ /// </summary>
+ public readonly ImmutableArray<ImmutableDictionary<string, Value>> Runtimes;
+
+ /// <summary>
+ /// Open the test data xml file
+ /// </summary>
+ /// <param name="testDataFile"></param>
+ public TestDataReader(string testDataFile)
+ {
+ XDocument doc = XDocument.Load(testDataFile);
+ XElement root = doc.Root;
+ Assert.Equal("TestData", root.Name);
+ foreach (XElement child in root.Elements())
+ {
+ switch (child.Name.LocalName)
+ {
+ case "Version":
+ Version = Version.Parse(child.Value);
+ break;
+ case "Target":
+ Target = Build(child);
+ break;
+ }
+ }
+ Modules = Target["Modules"].Values;
+ Threads = Target["Threads"].Values;
+ Runtimes = Target["Runtimes"].Values;
+ }
+
+ private static ImmutableDictionary<string, Value> Build(XElement node)
+ {
+ var members = new Dictionary<string, Value>();
+ foreach (XElement dataNode in node.Elements())
+ {
+ string name = dataNode.Name.LocalName;
+ if (dataNode.HasElements)
+ {
+ var items = new List<ImmutableDictionary<string, Value>>();
+ foreach (XElement subValue in dataNode.Elements())
+ {
+ if (subValue.HasElements)
+ {
+ // Has multiple elements (i.e. Modules, Threads, Runtimes,
+ // etc). Assumes the same name for each entry.
+ items.Add(Build(subValue));
+ }
+ else
+ {
+ // Only has sub members (i.e. RuntimeModule, etc.)
+ items.Add(Build(dataNode));
+ break;
+ }
+ }
+ members.Add(name, new Value(items.ToImmutableArray()));
+ }
+ else
+ {
+ members.Add(name, new Value(dataNode.Value));
+ }
+ }
+ return members.ToImmutableDictionary();
+ }
+ }
+
+ public static class TestDataExtensions
+ {
+ /// <summary>
+ /// Helper function to get a test data value
+ /// </summary>
+ /// <typeparam name="T">type to convert test data value</typeparam>
+ /// <param name="values">values collection to lookup name</param>
+ /// <param name="name">value name</param>
+ /// <param name="value">result value of type T</param>
+ /// <returns></returns>
+ public static bool TryGetValue<T>(
+ this ImmutableDictionary<string, TestDataReader.Value> values, string name, out T value)
+ {
+ if (values.TryGetValue(name, out TestDataReader.Value testValue))
+ {
+ value = testValue.GetValue<T>();
+ return true;
+ }
+ value = default;
+ return false;
+ }
+
+ /// <summary>
+ /// Finds the match item (i.e. IModule, IThread, etc.) in the test data.
+ /// </summary>
+ /// <typeparam name="T">field or property type</typeparam>
+ /// <param name="items">Modules, Threads, Registers, etc. test data</param>
+ /// <param name="propety">name of property to use for search</param>
+ /// <param name="propertyValue"></param>
+ /// <returns>test data values</returns>
+ public static ImmutableDictionary<string, TestDataReader.Value> Find<T>(
+ this ImmutableArray<ImmutableDictionary<string, TestDataReader.Value>> items, string propety, T propertyValue)
+ where T : IComparable
+ {
+ foreach (var item in items)
+ {
+ TestDataReader.Value value = item[propety];
+ if (propertyValue.CompareTo(value.GetValue<T>()) == 0)
+ {
+ return item;
+ }
+ }
+ return default;
+ }
+
+ /// <summary>
+ /// Compares the test data values with the properties in the instance with the same name. This is
+ /// used to compare ITarget, IModule, IThread, RegiserInfo instances to the test data.
+ /// </summary>
+ /// <param name="values">test data for the item</param>
+ /// <param name="instance">object to compare</param>
+ public static void CompareMembers(
+ this ImmutableDictionary<string, TestDataReader.Value> values, object instance)
+ {
+ foreach (KeyValuePair<string, TestDataReader.Value> testData in values)
+ {
+ MemberInfo[] members = instance.GetType().GetMember(
+ testData.Key,
+ MemberTypes.Field | MemberTypes.Property | MemberTypes.Method,
+ BindingFlags.Public | BindingFlags.Instance);
+
+ if (members.Length > 0)
+ {
+ MemberInfo member = members.Single();
+ object memberValue = null;
+ Type memberType = null;
+
+ switch (member.MemberType)
+ {
+ case MemberTypes.Property:
+ memberValue = ((PropertyInfo)member).GetValue(instance);
+ memberType = ((PropertyInfo)member).PropertyType;
+ break;
+ case MemberTypes.Field:
+ memberValue = ((FieldInfo)member).GetValue(instance);
+ memberType = ((FieldInfo)member).FieldType;
+ break;
+ case MemberTypes.Method:
+ if (((MethodInfo)member).GetParameters().Length == 0)
+ {
+ memberValue = ((MethodInfo)member).Invoke(instance, null);
+ memberType = ((MethodInfo)member).ReturnType;
+ }
+ break;
+ }
+ if (memberType != null)
+ {
+ if (testData.Value.IsSubValue)
+ {
+ Trace.TraceInformation($"CompareMembers {testData.Key} sub value:");
+ CompareMembers(testData.Value.Values.Single(), memberValue);
+ }
+ else
+ {
+ Type nullableType = Nullable.GetUnderlyingType(memberType);
+ memberType = nullableType ?? memberType;
+
+ if (nullableType != null && memberValue == null)
+ {
+ memberValue = string.Empty;
+ }
+ else if (memberType == typeof(string))
+ {
+ memberValue ??= string.Empty;
+ }
+ else if (memberValue is ImmutableArray<byte> buildId)
+ {
+ memberType = typeof(string);
+ memberValue = !buildId.IsDefaultOrEmpty ? string.Concat(buildId.Select((b) => b.ToString("x2"))) : string.Empty;
+ }
+ else if (!memberType.IsPrimitive && !memberType.IsEnum)
+ {
+ memberType = typeof(string);
+ memberValue = memberValue?.ToString() ?? string.Empty;
+ }
+ object testDataValue = testData.Value.GetValue(memberType);
+ Trace.TraceInformation($"CompareMembers {testData.Key}: expected '{testDataValue}' actual '{memberValue}'");
+
+ // Disable checking the VersionData property because downloading the necessary binary to map into the address is unreliable
+ // See issue: https://github.com/dotnet/diagnostics/issues/2955
+ if (testData.Key == "VersionData")
+ {
+ if (!object.Equals(testDataValue, memberValue))
+ {
+ Trace.TraceError($"CompareMembers VersionData: expected '{testDataValue}' != actual '{memberValue}'");
+ }
+ }
+ else
+ {
+ Assert.Equal(testDataValue, memberValue);
+ }
+ }
+ }
+ else
+ {
+ Trace.TraceWarning($"CompareMembers {testData.Key} member not found");
+ return;
+ }
+ }
+ else
+ {
+ Trace.TraceWarning($"CompareMembers {testData.Key} not found");
+ }
+ }
+ }
+ }
+}
--- /dev/null
+using Microsoft.Diagnostics.DebugServices;
+using System;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Xml.Linq;
+
+namespace Microsoft.Diagnostics.TestHelpers
+{
+ public class TestDataWriter
+ {
+ public readonly XElement Root;
+ public readonly XElement Target;
+
+ /// <summary>
+ /// Write a test data file from the target
+ /// </summary>
+ public TestDataWriter()
+ {
+ Root = new XElement("TestData");
+ Root.Add(new XElement("Version", "1.0.0"));
+ Target = new XElement("Target");
+ Root.Add(Target);
+ }
+
+ public void Build(ITarget target)
+ {
+ AddMembers(Target, typeof(ITarget), target, nameof(ITarget.Id), nameof(ITarget.GetTempDirectory));
+
+ var modulesElement = new XElement("Modules");
+ Target.Add(modulesElement);
+
+ var moduleService = target.Services.GetService<IModuleService>();
+ string runtimeModuleName = target.GetPlatformModuleName("coreclr");
+ foreach (IModule module in moduleService.EnumerateModules())
+ {
+ var moduleElement = new XElement("Module");
+ modulesElement.Add(moduleElement);
+ AddModuleMembers(moduleElement, module, runtimeModuleName);
+ }
+
+ var threadsElement = new XElement("Threads");
+ Target.Add(threadsElement);
+
+ var threadService = target.Services.GetService<IThreadService>();
+ var registerIndexes = new int[] { threadService.InstructionPointerIndex, threadService.StackPointerIndex, threadService.FramePointerIndex };
+ foreach (IThread thread in threadService.EnumerateThreads())
+ {
+ var threadElement = new XElement("Thread");
+ threadsElement.Add(threadElement);
+ AddMembers(threadElement, typeof(IThread), thread, nameof(IThread.ThreadIndex), nameof(IThread.GetThreadContext));
+
+ var registersElement = new XElement("Registers");
+ threadElement.Add(registersElement);
+ foreach (int registerIndex in registerIndexes)
+ {
+ var registerElement = new XElement("Register");
+ registersElement.Add(registerElement);
+
+ if (threadService.TryGetRegisterInfo(registerIndex, out RegisterInfo info))
+ {
+ AddMembers(registerElement, typeof(RegisterInfo), info, nameof(Object.ToString), nameof(Object.GetHashCode));
+ }
+ if (thread.TryGetRegisterValue(registerIndex, out ulong value))
+ {
+ registerElement.Add(new XElement("Value", $"0x{value:X16}"));
+ }
+ }
+ }
+
+ var runtimesElement = new XElement("Runtimes");
+ Target.Add(runtimesElement);
+
+ var runtimeService = target.Services.GetService<IRuntimeService>();
+ foreach (IRuntime runtime in runtimeService.EnumerateRuntimes())
+ {
+ var runtimeElement = new XElement("Runtime");
+ runtimesElement.Add(runtimeElement);
+ AddMembers(runtimeElement, typeof(IRuntime), runtime, nameof(IRuntime.GetDacFilePath), nameof(IRuntime.GetDbiFilePath));
+
+ var runtimeModuleElement = new XElement("RuntimeModule");
+ runtimeElement.Add(runtimeModuleElement);
+ AddModuleMembers(runtimeModuleElement, runtime.RuntimeModule, symbolModuleName: null);
+ }
+ }
+
+ public void Write(string testDataFile)
+ {
+ File.WriteAllText(testDataFile, Root.ToString());
+ }
+
+ private void AddModuleMembers(XElement element, IModule module, string symbolModuleName)
+ {
+ AddMembers(element, typeof(IModule), module, nameof(IModule.ModuleIndex), nameof(IModule.PdbFileInfos), nameof(IModule.VersionString));
+
+ if (symbolModuleName != null && IsModuleEqual(module, symbolModuleName))
+ {
+ IExportSymbols exportSymbols = module.Services.GetService<IExportSymbols>();
+ if (exportSymbols is not null)
+ {
+ XElement exportSymbolsElement = null;
+
+ string symbol1 = "coreclr_initialize";
+ if (exportSymbols.TryGetSymbolAddress(symbol1, out ulong offset1))
+ {
+ XElement symbolElement = AddExportSymbolSection();
+ symbolElement.Add(new XElement("Name", symbol1));
+ symbolElement.Add(new XElement("Value", ToHex(offset1)));
+ }
+ string symbol2 = "coreclr_execute_assembly";
+ if (exportSymbols.TryGetSymbolAddress(symbol2, out ulong offset2))
+ {
+ XElement symbolElement = AddExportSymbolSection();
+ symbolElement.Add(new XElement("Name", symbol2));
+ symbolElement.Add(new XElement("Value", ToHex(offset2)));
+ }
+
+ XElement AddExportSymbolSection()
+ {
+ if (exportSymbolsElement == null)
+ {
+ exportSymbolsElement = new XElement("ExportSymbols");
+ element.Add(exportSymbolsElement);
+ }
+ var symbolElement = new XElement("Symbol");
+ exportSymbolsElement.Add(symbolElement);
+ return symbolElement;
+ }
+ }
+
+ IModuleSymbols moduleSymbols = module.Services.GetService<IModuleSymbols>();
+ if (moduleSymbols is not null)
+ {
+ XElement symbolsElement = null;
+
+ string symbol1 = "coreclr_initialize";
+ if (moduleSymbols.TryGetSymbolAddress(symbol1, out ulong offset1))
+ {
+ XElement symbolElement = AddExportSymbolSection();
+ symbolElement.Add(new XElement("Name", symbol1));
+ symbolElement.Add(new XElement("Value", ToHex(offset1)));
+ symbolElement.Add(new XElement("Displacement", "0"));
+ }
+
+ XElement AddExportSymbolSection()
+ {
+ if (symbolsElement == null)
+ {
+ symbolsElement = new XElement("Symbols");
+ element.Add(symbolsElement);
+ }
+ var symbolElement = new XElement("Symbol");
+ symbolsElement.Add(symbolElement);
+ return symbolElement;
+ }
+ }
+ }
+ }
+
+ private void AddMembers(XElement element, Type type, object instance, params string[] membersToSkip)
+ {
+ MemberInfo[] members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance);
+ foreach (MemberInfo member in members)
+ {
+ if (membersToSkip.Any((skip) => member.Name == skip)) {
+ continue;
+ }
+ string result = null;
+ object memberValue = null;
+ Type memberType = null;
+
+ switch (member.MemberType)
+ {
+ case MemberTypes.Property:
+ memberValue = ((PropertyInfo)member).GetValue(instance);
+ memberType = ((PropertyInfo)member).PropertyType;
+ break;
+ case MemberTypes.Field:
+ memberValue = ((FieldInfo)member).GetValue(instance);
+ memberType = ((FieldInfo)member).FieldType;
+ break;
+ case MemberTypes.Method:
+ MethodInfo methodInfo = (MethodInfo)member;
+ if (!methodInfo.IsSpecialName && methodInfo.GetParameters().Length == 0 && methodInfo.ReturnType != typeof(void))
+ {
+ memberValue = ((MethodInfo)member).Invoke(instance, null);
+ memberType = ((MethodInfo)member).ReturnType;
+ }
+ break;
+ }
+ if (memberType != null)
+ {
+ Type nullableType = Nullable.GetUnderlyingType(memberType);
+ memberType = nullableType ?? memberType;
+
+ if (nullableType != null && memberValue == null)
+ {
+ result = "";
+ }
+ else if (memberType == typeof(string))
+ {
+ result = (string)memberValue ?? "";
+ }
+ else if (memberType == typeof(bool))
+ {
+ result = (bool)memberValue ? "true" : "false";
+ }
+ else if (memberValue is ImmutableArray<byte> buildId)
+ {
+ if (!buildId.IsDefaultOrEmpty)
+ {
+ result = string.Concat(buildId.Select((b) => b.ToString("x2")));
+ }
+ }
+ else if (memberType.IsEnum)
+ {
+ result = memberValue.ToString();
+ }
+ else if (memberType.IsPrimitive)
+ {
+ if (memberType == typeof(short) || memberType == typeof(int) || memberType == typeof(long))
+ {
+ result = memberValue.ToString();
+ }
+ else
+ {
+ int digits = Marshal.SizeOf(memberType) * 2;
+ result = string.Format($"0x{{0:X{digits}}}", memberValue);
+ }
+ }
+ else if (memberType.IsValueType || memberType == typeof(VersionData) || memberType == typeof(PdbFileInfo))
+ {
+ result = memberValue?.ToString();
+ }
+ }
+ if (result != null)
+ {
+ element.Add(new XElement(member.Name, result));
+ }
+ }
+ }
+
+ private string ToHex<T>(T value) where T : struct
+ {
+ int digits = Marshal.SizeOf(typeof(T)) * 2;
+ return string.Format($"0x{{0:X{digits}}}", value);
+ }
+
+ private bool IsModuleEqual(IModule module, string moduleName)
+ {
+ if (module.Target.OperatingSystem == OSPlatform.Windows) {
+ return StringComparer.OrdinalIgnoreCase.Equals(Path.GetFileName(module.FileName), moduleName);
+ }
+ else {
+ return string.Equals(Path.GetFileName(module.FileName), moduleName);
+ }
+ }
+ }
+}
--- /dev/null
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.DebugServices.Implementation;
+using Microsoft.Diagnostics.Runtime;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics.TestHelpers
+{
+ public class TestDump : TestHost, IHost
+ {
+ private readonly ServiceProvider _serviceProvider;
+ private readonly ContextService _contextService;
+ private readonly SymbolService _symbolService;
+ private DataTarget _dataTarget;
+ private int _targetIdFactory;
+
+ public TestDump(TestConfiguration config)
+ : base(config)
+ {
+ _serviceProvider = new ServiceProvider();
+ _contextService = new ContextService(this);
+ _symbolService = new SymbolService(this);
+ _serviceProvider.AddService<IContextService>(_contextService);
+ _serviceProvider.AddService<ISymbolService>(_symbolService);
+
+ // Automatically enable symbol server support
+ _symbolService.AddSymbolServer(msdl: true, symweb: false, symbolServerPath: null, authToken: null, timeoutInMinutes: 0);
+ _symbolService.AddCachePath(_symbolService.DefaultSymbolCache);
+ }
+
+ protected override ITarget GetTarget()
+ {
+ _dataTarget = DataTarget.LoadDump(DumpFile);
+
+ OSPlatform targetPlatform = _dataTarget.DataReader.TargetPlatform;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
+ targetPlatform = OSPlatform.OSX;
+ }
+ _symbolService.AddDirectoryPath(Path.GetDirectoryName(DumpFile));
+ return new TargetFromDataReader(_dataTarget.DataReader, targetPlatform, this, _targetIdFactory++, DumpFile);
+ }
+
+ #region IHost
+
+ public IServiceEvent OnShutdownEvent { get; } = new ServiceEvent();
+
+ HostType IHost.HostType => HostType.DotnetDump;
+
+ IServiceProvider IHost.Services => _serviceProvider;
+
+ IEnumerable<ITarget> IHost.EnumerateTargets() => Target != null ? new ITarget[] { Target } : Array.Empty<ITarget>();
+
+ void IHost.DestroyTarget(ITarget target)
+ {
+ if (target == null) {
+ throw new ArgumentNullException(nameof(target));
+ }
+ if (target == Target)
+ {
+ _contextService.ClearCurrentTarget();
+ if (target is IDisposable disposable) {
+ disposable.Dispose();
+ }
+ }
+ }
+
+ #endregion
+ }
+}
--- /dev/null
+using Microsoft.Diagnostics.DebugServices;
+
+namespace Microsoft.Diagnostics.TestHelpers
+{
+ public abstract class TestHost
+ {
+ private TestDataReader _testData;
+ private ITarget _target;
+
+ public readonly TestConfiguration Config;
+
+ public TestHost(TestConfiguration config)
+ {
+ Config = config;
+ }
+
+ public TestDataReader TestData
+ {
+ get
+ {
+ _testData ??= new TestDataReader(TestDataFile);
+ return _testData;
+ }
+ }
+
+ public ITarget Target
+ {
+ get
+ {
+ _target ??= GetTarget();
+ return _target;
+ }
+ }
+
+ public bool IsTestDbgEng => Config.AllSettings.TryGetValue("TestDbgEng", out string value) && value == "true";
+
+ protected abstract ITarget GetTarget();
+
+ public string DumpFile => TestConfiguration.MakeCanonicalPath(Config.AllSettings["DumpFile"]);
+
+ public string TestDataFile => TestConfiguration.MakeCanonicalPath(Config.AllSettings["TestDataFile"]);
+
+ public override string ToString() => DumpFile;
+ }
+
+ public static class TestHostExtensions
+ {
+ public static bool IsTestDbgEng(this TestConfiguration config) => config.AllSettings.TryGetValue("TestDbgEng", out string value) && value == "true";
+ }
+}
OSX = 3,
};
- internal static Guid IID_IDebuggerServices = new Guid("B4640016-6CA0-468E-BA2C-1FFF28DE7B72");
+ private static Guid IID_IDebuggerServices = new Guid("B4640016-6CA0-468E-BA2C-1FFF28DE7B72");
- private ref readonly IDebuggerServicesVTable VTable => ref Unsafe.AsRef<IDebuggerServicesVTable >(_vtable);
+ private ref readonly IDebuggerServicesVTable VTable => ref Unsafe.AsRef<IDebuggerServicesVTable>(_vtable);
private readonly HostType _hostType;
fixed (byte* versionBufferPtr = versionBuffer)
{
HResult hr = VTable.GetModuleVersionInformation(Self, (uint)index, 0, getVersionInfoPtr, versionBufferPtr, (uint)versionBufferSize, null);
- if (hr == HResult.S_OK) {
+ if (hr == HResult.S_OK)
+ {
fileInfo = *((VS_FIXEDFILEINFO*)versionBufferPtr);
}
return hr;
fixed (byte* versionBufferPtr = versionBuffer)
{
int hr = VTable.GetModuleVersionInformation(Self, (uint)index, 0, getVersionStringPtr, versionBufferPtr, (uint)versionBuffer.Length, null);
- if (hr == HResult.S_OK) {
+ if (hr == HResult.S_OK)
+ {
version = Marshal.PtrToStringAnsi(new IntPtr(versionBufferPtr));
}
return hr;
if (_hostType == HostType.DbgEng)
{
int index = symbol.IndexOf('!');
- if (index != -1) {
+ if (index != -1)
+ {
symbol = symbol.Remove(0, index + 1);
}
}
return VTable.GetOffsetBySymbol(Self, moduleIndex, symbolPtr, out address);
}
}
- }
- [StructLayout(LayoutKind.Sequential)]
- internal readonly unsafe struct IDebuggerServicesVTable
- {
- public readonly delegate* unmanaged[Stdcall]<IntPtr, out DebuggerServices.OperatingSystem, HResult> GetOperatingSystem;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, out DEBUG_CLASS, out DEBUG_CLASS_QUALIFIER, HResult> GetDebuggeeType;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, out IMAGE_FILE_MACHINE, HResult> GetExecutingProcessorType;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, byte*, byte*, IntPtr*, int, HResult> AddCommand;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, DEBUG_OUTPUT, byte*, void> OutputString;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, byte*, uint, out int, HResult> ReadVirtual;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, byte*, uint, out int, HResult> WriteVirtual;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, out uint, HResult> GetNumberModules;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, ulong, byte*, uint, out uint, byte*, uint, uint*, byte*, uint, uint*, HResult> GetModuleNames;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, out ulong, out ulong, out uint, out uint, HResult> GetModuleInfo;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, ulong, byte*, byte*, uint, uint*, HResult> GetModuleVersionInformation;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, HResult> GetNumberThreads;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, uint*, uint*, HResult> GetThreadIdsByIndex;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, uint, byte*, HResult> GetThreadContextBySystemId;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, HResult> GetCurrentProcessSystemId;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, HResult> GetCurrentThreadSystemId;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, HResult> SetCurrentThreadSystemId;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, out ulong, HResult> GetThreadTeb;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, byte*, HResult> VirtualUnwind;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, byte*, uint, out uint, HResult> GetSymbolPath;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, int, ulong, byte*, int, out uint, out ulong, HResult> GetSymbolByOffset;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, int, byte*, out ulong, HResult> GetOffsetBySymbol;
+ [StructLayout(LayoutKind.Sequential)]
+ private readonly unsafe struct IDebuggerServicesVTable
+ {
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, out DebuggerServices.OperatingSystem, HResult> GetOperatingSystem;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, out DEBUG_CLASS, out DEBUG_CLASS_QUALIFIER, HResult> GetDebuggeeType;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, out IMAGE_FILE_MACHINE, HResult> GetExecutingProcessorType;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, byte*, byte*, IntPtr*, int, HResult> AddCommand;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, DEBUG_OUTPUT, byte*, void> OutputString;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, byte*, uint, out int, HResult> ReadVirtual;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, byte*, uint, out int, HResult> WriteVirtual;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, out uint, HResult> GetNumberModules;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, ulong, byte*, uint, out uint, byte*, uint, uint*, byte*, uint, uint*, HResult> GetModuleNames;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, out ulong, out ulong, out uint, out uint, HResult> GetModuleInfo;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, ulong, byte*, byte*, uint, uint*, HResult> GetModuleVersionInformation;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, HResult> GetNumberThreads;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, uint*, uint*, HResult> GetThreadIdsByIndex;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, uint, byte*, HResult> GetThreadContextBySystemId;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, HResult> GetCurrentProcessSystemId;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, HResult> GetCurrentThreadSystemId;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, HResult> SetCurrentThreadSystemId;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, out ulong, HResult> GetThreadTeb;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, byte*, HResult> VirtualUnwind;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, byte*, uint, out uint, HResult> GetSymbolPath;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, int, ulong, byte*, int, out uint, out ulong, HResult> GetSymbolByOffset;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, int, byte*, out ulong, HResult> GetOffsetBySymbol;
+ }
}
}
{
internal unsafe class RemoteMemoryService : CallableCOMWrapper, IRemoteMemoryService
{
- internal static Guid IID_IRemoteMemoryService = new Guid("CD6A0F22-8BCF-4297-9366-F440C2D1C781");
+ private static Guid IID_IRemoteMemoryService = new Guid("CD6A0F22-8BCF-4297-9366-F440C2D1C781");
private ref readonly IRemoteMemoryServiceVTable VTable => ref Unsafe.AsRef<IRemoteMemoryServiceVTable>(_vtable);
{
return VTable.FreeVirtual(Self, address, size, typeFlags) == HResult.S_OK;
}
- }
- [StructLayout(LayoutKind.Sequential)]
- internal readonly unsafe struct IRemoteMemoryServiceVTable
- {
- public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, uint, uint, uint, out ulong, HResult> AllocVirtual;
- public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, uint, uint, HResult> FreeVirtual;
+ [StructLayout(LayoutKind.Sequential)]
+ private readonly unsafe struct IRemoteMemoryServiceVTable
+ {
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, uint, uint, uint, out ulong, HResult> AllocVirtual;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, uint, uint, HResult> FreeVirtual;
+ }
}
}
namespace SOS.Hosting
{
- internal sealed unsafe class CorDebugDataTargetWrapper : COMCallableIUnknown
+ public sealed unsafe class CorDebugDataTargetWrapper : COMCallableIUnknown
{
private static readonly Guid IID_ICorDebugDataTarget = new Guid("FE06DC28-49FB-4636-A4A3-E80DB4AE116C");
private static readonly Guid IID_ICorDebugDataTarget4 = new Guid("E799DC06-E099-4713-BDD9-906D3CC02CF2");
public IntPtr ICorDebugDataTarget { get; }
- internal CorDebugDataTargetWrapper(IServiceProvider services)
+ public CorDebugDataTargetWrapper(IServiceProvider services)
{
Debug.Assert(services != null);
_target = services.GetService<ITarget>();
namespace SOS.Hosting
{
- internal sealed unsafe class RuntimeWrapper : COMCallableIUnknown
+ public sealed unsafe class RuntimeWrapper : COMCallableIUnknown
{
/// <summary>
/// The runtime OS and type. Must match IRuntime::RuntimeConfiguration in runtime.h.
Unknown = 4
}
+ public static Guid IID_IXCLRDataProcess = new Guid("5c552ab6-fc09-4cb3-8e36-22fa03c798b7");
+ public static Guid IID_ICorDebugProcess = new Guid("3d6f5f64-7538-11d3-8d5b-00104b35e7ef");
private static readonly Guid IID_IRuntime = new Guid("A5F152B9-BA78-4512-9228-5091A4CB7E35");
- private static Guid IID_IXCLRDataProcess = new Guid("5c552ab6-fc09-4cb3-8e36-22fa03c798b7");
- private static Guid IID_ICorDebugProcess = new Guid("3d6f5f64-7538-11d3-8d5b-00104b35e7ef");
#region DAC and DBI function delegates
public IntPtr IRuntime { get; }
- internal RuntimeWrapper(IServiceProvider services, IRuntime runtime)
+ public RuntimeWrapper(IServiceProvider services, IRuntime runtime)
{
Debug.Assert(services != null);
Debug.Assert(runtime != null);
<LogDir>$(RootBinDir)/TestResults/$(TargetConfiguration)/sos.unittests_$(Timestamp)</LogDir>
<DumpDir>$(RootBinDir)/tmp/$(TargetConfiguration)\dumps</DumpDir>
- <OSRid>linux</OSRid>
- <OSRid Condition="$(OS) == OSX">osx</OSRid>
-
<TestWebApp3>true</TestWebApp3>
<TestWebApp3 Condition="'$(InternalReleaseTesting)' == 'true'">false</TestWebApp3>
may not have been built with the runtime pointed by RuntimeSymbolsPath
since we use the arcade provided SDK (in .dotnet) to build them. -->
<SetSymbolServer>-ms</SetSymbolServer>
- <BuildProjectRuntime>$(OSRid)-$(TargetArchitecture)</BuildProjectRuntime>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
</Option>
<!--
Default (prebuilt)
<RuntimeFrameworkVersion>$(RuntimeVersionLatest)</RuntimeFrameworkVersion>
<PublishSingleFile>true</PublishSingleFile>
<SetSymbolServer>-ms</SetSymbolServer>
- <BuildProjectRuntime>$(OSRid)-$(TargetArchitecture)</BuildProjectRuntime>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
</Option>
<Option Condition="'$(RuntimeVersionLatest)' != ''">
<BuildProjectFramework>$(BuildProjectFrameworkLatest)</BuildProjectFramework>
may not have been built with the runtime pointed by RuntimeSymbolsPath
since we use the arcade provided SDK (in .dotnet) to build them. -->
<SetSymbolServer>-ms</SetSymbolServer>
- <BuildProjectRuntime>win-$(TargetArchitecture)</BuildProjectRuntime>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
</Option>
<!--
Default (prebuilt)
<RuntimeFrameworkVersion>$(RuntimeVersionLatest)</RuntimeFrameworkVersion>
<PublishSingleFile>true</PublishSingleFile>
<SetSymbolServer>-ms</SetSymbolServer>
- <BuildProjectRuntime>win-$(TargetArchitecture)</BuildProjectRuntime>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
</Option>
<Option Condition="'$(RuntimeVersionLatest)' != ''">
<BuildProjectFramework>$(BuildProjectFrameworkLatest)</BuildProjectFramework>
<RuntimeFrameworkVersion>$(RuntimeVersionLatest)</RuntimeFrameworkVersion>
<PublishSingleFile>true</PublishSingleFile>
<SetSymbolServer>-ms</SetSymbolServer>
- <BuildProjectRuntime>win-$(TargetArchitecture)</BuildProjectRuntime>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
</Option>
<Option Condition="'$(AspNetCoreVersionLatest)' != ''">
<BuildProjectFramework>$(BuildProjectFrameworkLatest)</BuildProjectFramework>
<DebuggeeBuildProcess>cli</DebuggeeBuildProcess>
<DebuggeeBuildRoot>$(RootBinDir)\Debuggees</DebuggeeBuildRoot>
<BuildProjectFramework>$(DesktopFramework)</BuildProjectFramework>
- <BuildProjectRuntime>win-$(TargetArchitecture)</BuildProjectRuntime>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
<DebugType>full</DebugType>
<RuntimeSymbolsPath>$(DesktopFrameworkPath)</RuntimeSymbolsPath>
<SetHostRuntime>-netfx</SetHostRuntime>
<Project Sdk="Microsoft.NET.Sdk">
-
- <Import Project="$(RepositoryEngineeringDir)\InstallRuntimes.proj" />
-
<PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<NoWarn>;1591;1701</NoWarn>
<DefineConstants>$(DefineConstants);CORE_CLR</DefineConstants>
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\Microsoft.Diagnostics.TestHelpers\Microsoft.Diagnostics.TestHelpers.csproj" />
+ <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.TestHelpers\Microsoft.Diagnostics.TestHelpers.csproj" />
</ItemGroup>
<ItemGroup>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>$(SolutionDir)src\SOS\Strike\platform;$(SolutionDir)src\SOS\inc;$(SolutionDir)src\inc;$(SolutionDir)src\pal\prebuilt\inc;$(SolutionDir)src\SOS\extensions;$(SolutionDir)src\SOS\gcdump;C:\Program Files (x86)\Microsoft Visual Studio\Preview\Enterprise\DIA SDK\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src\shared;$(RepoRoot)src\shared\inc;$(RepoRoot)src\shared\pal\prebuilt\inc;$(RepoRoot)src\shared\pal\inc;$(RepoRoot)src\shared\pal\inc\rt;$(SolutionDir)src\shared\gcdump;$(SolutionDir)src\SOS\Strike\platform;$(SolutionDir)src\SOS\inc;$(SolutionDir)src\SOS\extensions;C:\Program Files (x86)\Microsoft Visual Studio\Preview\Enterprise\DIA SDK\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
<AssemblerListingLocation>Debug/</AssemblerListingLocation>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;_TARGET_WIN64_=1;SOS_TARGET_AMD64=1;SOS_TARGET_ARM64=1;STRIKE;USE_STL;FX_VER_INTERNALNAME_STR=SOS.dll;CMAKE_INTDIR="Debug";sos_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ObjectFileName>$(IntDir)</ObjectFileName>
+ <BrowseInformation>true</BrowseInformation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;_TARGET_WIN64_=1;SOS_TARGET_AMD64=1;SOS_TARGET_ARM64=1;STRIKE;USE_STL;FX_VER_INTERNALNAME_STR=SOS.dll;CMAKE_INTDIR=\"Debug\";sos_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProjectReference>
<LinkLibraryDependencies>false</LinkLibraryDependencies>
</ProjectReference>
+ <Bscmake>
+ <PreserveSbr>true</PreserveSbr>
+ </Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
<ClCompile>
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
// Current runtime instance
IRuntime* g_pRuntime = nullptr;
-#if !defined(__APPLE__)
+extern "C" bool TryGetSymbolWithCallback(
+ bool (*readMemory)(void* address, void* buffer, size_t size),
+ ULONG64 baseAddress,
+ const char* symbolName,
+ ULONG64* symbolAddress);
-extern "C" bool TryGetSymbol(uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress);
-
-bool ElfReaderReadMemory(void* address, void* buffer, size_t size)
+bool ReaderReadMemory(void* address, void* buffer, size_t size)
{
ULONG read = 0;
return SUCCEEDED(g_ExtData->ReadVirtual((ULONG64)address, buffer, (ULONG)size, &read));
}
-#endif // !defined(__APPLE__)
-
/**********************************************************************\
* Search all the modules in the process for the single-file host
\**********************************************************************/
return hr;
}
ULONG64 symbolAddress;
-#if !defined(__APPLE__)
- if (target->GetOperatingSystem() == ITarget::OperatingSystem::Linux)
+ if (target->GetOperatingSystem() == ITarget::OperatingSystem::Linux ||
+ target->GetOperatingSystem() == ITarget::OperatingSystem::OSX)
{
- if (!TryGetSymbol(baseAddress, symbolName, &symbolAddress)) {
+ if (!::TryGetSymbolWithCallback(ReaderReadMemory, baseAddress, symbolName, &symbolAddress)) {
continue;
}
}
else
-#endif // !defined(__APPLE__)
{
hr = debuggerServices->GetOffsetBySymbol(index, symbolName, &symbolAddress);
if (FAILED(hr)) {
if (strcmp(((RuntimeInfo*)buffer.GetPtr())->Signature, "DotNetRuntimeInfo") != 0) {
break;
}
+ if (((RuntimeInfo*)buffer.GetPtr())->Version <= 0) {
+ break;
+ }
*pModuleIndex = index;
*pModuleAddress = baseAddress;
*ppRuntimeInfo = (RuntimeInfo*)buffer.Detach();
#include <psapi.h>
#include <cordebug.h>
#include <xcordebug.h>
-#include <metahost.h>
#include <mscoree.h>
#include <tchar.h>
#include "gcinfo.h"
#include <corsym.h>
#include <clrdata.h>
#include <palclr.h>
-#include <metahost.h>
#include <new>
#include <functional>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>$(SolutionDir)src\SOS\inc;$(SolutionDir)src\SOS\extensions;$(SolutionDir)src\SOS\gcdump;$(SolutionDir)src\inc;$(SolutionDir)src\pal\prebuilt\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(SolutionDir)src\SOS\inc;$(SolutionDir)src\SOS\extensions;$(SolutionDir)src\shared\gcdump;$(SolutionDir)src\shared\inc;$(SolutionDir)src\shared\pal\prebuilt\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
<AssemblerListingLocation>Debug/</AssemblerListingLocation>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ObjectFileName>$(IntDir)</ObjectFileName>
+ <BrowseInformation>true</BrowseInformation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Lib>
<AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
</Lib>
+ <Bscmake>
+ <PreserveSbr>true</PreserveSbr>
+ </Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
<ClCompile>
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
</ItemGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>$(SolutionDir)src\SOS\inc;$(SolutionDir)src\SOS\extensions;$(SolutionDir)src\inc;$(SolutionDir)src\pal\inc;$(SolutionDir)src\pal\inc\rt;$(SolutionDir)src\pal\inc\rt\cpp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(SolutionDir)src\SOS\inc;$(SolutionDir)src\SOS\extensions;$(SolutionDir)src\shared\inc;$(SolutionDir)src\shared\pal\inc;$(SolutionDir)src\shared\pal\inc\rt;$(SolutionDir)src\shared\pal\inc\rt\cpp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_AMD64_;_WIN64;AMD64;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;DBG_TARGET_AMD64_UNIX;BIT64=1;PAL_STDCPP_COMPAT;_SECURE_SCL=0;LINUX64;PLATFORM_UNIX=1;FEATURE_PAL;FEATURE_PAL_ANSI;UNIX_AMD64_ABI;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <BrowseInformation>true</BrowseInformation>
</ClCompile>
+ <Bscmake>
+ <PreserveSbr>true</PreserveSbr>
+ </Bscmake>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
{
lldb::SBSection section = module.GetSectionAtIndex(j);
lldb::addr_t loadAddr = section.GetLoadAddress(target);
- lldb::addr_t byteSize = section.GetByteSize();
- if ((loadAddr != LLDB_INVALID_ADDRESS) && (offset >= loadAddr) && (offset < loadAddr + byteSize))
+ lldb::addr_t endAddr = loadAddr + section.GetByteSize();
+ ULONG64 endOffset = offset + bufferSize;
+ if ((loadAddr != LLDB_INVALID_ADDRESS) && (offset >= loadAddr) && (endOffset < endAddr))
{
lldb::SBData sectionData = section.GetSectionData(offset - loadAddr, bufferSize);
- read = sectionData.ReadRawData(error, 0, buffer, bufferSize);
- found = true;
- break;
+ if (sectionData.IsValid())
+ {
+ read = sectionData.ReadRawData(error, 0, buffer, bufferSize);
+ found = true;
+ break;
+ }
}
}
}
endif(CLR_CMAKE_HOST_UNIX)
set(DBGSHIM_LIBRARIES
- corguids
dbgutil
+ corguids
utilcodestaticnohost
)
)
else()
list(APPEND DBGSHIM_LIBRARIES
- coreclrpal
palrt
+ coreclrpal
)
endif(CLR_CMAKE_HOST_WIN32)
#include <tlhelp32.h>
#include <cor.h>
#include <sstring.h>
-#ifndef TARGET_UNIX
+#ifdef TARGET_WINDOWS
#include <securityutil.h>
#endif
#include <dbgenginemetrics.h>
#include <arrayholder.h>
-#ifndef TARGET_UNIX
+#ifdef TARGET_WINDOWS
#define PSAPI_VERSION 2
#include <psapi.h>
#endif
#include "dbgshim.h"
+#include "debugshim.h"
/*
} \
CONTRACTL_END;
-//-----------------------------------------------------------------------------
-// Public API.
-//
-// CreateProcessForLaunch - a stripped down version of the Windows CreateProcess
-// that can be supported cross-platform.
-//
-//-----------------------------------------------------------------------------
-DLLEXPORT
-HRESULT
-CreateProcessForLaunch(
- _In_ LPWSTR lpCommandLine,
- _In_ BOOL bSuspendProcess,
- _In_ LPVOID lpEnvironment,
- _In_ LPCWSTR lpCurrentDirectory,
- _Out_ PDWORD pProcessId,
- _Out_ HANDLE *pResumeHandle)
-{
- PUBLIC_CONTRACT;
-
- PROCESS_INFORMATION processInfo;
- STARTUPINFOW startupInfo;
- DWORD dwCreationFlags = 0;
-
- ZeroMemory(&processInfo, sizeof(processInfo));
- ZeroMemory(&startupInfo, sizeof(startupInfo));
-
- startupInfo.cb = sizeof(startupInfo);
-
- if (bSuspendProcess)
- {
- dwCreationFlags = CREATE_SUSPENDED;
- }
-
- BOOL result = CreateProcessW(
- NULL,
- lpCommandLine,
- NULL,
- NULL,
- FALSE,
- dwCreationFlags,
- lpEnvironment,
- lpCurrentDirectory,
- &startupInfo,
- &processInfo);
-
- if (!result) {
- *pProcessId = 0;
- *pResumeHandle = NULL;
- return HRESULT_FROM_WIN32(GetLastError());
- }
-
- if (processInfo.hProcess != NULL)
- {
- CloseHandle(processInfo.hProcess);
- }
-
- *pProcessId = processInfo.dwProcessId;
- *pResumeHandle = processInfo.hThread;
-
- return S_OK;
-}
-
-//-----------------------------------------------------------------------------
-// Public API.
-//
-// ResumeProcess - to be used with the CreateProcessForLaunch resume handle
-//
-//-----------------------------------------------------------------------------
-DLLEXPORT
-HRESULT
-ResumeProcess(
- _In_ HANDLE hResumeHandle)
-{
- PUBLIC_CONTRACT;
- if (ResumeThread(hResumeHandle) == (DWORD)-1)
- {
- return HRESULT_FROM_WIN32(GetLastError());
- }
- return S_OK;
-}
-
-//-----------------------------------------------------------------------------
-// Public API.
-//
-// CloseResumeHandle - to be used with the CreateProcessForLaunch resume handle
-//
-//-----------------------------------------------------------------------------
-DLLEXPORT
-HRESULT
-CloseResumeHandle(
- _In_ HANDLE hResumeHandle)
-{
- PUBLIC_CONTRACT;
- if (!CloseHandle(hResumeHandle))
- {
- return HRESULT_FROM_WIN32(GetLastError());
- }
- return S_OK;
-}
-
#ifdef TARGET_UNIX
static
-void
+bool
RuntimeStartupHandler(
- char *pszModulePath,
+ const char *pszModulePath,
HMODULE hModule,
PVOID parameter);
StartupHelperThread(
LPVOID p);
-static
+#endif // TARGET_UNIX
+
+struct ClrRuntimeInfo
+{
+ HMODULE ModuleHandle;
+ HANDLE ContinueStartupEvent;
+ CLR_ENGINE_METRICS EngineMetrics;
+ ClrInfo ClrInfo;
+
+ ClrRuntimeInfo()
+ {
+ ModuleHandle = NULL;
+ ContinueStartupEvent = INVALID_HANDLE_VALUE;
+
+ EngineMetrics.cbSize = sizeof(EngineMetrics);
+ EngineMetrics.dwDbiVersion = CorDebugLatestVersion;
+ EngineMetrics.phContinueStartupEvent = NULL;
+ }
+};
+
HRESULT
-GetContinueStartupEvent(
+GetRuntime(
DWORD debuggeePID,
- LPCWSTR szTelestoFullPath,
- _Out_ HANDLE *phContinueStartupEvent);
+ ClrRuntimeInfo& clrRuntimeInfo);
-#endif // TARGET_UNIX
+HRESULT
+GetTargetCLRMetrics(
+ LPCWSTR wszModulePath,
+ CLR_ENGINE_METRICS *pEngineMetricsOut,
+ ClrInfo* pClrInfoOut = NULL,
+ DWORD *pdwRVAContinueStartupEvent = NULL);
+
+void
+AppendDbiDllName(
+ SString & szFullDbiPath);
+
+bool
+CheckDbiAndRuntimeVersion(
+ SString & szFullDbiPath,
+ SString & szFullCoreClrPath);
// Functions that we'll look for in the loaded Mscordbi module.
typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)(
HMODULE hmodTargetCLR,
IUnknown **ppCordb);
-HRESULT CreateCoreDbgWithSandboxSupport(
- HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, LPCWSTR lpApplicationGroupId, int iDebuggerVersion, IUnknown **ppCordb)
-{
- FPCoreCLRCreateCordbObjectEx fpCreate =
- (FPCoreCLRCreateCordbObjectEx)GetProcAddress(hDBIModule, "CoreCLRCreateCordbObjectEx");
- if (fpCreate == NULL)
- {
- return CORDBG_E_INCOMPATIBLE_PROTOCOL;
- }
-
- return fpCreate(iDebuggerVersion, processId, lpApplicationGroupId, hCLRModule, ppCordb);
-}
-
-HRESULT CreateCoreDbgWithoutSandboxSupport(
- HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, int iDebuggerVersion, IUnknown **ppCordb)
-{
- FPCoreCLRCreateCordbObject fpCreate =
- (FPCoreCLRCreateCordbObject)GetProcAddress(hDBIModule, "CoreCLRCreateCordbObject");
- if (fpCreate == NULL)
- {
- return CORDBG_E_INCOMPATIBLE_PROTOCOL;
- }
-
- return fpCreate(iDebuggerVersion, processId, hCLRModule, ppCordb);
-}
+typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject3)(
+ int iDebuggerVersion,
+ DWORD pid,
+ LPCWSTR lpApplicationGroupId,
+ LPCWSTR dacModulePath,
+ HMODULE hmodTargetCLR,
+ IUnknown **ppCordb);
HRESULT CreateCoreDbg(
- HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, LPCWSTR lpApplicationGroupId, int iDebuggerVersion, IUnknown **ppCordb)
+ HMODULE hCLRModule,
+ DWORD processId,
+ SString& dbiModulePath,
+ SString& dacModulePath,
+ LPCWSTR lpApplicationGroupId,
+ int iDebuggerVersion,
+ IUnknown **ppCordb)
{
+ HMODULE hDbi = NULL;
HRESULT hr = S_OK;
- if (lpApplicationGroupId != NULL)
+ hDbi = LoadLibraryW(dbiModulePath);
+ if (hDbi != NULL)
{
- hr = CreateCoreDbgWithSandboxSupport(hCLRModule, hDBIModule, processId, lpApplicationGroupId, iDebuggerVersion, ppCordb);
+ FPCoreCLRCreateCordbObject3 fpCreate3 = (FPCoreCLRCreateCordbObject3)GetProcAddress(hDbi, "CoreCLRCreateCordbObject3");
+ if (fpCreate3 != NULL)
+ {
+ hr = fpCreate3(iDebuggerVersion, processId, lpApplicationGroupId, dacModulePath.IsEmpty() ? NULL : (LPCWSTR)dacModulePath, hCLRModule, ppCordb);
+ }
+ else
+ {
+ if (lpApplicationGroupId != NULL)
+ {
+ FPCoreCLRCreateCordbObjectEx fpCreateEx = (FPCoreCLRCreateCordbObjectEx)GetProcAddress(hDbi, "CoreCLRCreateCordbObjectEx");
+ if (fpCreateEx != NULL)
+ {
+ hr = fpCreateEx(iDebuggerVersion, processId, lpApplicationGroupId, hCLRModule, ppCordb);
+ }
+ else
+ {
+ hr = CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ }
+ }
+ else
+ {
+ FPCoreCLRCreateCordbObject fpCreate = (FPCoreCLRCreateCordbObject)GetProcAddress(hDbi, "CoreCLRCreateCordbObject");
+ if (fpCreate != NULL)
+ {
+ hr = fpCreate(iDebuggerVersion, processId, hCLRModule, ppCordb);
+ }
+ else
+ {
+ hr = CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ }
+ }
+ }
}
else
{
- hr = CreateCoreDbgWithoutSandboxSupport(hCLRModule, hDBIModule, processId, iDebuggerVersion, ppCordb);
+ hr = CORDBG_E_DEBUG_COMPONENT_MISSING;
+ }
+
+ if (FAILED(hr))
+ {
+ if (hDbi != NULL)
+ {
+ FreeLibrary(hDbi);
+ }
}
return hr;
{
LONG m_ref;
DWORD m_processId;
+ ICLRDebuggingLibraryProvider3* m_pLibraryProvider;
PSTARTUP_CALLBACK m_callback;
PVOID m_parameter;
#ifdef TARGET_UNIX
#endif // TARGET_UNIX
public:
- RuntimeStartupHelper(DWORD dwProcessId, PSTARTUP_CALLBACK pfnCallback, PVOID parameter) :
+
+ RuntimeStartupHelper(DWORD dwProcessId, ICLRDebuggingLibraryProvider3* pLibraryProvider, PSTARTUP_CALLBACK pfnCallback, PVOID parameter) :
m_ref(1),
m_processId(dwProcessId),
+ m_pLibraryProvider(pLibraryProvider),
m_callback(pfnCallback),
m_parameter(parameter),
#ifdef TARGET_UNIX
m_threadHandle(NULL)
#endif // TARGET_UNIX
{
+ if (pLibraryProvider != NULL)
+ {
+ pLibraryProvider->AddRef();
+ }
}
~RuntimeStartupHelper()
{
+ if (m_pLibraryProvider != NULL)
+ {
+ m_pLibraryProvider->Release();
+ }
#ifdef TARGET_UNIX
if (m_applicationGroupId != NULL)
{
PAL_UnregisterForRuntimeStartup(m_unregisterToken);
}
- void InvokeStartupCallback(char *pszModulePath, HMODULE hModule)
+ bool InvokeStartupCallback(const char *pszModulePath, HMODULE hModule)
{
IUnknown *pCordb = NULL;
- HMODULE hMod = NULL;
HRESULT hr = S_OK;
+ ClrInfo clrInfo;
// If either of these are NULL, there was an error from the PAL
// callback. GetLastError returns the error code from the PAL.
PAL_CPP_TRY
{
- char dbiPath[MAX_LONGPATH];
+ clrInfo.RuntimeModulePath.SetASCII(pszModulePath);
- char *pszLast = strrchr(pszModulePath, DIRECTORY_SEPARATOR_CHAR_A);
- if (pszLast == NULL)
- {
- _ASSERT(!"InvokeStartupCallback: can find separator in coreclr path\n");
- hr = E_INVALIDARG;
- goto exit;
+ // Get the DBI/DAC index info for regular and single-file apps
+ hr = GetTargetCLRMetrics(clrInfo.RuntimeModulePath, NULL, &clrInfo, NULL);
+ if (FAILED(hr))
+ {
+ return false;
}
- strncpy_s(dbiPath, ARRAY_SIZE(dbiPath), pszModulePath, pszLast - pszModulePath);
- strcat_s(dbiPath, ARRAY_SIZE(dbiPath), DIRECTORY_SEPARATOR_STR_A MAKEDLLNAME_A("mscordbi"));
-
- hMod = LoadLibraryA(dbiPath);
- if (hMod == NULL)
+ SString dbiModulePath;
+ SString dacModulePath;
+ if (m_pLibraryProvider != NULL)
{
- hr = CORDBG_E_DEBUG_COMPONENT_MISSING;
- goto exit;
+ hr = CLRDebuggingImpl::ProvideLibraries(clrInfo, m_pLibraryProvider, dbiModulePath, dacModulePath);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+ }
+ else
+ {
+ // Fallback to loading DBI side-by-side the runtime module
+ char *pszLast = strrchr(pszModulePath, DIRECTORY_SEPARATOR_CHAR_A);
+ if (pszLast == NULL)
+ {
+ _ASSERT(!"InvokeStartupCallback: can find separator in coreclr path\n");
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+ dbiModulePath.SetASCII(pszModulePath, pszLast - pszModulePath);
+ AppendDbiDllName(dbiModulePath);
}
- HRESULT hr = CreateCoreDbg(hModule, hMod, m_processId, m_applicationGroupId, CorDebugVersion_2_0, &pCordb);
+ hr = CreateCoreDbg(hModule, m_processId, dbiModulePath, dacModulePath, m_applicationGroupId, CorDebugVersion_2_0, &pCordb);
_ASSERTE((pCordb == NULL) == FAILED(hr));
if (FAILED(hr))
{
{
_ASSERTE(pCordb == NULL);
- if (hMod != NULL)
- {
- FreeLibrary(hMod);
- }
-
// Invoke the callback on error
m_callback(NULL, m_parameter, hr);
}
+
+ return true;
}
#else // TARGET_UNIX
return hr;
}
- bool AreAllHandlesValid(HANDLE *handleArray, DWORD arrayLength)
- {
- for (int i = 0; i < (int)arrayLength; i++)
- {
- HANDLE h = handleArray[i];
- if (h == INVALID_HANDLE_VALUE)
- {
- return false;
- }
- }
- return true;
- }
-
- HRESULT InternalEnumerateCLRs(HANDLE** ppHandleArray, _In_reads_(*pdwArrayLength) LPWSTR** ppStringArray, DWORD* pdwArrayLength)
+ HRESULT InternalGetRuntime(ClrRuntimeInfo& clrRuntimeInfo)
{
int numTries = 0;
HRESULT hr;
while (numTries < 25)
{
- hr = EnumerateCLRs(m_processId, ppHandleArray, ppStringArray, pdwArrayLength);
+ hr = GetRuntime(m_processId, clrRuntimeInfo);
// EnumerateCLRs uses the OS API CreateToolhelp32Snapshot which can return ERROR_BAD_LENGTH or
// ERROR_PARTIAL_COPY. If we get either of those, we try wait 1/10th of a second try again (that
// is the recommendation of the OS API owners).
if ((hr != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && (hr != HRESULT_FROM_WIN32(ERROR_BAD_LENGTH)))
{
- // Just return any other error or if no handles were found (which means the coreclr module wasn't found yet).
- if (FAILED(hr) || *ppHandleArray == NULL || *pdwArrayLength <= 0)
+ // Just return any other error or if no runtimes were found (which means the coreclr module wasn't found yet).
+ if (FAILED(hr) || hr == S_FALSE)
{
return hr;
}
- // If EnumerateCLRs succeeded but any of the handles are INVALID_HANDLE_VALUE, then sleep and retry
- // also. This fixes a race condition where dbgshim catches the coreclr module just being loaded but
- // before g_hContinueStartupEvent has been initialized.
- if (AreAllHandlesValid(*ppHandleArray, *pdwArrayLength))
+ // If GetRuntime succeeded but the handle is INVALID_HANDLE_VALUE, then sleep and retry also. This fixes a
+ // race condition where dbgshim catches the coreclr module just being loaded but before g_hContinueStartupEvent
+ // has been initialized.
+ if (clrRuntimeInfo.ContinueStartupEvent != INVALID_HANDLE_VALUE)
{
return hr;
}
- // Clean up memory allocated in EnumerateCLRs since this path it succeeded
- CloseCLREnumeration(*ppHandleArray, *ppStringArray, *pdwArrayLength);
-
- *ppHandleArray = NULL;
- *ppStringArray = NULL;
- *pdwArrayLength = 0;
}
// Sleep and retry enumerating the runtimes
return hr;
}
- void WakeRuntimes(HANDLE *handleArray, DWORD arrayLength)
- {
- if (handleArray != NULL)
- {
- for (int i = 0; i < (int)arrayLength; i++)
- {
- HANDLE h = handleArray[i];
- if (h != NULL && h != INVALID_HANDLE_VALUE)
- {
- SetEvent(h);
- }
- }
- }
- }
-
void Unregister()
{
m_canceled = true;
- HANDLE *handleArray = NULL;
- LPWSTR *stringArray = NULL;
- DWORD arrayLength = 0;
-
- // Wake up runtime(s)
- HRESULT hr = EnumerateCLRs(m_processId, &handleArray, &stringArray, &arrayLength);
+ // Wake up runtime
+ ClrRuntimeInfo clrRuntimeInfo;
+ HRESULT hr = GetRuntime(m_processId, clrRuntimeInfo);
if (SUCCEEDED(hr))
{
- WakeRuntimes(handleArray, arrayLength);
- CloseCLREnumeration(handleArray, stringArray, arrayLength);
+ if (clrRuntimeInfo.ContinueStartupEvent != NULL && clrRuntimeInfo.ContinueStartupEvent != INVALID_HANDLE_VALUE)
+ {
+ SetEvent(clrRuntimeInfo.ContinueStartupEvent);
+ }
}
// Wake up worker thread
HRESULT InvokeStartupCallback(bool *pCoreClrExists)
{
- HANDLE *handleArray = NULL;
- LPWSTR *stringArray = NULL;
- DWORD arrayLength = 0;
+ ClrRuntimeInfo clrRuntimeInfo;
HRESULT hr = S_OK;
PAL_CPP_TRY
{
IUnknown *pCordb = NULL;
- WCHAR verStr[MAX_LONGPATH];
- DWORD verLen;
*pCoreClrExists = FALSE;
- hr = InternalEnumerateCLRs(&handleArray, &stringArray, &arrayLength);
+ hr = InternalGetRuntime(clrRuntimeInfo);
if (FAILED(hr))
{
goto exit;
}
- for (int i = 0; i < (int)arrayLength; i++)
+ // S_FALSE means there are no runtimes and no falures
+ if (hr == S_OK)
{
*pCoreClrExists = TRUE;
- hr = CreateVersionStringFromModule(m_processId, stringArray[i], verStr, ARRAY_SIZE(verStr), &verLen);
- if (FAILED(hr))
+ SString dbiModulePath;
+ SString dacModulePath;
+ if (m_pLibraryProvider != NULL)
{
- goto exit;
+ hr = CLRDebuggingImpl::ProvideLibraries(clrRuntimeInfo.ClrInfo, m_pLibraryProvider, dbiModulePath, dacModulePath);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+ }
+ else
+ {
+ dbiModulePath.Set(clrRuntimeInfo.ClrInfo.RuntimeModulePath);
+ SString::Iterator iter = dbiModulePath.End();
+ if (dbiModulePath.FindBack(iter, DIRECTORY_SEPARATOR_CHAR_W))
+ {
+ iter++;
+ dbiModulePath.Truncate(iter);
+ }
+ else
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+ AppendDbiDllName(dbiModulePath);
+
+ if (!CheckDbiAndRuntimeVersion(dbiModulePath, clrRuntimeInfo.ClrInfo.RuntimeModulePath))
+ {
+ hr = CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ goto exit;
+ }
}
- hr = CreateDebuggingInterfaceFromVersion(verStr, &pCordb);
+ hr = CreateCoreDbg(clrRuntimeInfo.ModuleHandle, m_processId, dbiModulePath, dacModulePath, NULL, clrRuntimeInfo.EngineMetrics.dwDbiVersion, &pCordb);
+ _ASSERTE((pCordb == NULL) == FAILED(hr));
if (FAILED(hr))
{
goto exit;
}
m_callback(pCordb, m_parameter, S_OK);
-
- // Currently only the first coreclr module in a process is supported
- break;
+ }
+ else
+ {
+ hr = S_OK;
}
}
PAL_CPP_CATCH_ALL
exit:
if (*pCoreClrExists)
{
- // Wake up all the runtimes
- WakeRuntimes(handleArray, arrayLength);
+ // Wake up the runtime
+ if (clrRuntimeInfo.ContinueStartupEvent != NULL && clrRuntimeInfo.ContinueStartupEvent != INVALID_HANDLE_VALUE)
+ {
+ SetEvent(clrRuntimeInfo.ContinueStartupEvent);
+ }
}
- CloseCLREnumeration(handleArray, stringArray, arrayLength);
return hr;
}
bool coreclrExists = false;
HRESULT hr = InvokeStartupCallback(&coreclrExists);
- // The retry logic in InternalEnumerateCLRs failed if ERROR_TIMEOUT was returned.
+ // The retry logic in InternalGetRuntime failed if ERROR_TIMEOUT was returned.
if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_TIMEOUT)))
{
if (!coreclrExists && !m_canceled)
#ifdef TARGET_UNIX
static
-void
-RuntimeStartupHandler(char *pszModulePath, HMODULE hModule, PVOID parameter)
+bool
+RuntimeStartupHandler(const char *pszModulePath, HMODULE hModule, PVOID parameter)
{
RuntimeStartupHelper *helper = (RuntimeStartupHelper *)parameter;
- helper->InvokeStartupCallback(pszModulePath, hModule);
+ return helper->InvokeStartupCallback(pszModulePath, hModule);
}
#else // TARGET_UNIX
#endif // TARGET_UNIX
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// CreateProcessForLaunch - a stripped down version of the Windows CreateProcess
+// that can be supported cross-platform.
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateProcessForLaunch(
+ _In_ LPWSTR lpCommandLine,
+ _In_ BOOL bSuspendProcess,
+ _In_ LPVOID lpEnvironment,
+ _In_ LPCWSTR lpCurrentDirectory,
+ _Out_ PDWORD pProcessId,
+ _Out_ HANDLE *pResumeHandle)
+{
+ PUBLIC_CONTRACT;
+
+ PROCESS_INFORMATION processInfo;
+ STARTUPINFOW startupInfo;
+ DWORD dwCreationFlags = 0;
+
+ ZeroMemory(&processInfo, sizeof(processInfo));
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+
+ startupInfo.cb = sizeof(startupInfo);
+
+ if (bSuspendProcess)
+ {
+ dwCreationFlags = CREATE_SUSPENDED;
+ }
+
+ BOOL result = CreateProcessW(
+ NULL,
+ lpCommandLine,
+ NULL,
+ NULL,
+ FALSE,
+ dwCreationFlags,
+ lpEnvironment,
+ lpCurrentDirectory,
+ &startupInfo,
+ &processInfo);
+
+ if (!result) {
+ *pProcessId = 0;
+ *pResumeHandle = NULL;
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ if (processInfo.hProcess != NULL)
+ {
+ CloseHandle(processInfo.hProcess);
+ }
+
+ *pProcessId = processInfo.dwProcessId;
+ *pResumeHandle = processInfo.hThread;
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// ResumeProcess - to be used with the CreateProcessForLaunch resume handle
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+ResumeProcess(
+ _In_ HANDLE hResumeHandle)
+{
+ PUBLIC_CONTRACT;
+ if (ResumeThread(hResumeHandle) == (DWORD)-1)
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// CloseResumeHandle - to be used with the CreateProcessForLaunch resume handle
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CloseResumeHandle(
+ _In_ HANDLE hResumeHandle)
+{
+ PUBLIC_CONTRACT;
+ if (!CloseHandle(hResumeHandle))
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ return S_OK;
+}
+
//-----------------------------------------------------------------------------
// Public API.
//
_In_ PVOID parameter,
_Out_ PVOID *ppUnregisterToken)
{
- return RegisterForRuntimeStartupEx(dwProcessId, NULL, pfnCallback, parameter, ppUnregisterToken);
+ return RegisterForRuntimeStartup3(dwProcessId, NULL, NULL, pfnCallback, parameter, ppUnregisterToken);
}
+
//-----------------------------------------------------------------------------
// Public API.
//
_In_ PSTARTUP_CALLBACK pfnCallback,
_In_ PVOID parameter,
_Out_ PVOID *ppUnregisterToken)
+{
+ return RegisterForRuntimeStartup3(dwProcessId, lpApplicationGroupId, NULL, pfnCallback, parameter, ppUnregisterToken);
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// RegisterForRuntimeStartup3 -- executes the callback when the coreclr runtime
+// starts in the specified process. The callback is passed the proper ICorDebug
+// instance for the version of the runtime or an error if something fails. This
+// API works for launch and attach (and even the attach scenario if the runtime
+// hasn't been loaded yet) equally on both xplat and Windows. The callback is
+// always called on a separate thread. This API returns immediately.
+//
+// The callback is invoked when the coreclr runtime module is loaded during early
+// initialization. The runtime is blocked during initialization until the callback
+// returns.
+//
+// If the runtime is already loaded in the process (as in the normal attach case),
+// the callback is executed and the runtime is not blocked.
+//
+// The callback is always invoked on a separate thread and this API returns immediately.
+//
+// Only the first coreclr module instance found in the target process is currently
+// supported.
+//
+// dwProcessId -- process id of the target process
+// lpApplicationGroupId - A string representing the application group ID of a sandboxed
+// process running in Mac. Pass NULL if the process is not
+// running in a sandbox and other platforms.
+// pLibraryProvider - a callback for locating DBI and DAC
+// pfnCallback -- invoked when coreclr runtime starts
+// parameter -- data to pass to callback
+// ppUnregisterToken -- pointer to put the UnregisterForRuntimeStartup token.
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+RegisterForRuntimeStartup3(
+ _In_ DWORD dwProcessId,
+ _In_ LPCWSTR lpApplicationGroupId,
+ _In_ ICLRDebuggingLibraryProvider3* pLibraryProvider,
+ _In_ PSTARTUP_CALLBACK pfnCallback,
+ _In_ PVOID parameter,
+ _Out_ PVOID *ppUnregisterToken)
{
PUBLIC_CONTRACT;
HRESULT hr = S_OK;
- RuntimeStartupHelper *helper = new (nothrow) RuntimeStartupHelper(dwProcessId, pfnCallback, parameter);
+ RuntimeStartupHelper *helper = new (nothrow) RuntimeStartupHelper(dwProcessId, pLibraryProvider, pfnCallback, parameter);
if (helper == NULL)
{
hr = E_OUTOFMEMORY;
if (phStartupEvent == NULL)
return E_INVALIDARG;
-#ifndef TARGET_UNIX
+#ifdef TARGET_WINDOWS
HRESULT hr;
DWORD currentSessionId = 0, debuggeeSessionId = 0;
if (!ProcessIdToSessionId(GetCurrentProcessId(), ¤tSessionId))
#else
*phStartupEvent = NULL;
return E_NOTIMPL;
-#endif // TARGET_UNIX
+#endif // TARGET_WINDOWS
}
-// Refer to clr\src\mscoree\mscorwks_ntdef.src.
+
+//
+// Returns true iff the module represents CoreClr.
+//
+static
+bool
+IsCoreClr(
+ const WCHAR* pModulePath)
+{
+ _ASSERTE(pModulePath != NULL);
+
+ //strip off everything up to and including the last slash in the path to get name
+ const WCHAR* pModuleName = pModulePath;
+ while(wcschr(pModuleName, DIRECTORY_SEPARATOR_CHAR_W) != NULL)
+ {
+ pModuleName = wcschr(pModuleName, DIRECTORY_SEPARATOR_CHAR_W);
+ pModuleName++; // pass the slash
+ }
+
+ // MAIN_CLR_MODULE_NAME_W gets changed for desktop builds, so we directly code against the CoreClr name.
+ return _wcsicmp(pModuleName, MAKEDLLNAME_W(W("coreclr"))) == 0;
+}
+
+// Refer to src\coreclr\dlls\mscoree\mscorwks_ntdef.src
const WORD kOrdinalForMetrics = 2;
//-----------------------------------------------------------------------------
// startup event for a coreclr.dll specified by its full path.
//
// Arguments:
-// szTelestoFullPath - (in) full path of telesto
-// pEngineMetricsOut - (out) filled in based on metrics from target telesto.
+// wszModulePath - (in) full path of possible coreclr or single file module
+// pEngineMetricsOut - (out; optional) filled in based on metrics from target runtime
+// pClrInfoOut - (out; optional) filled in from the DotNetRuntimeInfo export for single-file apps
// pdwRVAContinueStartupEvent - (out; optional) return the RVA to the continue startup event
//
// Returns:
-// Throwss on error.
+// HRESULT
//
// Notes:
// When VS pops up the attach dialog box, it is actually enumerating all the processes on the machine
// That's why we need to be extra careful reading coreclr.dll in this function.
//-----------------------------------------------------------------------------
static
-void
+HRESULT
GetTargetCLRMetrics(
- LPCWSTR szTelestoFullPath,
+ LPCWSTR wszModulePath,
CLR_ENGINE_METRICS *pEngineMetricsOut,
- DWORD *pdwRVAContinueStartupEvent = NULL)
+ ClrInfo* pClrInfoOut,
+ DWORD *pdwRVAContinueStartupEvent)
{
- CONTRACTL
- {
- THROWS;
- }
- CONTRACTL_END;
+ CONSISTENCY_CHECK(wszModulePath != NULL);
- CONSISTENCY_CHECK(szTelestoFullPath != NULL);
- CONSISTENCY_CHECK(pEngineMetricsOut != NULL);
-
-#ifndef TARGET_UNIX
+#ifdef TARGET_WINDOWS
HRESULT hr = S_OK;
- HandleHolder hCoreClrFile = WszCreateFile(szTelestoFullPath,
+ HandleHolder hCoreClrFile = WszCreateFile(wszModulePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL, // default security descriptor
NULL);
if (hCoreClrFile == INVALID_HANDLE_VALUE)
{
- ThrowLastError();
+ return HRESULT_FROM_WIN32(GetLastError());
}
DWORD cbFileHigh = 0;
DWORD cbFileLow = GetFileSize(hCoreClrFile, &cbFileHigh);
if (cbFileLow == INVALID_FILE_SIZE)
{
- ThrowLastError();
+ return HRESULT_FROM_WIN32(GetLastError());
}
// A maximum size of 100 MB should be more than enough for coreclr.dll.
if ((cbFileHigh != 0) || (cbFileLow > 0x6400000) || (cbFileLow == 0))
{
- ThrowHR(E_FAIL);
+ return E_FAIL;
}
HandleHolder hCoreClrMap = WszCreateFileMapping(hCoreClrFile, NULL, PAGE_READONLY, cbFileHigh, cbFileLow, NULL);
if (hCoreClrMap == NULL)
{
- ThrowLastError();
+ return HRESULT_FROM_WIN32(GetLastError());
}
MapViewHolder hCoreClrMapView = MapViewOfFile(hCoreClrMap, FILE_MAP_READ, 0, 0, 0);
if (hCoreClrMapView == NULL)
{
- ThrowLastError();
+ return HRESULT_FROM_WIN32(GetLastError());
}
// At this point we have read the file into the process, but be careful because it is flat, i.e. not mapped.
// Check the NT headers.
if (!pedecoder.CheckNTFormat())
{
- ThrowHR(E_FAIL);
+ return E_FAIL;
}
// At this point we can safely read anything in the NT headers.
if (!pedecoder.HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT) ||
!pedecoder.CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT))
{
- ThrowHR(E_FAIL);
+ return E_FAIL;
}
- IMAGE_DATA_DIRECTORY * pExportDirectoryEntry = pedecoder.GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT);
-
- // At this point we can safely read the IMAGE_DATA_DIRECTORY of the export directory.
- if (!pedecoder.CheckDirectory(pExportDirectoryEntry))
+ // If we are looking for the DotNetRuntimeInfo export for a single-file app, do this before looking for
+ // engine metrics export ordinal for a faster out of the module search loop. There are plenty of other
+ // native modules with the metrics ordinal #2.
+ if (pClrInfoOut != NULL)
{
- ThrowHR(E_FAIL);
+ if (IsCoreClr(wszModulePath))
+ {
+ PEDecoder_ResourceCallbackFunction callback = ([](LPCWSTR lpName, LPCWSTR lpType, DWORD langid, BYTE* data, COUNT_T cbData, void* context) {
+ CLR_DEBUG_RESOURCE* pDebugResource = (CLR_DEBUG_RESOURCE*)data;
+ ClrInfo* pClrInfo = (ClrInfo*)context;
+ if (cbData != sizeof(CLR_DEBUG_RESOURCE) || pDebugResource->dwVersion != 0 || pDebugResource->signature != CLR_ID_ONECORE_CLR)
+ {
+ return false;
+ }
+ pClrInfo->IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Identity;
+ pClrInfo->DbiTimeStamp = pDebugResource->dwDbiTimeStamp;
+ pClrInfo->DbiSizeOfImage = pDebugResource->dwDbiSizeOfImage;
+ pClrInfo->DacTimeStamp = pDebugResource->dwDacTimeStamp;
+ pClrInfo->DacSizeOfImage = pDebugResource->dwDacSizeOfImage;
+ return true;
+ });
+ if (!pedecoder.EnumerateWin32Resources(W("CLRDEBUGINFO"), MAKEINTRESOURCEW(10), callback, pClrInfoOut))
+ {
+ return E_FAIL;
+ }
+ }
+ else
+ {
+ PTR_VOID runtimeInfoExport = pedecoder.GetExport(RUNTIME_INFO_SIGNATURE);
+ if (runtimeInfoExport == NULL)
+ {
+ return E_FAIL;
+ }
+ RuntimeInfo* pRuntimeInfo = reinterpret_cast<RuntimeInfo*>(runtimeInfoExport);
+ if (strncmp(pRuntimeInfo->Signature, RUNTIME_INFO_SIGNATURE, sizeof(pRuntimeInfo->Signature)) != 0)
+ {
+ return E_FAIL;
+ }
+ if (pRuntimeInfo->Version <= 0)
+ {
+ return E_FAIL;
+ }
+ if (pRuntimeInfo->DbiModuleIndex[0] < (sizeof(DWORD) + sizeof(DWORD)) || pRuntimeInfo->DacModuleIndex[0] < (sizeof(DWORD) + sizeof(DWORD)))
+ {
+ return E_FAIL;
+ }
+ pClrInfoOut->IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Identity;
+ pClrInfoOut->DbiTimeStamp = *((DWORD*)&pRuntimeInfo->DbiModuleIndex[1]);
+ pClrInfoOut->DbiSizeOfImage = *((DWORD*)&pRuntimeInfo->DbiModuleIndex[5]);
+ pClrInfoOut->DacTimeStamp = *((DWORD*)&pRuntimeInfo->DacModuleIndex[1]);
+ pClrInfoOut->DacSizeOfImage = *((DWORD*)&pRuntimeInfo->DacModuleIndex[5]);
+ }
}
- IMAGE_EXPORT_DIRECTORY * pExportDir =
- reinterpret_cast<IMAGE_EXPORT_DIRECTORY *>(pedecoder.GetDirectoryData(pExportDirectoryEntry));
-
- // At this point we have checked that everything in the export directory is readable.
- // Check to make sure the ordinal we have fits in the table in the export directory.
- // The "base" here is like the starting index of the arrays in the export directory.
- if ((pExportDir->Base > kOrdinalForMetrics) ||
- (pExportDir->NumberOfFunctions < (kOrdinalForMetrics - pExportDir->Base)))
+ if (pEngineMetricsOut != NULL)
{
- ThrowHR(E_FAIL);
- }
- DWORD dwRealIndex = kOrdinalForMetrics - pExportDir->Base;
+ IMAGE_DATA_DIRECTORY * pExportDirectoryEntry = pedecoder.GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT);
- // Check that we can read the RVA at the element (specified by the ordinal) in the export address table.
- // Then read the RVA to the CLR_ENGINE_METRICS.
- if (!pedecoder.CheckRva(pExportDir->AddressOfFunctions, (dwRealIndex + 1) * sizeof(DWORD)))
- {
- ThrowHR(E_FAIL);
- }
- DWORD rvaMetrics = *reinterpret_cast<DWORD *>(
- pedecoder.GetRvaData(pExportDir->AddressOfFunctions + dwRealIndex * sizeof(DWORD)));
+ // At this point we can safely read the IMAGE_DATA_DIRECTORY of the export directory.
- // Make sure we can safely read the CLR_ENGINE_METRICS at the RVA we have retrieved.
- if (!pedecoder.CheckRva(rvaMetrics, sizeof(*pEngineMetricsOut)))
- {
- ThrowHR(E_FAIL);
- }
+ if (!pedecoder.CheckDirectory(pExportDirectoryEntry))
+ {
+ return E_FAIL;
+ }
- // Finally, copy the CLR_ENGINE_METRICS into the output buffer.
- CLR_ENGINE_METRICS * pMetricsInFile = reinterpret_cast<CLR_ENGINE_METRICS *>(pedecoder.GetRvaData(rvaMetrics));
- *pEngineMetricsOut = *pMetricsInFile;
+ IMAGE_EXPORT_DIRECTORY * pExportDir =
+ reinterpret_cast<IMAGE_EXPORT_DIRECTORY *>(pedecoder.GetDirectoryData(pExportDirectoryEntry));
- // At this point, we have retrieved the CLR_ENGINE_METRICS from the target process and
- // stored it in output buffer.
- if (pEngineMetricsOut->cbSize != sizeof(*pEngineMetricsOut))
- {
- ThrowHR(E_INVALIDARG);
+ // At this point we have checked that everything in the export directory is readable.
+
+ // Check to make sure the ordinal we have fits in the table in the export directory.
+ // The "base" here is like the starting index of the arrays in the export directory.
+ if ((pExportDir->Base > kOrdinalForMetrics) ||
+ (pExportDir->NumberOfFunctions < (kOrdinalForMetrics - pExportDir->Base)))
+ {
+ return E_FAIL;
+ }
+ DWORD dwRealIndex = kOrdinalForMetrics - pExportDir->Base;
+
+ // Check that we can read the RVA at the element (specified by the ordinal) in the export address table.
+ // Then read the RVA to the CLR_ENGINE_METRICS.
+ if (!pedecoder.CheckRva(pExportDir->AddressOfFunctions, (dwRealIndex + 1) * sizeof(DWORD)))
+ {
+ return E_FAIL;
+ }
+ DWORD rvaMetrics = *reinterpret_cast<DWORD *>(
+ pedecoder.GetRvaData(pExportDir->AddressOfFunctions + dwRealIndex * sizeof(DWORD)));
+
+ // Make sure we can safely read the CLR_ENGINE_METRICS at the RVA we have retrieved.
+ if (!pedecoder.CheckRva(rvaMetrics, sizeof(*pEngineMetricsOut)))
+ {
+ return E_FAIL;
+ }
+
+ // Finally, copy the CLR_ENGINE_METRICS into the output buffer.
+ CLR_ENGINE_METRICS * pMetricsInFile = reinterpret_cast<CLR_ENGINE_METRICS *>(pedecoder.GetRvaData(rvaMetrics));
+ *pEngineMetricsOut = *pMetricsInFile;
+
+ // At this point, we have retrieved the CLR_ENGINE_METRICS from the target process and
+ // stored it in output buffer.
+ if (pEngineMetricsOut->cbSize != sizeof(*pEngineMetricsOut))
+ {
+ return E_INVALIDARG;
+ }
}
if (pdwRVAContinueStartupEvent != NULL)
{
+ _ASSERTE(pEngineMetricsOut != NULL);
+
// Note that the pointer stored in the CLR_ENGINE_METRICS is assuming that the DLL is loaded at its
// preferred base address. We need to translate that to an RVA.
if (((SIZE_T)pEngineMetricsOut->phContinueStartupEvent < (SIZE_T)pedecoder.GetPreferredBase()) ||
((SIZE_T)pEngineMetricsOut->phContinueStartupEvent >
((SIZE_T)pedecoder.GetPreferredBase() + pedecoder.GetVirtualSize())))
{
- ThrowHR(E_FAIL);
+ return E_FAIL;
}
DWORD rvaContinueStartupEvent =
// falls in the loaded image.
if ((rvaContinueStartupEvent + sizeof(HANDLE)) > pedecoder.GetVirtualSize())
{
- ThrowHR(E_FAIL);
+ return E_FAIL;
}
*pdwRVAContinueStartupEvent = rvaContinueStartupEvent;
}
-
// Holder will call FreeLibrary()
#else
- //TODO: So far on POSIX systems we only support one version of debugging interface
- // in future we might want to detect it the same way we do it on Windows.
- pEngineMetricsOut->cbSize = sizeof(*pEngineMetricsOut);
- pEngineMetricsOut->dwDbiVersion = CorDebugLatestVersion;
- pEngineMetricsOut->phContinueStartupEvent = NULL;
-
- if (pdwRVAContinueStartupEvent != NULL)
+ if (pClrInfoOut != NULL)
{
- *pdwRVAContinueStartupEvent = NULL;
- }
-#endif // TARGET_UNIX
-}
-
-// Returns true iff the module represents CoreClr.
-static
-bool
-IsCoreClr(
- const WCHAR* pModulePath)
-{
- _ASSERTE(pModulePath != NULL);
-
- //strip off everything up to and including the last slash in the path to get name
- const WCHAR* pModuleName = pModulePath;
- while(wcschr(pModuleName, DIRECTORY_SEPARATOR_CHAR_W) != NULL)
- {
- pModuleName = wcschr(pModuleName, DIRECTORY_SEPARATOR_CHAR_W);
- pModuleName++; // pass the slash
- }
-
- // MAIN_CLR_MODULE_NAME_W gets changed for desktop builds, so we directly code against the CoreClr name.
- return _wcsicmp(pModuleName, MAKEDLLNAME_W(W("coreclr"))) == 0;
-}
+ if (IsCoreClr(wszModulePath))
+ {
+ // Get the runtime index info (build id) for Linux/MacOS
+ if (!TryGetBuildIdFromFile(wszModulePath, pClrInfoOut->RuntimeBuildId, MAX_BUILDID_SIZE, &pClrInfoOut->RuntimeBuildIdSize))
+ {
+ return E_FAIL;
+ }
+ pClrInfoOut->IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Runtime;
+ }
+ else
+ {
+ RuntimeInfo runtimeInfo;
+ if (!TryReadSymbolFromFile(wszModulePath, RUNTIME_INFO_SIGNATURE, (BYTE*)&runtimeInfo, sizeof(RuntimeInfo)))
+ {
+ return E_FAIL;
+ }
+ if (strcmp(runtimeInfo.Signature, RUNTIME_INFO_SIGNATURE) != 0)
+ {
+ return E_FAIL;
+ }
+ pClrInfoOut->IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Identity;
-// Returns true iff the module sent is named CoreClr.dll and has the metrics expected in it's PE header.
-static
-bool
-IsCoreClrWithGoodHeader(
- HANDLE hProcess,
- HMODULE hModule)
-{
- HRESULT hr = S_OK;
+ // The first byte is the number of bytes in the index
+ pClrInfoOut->DbiBuildIdSize = runtimeInfo.DbiModuleIndex[0];
+ memcpy_s(&pClrInfoOut->DbiBuildId, sizeof(pClrInfoOut->DbiBuildId), &(runtimeInfo.DbiModuleIndex[1]), pClrInfoOut->DbiBuildIdSize);
- WCHAR modulePath[MAX_LONGPATH];
- modulePath[0] = W('\0');
- if(0 == GetModuleFileNameEx(hProcess, hModule, modulePath, MAX_LONGPATH))
- {
- return false;
+ pClrInfoOut->DacBuildIdSize = runtimeInfo.DacModuleIndex[0];
+ memcpy_s(&pClrInfoOut->DacBuildId, sizeof(pClrInfoOut->DacBuildId), &(runtimeInfo.DacModuleIndex[1]), pClrInfoOut->DacBuildIdSize);
+ }
}
- else
+
+ if (pEngineMetricsOut != NULL)
{
- modulePath[MAX_LONGPATH-1] = 0; // on older OS'es this doesn't get null terminated automatically on truncation
+ pEngineMetricsOut->cbSize = sizeof(*pEngineMetricsOut);
+ pEngineMetricsOut->dwDbiVersion = CorDebugVersion_4_0;
+ pEngineMetricsOut->phContinueStartupEvent = NULL;
}
- if (IsCoreClr(modulePath))
+ if (pdwRVAContinueStartupEvent != NULL)
{
- // We don't care about the particular error returned, only that
- // what we tried wasn't a 'real' coreclr.dll.
- EX_TRY
- {
- CLR_ENGINE_METRICS metricsStruct;
- GetTargetCLRMetrics(modulePath, &metricsStruct); // throws
-
- // If we got this far, then we think it's a good one.
- }
- EX_CATCH_HRESULT(hr);
- return (hr == S_OK);
+ *pdwRVAContinueStartupEvent = 0;
}
-
- return false;
+#endif // TARGET_WINDOWS
+ return S_OK;
}
+//
+// Enumerates all the modules in the process
+//
static
HRESULT
EnumProcessModulesInternal(
return S_OK;
}
+//
+// Finds any coreclr or single-file app in the process
+//
+static
+HRESULT
+GetRuntime(
+ DWORD debuggeePID,
+ ClrRuntimeInfo& clrRuntimeInfo)
+{
+ HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, debuggeePID);
+ if (hProcess == NULL)
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ // The modules in the array returned don't need to be closed
+ DWORD countModules;
+ ArrayHolder<HMODULE> modules = nullptr;
+ HRESULT hr = EnumProcessModulesInternal(hProcess, &countModules, &modules);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // This assumes we are only going to find one .NET runtime in the process. We do the module
+ // enumeration only once because looking for single-file runtime info symbol is expensive.
+
+ WCHAR modulePath[MAX_LONGPATH];
+ for (DWORD i = 0; i < countModules; i++)
+ {
+ modulePath[0] = W('\0');
+ if (GetModuleFileNameEx(hProcess, modules[i], modulePath, MAX_LONGPATH) == 0)
+ {
+ continue;
+ }
+ else
+ {
+ modulePath[MAX_LONGPATH - 1] = 0; // on older OS'es this doesn't get null terminated automatically on truncation
+ }
+
+ // Get the DBI/DAC index info for the regular coreclr module or check if single-file app by looking for the
+ // DotNetRuntimeInfo export. We need to get the metrics too because that is required to get the startup event.
+ DWORD rvaContinueStartupEvent = 0;
+ hr = GetTargetCLRMetrics(modulePath, &clrRuntimeInfo.EngineMetrics, &clrRuntimeInfo.ClrInfo, &rvaContinueStartupEvent);
+ if (SUCCEEDED(hr))
+ {
+ clrRuntimeInfo.ModuleHandle = modules[i];
+ EX_TRY
+ {
+ clrRuntimeInfo.ClrInfo.RuntimeModulePath.Set(modulePath);
+ }
+ EX_CATCH_HRESULT(hr);
+#ifdef TARGET_WINDOWS
+ if (rvaContinueStartupEvent != 0)
+ {
+ HANDLE continueEvent = NULL;
+ SIZE_T nBytesRead;
+ if (ReadProcessMemory(hProcess, ((BYTE*)modules[i]) + rvaContinueStartupEvent, &continueEvent, sizeof(continueEvent), &nBytesRead))
+ {
+ if (continueEvent != NULL && continueEvent != INVALID_HANDLE_VALUE)
+ {
+ if (DuplicateHandle(hProcess, continueEvent, GetCurrentProcess(), &continueEvent, EVENT_MODIFY_STATE, FALSE, 0))
+ {
+ clrRuntimeInfo.ContinueStartupEvent = continueEvent;
+ }
+ }
+ else
+ {
+ clrRuntimeInfo.ContinueStartupEvent = continueEvent;
+ }
+ }
+ }
+#endif
+ return S_OK;
+ }
+ }
+
+ // Didn't find any runtimes and there were no failures
+ return S_FALSE;
+}
+
//-----------------------------------------------------------------------------
// Public API.
//
// All out params must be non-NULL.
if ((ppHandleArrayOut == NULL) || (ppStringArrayOut == NULL) || (pdwArrayLengthOut == NULL))
+ {
return E_INVALIDARG;
+ }
- HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, debuggeePID);
- if (NULL == hProcess)
- return E_FAIL;
+ *pdwArrayLengthOut = 0;
+ *ppHandleArrayOut = NULL;
+ *ppStringArrayOut = NULL;
- // The modules in the array returned don't need to be closed
- DWORD countModules;
- ArrayHolder<HMODULE> modules = nullptr;
- HRESULT hr = EnumProcessModulesInternal(hProcess, &countModules, &modules);
+ ClrRuntimeInfo clrRuntimeInfo;
+ HRESULT hr = GetRuntime(debuggeePID, clrRuntimeInfo);
if (FAILED(hr))
{
return hr;
}
- //
- // count the number of coreclr.dll entries
- //
- DWORD count = 0;
- for(DWORD i = 0; i < countModules; i++)
- {
- if (IsCoreClrWithGoodHeader(hProcess, modules[i]))
- {
- count++;
- }
- }
-
- // If we didn't find anything, no point in continuing.
- if (count == 0)
+ // S_FALSE means there are no runtimes and no falures
+ if (hr == S_OK)
{
- *ppHandleArrayOut = NULL;
- *ppStringArrayOut = NULL;
- *pdwArrayLengthOut = 0;
-
- return S_OK;
- }
-
- size_t cbEventArrayData = sizeof(HANDLE) * count; // event array data
- size_t cbStringArrayData = sizeof(LPWSTR) * count; // string array data
- size_t cbStringData = sizeof(WCHAR) * count * MAX_LONGPATH; // strings data
- size_t cbBuffer = cbEventArrayData + cbStringArrayData + cbStringData;
-
- BYTE* pOutBuffer = new (nothrow) BYTE[cbBuffer];
- if (NULL == pOutBuffer)
- return E_OUTOFMEMORY;
+ // Allocate the buffers for one runtime
+ size_t cbEventArrayData = sizeof(HANDLE);
+ size_t cbStringArrayData = sizeof(LPWSTR);
+ size_t cbStringData = sizeof(WCHAR) * MAX_LONGPATH;
+ size_t cbBuffer = cbEventArrayData + cbStringArrayData + cbStringData;
- ZeroMemory(pOutBuffer, cbBuffer);
-
- HANDLE* pEventArray = (HANDLE*) &pOutBuffer[0];
- LPWSTR* pStringArray = (LPWSTR*) &pOutBuffer[cbEventArrayData];
- WCHAR* pStringData = (WCHAR*) &pOutBuffer[cbEventArrayData + cbStringArrayData];
- DWORD idx = 0;
-
- // There's no guarantee that another coreclr hasn't loaded already anyhow,
- // so if we get the corner case that the second time through we enumerate
- // more coreclrs, just ignore the extras.
- // This mismatch could happen when
- // a) take module shapshot
- // b) underlying file is opened for exclusive access/deleted/moved/ACL'd etc so we can't open it
- // c) count is determined
- // d) file is closed/copied/moved/ACL'd etc so we can find/open it again
- // e) this loop runs
- // Thus the loop checks idx < count
-
- for(DWORD i = 0; i < countModules && idx < count; i++)
- {
- if (IsCoreClrWithGoodHeader(hProcess, modules[i]))
+ BYTE* pOutBuffer = new (nothrow) BYTE[cbBuffer];
+ if (NULL == pOutBuffer)
{
- // fill in path
- pStringArray[idx] = &pStringData[idx * MAX_LONGPATH];
- GetModuleFileNameEx(hProcess, modules[i], pStringArray[idx], MAX_LONGPATH);
-
-#ifndef TARGET_UNIX
- // fill in event handle -- if GetContinueStartupEvent fails, it will still return
- // INVALID_HANDLE_VALUE in hContinueStartupEvent, which is what we want. we don't
- // want to bail out of the enumeration altogether if we can't get an event from
- // one telesto.
-
- HANDLE hContinueStartupEvent = INVALID_HANDLE_VALUE;
- HRESULT hr = GetContinueStartupEvent(debuggeePID, pStringArray[idx], &hContinueStartupEvent);
- pEventArray[idx] = hContinueStartupEvent;
-#else
- pEventArray[idx] = NULL;
-#endif // TARGET_UNIX
-
- idx++;
+ return E_OUTOFMEMORY;
}
- }
+ ZeroMemory(pOutBuffer, cbBuffer);
- // Patch things up so CloseCLREnumeration() can still have it's
- // pointer arithmatic checks succeed, and the user doesn't see a 'dead' entry.
- // Specifically, it's expected that pEventArray and pStringArray point to the
- // same contiguous chunk of memory so that pStringArray == pEventArray[*pdwArrayLengthOut].
- // This is expected to be a very rare case.
- if (idx < count)
- {
- // Move the string pointers back.
- LPWSTR* pSATemp = (LPWSTR*)&pOutBuffer[sizeof(HANDLE)*idx];
- for (DWORD i = 0; i < idx; i++)
- {
- pSATemp[i] = pStringArray[i];
- }
+ HANDLE* pEventArray = (HANDLE*) &pOutBuffer[0];
+ pEventArray[0] = clrRuntimeInfo.ContinueStartupEvent;
- // Fix up string array pointer.
- pStringArray = (LPWSTR*)&pOutBuffer[sizeof(HANDLE)*idx];
+ LPWSTR* pStringArray = (LPWSTR*) &pOutBuffer[cbEventArrayData];
+ pStringArray[0] = (WCHAR*) &pOutBuffer[cbEventArrayData + cbStringArrayData];
+ wcscpy_s(pStringArray[0], MAX_LONGPATH, clrRuntimeInfo.ClrInfo.RuntimeModulePath);
- // Strings themselves don't need moved.
+ *pdwArrayLengthOut = 1;
+ *ppHandleArrayOut = pEventArray;
+ *ppStringArrayOut = pStringArray;
}
- *ppHandleArrayOut = pEventArray;
- *ppStringArrayOut = pStringArray;
- *pdwArrayLengthOut = idx;
-
return S_OK;
}
if ((pHandleArray + dwArrayLength) != (HANDLE*)pStringArray)
return E_INVALIDARG;
-#ifndef TARGET_UNIX
+#ifdef TARGET_WINDOWS
for (DWORD i = 0; i < dwArrayLength; i++)
{
HANDLE hTemp = pHandleArray[i];
CloseHandle(hTemp);
}
}
-#endif // TARGET_UNIX
+#endif // TARGET_WINDOWS
delete[] pHandleArray;
return S_OK;
HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
if (NULL == hProcess)
{
- ThrowHR(E_FAIL);
+ ThrowHR(HRESULT_FROM_WIN32(GetLastError()));
}
// The modules in the array returned don't need to be closed
}
else if (pBuffer != NULL)
{
-
HRESULT hr = S_OK;
EX_TRY
{
BYTE* hmodTargetCLR = NULL;
CLR_ENGINE_METRICS metricsStruct;
- GetTargetCLRMetrics(szModuleName, &metricsStruct); // throws
- dbiVersion = (CorDebugInterfaceVersion) metricsStruct.dwDbiVersion;
-
- hmodTargetCLR = GetRemoteModuleBaseAddress(pidDebuggee, szModuleName); // throws
- if (hmodTargetCLR == NULL)
+ hr = GetTargetCLRMetrics(szModuleName, &metricsStruct);
+ if (SUCCEEDED(hr))
{
- hr = COR_E_FILENOTFOUND;
- }
- else
- {
- swprintf_s(pBuffer, cchBuffer, c_versionStrFormat, dbiVersion, pidDebuggee, hmodTargetCLR);
+ dbiVersion = (CorDebugInterfaceVersion) metricsStruct.dwDbiVersion;
+
+ hmodTargetCLR = GetRemoteModuleBaseAddress(pidDebuggee, szModuleName); // throws
+ if (hmodTargetCLR == NULL)
+ {
+ hr = COR_E_FILENOTFOUND;
+ }
+ else
+ {
+ swprintf_s(pBuffer, cchBuffer, c_versionStrFormat, dbiVersion, pidDebuggee, hmodTargetCLR);
+ }
}
}
EX_CATCH_HRESULT(hr);
// Step 1: (pid, hmodule) --> full path
//
HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pidDebuggee);
+ if (hProcess == NULL)
+ {
+ ThrowHR(HRESULT_FROM_WIN32(GetLastError()));
+ }
+
WCHAR modulePath[MAX_LONGPATH];
- if(0 == GetModuleFileNameEx(hProcess, hmodTargetCLR, modulePath, MAX_LONGPATH))
+ if (0 == GetModuleFileNameEx(hProcess, hmodTargetCLR, modulePath, MAX_LONGPATH))
{
ThrowHR(E_FAIL);
}
SString & szFullDbiPath,
SString & szFullCoreClrPath)
{
-#ifndef TARGET_UNIX
+#ifdef TARGET_WINDOWS
DWORD dwDbiVersionMS = 0;
DWORD dwDbiVersionLS = 0;
DWORD dwCoreClrVersionMS = 0;
}
#else
return true;
-#endif // TARGET_UNIX
+#endif // TARGET_WINDOWS
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+// Superceded by CreateDebuggingInterfaceFromVersionEx in SLv4.
+// Given a version string, create the matching mscordbi.dll for it.
+// Create a managed debugging interface for the specified version.
+//
+// Parameters:
+// szDebuggeeVersion - the version of the debuggee. This will map to a version of mscordbi.dll
+// ppCordb - the outparameter used to return the debugging interface object.
+//
+// Return:
+// S_OK on success. *ppCordb will be non-null.
+// CORDBG_E_INCOMPATIBLE_PROTOCOL - if the proper DBI is not available. This can be a very common error if
+// the right debug pack is not installed.
+// else Error. (*ppCordb will be null)
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateDebuggingInterfaceFromVersion(
+ _In_ LPCWSTR szDebuggeeVersion,
+ _Out_ IUnknown ** ppCordb
+)
+{
+ return CreateDebuggingInterfaceFromVersion3(CorDebugVersion_2_0, szDebuggeeVersion, NULL, NULL, ppCordb);
}
//-----------------------------------------------------------------------------
_In_ LPCWSTR szDebuggeeVersion,
_Out_ IUnknown ** ppCordb)
{
- return CreateDebuggingInterfaceFromVersion2(iDebuggerVersion, szDebuggeeVersion, NULL, ppCordb);
+ return CreateDebuggingInterfaceFromVersion3(iDebuggerVersion, szDebuggeeVersion, NULL, NULL, ppCordb);
}
//-----------------------------------------------------------------------------
_In_ LPCWSTR szDebuggeeVersion,
_In_ LPCWSTR szApplicationGroupId,
_Out_ IUnknown ** ppCordb)
+{
+ return CreateDebuggingInterfaceFromVersion3(iDebuggerVersion, szDebuggeeVersion, szApplicationGroupId, NULL, ppCordb);
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+// Given a version string, create the matching mscordbi.dll for it.
+// Create a managed debugging interface for the specified version.
+//
+// Parameters:
+// iDebuggerVersion - the version of interface the debugger (eg, Cordbg) expects.
+// szDebuggeeVersion - the version of the debuggee. This will map to a version of mscordbi.dll
+// lpApplicationGroupId - a string representing the application group ID of a sandboxed
+// process running in Mac. Pass NULL if the process is not
+// running in a sandbox and other platforms.
+// pLibraryProvider - a callback for locating DBI and DAC
+// ppCordb - the outparameter used to return the debugging interface object.
+//
+// Return:
+// S_OK on success. *ppCordb will be non-null.
+// CORDBG_E_INCOMPATIBLE_PROTOCOL - if the proper DBI is not available. This can be a very common error if
+// the right debug pack is not installed.
+// else Error. (*ppCordb will be null)
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateDebuggingInterfaceFromVersion3(
+ _In_ int iDebuggerVersion,
+ _In_ LPCWSTR szDebuggeeVersion,
+ _In_ LPCWSTR szApplicationGroupId,
+ _In_ ICLRDebuggingLibraryProvider3* pLibraryProvider,
+ _Out_ IUnknown ** ppCordb)
{
PUBLIC_CONTRACT;
- HRESULT hrIgnore = S_OK; // ignored HResult
+ IUnknown* pCordb = NULL;
+ SString szFullDbiPath;
+ SString szFullDacPath;
HRESULT hr = S_OK;
- HMODULE hMod = NULL;
- IUnknown * pCordb = NULL;
- LOG((LF_CORDB, LL_EVERYTHING, "Calling CreateDebuggerInterfaceFromVersion, ver=%S\n", szDebuggeeVersion));
+ LOG((LF_CORDB, LL_EVERYTHING, "Calling CreateDebuggerInterfaceFromVersion3, ver=%S\n", szDebuggeeVersion));
if ((szDebuggeeVersion == NULL) || (ppCordb == NULL))
{
hr = E_INVALIDARG;
- goto Exit;
+ goto exit;
}
//
// Step 1: Parse version information into internal data structures
//
- CorDebugInterfaceVersion iTargetVersion; // the CorDebugInterfaceVersion (CorDebugVersion_2_0)
- DWORD pidDebuggee; // OS process ID of the debuggee
- HMODULE hmodTargetCLR; // module of Telesto in target (the clrInstanceId)
+ CorDebugInterfaceVersion iTargetVersion; // the CorDebugInterfaceVersion (CorDebugVersion_2_0)
+ DWORD pidDebuggee; // OS process ID of the debuggee
+ HMODULE hmodTargetCLR; // module of Telesto in target (the clrInstanceId)
hr = ParseVersionString(szDebuggeeVersion, &iTargetVersion, &pidDebuggee, &hmodTargetCLR);
if (FAILED(hr))
- goto Exit;
+ {
+ goto exit;
+ }
//
- // Step 2: Find the proper dbi module (mscordbi) and load it.
+ // Step 2: Find the proper dbi module (mscordbi)
//
- // Check for dbi next to target CLR.
- // This will be very common for internal developer setups, but not common in end-user setups.
EX_TRY
{
- SString szFullDbiPath;
SString szFullCoreClrPath;
-
GetDbiFilenameNextToRuntime(pidDebuggee, hmodTargetCLR, szFullDbiPath, szFullCoreClrPath);
- if (!CheckDbiAndRuntimeVersion(szFullDbiPath, szFullCoreClrPath))
+ if (pLibraryProvider != NULL)
+ {
+ // Get the DBI/DAC index info for regular and single-file apps
+ ClrInfo clrInfo;
+ hr = GetTargetCLRMetrics(szFullCoreClrPath, NULL, &clrInfo, NULL);
+ if (SUCCEEDED(hr))
+ {
+ clrInfo.RuntimeModulePath.Set(szFullCoreClrPath);
+ hr = CLRDebuggingImpl::ProvideLibraries(clrInfo, pLibraryProvider, szFullDbiPath, szFullDacPath);
+ }
+ }
+ else
{
- hr = CORDBG_E_INCOMPATIBLE_PROTOCOL;
- goto Exit;
+ // Check for dbi next to target CLR.
+ // This will be very common for internal developer setups, but not common in end-user setups.
+ if (!CheckDbiAndRuntimeVersion(szFullDbiPath, szFullCoreClrPath))
+ {
+ hr = CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ goto exit;
+ }
}
-
- // We calculated where dbi would be, but haven't yet verified if it's there.
- // Try to load it. We're using this to check for file existence.
-
- // Issue:951525: coreclr mscordbi load fails on downlevel OS since LoadLibraryEx can't find
- // dependent forwarder DLLs. Force LoadLibrary to look for dependencies in szFullDbiPath plus the default
- // search paths.
-#ifndef TARGET_UNIX
- hMod = WszLoadLibraryEx(szFullDbiPath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
-#else
- hMod = LoadLibraryExW(szFullDbiPath, NULL, 0);
-#endif
}
- EX_CATCH_HRESULT(hrIgnore); // failure leaves hMod null
+ EX_CATCH_HRESULT(hr);
- // Couldn't find Dbi, likely because the right debug pack is not installed. Failure.
- if (NULL == hMod)
+ if (FAILED(hr))
{
// Check for the following two HRESULTs and return them specifically. These are returned by
// CreateToolhelp32Snapshot() and could be transient errors. The debugger may choose to retry.
- if ((hrIgnore == HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) || (hrIgnore == HRESULT_FROM_WIN32(ERROR_BAD_LENGTH)))
- {
- hr = hrIgnore;
- }
- else
+ if ((hr != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && (hr != HRESULT_FROM_WIN32(ERROR_BAD_LENGTH)))
{
hr = CORDBG_E_DEBUG_COMPONENT_MISSING;
}
- goto Exit;
+ goto exit;
}
//
- // Step 3: Now that module is loaded, instantiate an ICorDebug.
+ // Step 3: Load DBI and instantiate an ICorDebug instance.
//
- hr = CreateCoreDbg(hmodTargetCLR, hMod, pidDebuggee, szApplicationGroupId, iDebuggerVersion, &pCordb);
+ hr = CreateCoreDbg(hmodTargetCLR, pidDebuggee, szFullDbiPath, szFullDacPath, szApplicationGroupId, iDebuggerVersion, &pCordb);
_ASSERTE((pCordb == NULL) == FAILED(hr));
-Exit:
+exit:
if (FAILED(hr))
{
if (pCordb != NULL)
pCordb->Release();
pCordb = NULL;
}
-
- if (hMod != NULL)
- {
- _ASSERTE(pCordb == NULL);
- FreeLibrary(hMod);
- }
}
// Set our outparam.
return hr;
}
-//-----------------------------------------------------------------------------
-// Public API.
-// Superceded by CreateDebuggingInterfaceFromVersionEx in SLv4.
-// Given a version string, create the matching mscordbi.dll for it.
-// Create a managed debugging interface for the specified version.
-//
-// Parameters:
-// szDebuggeeVersion - the version of the debuggee. This will map to a version of mscordbi.dll
-// ppCordb - the outparameter used to return the debugging interface object.
-//
-// Return:
-// S_OK on success. *ppCordb will be non-null.
-// CORDBG_E_INCOMPATIBLE_PROTOCOL - if the proper DBI is not available. This can be a very common error if
-// the right debug pack is not installed.
-// else Error. (*ppCordb will be null)
-//-----------------------------------------------------------------------------
-DLLEXPORT
-HRESULT
-CreateDebuggingInterfaceFromVersion(
- _In_ LPCWSTR szDebuggeeVersion,
- _Out_ IUnknown ** ppCordb
-)
-{
- PUBLIC_CONTRACT;
-
- return CreateDebuggingInterfaceFromVersionEx(CorDebugVersion_2_0, szDebuggeeVersion, ppCordb);
-}
-
-#ifndef TARGET_UNIX
-
-//------------------------------------------------------------------------------
-// Manually retrieves the "continue startup" event from the correct CLR instance
-// in the target process.
-//
-// Arguments:
-// debuggeePID - (in) OS Process ID of debuggee
-// szTelestoFullPath - (in) full path to telesto within the process.
-// phContinueStartupEvent - (out)
-//
-// Returns:
-// S_OK on success.
-//------------------------------------------------------------------------------
-HRESULT
-GetContinueStartupEvent(
- DWORD debuggeePID,
- LPCWSTR szTelestoFullPath,
- _Out_ HANDLE* phContinueStartupEvent)
-{
- if ((phContinueStartupEvent == NULL) || (szTelestoFullPath == NULL))
- return E_INVALIDARG;
-
- HRESULT hr = S_OK;
- EX_TRY
- {
- *phContinueStartupEvent = INVALID_HANDLE_VALUE;
-
- DWORD dwCoreClrContinueEventOffset = 0;
- CLR_ENGINE_METRICS metricsStruct;
-
- GetTargetCLRMetrics(szTelestoFullPath, &metricsStruct, &dwCoreClrContinueEventOffset); // throws
-
-
- BYTE* pbBaseAddress = GetRemoteModuleBaseAddress(debuggeePID, szTelestoFullPath); // throws
-
-
- HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, debuggeePID);
- if (NULL == hProcess)
- ThrowHR(E_FAIL);
-
- HANDLE continueEvent = NULL;
-
- SIZE_T nBytesRead;
- if (!ReadProcessMemory(hProcess, pbBaseAddress + dwCoreClrContinueEventOffset, &continueEvent,
- sizeof(continueEvent), &nBytesRead))
- {
- ThrowHR(E_FAIL);
- }
-
- if (NULL != continueEvent && INVALID_HANDLE_VALUE != continueEvent)
- {
- if (!DuplicateHandle(hProcess, continueEvent, GetCurrentProcess(), &continueEvent,
- EVENT_MODIFY_STATE, FALSE, 0))
- {
- ThrowHR(E_FAIL);
- }
- }
-
- *phContinueStartupEvent = continueEvent;
- }
- EX_CATCH_HRESULT(hr)
- return hr;
-}
-
-#endif // !TARGET_UNIX
-
-#include "debugshim.h"
-
//-----------------------------------------------------------------------------
// Public API.
//
if (clsid != CLSID_CLRDebugging || riid != IID_ICLRDebugging)
return E_NOINTERFACE;
- GUID skuId = CLR_ID_ONECORE_CLR;
-
- CLRDebuggingImpl *pDebuggingImpl = new (nothrow) CLRDebuggingImpl(skuId);
+ CLRDebuggingImpl *pDebuggingImpl = new (nothrow) CLRDebuggingImpl(CLR_ID_ONECORE_CLR);
if (NULL == pDebuggingImpl)
return E_OUTOFMEMORY;
//*****************************************************************************
#include <windows.h>
+#include "metahost.h"
typedef VOID (*PSTARTUP_CALLBACK)(IUnknown *pCordb, PVOID parameter, HRESULT hr);
_In_ PVOID parameter,
_Out_ PVOID *ppUnregisterToken);
+EXTERN_C HRESULT
+RegisterForRuntimeStartup3(
+ _In_ DWORD dwProcessId,
+ _In_ LPCWSTR szApplicationGroupId,
+ _In_ ICLRDebuggingLibraryProvider3* pLibraryProvider,
+ _In_ PSTARTUP_CALLBACK pfnCallback,
+ _In_ PVOID parameter,
+ _Out_ PVOID *ppUnregisterToken);
+
EXTERN_C HRESULT
UnregisterForRuntimeStartup(
_In_ PVOID pUnregisterToken);
_In_ DWORD cchBuffer,
_Out_ DWORD* pdwLength);
+EXTERN_C HRESULT
+CreateDebuggingInterfaceFromVersion(
+ _In_ LPCWSTR szDebuggeeVersion,
+ _Out_ IUnknown ** ppCordb);
+
EXTERN_C HRESULT
CreateDebuggingInterfaceFromVersionEx(
_In_ int iDebuggerVersion,
_In_ LPCWSTR szDebuggeeVersion,
_Out_ IUnknown ** ppCordb);
-EXTERN_C
-DLLEXPORT
-HRESULT
+EXTERN_C HRESULT
CreateDebuggingInterfaceFromVersion2(
_In_ int iDebuggerVersion,
_In_ LPCWSTR szDebuggeeVersion,
_Out_ IUnknown ** ppCordb);
EXTERN_C HRESULT
-CreateDebuggingInterfaceFromVersion(
+CreateDebuggingInterfaceFromVersion3(
+ _In_ int iDebuggerVersion,
_In_ LPCWSTR szDebuggeeVersion,
+ _In_ LPCWSTR szApplicationGroupId,
+ _In_ ICLRDebuggingLibraryProvider3* pLibraryProvider,
_Out_ IUnknown ** ppCordb);
; The .NET Foundation licenses this file to you under the MIT license.
EXPORTS
- CreateProcessForLaunch
- ResumeProcess
- CloseResumeHandle
- RegisterForRuntimeStartup
- RegisterForRuntimeStartupEx
- UnregisterForRuntimeStartup
- GetStartupNotificationEvent
- EnumerateCLRs
- CloseCLREnumeration
- CreateVersionStringFromModule
- CreateDebuggingInterfaceFromVersion
- CreateDebuggingInterfaceFromVersionEx
- CreateDebuggingInterfaceFromVersion2
- CLRCreateInstance
+ CreateProcessForLaunch
+ ResumeProcess
+ CloseResumeHandle
+ RegisterForRuntimeStartup
+ RegisterForRuntimeStartupEx
+ RegisterForRuntimeStartup3
+ UnregisterForRuntimeStartup
+ GetStartupNotificationEvent
+ EnumerateCLRs
+ CloseCLREnumeration
+ CreateVersionStringFromModule
+ CreateDebuggingInterfaceFromVersion
+ CreateDebuggingInterfaceFromVersionEx
+ CreateDebuggingInterfaceFromVersion2
+ CreateDebuggingInterfaceFromVersion3
+ CLRCreateInstance
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src\shared;$(RepoRoot)src\shared\inc;$(RepoRoot)src\shared\pal\prebuilt\inc;$(RepoRoot)src\shared\pal\inc;$(RepoRoot)src\shared\pal\inc\rt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>WIN32;_WINDOWS;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="Debug";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ObjectFileName>$(IntDir)</ObjectFileName>
+ <BrowseInformation>true</BrowseInformation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR=\"Debug\";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProjectReference>
<LinkLibraryDependencies>false</LinkLibraryDependencies>
</ProjectReference>
+ <Bscmake>
+ <PreserveSbr>true</PreserveSbr>
+ </Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
<ClCompile>
<None Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="dbgshim.h" />
<ClInclude Include="debugshim.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
</ItemGroup>
<ItemGroup>
<ClInclude Include="debugshim.h" />
+ <ClInclude Include="dbgshim.h" />
</ItemGroup>
</Project>
\ No newline at end of file
CloseResumeHandle
RegisterForRuntimeStartup
RegisterForRuntimeStartupEx
+RegisterForRuntimeStartup3
UnregisterForRuntimeStartup
GetStartupNotificationEvent
EnumerateCLRs
CreateDebuggingInterfaceFromVersion
CreateDebuggingInterfaceFromVersionEx
CreateDebuggingInterfaceFromVersion2
+CreateDebuggingInterfaceFromVersion3
CLRCreateInstance
CorDebugPlatform targetPlatform;
HRESULT result = pDataTarget->GetPlatform(&targetPlatform);
-
- if(FAILED(result))
+ if (FAILED(result))
{
_ASSERTE(!"Unexpected error");
return false;
//PRECONDITION(CheckPointer(pDataTarget));
HRESULT hr = S_OK;
- ICorDebugDataTarget * pDt = NULL;
+ ClrInfo clrInfo;
+ SString dacModulePath;
+ SString dbiModulePath;
HMODULE hDbi = NULL;
HMODULE hDac = NULL;
- LPWSTR pDacModulePath = NULL;
- LPWSTR pDbiModulePath = NULL;
- DWORD dbiTimestamp;
- DWORD dbiSizeOfImage;
- WCHAR dbiName[MAX_PATH_FNAME] = { 0 };
- DWORD dacTimestamp;
- DWORD dacSizeOfImage;
- WCHAR dacName[MAX_PATH_FNAME] = { 0 };
+ ICorDebugDataTarget * pDt = NULL;
CLR_DEBUGGING_VERSION version;
- BOOL versionSupportedByCaller = FALSE;
// argument checking
if ((ppProcess != NULL || pFlags != NULL) && pLibraryProvider == NULL)
// The expectation is that new versions of the CLR will continue to use the same GUID
// (unless there's a reason to hide them from older shims), but debuggers will tell us the
// CLR version they're designed for and mscordbi.dll can decide whether or not to accept it.
- version.wStructVersion = 0;
- hr = GetCLRInfo(pDt,
- moduleBaseAddress,
- &version,
- &dbiTimestamp,
- &dbiSizeOfImage,
- dbiName,
- MAX_PATH_FNAME,
- &dacTimestamp,
- &dacSizeOfImage,
- dacName,
- MAX_PATH_FNAME);
+ hr = GetCLRInfo(pDt, moduleBaseAddress, &version, clrInfo);
}
// If we need to fetch either the process info or the flags info then we need to find
// mscordbi and DAC and do the version specific OVP work
if (SUCCEEDED(hr) && (ppProcess != NULL || pFlags != NULL))
{
- ICLRDebuggingLibraryProvider2* pLibraryProvider2;
- if (SUCCEEDED(pLibraryProvider->QueryInterface(__uuidof(ICLRDebuggingLibraryProvider2), (void**)&pLibraryProvider2)))
- {
- if (FAILED(pLibraryProvider2->ProvideLibrary2(dbiName, dbiTimestamp, dbiSizeOfImage, &pDbiModulePath)) ||
- pDbiModulePath == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
+ hr = ProvideLibraries(clrInfo, pLibraryProvider, dbiModulePath, dacModulePath, &hDbi, &hDac);
- if (SUCCEEDED(hr))
- {
- hDbi = LoadLibraryW(pDbiModulePath);
- if (hDbi == NULL)
- {
- hr = HRESULT_FROM_WIN32(GetLastError());
- }
- }
-
- if (SUCCEEDED(hr))
+ // Need to load the DAC first because DBI references the PAL exports in the DAC
+ if (SUCCEEDED(hr) && hDac == NULL)
+ {
+ hDac = LoadLibraryW(dacModulePath);
+ if (hDac == NULL)
{
- // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
- RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage);
-
- // Ask library provider for dac
- if (FAILED(pLibraryProvider2->ProvideLibrary2(dacName, dacTimestamp, dacSizeOfImage, &pDacModulePath)) ||
- pDacModulePath == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
-
- if (SUCCEEDED(hr))
- {
- hDac = LoadLibraryW(pDacModulePath);
- if (hDac == NULL)
- {
- hr = HRESULT_FROM_WIN32(GetLastError());
- }
- }
+ hr = HRESULT_FROM_WIN32(GetLastError());
}
-
- pLibraryProvider2->Release();
}
- else {
- // Ask library provider for dbi
- if (FAILED(pLibraryProvider->ProvideLibrary(dbiName, dbiTimestamp, dbiSizeOfImage, &hDbi)) ||
- hDbi == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
- if (SUCCEEDED(hr))
+ if (SUCCEEDED(hr) && hDbi == NULL)
+ {
+ hDbi = LoadLibraryW(dbiModulePath);
+ if (hDbi == NULL)
{
- // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
- RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage);
-
- // ask library provider for dac
- if (FAILED(pLibraryProvider->ProvideLibrary(dacName, dacTimestamp, dacSizeOfImage, &hDac)) ||
- hDac == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
+ hr = HRESULT_FROM_WIN32(GetLastError());
}
}
*ppProcess = NULL;
- if (SUCCEEDED(hr) && pDacModulePath != NULL)
+ if (SUCCEEDED(hr) && !dacModulePath.IsEmpty())
{
// Get access to the latest OVP implementation and call it
OpenVirtualProcessImpl2FnPtr ovpFn = (OpenVirtualProcessImpl2FnPtr)GetProcAddress(hDbi, "OpenVirtualProcessImpl2");
if (ovpFn != NULL)
{
- hr = ovpFn(moduleBaseAddress, pDataTarget, pDacModulePath, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags);
+ hr = ovpFn(moduleBaseAddress, pDataTarget, dacModulePath, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags);
if (FAILED(hr))
{
_ASSERTE(ppProcess == NULL || *ppProcess == NULL);
LoadLibraryWFnPtr loadLibraryWFn = (LoadLibraryWFnPtr)GetProcAddress(hDac, "LoadLibraryW");
if (loadLibraryWFn != NULL)
{
- hDac = loadLibraryWFn(pDacModulePath);
+ hDac = loadLibraryWFn(dacModulePath);
if (hDac == NULL)
{
hr = E_HANDLE;
}
}
- //version is still valid in some failure cases
- if (pVersion != NULL &&
- (SUCCEEDED(hr) ||
- (hr == CORDBG_E_UNSUPPORTED_DEBUGGING_MODEL) ||
- (hr == CORDBG_E_UNSUPPORTED_FORWARD_COMPAT)))
+ // version is still valid in some failure cases
+ if (pVersion != NULL && (SUCCEEDED(hr) || (hr == CORDBG_E_UNSUPPORTED_DEBUGGING_MODEL) || (hr == CORDBG_E_UNSUPPORTED_FORWARD_COMPAT)))
{
memcpy(pVersion, &version, sizeof(CLR_DEBUGGING_VERSION));
}
- if (pDacModulePath != NULL)
+ // free the data target we QI'ed earlier
+ if (pDt != NULL)
{
-#ifdef HOST_UNIX
- free(pDacModulePath);
-#else
- CoTaskMemFree(pDacModulePath);
-#endif
+ pDt->Release();
+ }
+
+ return hr;
+}
+
+// Call the library provider to get the DBI and DAC
+//
+// Arguments:
+// clrInfo - the runtime info
+// pLibraryProvider - a callback for locating DBI and DAC
+// dbiModulePath - returns the DBI module path
+// dacModulePath - returns the DAC module path
+HRESULT CLRDebuggingImpl::ProvideLibraries(
+ ClrInfo& clrInfo,
+ ICLRDebuggingLibraryProvider3* pLibraryProvider,
+ SString& dbiModulePath,
+ SString& dacModulePath)
+{
+ HMODULE hDbi = NULL;
+ HMODULE hDac = NULL;
+ HRESULT hr = CLRDebuggingImpl::ProvideLibraries(clrInfo, pLibraryProvider, dbiModulePath, dacModulePath, &hDbi, &hDac);
+ if (SUCCEEDED(hr))
+ {
+ // The dbgshim create DBI instance APIs don't support just ICLRDebuggingLibraryProvider which is what
+ // it means if the handles returned are not null. At least ICLRDebuggingLibraryProvider2 is needed and
+ // ICLRDebuggingLibraryProvider3 for Unix platforms.
+ if (hDbi != NULL || hDac != NULL)
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+ return hr;
+}
+
+// Call the library provider to get the DBI and DAC
+//
+// Arguments:
+// clrInfo - the runtime info
+// pLibraryProvider - a callback for locating DBI and DAC
+// dbiModulePath - returns the DBI module path
+// dacModulePath - returns the DAC module path
+// phDbi - returns the DBI module handle if old library provider
+// phDac - returns the DAC module handle if old library provider
+HRESULT CLRDebuggingImpl::ProvideLibraries(
+ ClrInfo& clrInfo,
+ IUnknown* punk,
+ SString& dbiModulePath,
+ SString& dacModulePath,
+ HMODULE* phDbi,
+ HMODULE* phDac)
+{
+ ReleaseHolder<ICLRDebuggingLibraryProvider3> pLibraryProvider3;
+ ReleaseHolder<ICLRDebuggingLibraryProvider2> pLibraryProvider2;
+ ReleaseHolder<ICLRDebuggingLibraryProvider> pLibraryProvider;
+ LPWSTR pDbiModulePath = NULL;
+ LPWSTR pDacModulePath = NULL;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(punk != NULL);
+ _ASSERTE(phDbi != NULL);
+ _ASSERTE(phDac != NULL);
+
+ // Validate the incoming index info
+ if (!clrInfo.IsValid())
+ {
+ hr = CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ goto exit;
+ }
+
+ if (SUCCEEDED(punk->QueryInterface(__uuidof(ICLRDebuggingLibraryProvider3), (void**)&pLibraryProvider3)))
+ {
+ const WCHAR* wszRuntimeModulePath = !clrInfo.RuntimeModulePath.IsEmpty() ? clrInfo.RuntimeModulePath.GetUnicode() : NULL;
+ if (clrInfo.WindowsTarget)
+ {
+ // Ask library provider for DBI
+ if (FAILED(pLibraryProvider3->ProvideWindowsLibrary(
+ clrInfo.DbiName,
+ wszRuntimeModulePath,
+ clrInfo.IndexType,
+ clrInfo.DbiTimeStamp,
+ clrInfo.DbiSizeOfImage,
+ &pDbiModulePath)) || pDbiModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+ // Ask library provider for DAC
+ if (FAILED(pLibraryProvider3->ProvideWindowsLibrary(
+ clrInfo.DacName,
+ wszRuntimeModulePath,
+ clrInfo.IndexType,
+ clrInfo.DacTimeStamp,
+ clrInfo.DacSizeOfImage,
+ &pDacModulePath)) || pDacModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+ }
+ else
+ {
+ BYTE* dbiBuildId = NULL;
+ ULONG dbiBuildIdSize = 0;
+ BYTE* dacBuildId = NULL;
+ ULONG dacBuildIdSize = 0;
+
+ // What kind of build id are we going to give the provider
+ switch (clrInfo.IndexType)
+ {
+ case LIBRARY_PROVIDER_INDEX_TYPE::Identity:
+ if (clrInfo.DbiBuildIdSize > 0)
+ {
+ dbiBuildId = clrInfo.DbiBuildId;
+ dbiBuildIdSize = clrInfo.DbiBuildIdSize;
+ }
+ if (clrInfo.DacBuildIdSize > 0)
+ {
+ dacBuildId = clrInfo.DacBuildId;
+ dacBuildIdSize = clrInfo.DacBuildIdSize;
+ }
+ break;
+ case LIBRARY_PROVIDER_INDEX_TYPE::Runtime:
+ if (clrInfo.RuntimeBuildIdSize > 0)
+ {
+ dbiBuildId = clrInfo.RuntimeBuildId;
+ dbiBuildIdSize = clrInfo.RuntimeBuildIdSize;
+ dacBuildId = clrInfo.RuntimeBuildId;
+ dacBuildIdSize = clrInfo.RuntimeBuildIdSize;
+ }
+ break;
+ default:
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+ // Ask library provider for DBI
+ if (FAILED(pLibraryProvider3->ProvideUnixLibrary(
+ clrInfo.DbiName,
+ wszRuntimeModulePath,
+ clrInfo.IndexType,
+ dbiBuildId,
+ dbiBuildIdSize,
+ &pDbiModulePath)) || pDbiModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+ // Ask library provider for DAC
+ if (FAILED(pLibraryProvider3->ProvideUnixLibrary(
+ clrInfo.DacName,
+ wszRuntimeModulePath,
+ clrInfo.IndexType,
+ dacBuildId,
+ dacBuildIdSize,
+ &pDacModulePath)) || pDacModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+ }
+ }
+ else if (SUCCEEDED(punk->QueryInterface(__uuidof(ICLRDebuggingLibraryProvider2), (void**)&pLibraryProvider2)))
+ {
+ // Ask library provider for DBI
+ if (FAILED(pLibraryProvider2->ProvideLibrary2(clrInfo.DbiName, clrInfo.DbiTimeStamp, clrInfo.DbiSizeOfImage, &pDbiModulePath)) || pDbiModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+
+ // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
+ RetargetDacIfNeeded(&clrInfo.DacTimeStamp, &clrInfo.DacSizeOfImage);
+
+ // Ask library provider for DAC
+ if (FAILED(pLibraryProvider2->ProvideLibrary2(clrInfo.DacName, clrInfo.DacTimeStamp, clrInfo.DacSizeOfImage, &pDacModulePath)) || pDacModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+ }
+ else if (SUCCEEDED(punk->QueryInterface(__uuidof(ICLRDebuggingLibraryProvider), (void**)&pLibraryProvider)))
+ {
+ // Ask library provider for DBI
+ if (FAILED(pLibraryProvider->ProvideLibrary(clrInfo.DbiName, clrInfo.DbiTimeStamp, clrInfo.DbiSizeOfImage, phDbi)) || *phDbi == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+
+ // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
+ RetargetDacIfNeeded(&clrInfo.DacTimeStamp, &clrInfo.DacSizeOfImage);
+
+ // ask library provider for DAC
+ if (FAILED(pLibraryProvider->ProvideLibrary(clrInfo.DacName, clrInfo.DacTimeStamp, clrInfo.DacSizeOfImage, phDac)) || *phDac == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
+ }
+ }
+ else
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ goto exit;
}
+exit:
if (pDbiModulePath != NULL)
{
+ dbiModulePath.Set(pDbiModulePath);
#ifdef HOST_UNIX
free(pDbiModulePath);
#else
CoTaskMemFree(pDbiModulePath);
#endif
}
-
- // free the data target we QI'ed earlier
- if (pDt != NULL)
+ if (pDacModulePath != NULL)
{
- pDt->Release();
+ dacModulePath.Set(pDacModulePath);
+#ifdef HOST_UNIX
+ free(pDacModulePath);
+#else
+ CoTaskMemFree(pDacModulePath);
+#endif
}
-
return hr;
}
#define PE_FIXEDFILEINFO_SIGNATURE 0xFEEF04BD
-// The format of the special debugging resource we embed in CLRs starting in
-// v4
-struct CLR_DEBUG_RESOURCE
-{
- DWORD dwVersion;
- GUID signature;
- DWORD dwDacTimeStamp;
- DWORD dwDacSizeOfImage;
- DWORD dwDbiTimeStamp;
- DWORD dwDbiSizeOfImage;
-};
-
// Checks to see if a module is a CLR and if so, fetches the debug data
// from the embedded resource
//
// Arguments
// pDataTarget - dataTarget for the process we are inspecting
// moduleBaseAddress - base address of a module we should inspect
-// pVersion - output, the version of the CLR detected if this is a CLR
-// pdwDbiTimeStamp - the timestamp of DBI as embedded in the CLR image
-// pdwDbiSizeOfImage - the SizeOfImage of DBI as embedded in the CLR image
-// pDbiName - output, the filename of DBI (as calculated by this function but that might change)
-// dwDbiNameCharCount - input, the number of WCHARs in the buffer pointed to by pDbiName
-// pdwDacTimeStampe - the timestamp of DAC as embedded in the CLR image
-// pdwDacSizeOfImage - the SizeOfImage of DAC as embedded in the CLR image
-// pDacName - output, the filename of DAC (as calculated by this function but that might change)
-// dwDacNameCharCount - input, the number of WCHARs in the buffer pointed to by pDacName
-HRESULT CLRDebuggingImpl::GetCLRInfo(ICorDebugDataTarget* pDataTarget,
+// clrInfo - various info about the runtime
+HRESULT CLRDebuggingImpl::GetCLRInfo(ICorDebugDataTarget * pDataTarget,
ULONG64 moduleBaseAddress,
CLR_DEBUGGING_VERSION* pVersion,
- DWORD* pdwDbiTimeStamp,
- DWORD* pdwDbiSizeOfImage,
- _Inout_updates_z_(dwDbiNameCharCount) WCHAR* pDbiName,
- DWORD dwDbiNameCharCount,
- DWORD* pdwDacTimeStamp,
- DWORD* pdwDacSizeOfImage,
- _Inout_updates_z_(dwDacNameCharCount) WCHAR* pDacName,
- DWORD dwDacNameCharCount)
+ ClrInfo& clrInfo)
{
+ memset(pVersion, 0, sizeof(CLR_DEBUGGING_VERSION));
#ifdef HOST_WINDOWS
- if(IsTargetWindows(pDataTarget))
+ if (IsTargetWindows(pDataTarget))
{
+ clrInfo.WindowsTarget = TRUE;
+
WORD imageFileMachine = 0;
DWORD resourceSectionRVA = 0;
HRESULT hr = GetMachineAndResourceSectionRVA(pDataTarget, moduleBaseAddress, &imageFileMachine, &resourceSectionRVA);
// We want the version resource which has type = RT_VERSION = 16, name = 1, language = 0x409
DWORD versionResourceRVA = 0;
DWORD versionResourceSize = 0;
- if(SUCCEEDED(hr))
+ if (SUCCEEDED(hr))
{
- hr = GetResourceRvaFromResourceSectionRva(pDataTarget, moduleBaseAddress, resourceSectionRVA, 16, 1, 0x409,
- &versionResourceRVA, &versionResourceSize);
+ hr = GetResourceRvaFromResourceSectionRva(pDataTarget, moduleBaseAddress, resourceSectionRVA, 16, 1, 0x409, &versionResourceRVA, &versionResourceSize);
+ if (FAILED(hr))
+ {
+ // The single-file apps are language "neutral" (0)
+ hr = GetResourceRvaFromResourceSectionRva(pDataTarget, moduleBaseAddress, resourceSectionRVA, 16, 1, 0, &versionResourceRVA, &versionResourceSize);
+ }
}
// At last we get our version info
VS_FIXEDFILEINFO fixedFileInfo = {0};
- if(SUCCEEDED(hr))
+ if (SUCCEEDED(hr))
{
// The version resource has 3 words, then the unicode string "VS_VERSION_INFO"
// (16 WCHARS including the null terminator)
hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + fixedFileInfoRVA, (BYTE*)&fixedFileInfo, sizeof(fixedFileInfo));
}
- //Verify the signature on the version resource
- if(SUCCEEDED(hr) && fixedFileInfo.dwSignature != PE_FIXEDFILEINFO_SIGNATURE)
+ // Verify the signature on the version resource
+ if (SUCCEEDED(hr) && fixedFileInfo.dwSignature != PE_FIXEDFILEINFO_SIGNATURE)
{
hr = CORDBG_E_NOT_CLR;
}
// Record the version information
- if(SUCCEEDED(hr))
+ if (SUCCEEDED(hr))
{
pVersion->wMajor = (WORD) (fixedFileInfo.dwProductVersionMS >> 16);
pVersion->wMinor = (WORD) (fixedFileInfo.dwProductVersionMS & 0xFFFF);
DWORD debugResourceRVA = 0;
DWORD debugResourceSize = 0;
BOOL useCrossPlatformNaming = FALSE;
- if(SUCCEEDED(hr))
+ if (SUCCEEDED(hr))
{
// the initial state is that we haven't found a proper resource
HRESULT hrGetResource = E_FAIL;
const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSARM");
#endif
- hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, resourceName, 0,
- &debugResourceRVA, &debugResourceSize);
+ hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, resourceName, 0, &debugResourceRVA, &debugResourceSize);
useCrossPlatformNaming = SUCCEEDED(hrGetResource);
-
#if defined(HOST_WINDOWS) && (defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM))
#if defined(HOST_X86)
#define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_I386
#endif
// if this is windows, and if host_arch matches target arch then we can fallback to searching for CLRDEBUGINFO on failure
- if(FAILED(hrGetResource) && (imageFileMachine == _HOST_MACHINE_TYPE))
+ if (FAILED(hrGetResource) && (imageFileMachine == _HOST_MACHINE_TYPE))
{
- hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, W("CLRDEBUGINFO"), 0,
- &debugResourceRVA, &debugResourceSize);
+ hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, W("CLRDEBUGINFO"), 0, &debugResourceRVA, &debugResourceSize);
}
#undef _HOST_MACHINE_TYPE
#endif
// if the search failed, we don't recognize the CLR
if(FAILED(hrGetResource))
+ {
hr = CORDBG_E_NOT_CLR;
+ }
}
CLR_DEBUG_RESOURCE debugResource;
- if(SUCCEEDED(hr) && debugResourceSize != sizeof(debugResource))
+ if (SUCCEEDED(hr) && debugResourceSize != sizeof(debugResource))
{
hr = CORDBG_E_NOT_CLR;
}
// Get the special debug resource from the image and return the results
- if(SUCCEEDED(hr))
+ if (SUCCEEDED(hr))
{
hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + debugResourceRVA, (BYTE*)&debugResource, sizeof(debugResource));
}
- if(SUCCEEDED(hr) && (debugResource.dwVersion != 0))
+ if (SUCCEEDED(hr) && (debugResource.dwVersion != 0))
{
hr = CORDBG_E_NOT_CLR;
}
// The signature needs to match m_skuId exactly, except for m_skuId=CLR_ID_ONECORE_CLR which is
// also compatible with the older CLR_ID_PHONE_CLR signature.
- if(SUCCEEDED(hr) &&
- (debugResource.signature != m_skuId) &&
- !( (debugResource.signature == CLR_ID_PHONE_CLR) && (m_skuId == CLR_ID_ONECORE_CLR) ))
+ if (SUCCEEDED(hr) && (debugResource.signature != m_skuId) && !( (debugResource.signature == CLR_ID_PHONE_CLR) && (m_skuId == CLR_ID_ONECORE_CLR) ))
{
hr = CORDBG_E_NOT_CLR;
}
- if(SUCCEEDED(hr) &&
- (debugResource.signature != CLR_ID_ONECORE_CLR) &&
- useCrossPlatformNaming)
+ if (SUCCEEDED(hr) && (debugResource.signature != CLR_ID_ONECORE_CLR) && useCrossPlatformNaming)
{
- FormatLongDacModuleName(pDacName, dwDacNameCharCount, imageFileMachine, &fixedFileInfo);
- swprintf_s(pDbiName, dwDbiNameCharCount, W("%s_%s.dll"), MAIN_DBI_MODULE_NAME_W, W("x86"));
+ FormatLongDacModuleName(clrInfo.DacName, MAX_PATH_FNAME, imageFileMachine, &fixedFileInfo);
+ swprintf_s(clrInfo.DbiName, MAX_PATH_FNAME, W("%s_%s.dll"), MAIN_DBI_MODULE_NAME_W, W("x86"));
}
else
{
if(m_skuId == CLR_ID_V4_DESKTOP)
- swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll"), CLR_DAC_MODULE_NAME_W);
+ swprintf_s(clrInfo.DacName, MAX_PATH_FNAME, W("%s.dll"), CLR_DAC_MODULE_NAME_W);
else
- swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll"), CORECLR_DAC_MODULE_NAME_W);
- swprintf_s(pDbiName, dwDbiNameCharCount, W("%s.dll"), MAIN_DBI_MODULE_NAME_W);
+ swprintf_s(clrInfo.DacName, MAX_PATH_FNAME, W("%s.dll"), CORECLR_DAC_MODULE_NAME_W);
+ swprintf_s(clrInfo.DbiName, MAX_PATH_FNAME, W("%s.dll"), MAIN_DBI_MODULE_NAME_W);
}
- if(SUCCEEDED(hr))
+ if (SUCCEEDED(hr))
{
- *pdwDbiTimeStamp = debugResource.dwDbiTimeStamp;
- *pdwDbiSizeOfImage = debugResource.dwDbiSizeOfImage;
- *pdwDacTimeStamp = debugResource.dwDacTimeStamp;
- *pdwDacSizeOfImage = debugResource.dwDacSizeOfImage;
+ clrInfo.IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Identity;
+ clrInfo.DbiTimeStamp = debugResource.dwDbiTimeStamp;
+ clrInfo.DbiSizeOfImage = debugResource.dwDbiSizeOfImage;
+ clrInfo.DacTimeStamp = debugResource.dwDacTimeStamp;
+ clrInfo.DacSizeOfImage = debugResource.dwDacSizeOfImage;
}
// any failure should be interpreted as this module not being a CLR
- if(FAILED(hr))
+ if (FAILED(hr))
{
return CORDBG_E_NOT_CLR;
}
else
#endif // !HOST_WINDOWS
{
- swprintf_s(pDacName, dwDacNameCharCount, W("%s"), MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W));
- swprintf_s(pDbiName, dwDbiNameCharCount, W("%s"), MAKEDLLNAME_W(MAIN_DBI_MODULE_NAME_W));
+ clrInfo.WindowsTarget = FALSE;
- pVersion->wMajor = 0;
- pVersion->wMinor = 0;
- pVersion->wBuild = 0;
- pVersion->wRevision = 0;
+ //
+ // Check if it is a single-file app
+ //
+ uint64_t symbolAddress;
+ if (TryGetSymbol(pDataTarget, moduleBaseAddress, RUNTIME_INFO_SIGNATURE, &symbolAddress))
+ {
+ RuntimeInfo runtimeInfo;
+ ULONG32 bytesRead;
+ if (SUCCEEDED(pDataTarget->ReadVirtual(symbolAddress, (BYTE*)&runtimeInfo, sizeof(RuntimeInfo), &bytesRead)))
+ {
+ if (strcmp(runtimeInfo.Signature, RUNTIME_INFO_SIGNATURE) == 0)
+ {
+ // This is a single-file app
+ clrInfo.IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Identity;
- *pdwDbiTimeStamp = 0;
- *pdwDbiSizeOfImage = 0;
- *pdwDacTimeStamp = 0;
- *pdwDacSizeOfImage = 0;
+ // The first byte is the number of bytes in the index
+ clrInfo.DbiBuildIdSize = runtimeInfo.DbiModuleIndex[0];
+ memcpy_s(&clrInfo.DbiBuildId, sizeof(clrInfo.DbiBuildId), &(runtimeInfo.DbiModuleIndex[1]), clrInfo.DbiBuildIdSize);
+
+ clrInfo.DacBuildIdSize = runtimeInfo.DacModuleIndex[0];
+ memcpy_s(&clrInfo.DacBuildId, sizeof(clrInfo.DacBuildId), &(runtimeInfo.DacModuleIndex[1]), clrInfo.DacBuildIdSize);
+ }
+ }
+ }
+
+ //
+ // If it wasn't a single-file app, then fallback to getting the runtime module's index information
+ //
+ if (!clrInfo.IsValid())
+ {
+ if (TryGetBuildId(pDataTarget, moduleBaseAddress, clrInfo.RuntimeBuildId, MAX_BUILDID_SIZE, &clrInfo.RuntimeBuildIdSize))
+ {
+ // This is normal non-single-file app
+ clrInfo.IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Runtime;
+ }
+ }
return S_OK;
}
return hr;
}
-
-
STDMETHODIMP CLRDebuggingImpl::QueryInterface(REFIID riid, void **ppvObject)
{
HRESULT hr = S_OK;
#include "cor.h"
#include "cordebug.h"
+#include "sstring.h"
#include <wchar.h>
#include <metahost.h>
+#include "runtimeinfo.h"
#define CORECLR_DAC_MODULE_NAME_W W("mscordaccore")
#define CLR_DAC_MODULE_NAME_W W("mscordacwks")
#define MAIN_DBI_MODULE_NAME_W W("mscordbi")
+#define MAX_BUILDID_SIZE 24
+
+// The format of the special debugging resource we embed in CLRs starting in v4
+struct CLR_DEBUG_RESOURCE
+{
+ DWORD dwVersion;
+ GUID signature;
+ DWORD dwDacTimeStamp;
+ DWORD dwDacSizeOfImage;
+ DWORD dwDbiTimeStamp;
+ DWORD dwDbiSizeOfImage;
+};
+
+struct ClrInfo
+{
+ BOOL WindowsTarget;
+ LIBRARY_PROVIDER_INDEX_TYPE IndexType;
+
+ SString RuntimeModulePath;
+ BYTE RuntimeBuildId[MAX_BUILDID_SIZE];
+ ULONG RuntimeBuildIdSize;
+
+ DWORD DbiTimeStamp;
+ DWORD DbiSizeOfImage;
+ BYTE DbiBuildId[MAX_BUILDID_SIZE];
+ ULONG DbiBuildIdSize;
+ WCHAR DbiName[MAX_PATH_FNAME];
+
+ DWORD DacTimeStamp;
+ DWORD DacSizeOfImage;
+ BYTE DacBuildId[MAX_BUILDID_SIZE];
+ ULONG DacBuildIdSize;
+ WCHAR DacName[MAX_PATH_FNAME];
+
+ ClrInfo()
+ {
+#ifdef HOST_UNIX
+ WindowsTarget = FALSE;
+#else
+ WindowsTarget = TRUE;
+#endif
+ IndexType = LIBRARY_PROVIDER_INDEX_TYPE::Unknown;
+
+ memset(&RuntimeBuildId, 0, sizeof(RuntimeBuildId));
+ RuntimeBuildIdSize = 0;
+
+ DbiTimeStamp = 0;
+ DbiSizeOfImage = 0;
+ memset(&DbiBuildId, 0, sizeof(DbiBuildId));
+ DbiBuildIdSize = 0;
+
+ DacTimeStamp = 0;
+ DacSizeOfImage = 0;;
+ memset(&DacBuildId, 0, sizeof(DacBuildId));
+ DacBuildIdSize = 0;
+
+ swprintf_s(DbiName, MAX_PATH_FNAME, W("%s"), MAKEDLLNAME_W(MAIN_DBI_MODULE_NAME_W));
+ swprintf_s(DacName, MAX_PATH_FNAME, W("%s"), MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W));
+ }
+
+ bool IsValid()
+ {
+ if (IndexType == LIBRARY_PROVIDER_INDEX_TYPE::Identity)
+ {
+ if (WindowsTarget)
+ {
+ return DbiTimeStamp != 0 && DbiSizeOfImage != 0 && DacTimeStamp != 0 && DacSizeOfImage;
+ }
+ else
+ {
+ return DbiBuildIdSize > 0 && DacBuildIdSize > 0;
+ }
+ }
+ else if (IndexType == LIBRARY_PROVIDER_INDEX_TYPE::Runtime)
+ {
+ // The runtime index info should never be needed or provided on Windows
+ if (!WindowsTarget)
+ {
+ return RuntimeBuildIdSize > 0;
+ }
+ }
+ return false;
+ }
+};
+
+extern "C" bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress);
+extern "C" bool TryGetBuildId(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, BYTE* buffer, ULONG bufferSize, PULONG pBuildIdSize);
+#ifdef TARGET_UNIX
+extern "C" bool TryReadSymbolFromFile(const WCHAR* modulePath, const char* symbolName, BYTE* buffer, ULONG32 size);
+extern "C" bool TryGetBuildIdFromFile(const WCHAR* modulePath, BYTE* buffer, ULONG bufferSize, PULONG pBuildSize);
+#endif
+
// forward declaration
struct ICorDebugDataTarget;
STDMETHOD(CanUnloadNow(HMODULE hModule));
- //IUnknown methods:
- STDMETHOD(QueryInterface(
- REFIID riid,
- void **ppvObject));
+ // IUnknown methods:
+ STDMETHOD(QueryInterface(REFIID riid, void **ppvObject));
// Standard AddRef implementation
STDMETHOD_(ULONG, AddRef());
// Standard Release implementation.
STDMETHOD_(ULONG, Release());
-
+ static HRESULT ProvideLibraries(ClrInfo& clrInfo,
+ ICLRDebuggingLibraryProvider3* pLibraryProvider,
+ SString& dbiModulePath,
+ SString& dacModulePath);
private:
- VOID RetargetDacIfNeeded(DWORD* pdwTimeStamp,
- DWORD* pdwSizeOfImage);
+ static HRESULT ProvideLibraries(ClrInfo& clrInfo,
+ IUnknown* pLibraryProvider,
+ SString& dbiModulePath,
+ SString& dacModulePath,
+ HMODULE* phDbi,
+ HMODULE* phDac);
+
+ static VOID RetargetDacIfNeeded(DWORD* pdwTimeStamp,
+ DWORD* pdwSizeOfImage);
HRESULT GetCLRInfo(ICorDebugDataTarget * pDataTarget,
ULONG64 moduleBaseAddress,
- CLR_DEBUGGING_VERSION * pVersion,
- DWORD * pdwDbiTimeStamp,
- DWORD * pdwDbiSizeOfImage,
- _Inout_updates_z_(dwDbiNameCharCount) WCHAR * pDbiName,
- DWORD dwDbiNameCharCount,
- DWORD * pdwDacTimeStamp,
- DWORD * pdwDacSizeOfImage,
- _Inout_updates_z_(dwDacNameCharCount) WCHAR * pDacName,
- DWORD dwDacNameCharCount);
+ CLR_DEBUGGING_VERSION* pVersion,
+ ClrInfo& clrInfo);
HRESULT FormatLongDacModuleName(_Inout_updates_z_(cchBuffer) WCHAR * pBuffer,
DWORD cchBuffer,
)
endif(NOT DEFINED CLR_CMAKE_HOST_OSX)
+if(CLR_CMAKE_TARGET_OSX)
+ list(APPEND DBGUTIL_SOURCES
+ machoreader.cpp
+ )
+endif(CLR_CMAKE_TARGET_OSX)
+
add_library(dbgutil STATIC ${DBGUTIL_SOURCES})
<ItemGroup>
<ClCompile Include="dbgutil.cpp" />
<ClCompile Include="elfreader.cpp" />
+ <ClCompile Include="machoreader.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="elfreader.h" />
+ <ClInclude Include="machoreader.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A9A7C879-C320-3327-BB84-16E1322E17AE}</ProjectGuid>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>..\..\SOS\dbgutil;..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src\shared;$(RepoRoot)src\shared\inc;$(RepoRoot)src\shared\pal\prebuilt\inc;$(RepoRoot)src\shared\pal\inc;$(RepoRoot)src\shared\pal\inc\rt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
<AssemblerListingLocation>Debug/</AssemblerListingLocation>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ObjectFileName>$(IntDir)</ObjectFileName>
+ <BrowseInformation>true</BrowseInformation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Lib>
<AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
</Lib>
+ <Bscmake>
+ <PreserveSbr>true</PreserveSbr>
+ </Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
<ClCompile>
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
// The .NET Foundation licenses this file to you under the MIT license.
#include <windows.h>
+#include <clrdata.h>
+#include <cor.h>
+#include <cordebug.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
-#include <limits.h>
#include "elfreader.h"
+#include "arrayholder.h"
#define Elf_Ehdr ElfW(Ehdr)
#define Elf_Phdr ElfW(Phdr)
static const char ElfMagic[] = { 0x7f, 'E', 'L', 'F', '\0' };
#endif
-extern bool ElfReaderReadMemory(void* address, void* buffer, size_t size);
+#ifdef HOST_UNIX
-class ElfReaderExport: public ElfReader
+class ElfReaderFromFile : public ElfReader
{
+private:
+ struct ProgramHeader
+ {
+ uint64_t Start;
+ uint64_t End;
+ uint64_t FileOffset;
+ };
+ PAL_FILE* m_file;
+ std::vector<ProgramHeader> m_programHeaders;
+
public:
- ElfReaderExport()
+ ElfReaderFromFile() : ElfReader(true),
+ m_file(NULL)
+ {
+ }
+
+ virtual ~ElfReaderFromFile()
+ {
+ if (m_file != NULL)
+ {
+ PAL_fclose(m_file);
+ m_file = NULL;
+ }
+ }
+
+ bool OpenFile(const WCHAR* modulePath)
+ {
+ _ASSERTE(m_file == NULL);
+ m_file = _wfopen(modulePath, W("rb"));
+ return m_file != NULL;
+ }
+
+ uint64_t GetFileOffset(uint64_t address)
+ {
+ for (const ProgramHeader& header : m_programHeaders)
+ {
+ if (address >= header.Start && address < header.End)
+ {
+ return address - header.Start + header.FileOffset;
+ }
+ }
+ return 0;
+ }
+
+ virtual void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr)
+ {
+ switch (phdr->p_type)
+ {
+ case PT_LOAD:
+ ProgramHeader header;
+ header.Start = loadbias + phdr->p_vaddr;
+ header.End = loadbias + phdr->p_vaddr + phdr->p_memsz;
+ header.FileOffset = phdr->p_offset;
+ m_programHeaders.push_back(header);
+ break;
+ }
+ }
+
+ virtual bool ReadMemory(void* address, void* buffer, size_t size)
+ {
+ if (m_file == NULL)
+ {
+ return false;
+ }
+ if (PAL_fseek(m_file, (LONG)address, SEEK_SET) != 0)
+ {
+ return false;
+ }
+ size_t read = PAL_fread(buffer, 1, size, m_file);
+ return read > 0;
+ }
+};
+
+//
+// Entry point to get an export symbol from a module file
+//
+extern "C" bool
+TryReadSymbolFromFile(const WCHAR* modulePath, const char* symbolName, BYTE* buffer, ULONG32 size)
+{
+ ElfReaderFromFile reader;
+ if (reader.OpenFile(modulePath))
{
+ if (reader.PopulateForSymbolLookup(0))
+ {
+ uint64_t symbolOffset;
+ if (reader.TryLookupSymbol(symbolName, &symbolOffset))
+ {
+ symbolOffset = reader.GetFileOffset(symbolOffset);
+ if (symbolOffset != 0)
+ {
+ return reader.ReadMemory((void*)symbolOffset, buffer, size);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+//
+// Entry point to get the ELF file's build id
+//
+extern "C" bool
+TryGetBuildIdFromFile(const WCHAR* modulePath, BYTE* buffer, ULONG bufferSize, PULONG pBuildSize)
+{
+ ElfReaderFromFile reader;
+ if (reader.OpenFile(modulePath))
+ {
+ if (reader.EnumerateProgramHeaders(0, nullptr, nullptr))
+ {
+ return reader.GetBuildId(buffer, bufferSize, pBuildSize);
+ }
+ }
+ return false;
+}
+
+#endif // HOST_UNIX
+
+typedef bool (*ReadMemoryCallback)(void* address, void* buffer, size_t size);
+
+class ElfReaderWithCallback : public ElfReader
+{
+private:
+ ReadMemoryCallback m_readMemory;
+
+public:
+ ElfReaderWithCallback(ReadMemoryCallback readMemory) : ElfReader(false),
+ m_readMemory(readMemory)
+ {
+ }
+
+ virtual ~ElfReaderWithCallback()
+ {
+ }
+
+private:
+ virtual bool ReadMemory(void* address, void* buffer, size_t size)
+ {
+ return m_readMemory(address, buffer, size);
+ }
+};
+
+//
+// Entry point to get an export symbol
+//
+extern "C" bool
+TryGetSymbolWithCallback(ReadMemoryCallback readMemory, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress)
+{
+ ElfReaderWithCallback reader(readMemory);
+ if (reader.PopulateForSymbolLookup(baseAddress))
+ {
+ uint64_t symbolOffset;
+ if (reader.TryLookupSymbol(symbolName, &symbolOffset))
+ {
+ *symbolAddress = baseAddress + symbolOffset;
+ return true;
+ }
+ }
+ *symbolAddress = 0;
+ return false;
+}
+
+class ElfReaderExport : public ElfReader
+{
+private:
+ ICorDebugDataTarget* m_dataTarget;
+
+public:
+ ElfReaderExport(ICorDebugDataTarget* dataTarget) : ElfReader(false),
+ m_dataTarget(dataTarget)
+ {
+ dataTarget->AddRef();
}
virtual ~ElfReaderExport()
{
+ m_dataTarget->Release();
}
private:
virtual bool ReadMemory(void* address, void* buffer, size_t size)
{
- return ElfReaderReadMemory(address, buffer, size);
+ uint32_t read = 0;
+ return SUCCEEDED(m_dataTarget->ReadVirtual(reinterpret_cast<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), (uint32_t)size, &read));
}
};
// Main entry point to get an export symbol
//
extern "C" bool
-TryGetSymbol(uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress)
+TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress)
{
- ElfReaderExport elfreader;
- if (elfreader.PopulateForSymbolLookup(baseAddress))
+ ElfReaderExport reader(dataTarget);
+ if (reader.PopulateForSymbolLookup(baseAddress))
{
uint64_t symbolOffset;
- if (elfreader.TryLookupSymbol(symbolName, &symbolOffset))
+ if (reader.TryLookupSymbol(symbolName, &symbolOffset))
{
*symbolAddress = baseAddress + symbolOffset;
return true;
return false;
}
+
+//
+// Get the build id of the module from a data target
+//
+extern "C" bool
+TryGetBuildId(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, BYTE* buffer, ULONG bufferSize, PULONG pBuildSize)
+{
+ ElfReaderExport reader(dataTarget);
+ if (reader.EnumerateProgramHeaders(baseAddress, nullptr, nullptr))
+ {
+ return reader.GetBuildId(buffer, bufferSize, pBuildSize);
+ }
+ return false;
+}
+
//
// ELF reader constructor/destructor
//
-ElfReader::ElfReader() :
+ElfReader::ElfReader(bool isFileLayout) :
+ m_isFileLayout(isFileLayout),
m_gnuHashTableAddr(nullptr),
m_stringTableAddr(nullptr),
m_stringTableSize(0),
m_symbolTableAddr(nullptr),
m_buckets(nullptr),
- m_chainsAddress(nullptr)
+ m_chainsAddress(nullptr),
+ m_noteStart(0),
+ m_noteEnd(0)
{
memset(&m_hashTable, 0, sizeof(m_hashTable));
}
if (symbolName.compare(possibleName) == 0)
{
*symbolOffset = symbol.st_value;
- Trace("TryLookupSymbol found '%s' at offset %" PRIxA "\n", symbolName.c_str(), *symbolOffset);
+ Trace("TryLookupSymbol found '%s' at offset %" PRIxA " in %d\n", symbolName.c_str(), *symbolOffset, symbol.st_shndx);
return true;
}
}
return true;
}
+size_t Align4(size_t x) { return (x + 3) & ~3; }
+
+bool
+ElfReader::GetBuildId(BYTE* buffer, ULONG bufferSize, PULONG pBuildSize)
+{
+ _ASSERTE(pBuildSize != nullptr);
+
+ if (m_noteStart != 0)
+ {
+ _ASSERTE(m_noteEnd != 0);
+ uint64_t address = m_noteStart;
+ while (address < m_noteEnd)
+ {
+ Elf_Nhdr nhdr;
+ if (!ReadMemory((PVOID)address, &nhdr, sizeof(nhdr)))
+ {
+ return false;
+ }
+ size_t nhdrSize = sizeof(Elf_Nhdr) + Align4(nhdr.n_namesz) + Align4(nhdr.n_descsz);
+ Trace("GetBuildId: type %d size %08x\n", nhdr.n_type, nhdrSize);
+ if (nhdr.n_type == NT_GNU_BUILD_ID)
+ {
+ ArrayHolder<BYTE> nhdrBuffer = new BYTE[nhdrSize];
+ if (!ReadMemory((PVOID)address, nhdrBuffer, nhdrSize))
+ {
+ return false;
+ }
+ const char* name = (const char*)(nhdrBuffer.GetPtr() + sizeof(Elf_Nhdr));
+ Trace("GetBuildId: name %s\n", name);
+ if (strncmp(name, ELF_NOTE_GNU, Align4(nhdr.n_namesz)) == 0)
+ {
+ *pBuildSize = nhdr.n_descsz;
+ memcpy_s(buffer, bufferSize, nhdrBuffer.GetPtr() + sizeof(Elf_Nhdr) + Align4(nhdr.n_namesz), nhdr.n_descsz);
+
+ Trace("GetBuildId: found id size %d\n", *pBuildSize);
+ return true;
+ }
+ }
+ address += nhdrSize;
+ }
+ }
+ return false;
+}
+
#ifdef HOST_UNIX
//
switch (ph.p_type)
{
+ case PT_NOTE:
+ m_noteStart = loadbias + ph.p_vaddr;
+ m_noteEnd = loadbias + ph.p_vaddr + ph.p_memsz;
+ break;
+
case PT_DYNAMIC:
- if (pdynamicAddr != nullptr) {
- *pdynamicAddr = reinterpret_cast<Elf_Dyn*>(loadbias + ph.p_vaddr);
+ if (pdynamicAddr != nullptr)
+ {
+ if (m_isFileLayout)
+ {
+ *pdynamicAddr = reinterpret_cast<Elf_Dyn*>(loadbias + ph.p_offset);
+ }
+ else
+ {
+ *pdynamicAddr = reinterpret_cast<Elf_Dyn*>(loadbias + ph.p_vaddr);
+ }
}
break;
}
class ElfReader
{
private:
+ bool m_isFileLayout;
void* m_gnuHashTableAddr; // DT_GNU_HASH
void* m_stringTableAddr; // DT_STRTAB
int m_stringTableSize; // DT_STRSIZ
int32_t* m_buckets; // gnu hash table buckets
void* m_chainsAddress;
+ uint64_t m_noteStart;
+ uint64_t m_noteEnd;
+
public:
- ElfReader();
+ ElfReader(bool isFileLayout);
virtual ~ElfReader();
#ifdef HOST_UNIX
bool EnumerateElfInfo(ElfW(Phdr)* phdrAddr, int phnum);
bool PopulateForSymbolLookup(uint64_t baseAddress);
bool TryLookupSymbol(std::string symbolName, uint64_t* symbolOffset);
bool EnumerateProgramHeaders(uint64_t baseAddress, uint64_t* ploadbias = nullptr, ElfW(Dyn)** pdynamicAddr = nullptr);
+ bool GetBuildId(BYTE* buffer, ULONG bufferSize, PULONG pBuildSize);
private:
bool GetSymbol(int32_t index, ElfW(Sym)* symbol);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include <windows.h>
+#include <clrdata.h>
+#include <cor.h>
+#include <cordebug.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <arrayholder.h>
+#include "machoreader.h"
+
+#if TARGET_64BIT
+#define PRIx PRIx64
+#define PRIu PRIu64
+#define PRId PRId64
+#define PRIA "016"
+#define PRIxA PRIA PRIx
+#else
+#define PRIx PRIx32
+#define PRIu PRIu32
+#define PRId PRId32
+#define PRIA "08"
+#define PRIxA PRIA PRIx
+#endif
+
+class MachOReaderFromFile : public MachOReader
+{
+private:
+ PAL_FILE* m_file;
+
+public:
+ MachOReaderFromFile() :
+ m_file(NULL)
+ {
+ }
+
+ virtual ~MachOReaderFromFile()
+ {
+ }
+
+ bool OpenFile(const WCHAR* modulePath)
+ {
+ _ASSERTE(m_file == NULL);
+ m_file = _wfopen(modulePath, W("rb"));
+ return m_file != NULL;
+ }
+
+ virtual bool ReadMemory(void* address, void* buffer, size_t size)
+ {
+ if (m_file == NULL)
+ {
+ return false;
+ }
+ if (PAL_fseek(m_file, (LONG)(intptr_t)address, SEEK_SET) != 0)
+ {
+ return false;
+ }
+ size_t read = PAL_fread(buffer, 1, size, m_file);
+ return read > 0;
+ }
+};
+
+//
+// Entry point to get an export symbol from a module file
+//
+extern "C" bool
+TryReadSymbolFromFile(const WCHAR* modulePath, const char* symbolName, BYTE* buffer, ULONG32 size)
+{
+ MachOReaderFromFile reader;
+ if (reader.OpenFile(modulePath))
+ {
+ MachOModule module(reader, true, 0);
+ if (module.ReadHeader())
+ {
+ uint64_t symbolOffset;
+ if (module.TryLookupSymbol(symbolName, &symbolOffset))
+ {
+ return reader.ReadMemory((void*)symbolOffset, buffer, size);
+ }
+ }
+ }
+ return false;
+}
+
+//
+// Entry point to get the MachO file's build id
+//
+extern "C" bool
+TryGetBuildIdFromFile(const WCHAR* modulePath, BYTE* buffer, ULONG bufferSize, PULONG pBuildSize)
+{
+ MachOReaderFromFile reader;
+ if (reader.OpenFile(modulePath))
+ {
+ MachOModule module(reader, true, 0);
+ return module.GetBuildId(buffer, bufferSize, pBuildSize);
+ }
+ return false;
+}
+
+typedef bool (*ReadMemoryCallback)(void* address, void* buffer, size_t size);
+
+class MachOReaderWithCallback : public MachOReader
+{
+private:
+ ReadMemoryCallback m_readMemory;
+
+public:
+ MachOReaderWithCallback(ReadMemoryCallback readMemory) :
+ m_readMemory(readMemory)
+ {
+ }
+
+ virtual ~MachOReaderWithCallback()
+ {
+ }
+
+private:
+ virtual bool ReadMemory(void* address, void* buffer, size_t size)
+ {
+ return m_readMemory(address, buffer, size);
+ }
+};
+
+//
+// Entry point to get an export symbol
+//
+extern "C" bool
+TryGetSymbolWithCallback(ReadMemoryCallback readMemory, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress)
+{
+ MachOReaderWithCallback reader(readMemory);
+ MachOModule module(reader, false, baseAddress);
+ if (module.ReadHeader())
+ {
+ uint64_t symbolOffset;
+ if (module.TryLookupSymbol(symbolName, &symbolOffset))
+ {
+ *symbolAddress = symbolOffset;
+ return true;
+ }
+ }
+ *symbolAddress = 0;
+ return false;
+}
+
+class MachOReaderExport : public MachOReader
+{
+private:
+ ICorDebugDataTarget* m_dataTarget;
+
+public:
+ MachOReaderExport(ICorDebugDataTarget* dataTarget) :
+ m_dataTarget(dataTarget)
+ {
+ dataTarget->AddRef();
+ }
+
+ virtual ~MachOReaderExport()
+ {
+ m_dataTarget->Release();
+ }
+
+private:
+ virtual bool ReadMemory(void* address, void* buffer, size_t size)
+ {
+ uint32_t read = 0;
+ return SUCCEEDED(m_dataTarget->ReadVirtual(reinterpret_cast<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), (uint32_t)size, &read));
+ }
+};
+
+//
+// Main entry point to get an export symbol
+//
+extern "C" bool
+TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress)
+{
+ MachOReaderExport reader(dataTarget);
+ MachOModule module(reader, false, baseAddress);
+ if (module.ReadHeader())
+ {
+ uint64_t symbolOffset;
+ if (module.TryLookupSymbol(symbolName, &symbolOffset))
+ {
+ *symbolAddress = symbolOffset;
+ return true;
+ }
+ }
+ *symbolAddress = 0;
+ return false;
+}
+
+//
+// Get the build id of the module from a data target
+//
+extern "C" bool
+TryGetBuildId(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, BYTE* buffer, ULONG bufferSize, PULONG pBuildSize)
+{
+ MachOReaderExport reader(dataTarget);
+ MachOModule module(reader, false, baseAddress);
+ return module.GetBuildId(buffer, bufferSize, pBuildSize);
+}
+
+//--------------------------------------------------------------------
+// MachO module
+//--------------------------------------------------------------------
+
+MachOModule::MachOModule(MachOReader& reader, bool isFileLayout, mach_vm_address_t baseAddress, mach_header_64* header, std::string* name) :
+ m_reader(reader),
+ m_isFileLayout(isFileLayout),
+ m_baseAddress(baseAddress),
+ m_loadBias(0),
+ m_commands(nullptr),
+ m_symtabCommand(nullptr),
+ m_nlists(nullptr),
+ m_strtabAddress(0)
+{
+ if (header != nullptr) {
+ m_header = *header;
+ }
+ if (name != nullptr) {
+ m_name = *name;
+ }
+}
+
+MachOModule::~MachOModule()
+{
+ if (m_commands != nullptr) {
+ free(m_commands);
+ m_commands = nullptr;
+ }
+ if (m_nlists != nullptr) {
+ free(m_nlists);
+ m_nlists = nullptr;
+ }
+}
+
+bool
+MachOModule::ReadHeader()
+{
+ _ASSERTE(sizeof(m_header) == sizeof(mach_header_64));
+ if (!m_reader.ReadMemory((void*)m_baseAddress, &m_header, sizeof(mach_header_64)))
+ {
+ m_reader.Trace("ERROR: failed to read header at %p\n", (void*)m_baseAddress);
+ return false;
+ }
+ m_reader.Trace("ReadHeader: magic %08x cputype %08x ncmds %d sizeofcmds %d\n", m_header.magic, m_header.cputype, m_header.ncmds, m_header.sizeofcmds);
+ return m_header.magic == 0xfeedfacf;
+}
+
+bool
+MachOModule::TryLookupSymbol(const char* symbolName, uint64_t* symbolValue)
+{
+ _ASSERTE(symbolValue != nullptr);
+
+ if (ReadSymbolTable())
+ {
+ _ASSERTE(m_nlists != nullptr);
+ _ASSERTE(m_strtabAddress != 0);
+
+ // First, search just the "external" export symbols
+ if (TryLookupSymbol(m_dysymtabCommand->iextdefsym, m_dysymtabCommand->nextdefsym, symbolName, symbolValue))
+ {
+ m_reader.Trace("SYM: Found '%s' in external symbols\n", symbolName);
+ return true;
+ }
+ m_reader.Trace("SYM: Missed '%s' in external symbols\n", symbolName);
+
+ // If not found in external symbols, search all of them
+ if (TryLookupSymbol(0, m_symtabCommand->nsyms, symbolName, symbolValue))
+ {
+ m_reader.Trace("SYM: Found '%s' in all symbols\n", symbolName);
+ return true;
+ }
+ m_reader.Trace("SYM: Missed '%s' in all symbols\n", symbolName);
+ }
+ *symbolValue = 0;
+ return false;
+}
+
+bool
+MachOModule::TryLookupSymbol(int start, int nsyms, const char* symbolName, uint64_t* symbolValue)
+{
+ for (int i = 0; i < nsyms; i++)
+ {
+ std::string name = GetSymbolName(start + i);
+
+ // Skip the leading underscores to match Linux externs
+ const char* currentName = name.length() > 0 && name[0] == '_' ? name.c_str() + 1 : name.c_str();
+
+ // Does this symbol match?
+ if (strcmp(currentName, symbolName) == 0)
+ {
+ *symbolValue = m_loadBias + m_nlists[start + i].n_value;
+ return true;
+ }
+ }
+ *symbolValue = 0;
+ return false;
+}
+
+bool
+MachOModule::GetBuildId(BYTE* buffer, ULONG bufferSize, PULONG pBuildSize)
+{
+ _ASSERTE(pBuildSize != nullptr);
+ if (ReadHeader())
+ {
+ if (ReadLoadCommands())
+ {
+ if (m_uuidCommand != nullptr)
+ {
+ *pBuildSize = sizeof(m_uuidCommand->uuid);
+ memcpy_s(buffer, bufferSize, m_uuidCommand->uuid, *pBuildSize);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+MachOModule::EnumerateSegments()
+{
+ if (!ReadLoadCommands())
+ {
+ return false;
+ }
+ _ASSERTE(!m_segments.empty());
+
+ for (const segment_command_64* segment : m_segments)
+ {
+ m_reader.VisitSegment(*this, *segment);
+
+ const section_64* section = (section_64*)((uint64_t)segment + sizeof(segment_command_64));
+
+ for (int s = 0; s < segment->nsects; s++, section++)
+ {
+ m_reader.VisitSection(*this, *section);
+ }
+ }
+ return true;
+}
+
+bool
+MachOModule::ReadLoadCommands()
+{
+ if (m_commands == nullptr)
+ {
+ // Read load commands
+ void* commandsAddress = (void*)(m_baseAddress + sizeof(mach_header_64));
+ m_commands = (load_command*)malloc(m_header.sizeofcmds);
+ if (m_commands == nullptr)
+ {
+ m_reader.Trace("ERROR: Failed to allocate %d byte load commands\n", m_header.sizeofcmds);
+ return false;
+ }
+ if (!m_reader.ReadMemory(commandsAddress, m_commands, m_header.sizeofcmds))
+ {
+ m_reader.Trace("ERROR: Failed to read load commands at %p of %d\n", commandsAddress, m_header.sizeofcmds);
+ return false;
+ }
+ load_command* command = m_commands;
+
+ for (int i = 0; i < m_header.ncmds; i++)
+ {
+ m_reader.TraceVerbose("CMD: load command cmd %02x (%d) size %d\n", command->cmd, command->cmd, command->cmdsize);
+
+ switch (command->cmd)
+ {
+ case LC_UUID:
+ m_uuidCommand = (uuid_command*)command;
+ break;
+
+ case LC_SYMTAB:
+ m_symtabCommand = (symtab_command*)command;
+ break;
+
+ case LC_DYSYMTAB:
+ m_dysymtabCommand = (dysymtab_command*)command;
+ break;
+
+ case LC_SEGMENT_64:
+ segment_command_64* segment = (segment_command_64*)command;
+ m_segments.push_back(segment);
+
+ // Calculate the load bias for the module. This is the value to add to the vmaddr of a
+ // segment to get the actual address.
+ if (!m_isFileLayout)
+ {
+ if (strcmp(segment->segname, SEG_TEXT) == 0)
+ {
+ m_loadBias = m_baseAddress - segment->vmaddr;
+ }
+ }
+
+ m_reader.TraceVerbose("CMD: vmaddr %016llx vmsize %016llx fileoff %016llx filesize %016llx nsects %d max %c%c%c init %c%c%c %02x %s\n",
+ segment->vmaddr,
+ segment->vmsize,
+ segment->fileoff,
+ segment->filesize,
+ segment->nsects,
+ (segment->maxprot & VM_PROT_READ) ? 'r' : '-',
+ (segment->maxprot & VM_PROT_WRITE) ? 'w' : '-',
+ (segment->maxprot & VM_PROT_EXECUTE) ? 'x' : '-',
+ (segment->initprot & VM_PROT_READ) ? 'r' : '-',
+ (segment->initprot & VM_PROT_WRITE) ? 'w' : '-',
+ (segment->initprot & VM_PROT_EXECUTE) ? 'x' : '-',
+ segment->flags,
+ segment->segname);
+
+ section_64* section = (section_64*)((uint64_t)segment + sizeof(segment_command_64));
+ for (int s = 0; s < segment->nsects; s++, section++)
+ {
+ m_reader.TraceVerbose(" addr %016llx size %016llx off %08x align %02x flags %02x %s\n",
+ section->addr,
+ section->size,
+ section->offset,
+ section->align,
+ section->flags,
+ section->sectname);
+ }
+ break;
+ }
+ // Get next load command
+ command = (load_command*)((char*)command + command->cmdsize);
+ }
+ m_reader.TraceVerbose("CMD: load bias %016llx\n", m_loadBias);
+ }
+
+ return true;
+}
+
+bool
+MachOModule::ReadSymbolTable()
+{
+ if (m_nlists == nullptr)
+ {
+ if (!ReadLoadCommands())
+ {
+ return false;
+ }
+ _ASSERTE(m_symtabCommand != nullptr);
+ _ASSERTE(m_strtabAddress == 0);
+
+ m_reader.TraceVerbose("SYM: symoff %08x nsyms %d stroff %08x strsize %d iext %d next %d iundef %d nundef %d extref %d nextref %d\n",
+ m_symtabCommand->symoff,
+ m_symtabCommand->nsyms,
+ m_symtabCommand->stroff,
+ m_symtabCommand->strsize,
+ m_dysymtabCommand->iextdefsym,
+ m_dysymtabCommand->nextdefsym,
+ m_dysymtabCommand->iundefsym,
+ m_dysymtabCommand->nundefsym,
+ m_dysymtabCommand->extrefsymoff,
+ m_dysymtabCommand->nextrefsyms);
+
+ // Read the entire symbol part of symbol table. An array of "nlist" structs.
+ void* symbolTableAddress = (void*)GetAddressFromFileOffset(m_symtabCommand->symoff);
+ size_t symtabSize = sizeof(nlist_64) * m_symtabCommand->nsyms;
+ m_nlists = (nlist_64*)malloc(symtabSize);
+ if (m_nlists == nullptr)
+ {
+ m_reader.Trace("ERROR: Failed to allocate %zu byte symtab\n", symtabSize);
+ return false;
+ }
+ if (!m_reader.ReadMemory(symbolTableAddress, m_nlists, symtabSize))
+ {
+ m_reader.Trace("ERROR: Failed to read symtab at %p of %zu\n", symbolTableAddress, symtabSize);
+ return false;
+ }
+
+ // Save the symbol string table address.
+ m_strtabAddress = GetAddressFromFileOffset(m_symtabCommand->stroff);
+ }
+ return true;
+}
+
+uint64_t
+MachOModule::GetAddressFromFileOffset(uint32_t offset)
+{
+ _ASSERTE(!m_segments.empty());
+ if (!m_isFileLayout)
+ {
+ for (const segment_command_64* segment : m_segments)
+ {
+ if (offset >= segment->fileoff && offset < (segment->fileoff + segment->filesize))
+ {
+ return m_loadBias + offset + segment->vmaddr - segment->fileoff;
+ }
+ }
+ }
+ return m_loadBias + offset;
+}
+
+std::string
+MachOModule::GetSymbolName(int index)
+{
+ uint64_t symbolNameAddress = m_strtabAddress + m_nlists[index].n_un.n_strx;
+ std::string result;
+ while (true)
+ {
+ char c = 0;
+ if (!m_reader.ReadMemory((void*)symbolNameAddress, &c, sizeof(char)))
+ {
+ m_reader.Trace("ERROR: Failed to read string table at %p\n", (void*)symbolNameAddress);
+ break;
+ }
+ if (c == '\0')
+ {
+ break;
+ }
+ result.append(1, c);
+ symbolNameAddress++;
+ }
+ return result;
+}
+
+//--------------------------------------------------------------------
+// MachO reader
+//--------------------------------------------------------------------
+
+MachOReader::MachOReader()
+{
+}
+
+bool
+MachOReader::EnumerateModules(mach_vm_address_t address, mach_header_64* header)
+{
+ _ASSERTE(header->magic == MH_MAGIC_64);
+ _ASSERTE(header->filetype == MH_DYLINKER);
+
+ MachOModule dylinker(*this, false, address, header);
+
+ // Search for symbol for the dyld image info cache
+ uint64_t dyldInfoAddress = 0;
+ if (!dylinker.TryLookupSymbol("dyld_all_image_infos", &dyldInfoAddress))
+ {
+ Trace("ERROR: Can not find the _dyld_all_image_infos symbol\n");
+ return false;
+ }
+
+ // Read the all image info from the dylinker image
+ dyld_all_image_infos dyldInfo;
+
+ if (!ReadMemory((void*)dyldInfoAddress, &dyldInfo, sizeof(dyld_all_image_infos)))
+ {
+ Trace("ERROR: Failed to read dyld_all_image_infos at %p\n", (void*)dyldInfoAddress);
+ return false;
+ }
+ std::string dylinkerPath;
+ if (!ReadString(dyldInfo.dyldPath, dylinkerPath))
+ {
+ Trace("ERROR: Failed to read name at %p\n", dyldInfo.dyldPath);
+ return false;
+ }
+ dylinker.SetName(dylinkerPath);
+ Trace("MOD: %016llx %08x %s\n", dylinker.BaseAddress(), dylinker.Header().flags, dylinker.Name().c_str());
+ VisitModule(dylinker);
+
+ void* imageInfosAddress = (void*)dyldInfo.infoArray;
+ size_t imageInfosSize = dyldInfo.infoArrayCount * sizeof(dyld_image_info);
+ Trace("MOD: infoArray %p infoArrayCount %d\n", dyldInfo.infoArray, dyldInfo.infoArrayCount);
+
+ ArrayHolder<dyld_image_info> imageInfos = new (std::nothrow) dyld_image_info[dyldInfo.infoArrayCount];
+ if (imageInfos == nullptr)
+ {
+ Trace("ERROR: Failed to allocate %zu byte image infos\n", imageInfosSize);
+ return false;
+ }
+ if (!ReadMemory(imageInfosAddress, imageInfos, imageInfosSize))
+ {
+ Trace("ERROR: Failed to read dyld_all_image_infos at %p\n", imageInfosAddress);
+ return false;
+ }
+ for (int i = 0; i < dyldInfo.infoArrayCount; i++)
+ {
+ mach_vm_address_t imageAddress = (mach_vm_address_t)imageInfos[i].imageLoadAddress;
+ const char* imageFilePathAddress = imageInfos[i].imageFilePath;
+
+ std::string imagePath;
+ if (!ReadString(imageFilePathAddress, imagePath))
+ {
+ Trace("ERROR: Failed to read image name at %p\n", imageFilePathAddress);
+ continue;
+ }
+ MachOModule module(*this, false, imageAddress, nullptr, &imagePath);
+ if (!module.ReadHeader())
+ {
+ continue;
+ }
+ Trace("MOD: %016llx %08x %s\n", imageAddress, module.Header().flags, imagePath.c_str());
+ VisitModule(module);
+ }
+ return true;
+}
+
+bool
+MachOReader::ReadString(const char* address, std::string& str)
+{
+ for (int i = 0; i < MAX_LONGPATH; i++)
+ {
+ char c = 0;
+ if (!ReadMemory((void*)(address + i), &c, sizeof(char)))
+ {
+ Trace("ERROR: Failed to read string at %p\n", (void*)(address + i));
+ return false;
+ }
+ if (c == '\0')
+ {
+ break;
+ }
+ str.append(1, c);
+ }
+ return true;
+}
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/dyld_images.h>
+#include <string>
+#include <vector>
+
+class MachOReader;
+
+class MachOModule
+{
+ friend MachOReader;
+private:
+ MachOReader& m_reader;
+ bool m_isFileLayout;
+ mach_vm_address_t m_baseAddress;
+ mach_vm_address_t m_loadBias;
+ mach_header_64 m_header;
+ std::string m_name;
+ load_command* m_commands;
+ std::vector<segment_command_64*> m_segments;
+ uuid_command* m_uuidCommand;
+ symtab_command* m_symtabCommand;
+ dysymtab_command* m_dysymtabCommand;
+ nlist_64* m_nlists;
+ uint64_t m_strtabAddress;
+
+public:
+ MachOModule(MachOReader& reader, bool isFileLayout, mach_vm_address_t baseAddress, mach_header_64* header = nullptr, std::string* name = nullptr);
+ ~MachOModule();
+
+ inline mach_vm_address_t BaseAddress() const { return m_baseAddress; }
+ inline mach_vm_address_t LoadBias() const { return m_loadBias; }
+ inline const mach_header_64& Header() const { return m_header; }
+ inline const std::string& Name() const { return m_name; }
+
+ bool ReadHeader();
+ bool TryLookupSymbol(const char* symbolName, uint64_t* symbolValue);
+ bool TryLookupSymbol(int start, int nsyms, const char* symbolName, uint64_t* symbolValue);
+ bool GetBuildId(BYTE* buffer, ULONG bufferSize, PULONG pBuildSize);
+ bool EnumerateSegments();
+
+private:
+ inline void SetName(std::string& name) { m_name = name; }
+
+ bool ReadSymbolTable();
+ bool ReadLoadCommands();
+ uint64_t GetAddressFromFileOffset(uint32_t offset);
+ std::string GetSymbolName(int index);
+};
+
+class MachOReader
+{
+ friend MachOModule;
+public:
+ MachOReader();
+ bool EnumerateModules(mach_vm_address_t address, mach_header_64* header);
+
+private:
+ bool ReadString(const char* address, std::string& str);
+ virtual void VisitModule(MachOModule& module) { };
+ virtual void VisitSegment(MachOModule& module, const segment_command_64& segment) { };
+ virtual void VisitSection(MachOModule& module, const section_64& section) { };
+ virtual bool ReadMemory(void* address, void* buffer, size_t size) = 0;
+ virtual void Trace(const char* format, ...) { };
+ virtual void TraceVerbose(const char* format, ...) { };
+};
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>..\..\SOS\dbgutil;..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src\shared;$(RepoRoot)src\shared\inc;$(RepoRoot)src\shared\pal\prebuilt\inc;$(RepoRoot)src\shared\pal\inc;$(RepoRoot)src\shared\pal\inc\rt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
<AssemblerListingLocation>Debug/</AssemblerListingLocation>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ObjectFileName>$(IntDir)</ObjectFileName>
+ <BrowseInformation>true</BrowseInformation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Lib>
<AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
</Lib>
+ <Bscmake>
+ <PreserveSbr>true</PreserveSbr>
+ </Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
<ClCompile>
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
-set( CORGUIDS_IDL_SOURCES
+set(CORGUIDS_IDL_SOURCES
+ metahost.idl
cordebug.idl
xcordebug.idl
clrdata.idl
VER_NEED_CURRENT = 1
};
+/* Note section contents. Each entry in the note section begins with
+ a header of a fixed form. */
+
+typedef struct
+{
+ Elf32_Word n_namesz; /* Length of the note's name. */
+ Elf32_Word n_descsz; /* Length of the note's descriptor. */
+ Elf32_Word n_type; /* Type of the note. */
+} Elf32_Nhdr;
+
+typedef struct
+{
+ Elf64_Word n_namesz; /* Length of the note's name. */
+ Elf64_Word n_descsz; /* Length of the note's descriptor. */
+ Elf64_Word n_type; /* Type of the note. */
+} Elf64_Nhdr;
+
+/* Known names of notes. */
+
+/* Note entries for GNU systems have this name. */
+#define ELF_NOTE_GNU "GNU"
+
+/* Build ID bits as generated by ld --build-id.
+ The descriptor consists of any nonzero number of bytes. */
+#define NT_GNU_BUILD_ID 3
#endif
// IID ICLRDebuggingLibraryProvider2 interface : uuid{E04E2FF1-DCFD-45D5-BCD1-16FFF2FAF7BA}
cpp_quote("EXTERN_GUID(IID_ICLRDebuggingLibraryProvider2, 0xE04E2FF1, 0xDCFD, 0x45D5, 0xBC, 0xD1, 0x16, 0xFF, 0xF2, 0xFA, 0xF7, 0xBA);")
+// IID ICLRDebuggingLibraryProvider3 interface : uuid{DE3AAB18-46A0-48B4-BF0D-2C336E69EA1B}
+cpp_quote("EXTERN_GUID(IID_ICLRDebuggingLibraryProvider3, 0xde3aab18, 0x46a0, 0x48b4, 0xbf, 0xd, 0x2c, 0x33, 0x6e, 0x69, 0xea, 0x1b);")
+
// For use in ICLRMetaHost::RequestRuntimeLoadedNotification
interface ICLRRuntimeInfo;
CallbackThreadSetFnPtr pfnCallbackThreadSet,
CallbackThreadUnsetFnPtr pfnCallbackThreadUnset);
+typedef enum
+{
+ Unknown = 0,
+ Identity = 1,
+ Runtime = 2,
+} LIBRARY_PROVIDER_INDEX_TYPE;
+
/**************************************************************************************
** ICLRMetaHost **
** Activated using mscoree!CLRCreateInstance. Does not do any policy decisions, get **
[out] LPWSTR* ppResolvedModulePath);
}
+ /**************************************************************************************
+ ** ICLRDebuggingLibraryProvider3 **
+ ** Implemented by API user **
+ ** This interface allows the debugger to provide module paths which are needed for **
+ ** debugging a particular CLR such as mscordbi and mscordacwks. **
+ **************************************************************************************/
+[
+ uuid(DE3AAB18-46A0-48B4-BF0D-2C336E69EA1B),
+ version(1.0),
+ helpstring("CLR debugging LibraryProvider callback interface"),
+ local
+]
+interface ICLRDebuggingLibraryProvider3 : IUnknown
+{
+ /**********************************************************************************
+ ** The goal of this method is to allow the debugger to provide a module path **
+ ** which is needed for debugging. The debugger may use any available means to **
+ ** locate and/or procure the module. See the security note below for important **
+ ** information about implementing this method securely. **
+ ** Arguments: **
+ ** pwzFileName - The name of the module being requested **
+ ** **
+ ** pwszRuntimeModule - The runtime module path or the module path of the **
+ ** single-file app. NULL when called from the **
+ ** OpenVirtualProcess API. **
+ ** **
+ ** indexType - The type of time stamp/file size is either the identity of the **
+ ** requested module or the runtime (coreclr) module's. **
+ ** **
+ ** dwTimeStamp - The date time stamp stored in the COFF file header of PE files **
+ ** **
+ ** dwSizeOfImage - The SizeOfImage field stored in the COFF optional file header **
+ ** of PE files **
+ ** **
+ ** ppResolvedModulePath - Where *ppResolvedModulePath is a null terminated **
+ ** path to the module dll. On Windows it should be **
+ ** allocated with CoTaskMemAlloc. On Unix it should be **
+ ** allocated with malloc. Failure leave it untouched. See **
+ ** security note below! **
+ ** **
+ ** Return value - S_OK if the module was provided, or any other convenient **
+ ** error HRESULT if the module could not be provided **
+ ** **
+ ** !!!!!!!!!!!!!! **
+ ** SECURITY NOTE: Anything the caller would not be willing to execute itself, it **
+ ** should not provide to the this API call **
+ ***********************************************************************************/
+ HRESULT ProvideWindowsLibrary(
+ [in] const WCHAR* pwszFileName,
+ [in] const WCHAR* pwszRuntimeModule,
+ [in] LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ [in] DWORD dwTimestamp,
+ [in] DWORD dwSizeOfImage,
+ [out] LPWSTR* ppResolvedModulePath);
+
+ /**********************************************************************************
+ ** The goal of this method is to allow the debugger to provide a module path **
+ ** which is needed for debugging. The debugger may use any available means to **
+ ** locate and/or procure the module. See the security note below for important **
+ ** information about implementing this method securely. **
+ ** Arguments: **
+ ** pwszFileName - The name of the module being requested **
+ ** **
+ ** pwszRuntimeModule - The runtime module path or the module path of the **
+ ** single-file app. NULL when called from the **
+ ** OpenVirtualProcess API. **
+ ** **
+ ** indexType - The type of the build id either the identity of the requested **
+ ** module or the runtime (coreclr) module's. Normally it will **
+ ** be the identity index type and the runtime index for Linux **
+ ** cross-DAC on Windows or for non-singlefile apps on Linux/MacOS. **
+ ** **
+ ** pbBuildId - The Linux or MacOS module build id **
+ ** **
+ ** iBuildIdSize - The number of bytes in the build id **
+ ** **
+ ** ppResolvedModulePath - Where *ppResolvedModulePath is a null terminated **
+ ** path to the module dll. On Windows it should be **
+ ** allocated with CoTaskMemAlloc. On Unix it should be **
+ ** allocated with malloc. Failure leave it untouched. See **
+ ** security note below! **
+ ** **
+ ** Return value - S_OK if the module was provided, or any other convenient **
+ ** error HRESULT if the module could not be provided **
+ ** **
+ ** !!!!!!!!!!!!!! **
+ ** SECURITY NOTE: Anything the caller would not be willing to execute itself, it **
+ ** should not provide to the this API call **
+ ***********************************************************************************/
+ HRESULT ProvideUnixLibrary(
+ [in] const WCHAR* pwszFileName,
+ [in] const WCHAR* pwszRuntimeModule,
+ [in] LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ [in] BYTE* pbBuildId,
+ [in] int iBuildIdSize,
+ [out] LPWSTR* ppResolvedModulePath);
+}
+
/**************************************************************************************
** ICLRDebugging **
** Activated using mscoree!CLRCreateInstance. **
// - Update the logic in ClrDataAccess::EnumMemCLRMainModuleInfo to ensure all needed state is in the dump.
typedef struct _RuntimeInfo
{
- const char Signature[18];
+ char Signature[18];
int Version;
- const SYMBOL_INDEX RuntimeModuleIndex[24];
- const SYMBOL_INDEX DacModuleIndex[24];
- const SYMBOL_INDEX DbiModuleIndex[24];
+ SYMBOL_INDEX RuntimeModuleIndex[24];
+ SYMBOL_INDEX DacModuleIndex[24];
+ SYMBOL_INDEX DbiModuleIndex[24];
} RuntimeInfo;
extern RuntimeInfo DotNetRuntimeInfo;
int
PALAPI
PAL_InitializeDLL();
-typedef VOID (*PPAL_STARTUP_CALLBACK)(
- char *modulePath,
+
+typedef bool (*PPAL_STARTUP_CALLBACK)(
+ const char *modulePath,
HMODULE hModule,
PVOID parameter);
--- /dev/null
+
+
+/* this ALWAYS GENERATED file contains the IIDs and CLSIDs */
+
+/* link this file in with the server and any clients */
+
+
+ /* File created by MIDL compiler version 8.01.0622 */
+/* Compiler settings for metahost.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622
+ 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 */
+
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+#include <rpc.h>
+#include <rpcndr.h>
+
+#ifdef _MIDL_USE_GUIDDEF_
+
+#ifndef INITGUID
+#define INITGUID
+#include <guiddef.h>
+#undef INITGUID
+#else
+#include <guiddef.h>
+#endif
+
+#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
+ DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8)
+
+#else // !_MIDL_USE_GUIDDEF_
+
+#ifndef __IID_DEFINED__
+#define __IID_DEFINED__
+
+typedef struct _IID
+{
+ unsigned long x;
+ unsigned short s1;
+ unsigned short s2;
+ unsigned char c[8];
+} IID;
+
+#endif // __IID_DEFINED__
+
+#ifndef CLSID_DEFINED
+#define CLSID_DEFINED
+typedef IID CLSID;
+#endif // CLSID_DEFINED
+
+#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
+ EXTERN_C __declspec(selectany) const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
+
+#endif // !_MIDL_USE_GUIDDEF_
+
+MIDL_DEFINE_GUID(IID, IID_ICLRMetaHost,0xD332DB9E,0xB9B3,0x4125,0x82,0x07,0xA1,0x48,0x84,0xF5,0x32,0x16);
+
+
+MIDL_DEFINE_GUID(IID, IID_ICLRDebuggingLibraryProvider,0x3151C08D,0x4D09,0x4f9b,0x88,0x38,0x28,0x80,0xBF,0x18,0xFE,0x51);
+
+
+MIDL_DEFINE_GUID(IID, IID_ICLRDebuggingLibraryProvider2,0xE04E2FF1,0xDCFD,0x45D5,0xBC,0xD1,0x16,0xFF,0xF2,0xFA,0xF7,0xBA);
+
+
+MIDL_DEFINE_GUID(IID, IID_ICLRDebuggingLibraryProvider3,0xDE3AAB18,0x46A0,0x48B4,0xBF,0x0D,0x2C,0x33,0x6E,0x69,0xEA,0x1B);
+
+
+MIDL_DEFINE_GUID(IID, IID_ICLRDebugging,0xD28F3C5A,0x9634,0x4206,0xA5,0x09,0x47,0x75,0x52,0xEE,0xFB,0x10);
+
+
+MIDL_DEFINE_GUID(IID, IID_ICLRRuntimeInfo,0xBD39D1D2,0xBA2F,0x486a,0x89,0xB0,0xB4,0xB0,0xCB,0x46,0x68,0x91);
+
+#undef MIDL_DEFINE_GUID
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
/* File created by MIDL compiler version 8.01.0622 */
-/* at Mon Jan 18 19:14:07 2038
- */
/* Compiler settings for metahost.idl:
Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622
protocol : dce , ms_ext, c_ext, robust
#endif /* __ICLRDebuggingLibraryProvider2_FWD_DEFINED__ */
+#ifndef __ICLRDebuggingLibraryProvider3_FWD_DEFINED__
+#define __ICLRDebuggingLibraryProvider3_FWD_DEFINED__
+typedef interface ICLRDebuggingLibraryProvider3 ICLRDebuggingLibraryProvider3;
+
+#endif /* __ICLRDebuggingLibraryProvider3_FWD_DEFINED__ */
+
+
#ifndef __ICLRDebugging_FWD_DEFINED__
#define __ICLRDebugging_FWD_DEFINED__
typedef interface ICLRDebugging ICLRDebugging;
EXTERN_GUID(IID_ICLRRuntimeInfo, 0xBD39D1D2, 0xBA2F, 0x486a, 0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91);
EXTERN_GUID(IID_ICLRDebuggingLibraryProvider, 0x3151c08d, 0x4d09, 0x4f9b, 0x88, 0x38, 0x28, 0x80, 0xbf, 0x18, 0xfe, 0x51);
EXTERN_GUID(IID_ICLRDebuggingLibraryProvider2, 0xE04E2FF1, 0xDCFD, 0x45D5, 0xBC, 0xD1, 0x16, 0xFF, 0xF2, 0xFA, 0xF7, 0xBA);
+EXTERN_GUID(IID_ICLRDebuggingLibraryProvider3, 0xde3aab18, 0x46a0, 0x48b4, 0xbf, 0xd, 0x2c, 0x33, 0x6e, 0x69, 0xea, 0x1b);
typedef HRESULT ( __stdcall *CallbackThreadSetFnPtr )( void);
CallbackThreadSetFnPtr pfnCallbackThreadSet,
CallbackThreadUnsetFnPtr pfnCallbackThreadUnset);
+typedef /* [public][public][public] */
+enum __MIDL___MIDL_itf_metahost_0000_0000_0001
+ {
+ Unknown = 0,
+ Identity = 1,
+ Runtime = 2
+ } LIBRARY_PROVIDER_INDEX_TYPE;
+
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0000_v0_0_c_ifspec;
#endif /* __ICLRDebuggingLibraryProvider2_INTERFACE_DEFINED__ */
+#ifndef __ICLRDebuggingLibraryProvider3_INTERFACE_DEFINED__
+#define __ICLRDebuggingLibraryProvider3_INTERFACE_DEFINED__
+
+/* interface ICLRDebuggingLibraryProvider3 */
+/* [object][local][helpstring][version][uuid] */
+
+
+EXTERN_C const IID IID_ICLRDebuggingLibraryProvider3;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("DE3AAB18-46A0-48B4-BF0D-2C336E69EA1B")
+ ICLRDebuggingLibraryProvider3 : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE ProvideWindowsLibrary(
+ /* [in] */ const WCHAR *pwszFileName,
+ /* [in] */ const WCHAR *pwszRuntimeModule,
+ /* [in] */ LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ /* [in] */ DWORD dwTimestamp,
+ /* [in] */ DWORD dwSizeOfImage,
+ /* [out] */ LPWSTR *ppResolvedModulePath) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ProvideUnixLibrary(
+ /* [in] */ const WCHAR *pwszFileName,
+ /* [in] */ const WCHAR *pwszRuntimeModule,
+ /* [in] */ LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ /* [in] */ BYTE *pbBuildId,
+ /* [in] */ int iBuildIdSize,
+ /* [out] */ LPWSTR *ppResolvedModulePath) = 0;
+
+ };
+
+
+#else /* C style interface */
+
+ typedef struct ICLRDebuggingLibraryProvider3Vtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ ICLRDebuggingLibraryProvider3 * This,
+ /* [in] */ REFIID riid,
+ /* [annotation][iid_is][out] */
+ _COM_Outptr_ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ ICLRDebuggingLibraryProvider3 * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ ICLRDebuggingLibraryProvider3 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *ProvideWindowsLibrary )(
+ ICLRDebuggingLibraryProvider3 * This,
+ /* [in] */ const WCHAR *pwszFileName,
+ /* [in] */ const WCHAR *pwszRuntimeModule,
+ /* [in] */ LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ /* [in] */ DWORD dwTimestamp,
+ /* [in] */ DWORD dwSizeOfImage,
+ /* [out] */ LPWSTR *ppResolvedModulePath);
+
+ HRESULT ( STDMETHODCALLTYPE *ProvideUnixLibrary )(
+ ICLRDebuggingLibraryProvider3 * This,
+ /* [in] */ const WCHAR *pwszFileName,
+ /* [in] */ const WCHAR *pwszRuntimeModule,
+ /* [in] */ LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ /* [in] */ BYTE *pbBuildId,
+ /* [in] */ int iBuildIdSize,
+ /* [out] */ LPWSTR *ppResolvedModulePath);
+
+ END_INTERFACE
+ } ICLRDebuggingLibraryProvider3Vtbl;
+
+ interface ICLRDebuggingLibraryProvider3
+ {
+ CONST_VTBL struct ICLRDebuggingLibraryProvider3Vtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define ICLRDebuggingLibraryProvider3_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define ICLRDebuggingLibraryProvider3_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define ICLRDebuggingLibraryProvider3_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define ICLRDebuggingLibraryProvider3_ProvideWindowsLibrary(This,pwszFileName,pwszRuntimeModule,indexType,dwTimestamp,dwSizeOfImage,ppResolvedModulePath) \
+ ( (This)->lpVtbl -> ProvideWindowsLibrary(This,pwszFileName,pwszRuntimeModule,indexType,dwTimestamp,dwSizeOfImage,ppResolvedModulePath) )
+
+#define ICLRDebuggingLibraryProvider3_ProvideUnixLibrary(This,pwszFileName,pwszRuntimeModule,indexType,pbBuildId,iBuildIdSize,ppResolvedModulePath) \
+ ( (This)->lpVtbl -> ProvideUnixLibrary(This,pwszFileName,pwszRuntimeModule,indexType,pbBuildId,iBuildIdSize,ppResolvedModulePath) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __ICLRDebuggingLibraryProvider3_INTERFACE_DEFINED__ */
+
+
#ifndef __ICLRDebugging_INTERFACE_DEFINED__
#define __ICLRDebugging_INTERFACE_DEFINED__
//
struct ProcessModules
{
- ProcessModules *Next;
- PVOID BaseAddress;
- CHAR Name[0];
+ ProcessModules* _next;
+ PVOID _baseAddress;
+ PVOID _minimumAddress;
+ CHAR _name[0];
+
+ ProcessModules* GetNext() const { return _next; }
+ PVOID GetBaseAddress() const { return _baseAddress == 0 ? _minimumAddress : _baseAddress; }
+ const CHAR* GetName() const { return _name; }
};
//
listHead = CreateProcessModules(m_processId, &count);
if (listHead == NULL)
{
- TRACE("CreateProcessModules failed for pid %d\n", m_processId);
+ ERROR("CreateProcessModules failed for pid %d\n", m_processId);
pe = ERROR_INVALID_PARAMETER;
goto exit;
}
- for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->GetNext())
{
- if (IsCoreClrModule(entry->Name))
+ bool found = false;
+
+ PAL_CPP_TRY
{
- PAL_CPP_TRY
- {
- TRACE("InvokeStartupCallback executing callback %p %s\n", entry->BaseAddress, entry->Name);
- m_callback(entry->Name, entry->BaseAddress, m_parameter);
- }
- PAL_CPP_CATCH_ALL
- {
- }
- PAL_CPP_ENDTRY
+ found = m_callback(entry->GetName(), entry->GetBaseAddress(), m_parameter);
+ }
+ PAL_CPP_CATCH_ALL
+ {
+ ERROR("InvokeStartupCallback exception in callback %p %s\n", entry->GetBaseAddress(), entry->GetName());
+ pe = ERROR_ACCESS_DENIED;
+ goto exit;
+ }
+ PAL_CPP_ENDTRY
- // Currently only the first coreclr module in a process is supported
+ if (found)
+ {
+ TRACE("InvokeStartupCallback found module %p %s\n", entry->GetBaseAddress(), entry->GetName());
break;
}
}
ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count);
if (listHead != NULL)
{
- for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->GetNext())
{
if (cb <= 0)
{
break;
}
cb -= sizeof(HMODULE);
- *lphModule = (HMODULE)entry->BaseAddress;
+ *lphModule = (HMODULE)entry->GetBaseAddress();
lphModule++;
}
}
ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count);
if (listHead != NULL)
{
- for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->GetNext())
{
- if ((HMODULE)entry->BaseAddress == hModule)
+ if ((HMODULE)entry->GetBaseAddress() == hModule)
{
// Convert CHAR string into WCHAR string
- result = MultiByteToWideChar(CP_ACP, 0, entry->Name, -1, lpFilename, nSize);
+ result = MultiByteToWideChar(CP_ACP, 0, entry->GetName(), -1, lpFilename, nSize);
break;
}
}
const char *moduleName = rwpi.prp_vip.vip_path;
bool dup = false;
- for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->GetNext())
{
- if (strcmp(moduleName, entry->Name) == 0)
+ if (strcmp(moduleName, entry->_name) == 0)
{
dup = true;
break;
count = 0;
break; // no memory
}
- memcpy_s(entry->Name, cbModuleName, moduleName, cbModuleName);
- entry->BaseAddress = (void *)rwpi.prp_prinfo.pri_address;
- entry->Next = listHead;
+ memcpy_s(entry->_name, cbModuleName, moduleName, cbModuleName);
+ entry->_minimumAddress = 0;
+ entry->_baseAddress = (void *)rwpi.prp_prinfo.pri_address;
+ entry->_next = listHead;
listHead = entry;
count++;
}
int devHi, devLo, inode;
char moduleName[PATH_MAX];
- if (sscanf_s(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName, ARRAY_SIZE(moduleName)) == 7)
+ if (sscanf_s(line, "%p-%p %*[-rwxsp] %p %x:%x %d %[^\n]\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName, ARRAY_SIZE(moduleName)) == 7)
{
if (inode != 0)
{
bool dup = false;
- for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->GetNext())
{
- if (strcmp(moduleName, entry->Name) == 0)
+ if (strcmp(moduleName, entry->GetName()) == 0)
{
+ if (entry->_baseAddress == 0 && offset == 0)
+ {
+ entry->_baseAddress = startAddress;
+ }
+ entry->_minimumAddress = std::min(startAddress, entry->_minimumAddress);
dup = true;
break;
}
count = 0;
break;
}
- strcpy_s(entry->Name, cbModuleName, moduleName);
- entry->BaseAddress = startAddress;
- entry->Next = listHead;
+ strcpy_s(entry->_name, cbModuleName, moduleName);
+ entry->_baseAddress = 0;
+ entry->_minimumAddress = startAddress;
+ if (offset == 0)
+ {
+ entry->_baseAddress = startAddress;
+ }
+ entry->_next = listHead;
listHead = entry;
count++;
}
{
for (ProcessModules *entry = listHead; entry != NULL; )
{
- ProcessModules *next = entry->Next;
+ ProcessModules *next = entry->GetNext();
free(entry);
entry = next;
}
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src\shared;$(RepoRoot)src\shared\inc;$(RepoRoot)src\shared\pal\prebuilt\inc;$(RepoRoot)src\shared\pal\inc;$(RepoRoot)src\shared\pal\inc\rt;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>WIN32;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ObjectFileName>$(IntDir)</ObjectFileName>
+ <BrowseInformation>true</BrowseInformation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Lib>
<AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
</Lib>
+ <Bscmake>
+ <PreserveSbr>true</PreserveSbr>
+ </Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
<ClCompile>
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
--- /dev/null
+<Configuration>
+ <Import ConfigFile="Debugger.Tests.Common.txt" />
+ <DotNetRoot>$(RepoRootDir)/.dotnet-test</DotNetRoot>
+ <Import ConfigFile="$(DotNetRoot)/Debugger.Tests.Versions.txt" />
+
+ <RootBinDir>$(RepoRootDir)/artifacts</RootBinDir>
+ <InstallDir>$(RootBinDir)/bin/$(OS).$(TargetArchitecture).$(TargetConfiguration)</InstallDir>
+ <LogDir>$(RootBinDir)/TestResults/$(TargetConfiguration)/dbgshim.unittests_$(Timestamp)</LogDir>
+
+ <!-- Build the debuggee for this framework version but run it on latest -->
+ <BuildProjectFrameworkLatest Condition="StartsWith('$(RuntimeVersionLatest)', '6')">net6.0</BuildProjectFrameworkLatest>
+
+ <TestProduct>ProjectK</TestProduct>
+ <DebuggeeSourceRoot>$(RepoRootDir)\src\tests\DbgShim.UnitTests\Debuggees</DebuggeeSourceRoot>
+ <DebuggeeBuildProcess>cli</DebuggeeBuildProcess>
+ <DebuggeeName>SimpleDebuggee</DebuggeeName>
+
+ !-- Use the global.json SDK to build and the test SDK/runtime to run -->
+ <!--
+ <CliPath>$(RepoRootDir)/.dotnet/dotnet</CliPath>
+ -->
+ <CliPath>$(DotNetRoot)/dotnet</CliPath>
+
+ <NuGetPackageFeeds>
+ dotnet6=https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet6/nuget/v3/index.json;
+ dotnet-core=https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json;
+ dotnet-public=https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json
+ </NuGetPackageFeeds>
+
+ <!-- Run OpenVirtualProcess tests on Linux -->
+ <RunTestsLinux_x64>false</RunTestsLinux_x64>
+ <RunTestsLinux_x64 Condition="'$(OS)' == 'Linux'">true</RunTestsLinux_x64>
+ <RunTestsLinux_x64 Condition="'$(TargetArchitecture)' != 'x64'">false</RunTestsLinux_x64>
+
+ <RunTestsLinux_arm64>false</RunTestsLinux_arm64>
+ <RunTestsLinux_arm64 Condition="'$(OS)' == 'Linux'">true</RunTestsLinux_arm64>
+ <RunTestsLinux_arm64 Condition="'$(TargetArchitecture)' != 'arm64'">false</RunTestsLinux_arm64>
+
+ <Options>
+ <!-- Run OpenVirtualProcess tests on Linux -->
+ <Option>
+ <TestName>OpenVirtualProcess</TestName>
+ <RuntimeFrameworkVersion>$(RuntimeVersionLatest)</RuntimeFrameworkVersion>
+
+ <!-- Run OpenVirtualProcess tests on Linux x64 -->
+ <Options Condition="'$(RunTestsLinux_x64)' == 'true'">
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_x64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_x64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp.xml</TestDataFile>
+ </Option>
+ </Options>
+
+ <!-- Run OpenVirtualProcess tests on Linux arm64 -->
+ <Options Condition="'$(RunTestsLinux_arm64)' == 'true'">
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_arm64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_arm64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp.xml</TestDataFile>
+ </Option>
+ </Options>
+ </Option>
+
+ <!-- Run the rest of the tests on Linux/MacOS for all architectures -->
+ <Option>
+ <Options>
+ <!--
+ Single file
+ -->
+ <Option Condition="'$(RuntimeVersionLatest)' != ''">
+ <DebuggeeBuildRoot>$(RootBinDir)/Debuggees/SingleFile</DebuggeeBuildRoot>
+ <BuildProjectFramework>$(BuildProjectFrameworkLatest)</BuildProjectFramework>
+ <PublishSingleFile>true</PublishSingleFile>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
+ <RuntimeFrameworkVersion>$(SingleFileRuntimeVersion)</RuntimeFrameworkVersion>
+ </Option>
+ <!--
+ Default
+ -->
+ <Option Condition="'$(RuntimeVersionLatest)' != ''">
+ <DebuggeeBuildRoot>$(RootBinDir)/Debuggees</DebuggeeBuildRoot>
+ <BuildProjectFramework>$(BuildProjectFrameworkLatest)</BuildProjectFramework>
+ <RuntimeFrameworkVersion>$(RuntimeVersionLatest)</RuntimeFrameworkVersion>
+ </Option>
+ </Options>
+ </Option>
+ </Options>
+
+ <!-- Single-file debuggees don't need the host -->
+ <HostExe Condition="'$(PublishSingleFile)' != 'true'">$(DotNetRoot)/dotnet</HostExe>
+ <HostArgs Condition="'$(PublishSingleFile)' != 'true'">--fx-version $(RuntimeFrameworkVersion)</HostArgs>
+
+ <RuntimeModuleDir>$(DotNetRoot)/shared/Microsoft.NETCore.App/$(RuntimeFrameworkVersion)</RuntimeModuleDir>
+
+ <Options>
+ <Option Condition="'$(OS)' == 'Linux'">
+ <!--
+ <DbgShimPath>$(DotNetRoot)/shared/Microsoft.NETCore.App/$(RuntimeFrameworkVersion)/libdbgshim.so</DbgShimPath>
+ -->
+ <DbgShimPath>$(InstallDir)/libdbgshim.so</DbgShimPath>
+ <RuntimeModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)/libcoreclr.so</RuntimeModulePath>
+ <DbiModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)/libmscordbi.so</DbiModulePath>
+ <DacModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)/libmscordaccore.so</DacModulePath>
+ </Option>
+ <Option Condition="'$(OS)' == 'OSX'">
+ <!--
+ <DbgShimPath>$(DotNetRoot)/shared/Microsoft.NETCore.App/$(RuntimeFrameworkVersion)/libdbgshim.dylib</DbgShimPath>
+ -->
+ <DbgShimPath>$(InstallDir)/libdbgshim.dylib</DbgShimPath>
+ <RuntimeModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)/libcoreclr.dylib</RuntimeModulePath>
+ <DbiModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)/libmscordbi.dylib</DbiModulePath>
+ <DacModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)/libmscordaccore.dylib</DacModulePath>
+ </Option>
+ </Options>
+</Configuration>
--- /dev/null
+<Configuration>
+ <Import ConfigFile="Debugger.Tests.Common.txt" />
+ <DotNetRoot Condition="'$(TargetArchitecture)' != 'x86'">$(RepoRootDir)\.dotnet-test</DotNetRoot>
+ <DotNetRoot Condition="'$(TargetArchitecture)' == 'x86'">$(RepoRootDir)\.dotnet-test\x86</DotNetRoot>
+ <Import ConfigFile="$(DotNetRoot)\Debugger.Tests.Versions.txt" />
+
+ <RootBinDir>$(RepoRootDir)\artifacts</RootBinDir>
+ <InstallDir>$(RootBinDir)\bin\Windows_NT.$(TargetArchitecture).$(TargetConfiguration)</InstallDir>
+ <LogDir>$(RootBinDir)\TestResults\$(TargetConfiguration)\dbgshim.unittests_$(Timestamp)</LogDir>
+
+ <!-- Build the debuggee for this framework version but run it on latest -->
+ <BuildProjectFrameworkLatest Condition="StartsWith('$(RuntimeVersionLatest)', '6')">net6.0</BuildProjectFrameworkLatest>
+
+ <TestProduct>ProjectK</TestProduct>
+ <DebuggeeSourceRoot>$(RepoRootDir)\src\tests\DbgShim.UnitTests\Debuggees</DebuggeeSourceRoot>
+ <DebuggeeBuildProcess>cli</DebuggeeBuildProcess>
+ <DebuggeeName>SimpleDebuggee</DebuggeeName>
+
+ <CliPath>$(DotNetRoot)\dotnet.exe</CliPath>
+
+ <NuGetPackageFeeds>
+ dotnet6=https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet6/nuget/v3/index.json;
+ dotnet-core=https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json;
+ dotnet-public=https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json
+ </NuGetPackageFeeds>
+
+ <RunTestsWindows_x64>true</RunTestsWindows_x64>
+ <RunTestsWindows_x64 Condition="'$(TargetArchitecture)' != 'x64'">false</RunTestsWindows_x64>
+
+ <RunTestsWindows_x86>true</RunTestsWindows_x86>
+ <RunTestsWindows_x86 Condition="'$(TargetArchitecture)' != 'x86'">false</RunTestsWindows_x86>
+
+ <Options>
+ <!-- Run OpenVirtualProcess tests on Windows -->
+ <Option>
+ <TestName>OpenVirtualProcess</TestName>
+ <RuntimeFrameworkVersion>$(RuntimeVersionLatest)</RuntimeFrameworkVersion>
+
+ <!-- Run OpenVirtualProcess tests on Windows x64 -->
+ <Options Condition="'$(RunTestsWindows_x64)' == 'true'">
+ <Option>
+ <DumpFile>$(Package_TestAssets_Windows_x64_6_0)\DivZero\SOS.DivZero.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Windows_x64_6_0)\DivZero\SOS.DivZero.Heap.dmp.xml</TestDataFile>
+ </Option>
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_x64_6_0)\WebApp3\SOS.WebApp3.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_x64_6_0)\WebApp3\SOS.WebApp3.Heap.dmp.xml</TestDataFile>
+ </Option>
+ </Options>
+
+ <!-- Run OpenVirtualProcess tests on Windows x86 -->
+ <Options Condition="'$(RunTestsWindows_x86)' == 'true'">
+ <Option>
+ <DumpFile>$(Package_TestAssets_Windows_x86_6_0)\DivZero\SOS.DivZero.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Windows_x86_6_0)\DivZero\SOS.DivZero.Heap.dmp.xml</TestDataFile>
+ </Option>
+ </Options>
+ </Option>
+
+ <!-- Run the rest of the tests on Windows for all architectures -->
+ <Option>
+ <Options>
+ <!--
+ Single file
+ -->
+ <Option Condition="'$(RuntimeVersionLatest)' != ''">
+ <DebuggeeBuildRoot>$(RootBinDir)\Debuggees\SingleFile</DebuggeeBuildRoot>
+ <BuildProjectFramework>$(BuildProjectFrameworkLatest)</BuildProjectFramework>
+ <PublishSingleFile>true</PublishSingleFile>
+ <BuildProjectRuntime>$(TargetRid)</BuildProjectRuntime>
+ <RuntimeFrameworkVersion>$(SingleFileRuntimeVersion)</RuntimeFrameworkVersion>
+ </Option>
+ <!--
+ Default
+ -->
+ <Option Condition="'$(RuntimeVersionLatest)' != ''">
+ <DebuggeeBuildRoot>$(RootBinDir)\Debuggees</DebuggeeBuildRoot>
+ <BuildProjectFramework>$(BuildProjectFrameworkLatest)</BuildProjectFramework>
+ <RuntimeFrameworkVersion>$(RuntimeVersionLatest)</RuntimeFrameworkVersion>
+ </Option>
+ </Options>
+ </Option>
+ </Options>
+
+ <!-- Single-file debuggees don't need the host -->
+ <HostExe Condition="'$(PublishSingleFile)' != 'true'">$(DotNetRoot)\dotnet.exe</HostExe>
+ <HostArgs Condition="'$(PublishSingleFile)' != 'true'">--fx-version $(RuntimeFrameworkVersion)</HostArgs>
+
+ <RuntimeModuleDir>$(DotNetRoot)\shared\Microsoft.NETCore.App\$(RuntimeFrameworkVersion)</RuntimeModuleDir>
+
+ <!--
+ <DbgShimPath>$(DotNetRoot)\shared\Microsoft.NETCore.App\$(RuntimeFrameworkVersion)\dbgshim.dll</DbgShimPath>
+ -->
+ <DbgShimPath>$(InstallDir)\dbgshim.dll</DbgShimPath>
+ <RuntimeModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)\coreclr.dll</RuntimeModulePath>
+ <DbiModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)\mscordbi.dll</DbiModulePath>
+ <DacModulePath Condition="'$(RuntimeModuleDir)' != ''">$(RuntimeModuleDir)\mscordaccore.dll</DacModulePath>
+</Configuration>
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <DbgShimConfigFileName>$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt</DbgShimConfigFileName>
+ <TestAssetsVersion>1.0.257801</TestAssetsVersion>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Remove="Debuggees\SimpleDebuggee\SimpleDebuggee.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.TestHelpers\Microsoft.Diagnostics.TestHelpers.csproj" />
+ <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\SOS\SOS.Hosting\SOS.Hosting.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Content Include="$(MSBuildThisFileDirectory)ConfigFiles\Windows\Debugger.Tests.Config.txt" Condition="'$(OS)' == 'Windows_NT'">
+ <Link>Debugger.Tests.Config.txt</Link>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ <Content Include="$(MSBuildThisFileDirectory)ConfigFiles\Unix\Debugger.Tests.Config.txt" Condition="$(OS) == 'Unix'">
+ <Link>Debugger.Tests.Config.txt</Link>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+
+ <ItemGroup Condition="'$(OS)' == 'Windows_NT'">
+ <PackageDownload Include="TestAssets.Windows.x64.6.0" Version="[$(TestAssetsVersion)]" />
+ <PackageDownload Include="TestAssets.Windows.x86.6.0" Version="[$(TestAssetsVersion)]" />
+ </ItemGroup>
+
+ <ItemGroup Condition="'$(OS)' == 'Unix' OR '$(OS)' == 'Windows_NT'">
+ <PackageDownload Include="TestAssets.Linux.x64.6.0" Version="[$(TestAssetsVersion)]" />
+ <PackageDownload Include="TestAssets.Linux.arm64.6.0" Version="[$(TestAssetsVersion)]" />
+ </ItemGroup>
+
+ <!--
+ Windows test configuration file
+ -->
+ <ItemGroup Condition="'$(OS)' == 'Windows_NT'">
+ <ConfigFileEntries Include="Windows_NT">
+ <ConfigFileEntry>
+<![CDATA[
+<Configuration>
+ <TargetConfiguration>$(Configuration)</TargetConfiguration>
+ <RepoRootDir>$(RepoRoot)</RepoRootDir>
+ <Package_TestAssets_Windows_x64_6_0>$(NuGetPackageRoot)testassets.windows.x64.6.0\$(TestAssetsVersion)\content</Package_TestAssets_Windows_x64_6_0>
+ <Package_TestAssets_Windows_x86_6_0>$(NuGetPackageRoot)testassets.windows.x86.6.0\$(TestAssetsVersion)\content</Package_TestAssets_Windows_x86_6_0>
+ <Package_TestAssets_Linux_x64_6_0>$(NuGetPackageRoot)testassets.linux.x64.6.0\$(TestAssetsVersion)\content</Package_TestAssets_Linux_x64_6_0>
+ <Package_TestAssets_Linux_arm64_6_0>$(NuGetPackageRoot)testassets.linux.arm64.6.0\$(TestAssetsVersion)\content</Package_TestAssets_Linux_arm64_6_0>
+</Configuration>
+]]>
+ </ConfigFileEntry>
+ </ConfigFileEntries>
+ </ItemGroup>
+
+ <!--
+ Linux/MacOS test configuration file
+ -->
+ <ItemGroup Condition="'$(OS)' == 'Unix'">
+ <ConfigFileEntries Include="Unix">
+ <ConfigFileEntry>
+<![CDATA[
+<Configuration>
+ <TargetConfiguration>$(Configuration)</TargetConfiguration>
+ <RepoRootDir>$(RepoRoot)</RepoRootDir>
+ <Package_TestAssets_Linux_x64_6_0>$(NuGetPackageRoot)testassets.linux.x64.6.0/$(TestAssetsVersion)/content</Package_TestAssets_Linux_x64_6_0>
+ <Package_TestAssets_Linux_arm64_6_0>$(NuGetPackageRoot)testassets.linux.arm64.6.0/$(TestAssetsVersion)/content</Package_TestAssets_Linux_arm64_6_0>
+</Configuration>
+]]>
+ </ConfigFileEntry>
+ </ConfigFileEntries>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Debuggees\SimpleDebuggee\SimpleDebuggee.cs" />
+ </ItemGroup>
+
+ <Target Name="DbgShimWriteTestConfigFile" Outputs="$(DbgShimConfigFileName)" AfterTargets="Build">
+ <PropertyGroup>
+ <TestConfigFileLines>@(ConfigFileEntries->Metadata("ConfigFileEntry"))</TestConfigFileLines>
+ </PropertyGroup>
+ <WriteLinesToFile File="$(DbgShimConfigFileName)" Lines="$(TestConfigFileLines)" Overwrite="true" WriteOnlyWhenDifferent="true" />
+ <Message Importance="High" Text="Created config file $(DbgShimConfigFileName)" />
+ <ItemGroup>
+ <FileWrites Include="$(DbgShimConfigFileName)" />
+ </ItemGroup>
+ </Target>
+</Project>
--- /dev/null
+// 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 Microsoft.Diagnostics.Runtime;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Microsoft.Diagnostics
+{
+ public class DbgShimAPI
+ {
+ private static bool _initialized = false;
+
+ private static CreateProcessForLaunchDelegate _createProcessForLaunch;
+ private static CloseResumeHandleDelegate _closeResumeHandle;
+ private static ResumeProcessDelegate _resumeProcess;
+
+ private static RegisterForRuntimeStartupDelegate _registerForRuntimeStartup;
+ private static RegisterForRuntimeStartupExDelegate _registerForRuntimeStartupEx;
+ private static RegisterForRuntimeStartup3Delegate _registerForRuntimeStartup3;
+ private static UnregisterForRuntimeStartupDelegate _unregisterForRuntimeStartup;
+
+ private static EnumerateCLRsDelegate _enumerateCLRs;
+ private static CloseCLREnumerationDelegate _closeCLREnumeration;
+ private static CreateVersionStringFromModuleDelegate _createVersionStringFromModule;
+
+ private static CreateDebuggingInterfaceFromVersionDelegate _createDebuggingInterfaceFromVersion;
+ private static CreateDebuggingInterfaceFromVersionExDelegate _createDebuggingInterfaceFromVersionEx;
+ private static CreateDebuggingInterfaceFromVersion2Delegate _createDebuggingInterfaceFromVersion2;
+ private static CreateDebuggingInterfaceFromVersion3Delegate _createDebuggingInterfaceFromVersion3;
+
+ private static CLRCreateInstanceDelegate _clrCreateInstance;
+
+ private static IntPtr _dbgshimModuleHandle = IntPtr.Zero;
+
+ public const int CorDebugVersion_2_0 = 3;
+ public const int CorDebugVersion_4_0 = 4;
+
+ public static void Initialize(string dbgshimPath)
+ {
+ // check if already initialized
+ if (_initialized)
+ {
+ return;
+ }
+ if (string.IsNullOrWhiteSpace(dbgshimPath) || !File.Exists(dbgshimPath))
+ {
+ throw new ArgumentException($"Dbgshim path not set or the dbgshim at '{dbgshimPath}' does not exists");
+ }
+ _dbgshimModuleHandle = DataTarget.PlatformFunctions.LoadLibrary(dbgshimPath);
+ _createProcessForLaunch = GetDelegateFunction<CreateProcessForLaunchDelegate>("CreateProcessForLaunch");
+ _resumeProcess = GetDelegateFunction<ResumeProcessDelegate>("ResumeProcess");
+ _closeResumeHandle = GetDelegateFunction<CloseResumeHandleDelegate>("CloseResumeHandle");
+ _registerForRuntimeStartup = GetDelegateFunction<RegisterForRuntimeStartupDelegate>("RegisterForRuntimeStartup");
+ _registerForRuntimeStartupEx = GetDelegateFunction<RegisterForRuntimeStartupExDelegate>("RegisterForRuntimeStartupEx");
+ _registerForRuntimeStartup3 = GetDelegateFunction<RegisterForRuntimeStartup3Delegate>("RegisterForRuntimeStartup3", optional: true);
+ _unregisterForRuntimeStartup = GetDelegateFunction<UnregisterForRuntimeStartupDelegate>("UnregisterForRuntimeStartup");
+ _enumerateCLRs = GetDelegateFunction<EnumerateCLRsDelegate>("EnumerateCLRs");
+ _closeCLREnumeration = GetDelegateFunction<CloseCLREnumerationDelegate>("CloseCLREnumeration");
+ _createVersionStringFromModule = GetDelegateFunction<CreateVersionStringFromModuleDelegate>("CreateVersionStringFromModule");
+ _createDebuggingInterfaceFromVersion = GetDelegateFunction<CreateDebuggingInterfaceFromVersionDelegate>("CreateDebuggingInterfaceFromVersion");
+ _createDebuggingInterfaceFromVersionEx = GetDelegateFunction<CreateDebuggingInterfaceFromVersionExDelegate>("CreateDebuggingInterfaceFromVersionEx");
+ _createDebuggingInterfaceFromVersion2 = GetDelegateFunction<CreateDebuggingInterfaceFromVersion2Delegate>("CreateDebuggingInterfaceFromVersion2");
+ _createDebuggingInterfaceFromVersion3 = GetDelegateFunction<CreateDebuggingInterfaceFromVersion3Delegate>("CreateDebuggingInterfaceFromVersion3", optional: true);
+ _clrCreateInstance = GetDelegateFunction<CLRCreateInstanceDelegate>("CLRCreateInstance");
+ _initialized = true;
+ }
+
+ public static bool IsRegisterForRuntimeStartup3Supported => _registerForRuntimeStartup3 != default;
+
+ public static bool IsCreateDebuggingInterfaceFromVersion3Supported => _createDebuggingInterfaceFromVersion3 != default;
+
+ public static HResult CreateProcessForLaunch(string commandLine, bool suspendProcess, string currentDirectory, out int processId, out IntPtr resumeHandle)
+ {
+ return _createProcessForLaunch(commandLine, suspendProcess, lpEnvironment: IntPtr.Zero, currentDirectory, out processId, out resumeHandle);
+ }
+
+ public static HResult ResumeProcess(IntPtr handle) => _resumeProcess(handle);
+
+ public static HResult CloseResumeHandle(IntPtr handle) => _closeResumeHandle(handle);
+
+ public delegate void RuntimeStartupCallbackDelegate(ICorDebug cordbg, object parameter, HResult hresult);
+
+ public static HResult RegisterForRuntimeStartup(int pid, object parameter, out IntPtr unregisterToken, RuntimeStartupCallbackDelegate callback)
+ {
+ IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out IntPtr nativeParameter);
+ return _registerForRuntimeStartup((uint)pid, nativeCallback, nativeParameter, out unregisterToken);
+ }
+
+ public static HResult RegisterForRuntimeStartupEx(int pid, string applicationGroupId, object parameter, out IntPtr unregisterToken, RuntimeStartupCallbackDelegate callback)
+ {
+ IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out IntPtr nativeParameter);
+ return _registerForRuntimeStartupEx((uint)pid, applicationGroupId, nativeCallback, nativeParameter, out unregisterToken);
+ }
+
+ public static HResult RegisterForRuntimeStartup3(int pid, string applicationGroupId, object parameter, IntPtr libraryProvider, out IntPtr unregisterToken, RuntimeStartupCallbackDelegate callback)
+ {
+ if (_registerForRuntimeStartup3 == default)
+ {
+ throw new NotSupportedException("RegisterForRuntimeStartup3 not supported");
+ }
+ IntPtr nativeCallback = RuntimeStartupCallback(parameter, callback, out IntPtr nativeParameter);
+ return _registerForRuntimeStartup3((uint)pid, applicationGroupId, libraryProvider, nativeCallback, nativeParameter, out unregisterToken);
+ }
+
+ private delegate void NativeRuntimeStartupCallbackDelegate(IntPtr cordbg, IntPtr parameter, HResult hresult);
+
+ private static IntPtr RuntimeStartupCallback(object parameter, RuntimeStartupCallbackDelegate callback, out IntPtr nativeParameter)
+ {
+ NativeRuntimeStartupCallbackDelegate native = (IntPtr cordbg, IntPtr param, HResult hresult) => {
+ GCHandle gch = GCHandle.FromIntPtr(param);
+ callback(ICorDebug.Create(cordbg), gch.Target, hresult);
+ gch.Free();
+ };
+ GCHandle gchParameter = GCHandle.Alloc(parameter);
+ nativeParameter = GCHandle.ToIntPtr(gchParameter);
+ return Marshal.GetFunctionPointerForDelegate(native);
+ }
+
+ public static HResult UnregisterForRuntimeStartup(IntPtr unregisterToken) => _unregisterForRuntimeStartup(unregisterToken);
+
+ private const int HRESULT_ERROR_PARTIAL_COPY = unchecked((int)0x8007012b);
+ private const int HRESULT_ERROR_BAD_LENGTH = unchecked((int)0x80070018);
+
+ public static unsafe HResult EnumerateCLRs(int processId, Action<IntPtr[], string[]> callback)
+ {
+ HResult hr = HResult.S_OK;
+
+ int numRetries = 0;
+ while (numRetries < 25)
+ {
+ hr = _enumerateCLRs(processId, out IntPtr* handleArray, out char** stringArray, out int arrayLength);
+ if (hr == HResult.S_OK)
+ {
+ IntPtr[] handles = new IntPtr[arrayLength];
+ string[] moduleNames = new string[arrayLength];
+ try
+ {
+ for (int i = 0; i < arrayLength; i++)
+ {
+ handles[i] = handleArray[i];
+ moduleNames[i] = new string(stringArray[i]);
+ }
+ callback(handles, moduleNames);
+ }
+ finally
+ {
+ hr = _closeCLREnumeration(handleArray, stringArray, arrayLength);
+ }
+ break;
+ }
+ // EnumerateCLRs uses the OS API CreateToolhelp32Snapshot which can return ERROR_BAD_LENGTH or
+ // ERROR_PARTIAL_COPY. If we get either of those, we try wait 1/10th of a second try again (that
+ // is the recommendation of the OS API owners).
+ if ((hr != HRESULT_ERROR_PARTIAL_COPY) && (hr != HRESULT_ERROR_BAD_LENGTH))
+ {
+ break;
+ }
+ Thread.Sleep(100);
+ numRetries++;
+ }
+ return hr;
+ }
+
+ private const int HRESULT_ERROR_INSUFFICIENT_BUFFER = unchecked((int)0x8007007a);
+
+ public static unsafe HResult CreateVersionStringFromModule(int processId, string modulePath, out string versionString)
+ {
+ versionString = null;
+ HResult hr = _createVersionStringFromModule(processId, modulePath, null, 0, out int versionStringSize);
+ if (hr == HRESULT_ERROR_INSUFFICIENT_BUFFER)
+ {
+ char[] versionBuffer = new char[versionStringSize];
+ fixed (char* versionBufferPtr = versionBuffer)
+ {
+ hr = _createVersionStringFromModule(processId, modulePath, versionBufferPtr, versionStringSize, out versionStringSize);
+ if (hr == 0)
+ {
+ versionString = new string(versionBuffer);
+ }
+ }
+ }
+ return hr;
+ }
+
+ public static HResult CreateDebuggingInterfaceFromVersion(string versionString, out ICorDebug cordbg)
+ {
+ HResult hr = _createDebuggingInterfaceFromVersion(versionString, out IntPtr punk);
+ cordbg = ICorDebug.Create(punk);
+ return hr;
+ }
+
+ public static HResult CreateDebuggingInterfaceFromVersionEx(int debuggerVersion, string versionString, out ICorDebug cordbg)
+ {
+ HResult hr = _createDebuggingInterfaceFromVersionEx(debuggerVersion, versionString, out IntPtr punk);
+ cordbg = ICorDebug.Create(punk);
+ return hr;
+ }
+
+ public static HResult CreateDebuggingInterfaceFromVersion2(int debuggerVersion, string versionString, string applicationGroupId, out ICorDebug cordbg)
+ {
+ HResult hr = _createDebuggingInterfaceFromVersion2(debuggerVersion, versionString, applicationGroupId, out IntPtr punk);
+ cordbg = ICorDebug.Create(punk);
+ return hr;
+ }
+
+ public static HResult CreateDebuggingInterfaceFromVersion3(int debuggerVersion, string versionString, string applicationGroupId, IntPtr libraryProvider, out ICorDebug cordbg)
+ {
+ if (_createDebuggingInterfaceFromVersion3 == default)
+ {
+ throw new NotSupportedException("CreateDebuggingInterfaceFromVersion3 not supported");
+ }
+ HResult hr = _createDebuggingInterfaceFromVersion3(debuggerVersion, versionString, applicationGroupId, libraryProvider, out IntPtr punk);
+ cordbg = ICorDebug.Create(punk);
+ return hr;
+ }
+
+ public static HResult CLRCreateInstance(out ICLRDebugging clrDebugging)
+ {
+ HResult hr = _clrCreateInstance(ICLRDebugging.CLSID_ICLRDebugging, ICLRDebugging.IID_ICLRDebugging, out IntPtr punk);
+ clrDebugging = ICLRDebugging.Create(punk);
+ return hr;
+ }
+
+ private static T GetDelegateFunction<T>(string functionName, bool optional = false)
+ where T : Delegate
+ {
+ IntPtr functionAddress = DataTarget.PlatformFunctions.GetLibraryExport(_dbgshimModuleHandle, functionName);
+ if (functionAddress == IntPtr.Zero) {
+ if (optional)
+ {
+ return default;
+ }
+ throw new ArgumentException($"Failed to get address of dbgshim!{functionName}");
+ }
+ return (T)Marshal.GetDelegateForFunctionPointer(functionAddress, typeof(T));
+ }
+
+ #region DbgShim pinvoke delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
+ private delegate HResult CreateProcessForLaunchDelegate(
+ [MarshalAs(UnmanagedType.LPWStr)] string lpCommandLine,
+ [MarshalAs(UnmanagedType.Bool)] bool bSuspendProcess,
+ IntPtr lpEnvironment,
+ [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory,
+ out int processId,
+ out IntPtr suspendHandle);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
+ private delegate HResult ResumeProcessDelegate(
+ IntPtr handle);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
+ private delegate HResult CloseResumeHandleDelegate(
+ IntPtr handle);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private delegate HResult RegisterForRuntimeStartupDelegate(
+ uint processId,
+ IntPtr callback,
+ IntPtr parameter,
+ out IntPtr unregisterToken);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private delegate HResult RegisterForRuntimeStartupExDelegate(
+ uint processId,
+ [MarshalAs(UnmanagedType.LPWStr)] string applicationGroupId,
+ IntPtr callback,
+ IntPtr parameter,
+ out IntPtr unregisterToken);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private delegate HResult RegisterForRuntimeStartup3Delegate(
+ uint processId,
+ [MarshalAs(UnmanagedType.LPWStr)] string applicationGroupId,
+ IntPtr libraryProvider,
+ IntPtr callback,
+ IntPtr parameter,
+ out IntPtr unregisterToken);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private delegate HResult UnregisterForRuntimeStartupDelegate(
+ IntPtr unregisterToken);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult EnumerateCLRsDelegate(
+ int processId,
+ out IntPtr* handleArray,
+ out char** stringArray,
+ out int arrayLength);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult CloseCLREnumerationDelegate(
+ IntPtr* handleArray,
+ char** stringArray,
+ int arrayLength);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult CreateVersionStringFromModuleDelegate(
+ int processId,
+ [MarshalAs(UnmanagedType.LPWStr)] string moduleName,
+ char* versionString,
+ int versionStringLength,
+ out int actualVersionStringLength);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult CreateDebuggingInterfaceFromVersionDelegate(
+ [MarshalAs(UnmanagedType.LPWStr)] string versionString,
+ out IntPtr cordbg);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult CreateDebuggingInterfaceFromVersionExDelegate(
+ int debuggerVersion,
+ [MarshalAs(UnmanagedType.LPWStr)] string versionString,
+ out IntPtr cordbg);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult CreateDebuggingInterfaceFromVersion2Delegate(
+ int debuggerVersion,
+ [MarshalAs(UnmanagedType.LPWStr)] string versionString,
+ [MarshalAs(UnmanagedType.LPWStr)] string applicationGroupId,
+ out IntPtr cordbg);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult CreateDebuggingInterfaceFromVersion3Delegate(
+ int debuggerVersion,
+ [MarshalAs(UnmanagedType.LPWStr)] string versionString,
+ [MarshalAs(UnmanagedType.LPWStr)] string applicationGroupId,
+ IntPtr libraryProvider,
+ out IntPtr cordbg);
+
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ private unsafe delegate HResult CLRCreateInstanceDelegate(
+ in Guid clrsid,
+ in Guid riid,
+ out IntPtr pInterface);
+
+ #endregion
+ }
+}
--- /dev/null
+// 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 Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using Microsoft.Diagnostics.TestHelpers;
+using SOS.Hosting;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Extensions;
+
+namespace Microsoft.Diagnostics
+{
+ public class DbgShimTests : IDisposable
+ {
+ private const string ListenerName = "DbgShimTests";
+
+ public static IEnumerable<object[]> GetConfigurations(string key, string value)
+ {
+ return TestRunConfiguration.Instance.Configurations.Where((c) => key == null || c.AllSettings.GetValueOrDefault(key) == value).Select(c => new[] { c });
+ }
+
+ public static IEnumerable<object[]> Configurations => GetConfigurations("TestName", null);
+
+ private ITestOutputHelper Output { get; }
+
+ public DbgShimTests(ITestOutputHelper output)
+ {
+ Output = output;
+ LoggingListener.EnableListener(output, ListenerName);
+ }
+
+ void IDisposable.Dispose() => Trace.Listeners.Remove(ListenerName);
+
+ /// <summary>
+ /// Test RegisterForRuntimeStartup for launch
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task Launch1(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(Launch1), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: true);
+ TestRegisterForRuntimeStartup(debuggeeInfo, 1);
+
+ // Once the debuggee is resumed now wait until it starts
+ Assert.True(await debuggeeInfo.WaitForDebuggee());
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test RegisterForRuntimeStartupEx for launch
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task Launch2(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(Launch2), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: true);
+ TestRegisterForRuntimeStartup(debuggeeInfo, 2);
+
+ // Once the debuggee is resumed now wait until it starts
+ Assert.True(await debuggeeInfo.WaitForDebuggee());
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test RegisterForRuntimeStartup3 for launch
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task Launch3(TestConfiguration config)
+ {
+ if (OS.Kind == OSKind.OSX && config.PublishSingleFile)
+ {
+ throw new SkipTestException("Launch3 single-file on MacOS");
+ }
+ DbgShimAPI.Initialize(config.DbgShimPath());
+ if (!DbgShimAPI.IsRegisterForRuntimeStartup3Supported)
+ {
+ throw new SkipTestException("IsRegisterForRuntimeStartup3 not supported");
+ }
+ await RemoteInvoke(config, nameof(Launch3), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: true);
+ TestRegisterForRuntimeStartup(debuggeeInfo, 3);
+
+ // Once the debuggee is resumed now wait until it starts
+ Assert.True(await debuggeeInfo.WaitForDebuggee());
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test RegisterForRuntimeStartup for attach
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task Attach1(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(Attach1), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ TestRegisterForRuntimeStartup(debuggeeInfo, 1);
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test RegisterForRuntimeStartupEx for attach
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task Attach2(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(Attach2), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ TestRegisterForRuntimeStartup(debuggeeInfo, 2);
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test RegisterForRuntimeStartup3 for attach
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task Attach3(TestConfiguration config)
+ {
+ if (OS.Kind == OSKind.OSX && config.PublishSingleFile)
+ {
+ throw new SkipTestException("Attach3 single-file on MacOS");
+ }
+ DbgShimAPI.Initialize(config.DbgShimPath());
+ if (!DbgShimAPI.IsRegisterForRuntimeStartup3Supported)
+ {
+ throw new SkipTestException("IsRegisterForRuntimeStartup3 not supported");
+ }
+ await RemoteInvoke(config, nameof(Attach3), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ TestRegisterForRuntimeStartup(debuggeeInfo, 3);
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test EnumerateCLRs/CloseCLREnumeration
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task EnumerateCLRs(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(EnumerateCLRs), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ Trace.TraceInformation("EnumerateCLRs pid {0} START", debuggeeInfo.ProcessId);
+ HResult hr = DbgShimAPI.EnumerateCLRs(debuggeeInfo.ProcessId, (IntPtr[] continueEventHandles, string[] moduleNames) =>
+ {
+ Assert.Single(continueEventHandles);
+ Assert.Single(moduleNames);
+ for (int i = 0; i < continueEventHandles.Length; i++)
+ {
+ Trace.TraceInformation("EnumerateCLRs pid {0} {1:X16} {2}", debuggeeInfo.ProcessId, continueEventHandles[i].ToInt64(), moduleNames[i]);
+ AssertX.FileExists("ModuleFilePath", moduleNames[i], debuggeeInfo.Output);
+ }
+ });
+ AssertResult(hr);
+ Trace.TraceInformation("EnumerateCLRs pid {0} DONE", debuggeeInfo.ProcessId);
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test CreateVersionStringFromModule/CreateDebuggingInterfaceFromVersion
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task CreateDebuggingInterfaceFromVersion(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(CreateDebuggingInterfaceFromVersion), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ TestCreateDebuggingInterface(debuggeeInfo, 0);
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test CreateVersionStringFromModule/CreateDebuggingInterfaceFromVersionEx
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task CreateDebuggingInterfaceFromVersionEx(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(CreateDebuggingInterfaceFromVersionEx), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ TestCreateDebuggingInterface(debuggeeInfo, 1);
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test CreateVersionStringFromModule/CreateDebuggingInterfaceFromVersion2
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task CreateDebuggingInterfaceFromVersion2(TestConfiguration config)
+ {
+ await RemoteInvoke(config, nameof(CreateDebuggingInterfaceFromVersion2), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ TestCreateDebuggingInterface(debuggeeInfo, 2);
+ return 0;
+ });
+ }
+
+ /// <summary>
+ /// Test CreateVersionStringFromModule/CreateDebuggingInterfaceFromVersion3
+ /// </summary>
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task CreateDebuggingInterfaceFromVersion3(TestConfiguration config)
+ {
+ if (OS.Kind == OSKind.OSX && config.PublishSingleFile)
+ {
+ throw new SkipTestException("CreateDebuggingInterfaceFromVersion3 single-file on MacOS");
+ }
+ DbgShimAPI.Initialize(config.DbgShimPath());
+ if (!DbgShimAPI.IsCreateDebuggingInterfaceFromVersion3Supported)
+ {
+ throw new SkipTestException("CreateDebuggingInterfaceFromVersion3 not supported");
+ }
+ await RemoteInvoke(config, nameof(CreateDebuggingInterfaceFromVersion3), static async (string configXml) =>
+ {
+ using DebuggeeInfo debuggeeInfo = await StartDebuggee(configXml, launch: false);
+ TestCreateDebuggingInterface(debuggeeInfo, 3);
+ return 0;
+ });
+ }
+
+ [SkippableTheory, MemberData(nameof(GetConfigurations), "TestName", "OpenVirtualProcess")]
+ public async Task OpenVirtualProcess(TestConfiguration config)
+ {
+ // The current Linux test assets are not alpine/musl
+ if (OS.IsAlpine)
+ {
+ throw new SkipTestException("Not supported on Alpine Linux (musl)");
+ }
+ if (!config.AllSettings.ContainsKey("DumpFile"))
+ {
+ throw new SkipTestException("OpenVirtualProcessTest: No dump file");
+ }
+ await RemoteInvoke(config, nameof(OpenVirtualProcess), static (string configXml) =>
+ {
+ AfterInvoke(configXml, out TestConfiguration cfg, out ITestOutputHelper output);
+
+ DbgShimAPI.Initialize(cfg.DbgShimPath());
+ AssertResult(DbgShimAPI.CLRCreateInstance(out ICLRDebugging clrDebugging));
+ Assert.NotNull(clrDebugging);
+
+ TestDump testDump = new(cfg);
+ ITarget target = testDump.Target;
+ IRuntimeService runtimeService = target.Services.GetService<IRuntimeService>();
+ IRuntime runtime = runtimeService.EnumerateRuntimes().Single();
+
+ CorDebugDataTargetWrapper dataTarget = new(target.Services);
+ LibraryProviderWrapper libraryProvider = new(target.OperatingSystem, runtime.RuntimeModule.BuildId, runtime.GetDbiFilePath(), runtime.GetDacFilePath());
+ ClrDebuggingVersion maxDebuggerSupportedVersion = new()
+ {
+ StructVersion = 0,
+ Major = 4,
+ Minor = 0,
+ Build = 0,
+ Revision = 0,
+ };
+ HResult hr = clrDebugging.OpenVirtualProcess(
+ runtime.RuntimeModule.ImageBase,
+ dataTarget.ICorDebugDataTarget,
+ libraryProvider.ILibraryProvider,
+ maxDebuggerSupportedVersion,
+ in RuntimeWrapper.IID_ICorDebugProcess,
+ out IntPtr corDebugProcess,
+ out ClrDebuggingVersion version,
+ out ClrDebuggingProcessFlags flags);
+
+ AssertResult(hr);
+ Assert.NotEqual(IntPtr.Zero, corDebugProcess);
+ Assert.Equal(1, COMHelper.Release(corDebugProcess));
+ Assert.Equal(0, COMHelper.Release(corDebugProcess));
+ Assert.Equal(0, clrDebugging.Release());
+ return Task.FromResult(0);
+ });
+ }
+
+ #region Helper functions
+
+ private static async Task<DebuggeeInfo> StartDebuggee(string configXml, bool launch)
+ {
+ AfterInvoke(configXml, out TestConfiguration config, out ITestOutputHelper output);
+
+ DebuggeeInfo debuggeeInfo = new(output, config, launch);
+ string debuggeeName = config.DebuggeeName();
+
+ Assert.NotNull(debuggeeName);
+ Assert.NotNull(config.DbgShimPath());
+
+ DbgShimAPI.Initialize(config.DbgShimPath());
+
+ // Restore and build the debuggee
+ DebuggeeConfiguration debuggeeConfig = await DebuggeeCompiler.Execute(config, debuggeeName, debuggeeInfo.Output);
+
+ // Build the debuggee command line
+ StringBuilder commandLine = new();
+
+ // Get the full launch command line (includes the host if required)
+ if (!string.IsNullOrWhiteSpace(config.HostExe))
+ {
+ commandLine.Append(config.HostExe);
+ commandLine.Append(" ");
+ if (!string.IsNullOrWhiteSpace(config.HostArgs))
+ {
+ commandLine.Append(config.HostArgs);
+ commandLine.Append(" ");
+ }
+ }
+ commandLine.Append(debuggeeConfig.BinaryExePath);
+ commandLine.Append(" ");
+ commandLine.Append(debuggeeInfo.PipeName);
+
+ Trace.TraceInformation("CreateProcessForLaunch {0} {1} {2}", launch, commandLine.ToString(), debuggeeInfo.PipeName);
+ AssertResult(DbgShimAPI.CreateProcessForLaunch(commandLine.ToString(), launch, currentDirectory: null, out int processId, out IntPtr resumeHandle));
+ Assert.NotEqual(IntPtr.Zero, resumeHandle);
+ Trace.TraceInformation("CreateProcessForLaunch pid {0} {1}", processId, commandLine.ToString());
+
+ debuggeeInfo.ResumeHandle = resumeHandle;
+ debuggeeInfo.SetProcessId(processId);
+
+ // Wait for debuggee to start if attach/run
+ if (!launch)
+ {
+ Assert.True(await debuggeeInfo.WaitForDebuggee());
+ }
+ Trace.TraceInformation("CreateProcessForLaunch pid {0} DONE", processId);
+ return debuggeeInfo;
+ }
+
+ private static void TestRegisterForRuntimeStartup(DebuggeeInfo debuggeeInfo, int api)
+ {
+ TestConfiguration config = debuggeeInfo.TestConfiguration;
+ AutoResetEvent wait = new AutoResetEvent(false);
+ string applicationGroupId = null;
+ IntPtr unregisterToken = IntPtr.Zero;
+ HResult result = HResult.S_OK;
+ HResult callbackResult = HResult.S_OK;
+ Exception callbackException = null;
+ ICorDebug corDebug = null;
+
+ Trace.TraceInformation("RegisterForRuntimeStartup pid {0} launch {1} api {2} START", debuggeeInfo.ProcessId, debuggeeInfo.Launch, api);
+
+ DbgShimAPI.RuntimeStartupCallbackDelegate callback = (ICorDebug cordbg, object parameter, HResult hr) => {
+ Trace.TraceInformation("RegisterForRuntimeStartup in callback pid {0} hr {1:X}", debuggeeInfo.ProcessId, hr);
+ corDebug = cordbg;
+ callbackResult = hr;
+ try
+ {
+ // Only check the ICorDebug instance if success
+ if (hr)
+ {
+ TestICorDebug(debuggeeInfo, cordbg);
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError(ex.ToString());
+ callbackException = ex;
+ }
+ wait.Set();
+ };
+
+ switch (api)
+ {
+ case 1:
+ result = DbgShimAPI.RegisterForRuntimeStartup(debuggeeInfo.ProcessId, parameter: IntPtr.Zero, out unregisterToken, callback);
+ break;
+ case 2:
+ result = DbgShimAPI.RegisterForRuntimeStartupEx(debuggeeInfo.ProcessId, applicationGroupId, parameter: IntPtr.Zero, out unregisterToken, callback);
+ break;
+ case 3:
+ LibraryProviderWrapper libraryProvider = new(config.RuntimeModulePath(), config.DbiModulePath(), config.DacModulePath());
+ result = DbgShimAPI.RegisterForRuntimeStartup3(debuggeeInfo.ProcessId, applicationGroupId, parameter: IntPtr.Zero, libraryProvider.ILibraryProvider, out unregisterToken, callback);
+ break;
+ default:
+ throw new ArgumentException(nameof(api));
+ }
+
+ if (debuggeeInfo.Launch)
+ {
+ AssertResult(debuggeeInfo.ResumeDebuggee());
+ }
+
+ AssertResult(result);
+
+ Trace.TraceInformation("RegisterForRuntimeStartup pid {0} waiting for callback", debuggeeInfo.ProcessId);
+ Assert.True(wait.WaitOne());
+ Trace.TraceInformation("RegisterForRuntimeStartup pid {0} after callback wait", debuggeeInfo.ProcessId);
+
+ AssertResult(DbgShimAPI.UnregisterForRuntimeStartup(unregisterToken));
+ Assert.Null(callbackException);
+
+ switch (api)
+ {
+ case 1:
+ case 2:
+ // The old APIs fail on single file apps
+ Assert.Equal(!debuggeeInfo.TestConfiguration.PublishSingleFile, callbackResult);
+ break;
+ case 3:
+ // The new API should always succeed
+ AssertResult(callbackResult);
+ break;
+ }
+
+ if (callbackResult)
+ {
+ AssertResult(debuggeeInfo.WaitForCreateProcess());
+ Assert.Equal(0, corDebug.Release());
+ }
+ else
+ {
+ debuggeeInfo.Kill();
+ }
+
+ Trace.TraceInformation("RegisterForRuntimeStartup pid {0} DONE", debuggeeInfo.ProcessId);
+ }
+
+ private static void TestCreateDebuggingInterface(DebuggeeInfo debuggeeInfo, int api)
+ {
+ Trace.TraceInformation("TestCreateDebuggingInterface pid {0} api {1} START", debuggeeInfo.ProcessId, api);
+ HResult hr = DbgShimAPI.EnumerateCLRs(debuggeeInfo.ProcessId, (IntPtr[] continueEventHandles, string[] moduleNames) =>
+ {
+ TestConfiguration config = debuggeeInfo.TestConfiguration;
+ Assert.Single(continueEventHandles);
+ Assert.Single(moduleNames);
+ for (int i = 0; i < continueEventHandles.Length; i++)
+ {
+ Trace.TraceInformation("TestCreateDebuggingInterface pid {0} {1:X16} {2}", debuggeeInfo.ProcessId, continueEventHandles[i].ToInt64(), moduleNames[i]);
+ AssertX.FileExists("ModuleFilePath", moduleNames[i], debuggeeInfo.Output);
+
+ AssertResult(DbgShimAPI.CreateVersionStringFromModule(debuggeeInfo.ProcessId, moduleNames[i], out string versionString));
+ Trace.TraceInformation("TestCreateDebuggingInterface pid {0} version string {1}", debuggeeInfo.ProcessId, versionString);
+ Assert.False(string.IsNullOrWhiteSpace(versionString));
+
+ ICorDebug corDebug = null;
+ string applicationGroupId = null;
+ HResult result;
+ switch (api)
+ {
+ case 0:
+ result = DbgShimAPI.CreateDebuggingInterfaceFromVersion(versionString, out corDebug);
+ break;
+ case 1:
+ result = DbgShimAPI.CreateDebuggingInterfaceFromVersionEx(DbgShimAPI.CorDebugVersion_4_0, versionString, out corDebug);
+ break;
+ case 2:
+ result = DbgShimAPI.CreateDebuggingInterfaceFromVersion2(DbgShimAPI.CorDebugVersion_4_0, versionString, applicationGroupId, out corDebug);
+ break;
+ case 3:
+ LibraryProviderWrapper libraryProvider = new(config.RuntimeModulePath(), config.DbiModulePath(), config.DacModulePath());
+ result = DbgShimAPI.CreateDebuggingInterfaceFromVersion3(DbgShimAPI.CorDebugVersion_4_0, versionString, applicationGroupId, libraryProvider.ILibraryProvider, out corDebug);
+ break;
+ default:
+ throw new ArgumentException(nameof(api));
+ }
+
+ Trace.TraceInformation("TestCreateDebuggingInterface pid {0} after API {1} call", debuggeeInfo.ProcessId, api);
+
+ switch (api)
+ {
+ case 0:
+ case 1:
+ case 2:
+ // The old APIs fail on single file apps
+ Assert.Equal(!debuggeeInfo.TestConfiguration.PublishSingleFile, result);
+ break;
+ case 3:
+ // The new API should always succeed
+ AssertResult(result);
+ break;
+ }
+
+ if (result)
+ {
+ TestICorDebug(debuggeeInfo, corDebug);
+ AssertResult(debuggeeInfo.WaitForCreateProcess());
+ Assert.Equal(0, corDebug.Release());
+ }
+ else
+ {
+ Trace.TraceInformation("TestCreateDebuggingInterface pid {0} FAILED {1}", debuggeeInfo.ProcessId, result);
+ }
+ }
+ });
+ AssertResult(hr);
+ Trace.TraceInformation("TestCreateDebuggingInterface pid {0} DONE", debuggeeInfo.ProcessId);
+ }
+
+ private static readonly Guid IID_ICorDebugProcess = new Guid("3D6F5F64-7538-11D3-8D5B-00104B35E7EF");
+
+ private static void TestICorDebug(DebuggeeInfo debuggeeInfo, ICorDebug corDebug)
+ {
+ Assert.NotNull(corDebug);
+ AssertResult(corDebug.Initialize());
+ ManagedCallbackWrapper managedCallback = new(debuggeeInfo);
+ AssertResult(corDebug.SetManagedHandler(managedCallback.ICorDebugManagedCallback));
+ Trace.TraceInformation("TestICorDebug pid before DebugActiveProcess {0}", debuggeeInfo.ProcessId);
+ AssertResult(corDebug.DebugActiveProcess(debuggeeInfo.ProcessId, out IntPtr process));
+ Trace.TraceInformation("TestICorDebug pid after DebugActiveProcess {0}", debuggeeInfo.ProcessId);
+ AssertResult(COMHelper.QueryInterface(process, IID_ICorDebugProcess, out IntPtr icdp));
+ Assert.True(icdp != IntPtr.Zero);
+ COMHelper.Release(icdp);
+ }
+
+ /// <summary>
+ /// The reason we are running each test in it's own process using the remote executor is that the DBI/DAC are
+ /// never unloaded (and the existing dbgshim interfaces don't allow a way to do this). This is a problem on
+ /// Linux/MacOS to have multiple DAC loaded because DBI references the DAC's PAL exports and the loader gets
+ /// confused which DAC should be used for which DBI. This isn't a problem on Windows since there is no PAL.
+ /// </summary>
+ /// <param name="config">test configuration</param>
+ /// <param name="testName">name of test for the log file</param>
+ /// <param name="method">delegate to call in the remote process</param>
+ private async Task RemoteInvoke(TestConfiguration config, string testName, Func<string, Task<int>> method)
+ {
+ string singlefile = config.PublishSingleFile ? ".SingleFile" : "";
+ testName = $"DbgShim.UnitTests{singlefile}.{testName}";
+ string dumpPath = Path.Combine(config.LogDirPath, testName + ".dmp");
+ using TestRunner.OutputHelper output = TestRunner.ConfigureLogging(config, Output, testName);
+ int exitCode = await RemoteExecutorHelper.RemoteInvoke(output, config, TimeSpan.FromMinutes(5), dumpPath, method);
+ Assert.Equal(0, exitCode);
+ }
+
+ /// <summary>
+ /// Used in the remote invoke delegate to deserialize the test configuration xml and setup test output logging.
+ /// </summary>
+ /// <param name="config">test configuration xml</param>
+ /// <param name="config">test configuration instance</param>
+ /// <param name="output">test output instance</param>
+ private static void AfterInvoke(string configXml, out TestConfiguration config, out ITestOutputHelper output)
+ {
+ config = TestConfiguration.Deserialize(configXml);
+ output = new ConsoleTestOutputHelper();
+ LoggingListener.EnableListener(output, ListenerName);
+ }
+
+ private static void AssertResult(HResult hr)
+ {
+ Assert.Equal<HResult>(HResult.S_OK, hr);
+ }
+
+ #endregion
+ }
+
+ public static class DbgShimTestExtensions
+ {
+ public static string DbgShimPath(this TestConfiguration config)
+ {
+ return TestConfiguration.MakeCanonicalPath(config.GetValue("DbgShimPath"));
+ }
+
+ public static string RuntimeModulePath(this TestConfiguration config)
+ {
+ return TestConfiguration.MakeCanonicalPath(config.GetValue("RuntimeModulePath"));
+ }
+
+ public static string DbiModulePath(this TestConfiguration config)
+ {
+ return TestConfiguration.MakeCanonicalPath(config.GetValue("DbiModulePath"));
+ }
+
+ public static string DacModulePath(this TestConfiguration config)
+ {
+ return TestConfiguration.MakeCanonicalPath(config.GetValue("DacModulePath"));
+ }
+
+ public static string DebuggeeName(this TestConfiguration config)
+ {
+ return config.GetValue("DebuggeeName");
+ }
+ }
+}
--- /dev/null
+// 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 Microsoft.Diagnostics.Runtime.Utilities;
+using Microsoft.Diagnostics.TestHelpers;
+using System;
+using System.Diagnostics;
+using System.IO.Pipes;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.Diagnostics
+{
+ public class DebuggeeInfo : IDisposable
+ {
+ public readonly ITestOutputHelper Output;
+ public readonly TestConfiguration TestConfiguration;
+ public readonly bool Launch;
+ public readonly string PipeName;
+
+ public int ProcessId { get; private set; }
+ public IntPtr ResumeHandle { get; set; }
+
+ private readonly AutoResetEvent _createProcessEvent = new AutoResetEvent(false);
+ private readonly NamedPipeServerStream _pipeServer;
+ private HResult _createProcessResult = HResult.E_FAIL;
+ private Process _process;
+
+ public DebuggeeInfo(ITestOutputHelper output, TestConfiguration config, bool launch)
+ {
+ Output = output;
+ TestConfiguration = config;
+ Launch = launch;
+ PipeName = Guid.NewGuid().ToString();
+ _pipeServer = new NamedPipeServerStream(PipeName);
+ }
+
+ public void SetProcessId(int processId)
+ {
+ ProcessId = processId;
+ try
+ {
+ _process = Process.GetProcessById(processId);
+ }
+ catch (Exception ex) when (ex is ArgumentException || ex is InvalidOperationException)
+ {
+ Trace.TraceError($"DebuggeeInfo.SetProcessId({processId}): {ex}");
+ }
+ }
+
+ public void SetCreateProcessResult(HResult hr)
+ {
+ _createProcessResult = hr;
+ _createProcessEvent.Set();
+ }
+
+ public HResult WaitForCreateProcess()
+ {
+ Assert.True(_createProcessEvent.WaitOne());
+ return _createProcessResult;
+ }
+
+ public async Task<bool> WaitForDebuggee()
+ {
+ if (_process is null)
+ {
+ Trace.TraceWarning("DebuggeeInfo.WaitForDebuggee: no process");
+ return true;
+ }
+ try
+ {
+ var source = new CancellationTokenSource(TimeSpan.FromMinutes(5));
+ Trace.TraceInformation($"DebuggeeInfo.WaitForDebuggee: waiting {ProcessId}");
+ await _pipeServer.WaitForConnectionAsync(source.Token);
+ Trace.TraceInformation($"DebuggeeInfo.WaitForDebuggee: after wait {ProcessId}");
+ }
+ catch (OperationCanceledException ex)
+ {
+ Trace.TraceError($"DebuggeeInfo.WaitForDebuggee: canceled {ex}");
+ return false;
+ }
+ return true;
+ }
+
+ public HResult ResumeDebuggee()
+ {
+ HResult result = HResult.S_OK;
+ if (ResumeHandle != IntPtr.Zero)
+ {
+ Trace.TraceInformation($"DebuggeeInfo.ResumeDebuggee {ProcessId} handle {ResumeHandle:X8}");
+ result = DbgShimAPI.ResumeProcess(ResumeHandle);
+ DbgShimAPI.CloseResumeHandle(ResumeHandle);
+ ResumeHandle = IntPtr.Zero;
+ }
+ return result;
+ }
+
+ public void Disconnect()
+ {
+ try
+ {
+ _pipeServer.Disconnect();
+ }
+ catch (Exception ex) when (ex is InvalidOperationException)
+ {
+ Trace.TraceError(ex.ToString());
+ }
+ }
+
+ public void Kill()
+ {
+ if (_process is not null)
+ {
+ Trace.TraceInformation($"DebuggeeInfo: kill process {ProcessId}");
+ try
+ {
+ _process.Kill();
+ _process = null;
+ }
+ catch (Exception ex) when (ex is NotSupportedException || ex is InvalidOperationException)
+ {
+ Trace.TraceError(ex.ToString());
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ Trace.TraceInformation($"DebuggeeInfo: disposing process {ProcessId}");
+ ResumeDebuggee();
+ _pipeServer.Dispose();
+ Kill();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Diagnostics;
+using System.IO.Pipes;
+using System.Threading;
+
+class Simple
+{
+ public static int Main(string[] args)
+ {
+ string pipeServerName = args[0];
+ int pid = Process.GetCurrentProcess().Id;
+ Console.WriteLine("{0} SimpleDebuggee: pipe server: {1}", pid, pipeServerName);
+ Console.Out.Flush();
+
+ if (pipeServerName != null)
+ {
+ try
+ {
+ int timeout = TimeSpan.FromMinutes(5).Milliseconds;
+ using var pipeStream = new NamedPipeClientStream(pipeServerName);
+
+ Console.WriteLine("{0} SimpleDebuggee: connecting to pipe", pid);
+ Console.Out.Flush();
+ pipeStream.Connect(timeout);
+
+ Console.WriteLine("{0} SimpleDebuggee: connected to pipe", pid);
+ Console.Out.Flush();
+
+ // Wait for server to send something
+ int input = pipeStream.ReadByte();
+
+ Console.WriteLine("{0} SimpleDebuggee: waking up {1}", pid, input);
+ Console.Out.Flush();
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine(ex.ToString());
+ Console.Error.Flush();
+ }
+ }
+ return 0;
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework Condition="'$(BuildProjectFramework)' != ''">$(BuildProjectFramework)</TargetFramework>
+ <TargetFrameworks Condition="'$(BuildProjectFramework)' == ''">$(BuildTargetFrameworks)</TargetFrameworks>
+ </PropertyGroup>
+</Project>
--- /dev/null
+{
+ "rollForwardOnNoCandidateFx": 2
+}
\ No newline at end of file
--- /dev/null
+// 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 Microsoft.Diagnostics.Runtime;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using SOS.Hosting;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics
+{
+ public unsafe class ICLRDebugging : CallableCOMWrapper
+ {
+ public static readonly Guid IID_ICLRDebugging = new Guid("D28F3C5A-9634-4206-A509-477552EEFB10");
+ public static readonly Guid CLSID_ICLRDebugging = new Guid("BACC578D-FBDD-48A4-969F-02D932B74634");
+
+ private ref readonly ICLRDebuggingVTable VTable => ref Unsafe.AsRef<ICLRDebuggingVTable>(_vtable);
+
+ public static ICLRDebugging Create(IntPtr punk) => punk != IntPtr.Zero ? new ICLRDebugging(punk) : null;
+
+ private ICLRDebugging(IntPtr punk)
+ : base(new RefCountedFreeLibrary(IntPtr.Zero), IID_ICLRDebugging, punk)
+ {
+ SuppressRelease();
+ }
+
+ public HResult OpenVirtualProcess(
+ ulong moduleBaseAddress,
+ IntPtr dataTarget,
+ IntPtr libraryProvider,
+ ClrDebuggingVersion maxDebuggerSupportedVersion,
+ in Guid riidProcess,
+ out IntPtr process,
+ out ClrDebuggingVersion version,
+ out ClrDebuggingProcessFlags flags)
+ {
+ return VTable.OpenVirtualProcess(
+ Self,
+ moduleBaseAddress,
+ dataTarget,
+ libraryProvider,
+ in maxDebuggerSupportedVersion,
+ in riidProcess,
+ out process,
+ out version,
+ out flags);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private readonly unsafe struct ICLRDebuggingVTable
+ {
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, IntPtr, IntPtr, in ClrDebuggingVersion, in Guid, out IntPtr, out ClrDebuggingVersion, out ClrDebuggingProcessFlags, HResult> OpenVirtualProcess;
+ }
+ }
+}
--- /dev/null
+// 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 Microsoft.Diagnostics.Runtime;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics
+{
+ public unsafe class ICorDebug : CallableCOMWrapper
+ {
+ private static readonly Guid IID_ICorDebug = new Guid("3d6f5f61-7538-11d3-8d5b-00104b35e7ef");
+
+ private ref readonly ICorDebugVTable VTable => ref Unsafe.AsRef<ICorDebugVTable>(_vtable);
+
+ public static ICorDebug Create(IntPtr punk) => punk != IntPtr.Zero ? new ICorDebug(punk) : null;
+
+ private ICorDebug(IntPtr punk)
+ : base(new RefCountedFreeLibrary(IntPtr.Zero), IID_ICorDebug, punk)
+ {
+ SuppressRelease();
+ }
+
+ public HResult Initialize() => VTable.Initialize(Self);
+
+ public HResult Terminate() => VTable.Terminate(Self);
+
+ public HResult SetManagedHandler(IntPtr managedCallback) => VTable.SetManangedHandler(Self, managedCallback);
+
+ public HResult DebugActiveProcess(int processId, out IntPtr process) => VTable.DebugActiveProcess(Self, processId, 0, out process);
+
+ [StructLayout(LayoutKind.Sequential)]
+ private readonly unsafe struct ICorDebugVTable
+ {
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> Initialize;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> Terminate;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, IntPtr, HResult> SetManangedHandler;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, IntPtr, HResult> SetUnmanangedHandler;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> CreateProcess;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, int, int, out IntPtr, HResult> DebugActiveProcess;
+ }
+ }
+}
--- /dev/null
+// 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 Microsoft.Diagnostics.Runtime;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics
+{
+ public unsafe class ICorDebugController : CallableCOMWrapper
+ {
+ private static readonly Guid IID_ICorDebugController = new Guid("3D6F5F62-7538-11D3-8D5B-00104B35E7EF");
+
+ private ref readonly ICorDebugControllerVTable VTable => ref Unsafe.AsRef<ICorDebugControllerVTable>(_vtable);
+
+ public static ICorDebugController Create(IntPtr punk) => punk != IntPtr.Zero ? new ICorDebugController(punk) : null;
+
+ private ICorDebugController(IntPtr punk)
+ : base(new RefCountedFreeLibrary(IntPtr.Zero), IID_ICorDebugController, punk)
+ {
+ SuppressRelease();
+ }
+
+ public HResult Stop() => VTable.Stop(Self, uint.MaxValue);
+
+ public HResult Continue(bool isOutOfBand) => VTable.Continue(Self, isOutOfBand ? 1 : 0);
+
+ public HResult Detach() => VTable.Detach(Self);
+
+ public HResult Terminate(uint exitCode) => VTable.Terminate(Self, exitCode);
+
+ [StructLayout(LayoutKind.Sequential)]
+ private readonly unsafe struct ICorDebugControllerVTable
+ {
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, HResult> Stop;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, int, HResult> Continue;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> IsRunning_dummy;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> HasQueuedCallbacks_dummy;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> EnumerateThreads_dummy;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> SetAllThreadsDebugState_dummy;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, HResult> Detach;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, HResult> Terminate;
+ }
+ }
+}
--- /dev/null
+// 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 Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.DebugServices.Implementation;
+using Microsoft.Diagnostics.Runtime;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using Microsoft.FileFormats;
+using Microsoft.FileFormats.PE;
+using Microsoft.SymbolStore;
+using Microsoft.SymbolStore.KeyGenerators;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace SOS.Hosting
+{
+ public sealed unsafe class LibraryProviderWrapper : COMCallableIUnknown, IHost
+ {
+ public enum LIBRARY_PROVIDER_INDEX_TYPE
+ {
+ Unknown = 0,
+ Identity = 1,
+ Runtime = 2,
+ };
+
+ public static readonly Guid IID_ICLRDebuggingLibraryProvider = new Guid("3151C08D-4D09-4f9b-8838-2880BF18FE51");
+ public static readonly Guid IID_ICLRDebuggingLibraryProvider2 = new Guid("E04E2FF1-DCFD-45D5-BCD1-16FFF2FAF7BA");
+ public static readonly Guid IID_ICLRDebuggingLibraryProvider3 = new Guid("DE3AAB18-46A0-48B4-BF0D-2C336E69EA1B");
+
+ public IntPtr ILibraryProvider { get; }
+
+ private readonly OSPlatform _targetOS;
+ private readonly ImmutableArray<byte> _runtimeModuleBuildId;
+ private readonly string _dbiModulePath;
+ private readonly string _dacModulePath;
+ private ISymbolService _symbolService;
+
+ public LibraryProviderWrapper(string runtimeModulePath, string dbiModulePath, string dacModulePath)
+ : this(GetRunningOS(), GetBuildId(runtimeModulePath), dbiModulePath, dacModulePath)
+ {
+ }
+
+ public LibraryProviderWrapper(OSPlatform targetOS, ImmutableArray<byte> runtimeModuleBuildId, string dbiModulePath, string dacModulePath)
+ {
+ _targetOS = targetOS;
+ _runtimeModuleBuildId = runtimeModuleBuildId;
+ _dbiModulePath = dbiModulePath;
+ _dacModulePath = dacModulePath;
+
+ VTableBuilder builder = AddInterface(IID_ICLRDebuggingLibraryProvider, validate: false);
+ builder.AddMethod(new ProvideLibraryDelegate(ProvideLibrary));
+ ILibraryProvider = builder.Complete();
+
+ builder = AddInterface(IID_ICLRDebuggingLibraryProvider2, validate: false);
+ builder.AddMethod(new ProvideLibrary2Delegate(ProvideLibrary2));
+ builder.Complete();
+
+ builder = AddInterface(IID_ICLRDebuggingLibraryProvider3, validate: false);
+ builder.AddMethod(new ProvideWindowsLibraryDelegate(ProvideWindowsLibrary));
+ builder.AddMethod(new ProvideUnixLibraryDelegate(ProvideUnixLibrary));
+ builder.Complete();
+
+ AddRef();
+ }
+
+ private static OSPlatform GetRunningOS()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return OSPlatform.Windows;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return OSPlatform.Linux;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return OSPlatform.OSX;
+ throw new NotSupportedException($"OS not supported {RuntimeInformation.OSDescription}");
+ }
+
+ protected override void Destroy()
+ {
+ Trace.TraceInformation("LibraryProviderWrapper.Destroy");
+ }
+
+ private HResult ProvideLibrary(
+ IntPtr self,
+ string fileName,
+ uint timeStamp,
+ uint sizeOfImage,
+ out IntPtr moduleHandle)
+ {
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideLibrary {fileName} {timeStamp:X8} {sizeOfImage:X8}");
+ try
+ {
+ // This should only be called when hosted on Windows because of the PAL module handle problems
+ Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
+
+ string modulePath = null;
+ if (fileName.Contains(DbiName))
+ {
+ if (_dbiModulePath is not null)
+ {
+ modulePath = _dbiModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DbiName, timeStamp, sizeOfImage);
+ }
+ }
+ // This needs to work for long named DAC's so remove the extension
+ else if (fileName.Contains(Path.GetFileNameWithoutExtension(DacName)))
+ {
+ if (_dacModulePath is not null)
+ {
+ modulePath = _dacModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DacName, timeStamp, sizeOfImage);
+ }
+ }
+ TestGetPEInfo(modulePath, timeStamp, sizeOfImage);
+ moduleHandle = DataTarget.PlatformFunctions.LoadLibrary(modulePath);
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideLibrary SUCCEEDED {modulePath}");
+ return HResult.S_OK;
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError($"LibraryProviderWrapper.ProvideLibrary {ex}");
+ }
+ Trace.TraceError($"LibraryProviderWrapper.ProvideLibrary FAILED");
+ moduleHandle = IntPtr.Zero;
+ return HResult.E_INVALIDARG;
+ }
+
+ private HResult ProvideLibrary2(
+ IntPtr self,
+ string fileName,
+ uint timeStamp,
+ uint sizeOfImage,
+ out IntPtr modulePathOut)
+ {
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideLibrary2 {fileName} {timeStamp:X8} {sizeOfImage:X8}");
+ try
+ {
+ string modulePath = null;
+ if (fileName.Contains(DbiName))
+ {
+ if (_dbiModulePath != null)
+ {
+ modulePath = _dbiModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DbiName, timeStamp, sizeOfImage);
+ }
+ }
+ // This needs to work for long named DAC's so remove the extension
+ else if (fileName.Contains(Path.GetFileNameWithoutExtension(DacName)))
+ {
+ if (_dacModulePath != null)
+ {
+ modulePath = _dacModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DacName, timeStamp, sizeOfImage);
+ }
+ }
+ // If this is called on Linux or MacOS don't verify. This should only happen if
+ // these tests are run against an old dbgshim version.
+ if (_targetOS == OSPlatform.Windows)
+ {
+ TestGetPEInfo(modulePath, timeStamp, sizeOfImage);
+ }
+ modulePathOut = Marshal.StringToCoTaskMemUni(modulePath);
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideLibrary2 SUCCEEDED {modulePath}");
+ return HResult.S_OK;
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError($"LibraryProviderWrapper.ProvideLibrary2 {ex}");
+ }
+ Trace.TraceError("LibraryProviderWrapper.ProvideLibrary2 FAILED");
+ modulePathOut = IntPtr.Zero;
+ return HResult.E_INVALIDARG;
+ }
+
+ private HResult ProvideWindowsLibrary(
+ IntPtr self,
+ string fileName,
+ string runtimeModulePath,
+ LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ uint timeStamp,
+ uint sizeOfImage,
+ out IntPtr modulePathOut)
+ {
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideWindowsLibrary {fileName} {runtimeModulePath} {timeStamp:X8} {sizeOfImage:X8}");
+ try
+ {
+ // Should only be called for Windows targets
+ Assert.Equal(OSPlatform.Windows, _targetOS);
+
+ // Should always get the identity on Windows
+ Assert.Equal(LIBRARY_PROVIDER_INDEX_TYPE.Identity, indexType);
+
+ string modulePath = null;
+ if (fileName.Contains(DbiName))
+ {
+ if (_dbiModulePath != null)
+ {
+ modulePath = _dbiModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DbiName, timeStamp, sizeOfImage);
+ }
+ }
+ // This needs to work for long named DAC's so remove the extension
+ else if (fileName.Contains(Path.GetFileNameWithoutExtension(DacName)))
+ {
+ if (_dacModulePath != null)
+ {
+ modulePath = _dacModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DacName, timeStamp, sizeOfImage);
+ }
+ }
+ TestGetPEInfo(modulePath, timeStamp, sizeOfImage);
+ modulePathOut = Marshal.StringToCoTaskMemUni(modulePath);
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideWindowsLibrary SUCCEEDED {modulePath}");
+ return HResult.S_OK;
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError($"LibraryProviderWrapper.ProvideWindowsLibrary {ex}");
+ }
+ Trace.TraceError("LibraryProviderWrapper.ProvideWindowsLibrary FAILED");
+ modulePathOut = IntPtr.Zero;
+ return HResult.E_INVALIDARG;
+ }
+
+ private HResult ProvideUnixLibrary(
+ IntPtr self,
+ string fileName,
+ string runtimeModulePath,
+ LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ byte* buildIdBytes,
+ int buildIdSize,
+ out IntPtr modulePathOut)
+ {
+ try
+ {
+ // Should only be called for Unix targets
+ Assert.NotEqual(OSPlatform.Windows, _targetOS);
+
+ byte[] buildId = Array.Empty<byte>();
+ string modulePath = null;
+ if (buildIdBytes != null && buildIdSize > 0)
+ {
+ Span<byte> span = new Span<byte>(buildIdBytes, buildIdSize);
+ buildId = span.ToArray();
+ }
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideUnixLibrary {fileName} {runtimeModulePath} {indexType} {string.Concat(buildId.Select((b) => b.ToString("x2")))}");
+ if (fileName.Contains(DbiName))
+ {
+ if (_dbiModulePath != null)
+ {
+ modulePath = _dbiModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DbiName, buildId);
+ }
+ }
+ else if (fileName.Contains(DacName))
+ {
+ if (_dacModulePath != null)
+ {
+ modulePath = _dacModulePath;
+ }
+ else
+ {
+ modulePath = DownloadModule(DacName, buildId);
+ }
+ }
+ if (modulePath != null)
+ {
+ switch (indexType)
+ {
+ case LIBRARY_PROVIDER_INDEX_TYPE.Identity:
+ TestBuildId(GetBuildId(modulePath), buildId);
+ break;
+
+ case LIBRARY_PROVIDER_INDEX_TYPE.Runtime:
+ TestBuildId(_runtimeModuleBuildId, buildId);
+ break;
+ }
+ modulePathOut = Marshal.StringToCoTaskMemUni(modulePath);
+ Trace.TraceInformation($"LibraryProviderWrapper.ProvideUnixLibrary SUCCEEDED {modulePath}");
+ return HResult.S_OK;
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError($"LibraryProviderWrapper.ProvideUnixLibrary {ex}");
+ }
+ Trace.TraceError("LibraryProviderWrapper.ProvideUnixLibrary FAILED");
+ modulePathOut = IntPtr.Zero;
+ return HResult.E_INVALIDARG;
+ }
+
+ private string DownloadModule(string moduleName, uint timeStamp, uint sizeOfImage)
+ {
+ Assert.True(timeStamp != 0 && sizeOfImage != 0);
+ SymbolStoreKey key = PEFileKeyGenerator.GetKey(moduleName, timeStamp, sizeOfImage);
+ Assert.NotNull(key);
+ string downloadedPath = SymbolService.DownloadFile(key);
+ Assert.NotNull(downloadedPath);
+ return downloadedPath;
+ }
+
+ private string DownloadModule(string moduleName, byte[] buildId)
+ {
+ Assert.True(buildId.Length > 0);
+ SymbolStoreKey key = null;
+ OSPlatform platform;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // This is the cross-DAC case when OpenVirtualProcess calls on a Linux/MacOS dump. Should never
+ // get here for a Windows dump or for live sessions (RegisterForRuntimeStartup, etc).
+ platform = _targetOS;
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ platform = OSPlatform.Linux;
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ platform = OSPlatform.OSX;
+ }
+ else
+ {
+ throw new NotSupportedException($"OS not supported {RuntimeInformation.OSDescription}");
+ }
+ if (platform == OSPlatform.Linux)
+ {
+ key = ELFFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, moduleName, buildId, symbolFile: false, symbolFileName: null).SingleOrDefault();
+ }
+ else if (platform == OSPlatform.OSX)
+ {
+ key = MachOFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, moduleName, buildId, symbolFile: false, symbolFileName: null).SingleOrDefault();
+ }
+ Assert.NotNull(key);
+ string downloadedPath = SymbolService.DownloadFile(key);
+ Assert.NotNull(downloadedPath);
+ return downloadedPath;
+ }
+
+ private void TestGetPEInfo(string filePath, uint timeStamp, uint sizeOfImage)
+ {
+ if (filePath != null && timeStamp != 0 && sizeOfImage != 0)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ using Stream stream = Utilities.TryOpenFile(filePath);
+ if (stream is not null)
+ {
+ var peFile = new PEFile(new StreamAddressSpace(stream), false);
+ if (peFile.IsValid())
+ {
+ Assert.Equal(peFile.Timestamp, timeStamp);
+ Assert.Equal(peFile.SizeOfImage, sizeOfImage);
+ return;
+ }
+ }
+ throw new ArgumentException($"GetPEInfo {filePath} not valid PE file");
+ }
+ }
+ }
+
+ private void TestBuildId(ImmutableArray<byte> expectedBuildId, byte[] actualBuildId)
+ {
+ if (expectedBuildId.Length > 0)
+ {
+ Assert.Equal(expectedBuildId, actualBuildId);
+ }
+ }
+
+ private static ImmutableArray<byte> GetBuildId(string filePath)
+ {
+ if (filePath != null)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ using Utilities.ELFModule elfModule = Utilities.OpenELFFile(filePath);
+ if (elfModule is not null)
+ {
+ return elfModule.BuildID.ToImmutableArray();
+ }
+ throw new ArgumentException($"TestBuildId {filePath} not valid ELF file");
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ using Utilities.MachOModule machOModule = Utilities.OpenMachOFile(filePath);
+ if (machOModule is not null)
+ {
+ return machOModule.Uuid.ToImmutableArray();
+ }
+ throw new ArgumentException($"TestBuildId {filePath} not valid MachO file");
+ }
+ }
+ return ImmutableArray<byte>.Empty;
+ }
+
+ private string DbiName
+ {
+ get
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return "mscordbi.dll";
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return "libmscordbi.so";
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return "libmscordbi.dylib";
+ throw new NotSupportedException($"OS not supported {RuntimeInformation.OSDescription}");
+ }
+ }
+
+ private string DacName
+ {
+ get
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return "mscordaccore.dll";
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return "libmscordaccore.so";
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return "libmscordaccore.dylib";
+ throw new NotSupportedException($"OS not supported {RuntimeInformation.OSDescription}");
+ }
+ }
+
+ private ISymbolService SymbolService
+ {
+ get
+ {
+ if (_symbolService is null)
+ {
+ _symbolService = new SymbolService(this);
+ _symbolService.AddSymbolServer(msdl: true, symweb: false, symbolServerPath: null, authToken: null, timeoutInMinutes: 0);
+ _symbolService.AddCachePath(SymbolService.DefaultSymbolCache);
+ }
+ return _symbolService;
+ }
+ }
+
+ #region IHost
+
+ IServiceEvent IHost.OnShutdownEvent => throw new NotImplementedException();
+
+ HostType IHost.HostType => HostType.DotnetDump;
+
+ IServiceProvider IHost.Services => throw new NotImplementedException();
+
+ IEnumerable<ITarget> IHost.EnumerateTargets() => throw new NotImplementedException();
+
+ void IHost.DestroyTarget(ITarget target) => throw new NotImplementedException();
+
+ #endregion
+
+ #region ICLRDebuggingLibraryProvider* delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ProvideLibraryDelegate(
+ [In] IntPtr self,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string fileName,
+ [In] uint timeStamp,
+ [In] uint sizeOfImage,
+ out IntPtr moduleHandle);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ProvideLibrary2Delegate(
+ [In] IntPtr self,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string fileName,
+ [In] uint timeStamp,
+ [In] uint sizeOfImage,
+ out IntPtr modulePath);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ProvideWindowsLibraryDelegate(
+ [In] IntPtr self,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string fileName,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string runtimeModulePath,
+ [In] LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ [In] uint timeStamp,
+ [In] uint sizeOfImage,
+ out IntPtr modulePath);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ProvideUnixLibraryDelegate(
+ [In] IntPtr self,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string fileName,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string runtimeModulePath,
+ [In] LIBRARY_PROVIDER_INDEX_TYPE indexType,
+ [In] byte* buildIdBytes,
+ [In] int buildIdSize,
+ out IntPtr modulePath);
+
+ #endregion
+ }
+}
--- /dev/null
+// 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 Microsoft.Diagnostics.Runtime.Utilities;
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics
+{
+ public sealed class ManagedCallbackWrapper : COMCallableIUnknown
+ {
+ private static readonly Guid IID_ICorDebugManagedCallback = new Guid("3D6F5F60-7538-11D3-8D5B-00104B35E7EF");
+ private static readonly Guid IID_ICorDebugManagedCallback2 = new Guid("250E5EEA-DB5C-4C76-B6F3-8C46F12E3203");
+
+ private readonly DebuggeeInfo _startInfo;
+
+ public IntPtr ICorDebugManagedCallback { get; }
+
+ public ManagedCallbackWrapper(DebuggeeInfo startInfo)
+ {
+ _startInfo = startInfo;
+
+ VTableBuilder builder = AddInterface(IID_ICorDebugManagedCallback, validate: false);
+ builder.AddMethod(new BreakpointDelegate((self, pAppDomain, pThread, pBreakpoint) => HResult.E_NOTIMPL));
+ builder.AddMethod(new StepCompleteDelegate((self, pAppDomain, pThread, pStepper, reason) => HResult.E_NOTIMPL));
+ builder.AddMethod(new BreakDelegate((self, pAppDomain, pThread) => WriteLine("Break")));
+ builder.AddMethod(new ExceptionDelegate((self, pAppDomain, pThread, unhandled) => WriteLine("Exception")));
+ builder.AddMethod(new EvalCompleteDelegate((self, pAppDomain, pThread, pEval) => HResult.E_NOTIMPL));
+ builder.AddMethod(new EvalExceptionDelegate((self, pAppDomain, pThread, pEval) => HResult.E_NOTIMPL));
+ builder.AddMethod(new CreateProcessDelegate((self, pProcess) => CreateProcess(pProcess)));
+ builder.AddMethod(new ExitProcessDelegate((self, pProcess) => WriteLine("ExitProcess")));
+ builder.AddMethod(new CreateThreadDelegate((self, pAppDomain, pThread) => HResult.E_NOTIMPL));
+ builder.AddMethod(new ExitThreadDelegate((self, pAppDomain, pThread) => HResult.E_NOTIMPL));
+ builder.AddMethod(new LoadModuleDelegate((self, pAppDomain, pModule) => HResult.E_NOTIMPL));
+ builder.AddMethod(new UnloadModuleDelegate((self, pAppDomain, pModule) => HResult.E_NOTIMPL));
+ builder.AddMethod(new LoadClassDelegate((self, pAppDomain, c) => HResult.E_NOTIMPL));
+ builder.AddMethod(new UnloadClassDelegate((self, pAppDomain, c) => HResult.E_NOTIMPL));
+ builder.AddMethod(new DebuggerErrorDelegate((self, pProcess, errorHR, errorCode) => WriteLine($"DebuggerError {errorHR} {errorCode:X8}")));
+ builder.AddMethod(new LogMessageDelegate((self, pAppDomain, pThread, lLevel, pLogSwitchName, pMessage) => HResult.E_NOTIMPL));
+ builder.AddMethod(new LogSwitchDelegate((self, pAppDomain, pThread, lLevel, ulReason, pLogSwitchName, pParentName) => HResult.E_NOTIMPL));
+ builder.AddMethod(new CreateAppDomainDelegate((self, pProcess, pAppDomain) => HResult.E_NOTIMPL));
+ builder.AddMethod(new ExitAppDomainDelegate((self, pProcess, pAppDomain) => HResult.E_NOTIMPL));
+ builder.AddMethod(new LoadAssemblyDelegate((self, pAppDomain, pAssembly) => HResult.E_NOTIMPL));
+ builder.AddMethod(new UnloadAssemblyDelegate((self, pAppDomain, pAssembly) => HResult.E_NOTIMPL));
+ builder.AddMethod(new ControlCTrapDelegate((self, pProcess) => HResult.E_NOTIMPL));
+ builder.AddMethod(new NameChangeDelegate((self, pAppDomain, pThread) => HResult.E_NOTIMPL));
+ builder.AddMethod(new UpdateModuleSymbolsDelegate((self, pAppDomain, pModule, pSymbolStream) => HResult.E_NOTIMPL));
+ builder.AddMethod(new EditAndContinueRemapDelegate((self, pAppDomain, pThread, pFunction, fAccurate) => HResult.E_NOTIMPL));
+ builder.AddMethod(new BreakpointSetErrorDelegate((self, pAppDomain, pThread, pBreakpoint, dwError) => HResult.E_NOTIMPL));
+ ICorDebugManagedCallback = builder.Complete();
+
+ builder = AddInterface(IID_ICorDebugManagedCallback2, validate: false);
+ builder.AddMethod(new FunctionRemapOpportunityDelegate((self, pAppDomain, pThread, pOldFunction, pNewFunction, oldILOffset) => HResult.E_NOTIMPL));
+ builder.AddMethod(new CreateConnectionDelegate((IntPtr self, IntPtr pProcess, uint dwConnectionId, ref ushort pConnName) => HResult.E_NOTIMPL));
+ builder.AddMethod(new ChangeConnectionDelegate((self, pProcess, dwConnectionId) => HResult.E_NOTIMPL));
+ builder.AddMethod(new DestroyConnectionDelegate((self, pProcess, dwConnectionId) => HResult.E_NOTIMPL));
+ builder.AddMethod(new ExceptionDelegate2((self, pAppDomain, pThread, pFrame, nOffset, dwEventType, dwFlags) => HResult.E_NOTIMPL));
+ builder.AddMethod(new ExceptionUnwindDelegate((self, pAppDomain, pThread, dwEventType, dwFlags) => HResult.E_NOTIMPL));
+ builder.AddMethod(new FunctionRemapCompleteDelegate((self, pAppDomain, pThread, pFunction) => HResult.E_NOTIMPL));
+ builder.AddMethod(new MDANotificationDelegate((self, pController, pThread, pMDA) => HResult.E_NOTIMPL));
+ builder.Complete();
+
+ AddRef();
+ }
+
+ private HResult CreateProcess(IntPtr pController)
+ {
+ Trace.TraceInformation("ManagedCallbackWrapper.CreateProcess");
+ ICorDebugController process = ICorDebugController.Create(pController);
+ _startInfo.SetCreateProcessResult(process.Continue(isOutOfBand: false));
+ process.Release();
+ return HResult.S_OK;
+ }
+
+ private HResult WriteLine(string message)
+ {
+ Trace.TraceInformation("ManagedCallbackWrapper." + message);
+ return HResult.E_NOTIMPL;
+ }
+
+ #region ICorDebugManagedCallback delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult BreakpointDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pBreakpoint);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult StepCompleteDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pStepper, [In] int reason);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult BreakDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ExceptionDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] int unhandled);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult EvalCompleteDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pEval);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult EvalExceptionDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pEval);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult CreateProcessDelegate([In] IntPtr self, [In] IntPtr pProcess);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ExitProcessDelegate([In] IntPtr self, [In] IntPtr pProcess);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult CreateThreadDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr thread);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ExitThreadDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr thread);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult LoadModuleDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pModule);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult UnloadModuleDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pModule);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult LoadClassDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr c);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult UnloadClassDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr c);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult DebuggerErrorDelegate([In] IntPtr self, [In] IntPtr pProcess, [In] HResult errorHR, [In] uint errorCode);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult LogMessageDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] int lLevel, [In, MarshalAs(UnmanagedType.LPWStr)] string pLogSwitchName, [In, MarshalAs(UnmanagedType.LPWStr)] string pMessage);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult LogSwitchDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] int lLevel, [In] uint ulReason, [In, MarshalAs(UnmanagedType.LPWStr)] string pLogSwitchName, [In, MarshalAs(UnmanagedType.LPWStr)] string pParentName);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult CreateAppDomainDelegate([In] IntPtr self, [In] IntPtr pProcess, [In] IntPtr pAppDomain);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ExitAppDomainDelegate([In] IntPtr self, [In] IntPtr pProcess, [In] IntPtr pAppDomain);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult LoadAssemblyDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pAssembly);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult UnloadAssemblyDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pAssembly);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ControlCTrapDelegate([In] IntPtr self, [In] IntPtr pProcess);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult NameChangeDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult UpdateModuleSymbolsDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pModule, [In] IntPtr pSymbolStream);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult EditAndContinueRemapDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pFunction, [In] int fAccurate);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult BreakpointSetErrorDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pBreakpoint, [In] uint dwError);
+
+ #endregion
+
+ #region ICorDebugManagedCallback2 delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult FunctionRemapOpportunityDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pOldFunction, [In] IntPtr pNewFunction, [In] uint oldILOffset);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult CreateConnectionDelegate([In] IntPtr self, [In] IntPtr pProcess, [In] uint dwConnectionId, [In] ref ushort pConnName);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ChangeConnectionDelegate([In] IntPtr self, [In] IntPtr pProcess, [In] uint dwConnectionId);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult DestroyConnectionDelegate([In] IntPtr self, [In] IntPtr pProcess, [In] uint dwConnectionId);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ExceptionDelegate2([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pFrame, [In] uint nOffset, [In] int dwEventType, [In] uint dwFlags);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult ExceptionUnwindDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] int dwEventType, [In] uint dwFlags);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult FunctionRemapCompleteDelegate([In] IntPtr self, [In] IntPtr pAppDomain, [In] IntPtr pThread, [In] IntPtr pFunction);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate HResult MDANotificationDelegate([In] IntPtr self, [In] IntPtr pController, [In] IntPtr pThread, [In] IntPtr pMDA);
+
+ #endregion
+ }
+}
<DumpFile>$(Package_TestAssets_Linux_x64_5_0)/LineNums/SOS.LineNums.Heap.dmp</DumpFile>
<TestDataFile>$(Package_TestAssets_Linux_x64_5_0)/LineNums/SOS.LineNums.Heap.dmp.xml</TestDataFile>
</Option>
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_x64_6_0)/DivZero/SOS.DivZero.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_x64_6_0)/DivZero/SOS.DivZero.Heap.dmp.xml</TestDataFile>
+ </Option>
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_x64_6_0)/DivZero/SOS.DivZero.Triage.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_x64_6_0)/DivZero/SOS.DivZero.Triage.dmp.xml</TestDataFile>
+ </Option>
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_x64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_x64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp.xml</TestDataFile>
+ </Option>
</Options>
<!-- Linux arm64 testing -->
<DumpFile>$(Package_TestAssets_Linux_arm64_5_0)/LineNums/SOS.LineNums.Heap.dmp</DumpFile>
<TestDataFile>$(Package_TestAssets_Linux_arm64_5_0)/LineNums/SOS.LineNums.Heap.dmp.xml</TestDataFile>
</Option>
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_arm64_6_0)/DivZero/SOS.DivZero.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_arm64_6_0)/DivZero/SOS.DivZero.Heap.dmp.xml</TestDataFile>
+ </Option>
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_arm64_6_0)/DivZero/SOS.DivZero.Triage.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_arm64_6_0)/DivZero/SOS.DivZero.Triage.dmp.xml</TestDataFile>
+ </Option>
+ <Option>
+ <DumpFile>$(Package_TestAssets_Linux_arm64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp</DumpFile>
+ <TestDataFile>$(Package_TestAssets_Linux_arm64_6_0)/WebApp3/SOS.WebApp3.Heap.dmp.xml</TestDataFile>
+ </Option>
</Options>
<!-- Run tests on OSX x64 -->
-using Microsoft.Diagnostics.Repl;
-using Microsoft.Diagnostics.Runtime;
+using Microsoft.Diagnostics.Runtime;
using Microsoft.Diagnostics.TestHelpers;
using System;
using System.Collections.Generic;
{
_configurations ??= TestRunConfiguration.Instance.Configurations
.Where((config) => config.AllSettings.ContainsKey("DumpFile"))
- .Select((config) => TestHost.CreateHost(config))
+ .Select((config) => CreateHost(config))
.Select((host) => new[] { host }).ToImmutableArray();
return _configurations;
}
+ private static TestHost CreateHost(TestConfiguration config)
+ {
+ if (config.IsTestDbgEng())
+ {
+ return new TestDbgEng(config);
+ }
+ else
+ {
+ return new TestDump(config);
+ }
+ }
+
ITestOutputHelper Output { get; set; }
public DebugServicesTests(ITestOutputHelper output)
{
Output = output;
-
- if (Trace.Listeners[ListenerName] == null)
- {
- Trace.Listeners.Add(new LoggingListener(output));
- Trace.AutoFlush = true;
- }
+ LoggingListener.EnableListener(output, ListenerName);
}
void IDisposable.Dispose() => Trace.Listeners.Remove(ListenerName);
}
}
}
-
- class LoggingListener : TraceListener
- {
- private readonly CharToLineConverter _converter;
-
- internal LoggingListener(ITestOutputHelper output)
- : base(ListenerName)
- {
- _converter = new CharToLineConverter((text) => {
- output.WriteLine(text);
- });
- }
-
- public override void Write(string message)
- {
- _converter.Input(message);
- }
-
- public override void WriteLine(string message)
- {
- _converter.Input(message + Environment.NewLine);
- }
- }
}
}
<PropertyGroup>
<!-- Needs to be netcoreapp3.1 and not higher so this test assembly can be loaded by dotnet-dump via the DOTNET_DIAGNOSTIC_EXTENSIONS env var -->
<TargetFramework>netcoreapp3.1</TargetFramework>
- <TestConfigFileName>$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt</TestConfigFileName>
+ <DebugServicesConfigFileName>$(OutputPath)$(TargetFramework)\Debugger.Tests.Common.txt</DebugServicesConfigFileName>
<TestAssetsVersion>1.0.257801</TestAssetsVersion>
<!-- Controls the test asset package restore and the tests that use them -->
<RunTests>true</RunTests>
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.DebugServices.Implementation\Microsoft.Diagnostics.DebugServices.Implementation.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.DebugServices\Microsoft.Diagnostics.DebugServices.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.TestHelpers\Microsoft.Diagnostics.TestHelpers.csproj" />
- <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Repl\Microsoft.Diagnostics.Repl.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\SOS\SOS.Extensions\SOS.Extensions.csproj" />
</ItemGroup>
</ConfigFileEntries>
</ItemGroup>
- <Target Name="WriteTestConfigFile" Outputs="$(TestConfigFileName)" AfterTargets="Build">
+ <Target Name="DebugServicesWriteTestConfigFile" Outputs="$(DebugServicesConfigFileName)" AfterTargets="Build">
<PropertyGroup>
- <TestConfigFileLines>@(ConfigFileEntries->Metadata("ConfigFileEntry"))</TestConfigFileLines>
+ <TestConfigFileLines>@(ConfigFileEntries->Metadata("ConfigFileEntry"))</TestConfigFileLines>
</PropertyGroup>
- <WriteLinesToFile File="$(TestConfigFileName)" Lines="$(TestConfigFileLines)" Overwrite="true" WriteOnlyWhenDifferent="true" />
- <Message Importance="High" Text="Created config file $(TestConfigFileName)" />
+ <WriteLinesToFile File="$(DebugServicesConfigFileName)" Lines="$(TestConfigFileLines)" Overwrite="true" WriteOnlyWhenDifferent="true" />
+ <Message Importance="High" Text="Created config file $(DebugServicesConfigFileName)" />
<ItemGroup>
- <FileWrites Include="$(TestConfigFileName)" />
+ <FileWrites Include="$(DebugServicesConfigFileName)" />
</ItemGroup>
</Target>
</Project>
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
-using System.Globalization;
-using System.Linq;
-using System.Reflection;
-using System.Xml.Linq;
-using Xunit;
-
-namespace Microsoft.Diagnostics.DebugServices.UnitTests
-{
- public class TestDataReader
- {
- /// <summary>
- /// Test data value
- /// </summary>
- public class Value
- {
- private readonly object _value;
-
- internal Value(string valueString)
- {
- _value = valueString;
- }
-
- internal Value(ImmutableArray<ImmutableDictionary<string, Value>> values)
- {
- _value = values;
- }
-
- /// <summary>
- /// Returns true if sub values
- /// </summary>
- public bool IsSubValue => _value is ImmutableArray<ImmutableDictionary<string, Value>>;
-
- /// <summary>
- /// Return the sub values for nested test data
- /// </summary>
- public ImmutableArray<ImmutableDictionary<string, Value>> Values
- {
- get { return _value is ImmutableArray<ImmutableDictionary<string, Value>> values ? values : ImmutableArray<ImmutableDictionary<string, Value>>.Empty; }
- }
-
- /// <summary>
- /// Get the test data value as type T
- /// </summary>
- public T GetValue<T>()
- {
- return (T)GetValue(typeof(T));
- }
-
- /// <summary>
- /// Get the test data value as "type"
- /// </summary>
- public object GetValue(Type type)
- {
- object value = _value;
- if (value is string valueString)
- {
- GetValue(type, valueString, ref value);
- }
- return value;
- }
-
- /// <summary>
- /// Convert test data string to value
- /// </summary>
- /// <param name="type">type to convert to</param>
- /// <param name="valueString">test data string</param>
- /// <param name="result">resulting object</param>
- public static void GetValue(Type type, string valueString, ref object result)
- {
- valueString = valueString.Trim();
- if (type == typeof(string))
- {
- result = valueString ?? "";
- }
- else if (type == typeof(bool))
- {
- switch (valueString.ToLowerInvariant())
- {
- case "true":
- result = true;
- break;
- case "false":
- result = false;
- break;
- }
- }
- else if (type.IsEnum)
- {
- result = Enum.Parse(type, valueString);
- }
- else if (type.IsPrimitive)
- {
- NumberStyles style = valueString.StartsWith("0x") ? NumberStyles.HexNumber : NumberStyles.Integer;
- if (ulong.TryParse(valueString.Replace("0x", ""), style, CultureInfo.InvariantCulture, out ulong integerValue))
- {
- result = Convert.ChangeType(integerValue, type);
- }
- }
- }
- }
-
- /// <summary>
- /// Test data file version
- /// </summary>
- public readonly Version Version;
-
- /// <summary>
- /// Target test data
- /// </summary>
- public readonly ImmutableDictionary<string, Value> Target;
-
- /// <summary>
- /// Shortcut to the module test data
- /// </summary>
- public readonly ImmutableArray<ImmutableDictionary<string, Value>> Modules;
-
- /// <summary>
- /// Shortcut to the thread test data
- /// </summary>
- public readonly ImmutableArray<ImmutableDictionary<string, Value>> Threads;
-
- /// <summary>
- /// Shortcut to the runtime test data
- /// </summary>
- public readonly ImmutableArray<ImmutableDictionary<string, Value>> Runtimes;
-
- /// <summary>
- /// Open the test data xml file
- /// </summary>
- /// <param name="testDataFile"></param>
- public TestDataReader(string testDataFile)
- {
- XDocument doc = XDocument.Load(testDataFile);
- XElement root = doc.Root;
- Assert.Equal("TestData", root.Name);
- foreach (XElement child in root.Elements())
- {
- switch (child.Name.LocalName)
- {
- case "Version":
- Version = Version.Parse(child.Value);
- break;
- case "Target":
- Target = Build(child);
- break;
- }
- }
- Modules = Target["Modules"].Values;
- Threads = Target["Threads"].Values;
- Runtimes = Target["Runtimes"].Values;
- }
-
- private static ImmutableDictionary<string, Value> Build(XElement node)
- {
- var members = new Dictionary<string, Value>();
- foreach (XElement dataNode in node.Elements())
- {
- string name = dataNode.Name.LocalName;
- if (dataNode.HasElements)
- {
- var items = new List<ImmutableDictionary<string, Value>>();
- foreach (XElement subValue in dataNode.Elements())
- {
- if (subValue.HasElements)
- {
- // Has multiple elements (i.e. Modules, Threads, Runtimes,
- // etc). Assumes the same name for each entry.
- items.Add(Build(subValue));
- }
- else
- {
- // Only has sub members (i.e. RuntimeModule, etc.)
- items.Add(Build(dataNode));
- break;
- }
- }
- members.Add(name, new Value(items.ToImmutableArray()));
- }
- else
- {
- members.Add(name, new Value(dataNode.Value));
- }
- }
- return members.ToImmutableDictionary();
- }
- }
-
- public static class TestDataExtensions
- {
- /// <summary>
- /// Helper function to get a test data value
- /// </summary>
- /// <typeparam name="T">type to convert test data value</typeparam>
- /// <param name="values">values collection to lookup name</param>
- /// <param name="name">value name</param>
- /// <param name="value">result value of type T</param>
- /// <returns></returns>
- public static bool TryGetValue<T>(
- this ImmutableDictionary<string, TestDataReader.Value> values, string name, out T value)
- {
- if (values.TryGetValue(name, out TestDataReader.Value testValue))
- {
- value = testValue.GetValue<T>();
- return true;
- }
- value = default;
- return false;
- }
-
- /// <summary>
- /// Finds the match item (i.e. IModule, IThread, etc.) in the test data.
- /// </summary>
- /// <typeparam name="T">field or property type</typeparam>
- /// <param name="items">Modules, Threads, Registers, etc. test data</param>
- /// <param name="propety">name of property to use for search</param>
- /// <param name="propertyValue"></param>
- /// <returns>test data values</returns>
- public static ImmutableDictionary<string, TestDataReader.Value> Find<T>(
- this ImmutableArray<ImmutableDictionary<string, TestDataReader.Value>> items, string propety, T propertyValue)
- where T : IComparable
- {
- foreach (var item in items)
- {
- TestDataReader.Value value = item[propety];
- if (propertyValue.CompareTo(value.GetValue<T>()) == 0)
- {
- return item;
- }
- }
- return default;
- }
-
- /// <summary>
- /// Compares the test data values with the properties in the instance with the same name. This is
- /// used to compare ITarget, IModule, IThread, RegiserInfo instances to the test data.
- /// </summary>
- /// <param name="values">test data for the item</param>
- /// <param name="instance">object to compare</param>
- public static void CompareMembers(
- this ImmutableDictionary<string, TestDataReader.Value> values, object instance)
- {
- foreach (KeyValuePair<string, TestDataReader.Value> testData in values)
- {
- MemberInfo[] members = instance.GetType().GetMember(
- testData.Key,
- MemberTypes.Field | MemberTypes.Property | MemberTypes.Method,
- BindingFlags.Public | BindingFlags.Instance);
-
- if (members.Length > 0)
- {
- MemberInfo member = members.Single();
- object memberValue = null;
- Type memberType = null;
-
- switch (member.MemberType)
- {
- case MemberTypes.Property:
- memberValue = ((PropertyInfo)member).GetValue(instance);
- memberType = ((PropertyInfo)member).PropertyType;
- break;
- case MemberTypes.Field:
- memberValue = ((FieldInfo)member).GetValue(instance);
- memberType = ((FieldInfo)member).FieldType;
- break;
- case MemberTypes.Method:
- if (((MethodInfo)member).GetParameters().Length == 0)
- {
- memberValue = ((MethodInfo)member).Invoke(instance, null);
- memberType = ((MethodInfo)member).ReturnType;
- }
- break;
- }
- if (memberType != null)
- {
- if (testData.Value.IsSubValue)
- {
- Trace.TraceInformation($"CompareMembers {testData.Key} sub value:");
- CompareMembers(testData.Value.Values.Single(), memberValue);
- }
- else
- {
- Type nullableType = Nullable.GetUnderlyingType(memberType);
- memberType = nullableType ?? memberType;
-
- if (nullableType != null && memberValue == null)
- {
- memberValue = string.Empty;
- }
- else if (memberType == typeof(string))
- {
- memberValue ??= string.Empty;
- }
- else if (memberValue is ImmutableArray<byte> buildId)
- {
- memberType = typeof(string);
- memberValue = !buildId.IsDefaultOrEmpty ? string.Concat(buildId.Select((b) => b.ToString("x2"))) : string.Empty;
- }
- else if (!memberType.IsPrimitive && !memberType.IsEnum)
- {
- memberType = typeof(string);
- memberValue = memberValue?.ToString() ?? string.Empty;
- }
- object testDataValue = testData.Value.GetValue(memberType);
- Trace.TraceInformation($"CompareMembers {testData.Key}: '{memberValue}' == '{testDataValue}'");
- Assert.Equal(memberValue, testDataValue);
- }
- }
- else
- {
- Trace.TraceWarning($"CompareMembers {testData.Key} member not found");
- return;
- }
- }
- else
- {
- Trace.TraceWarning($"CompareMembers {testData.Key} not found");
- }
- }
- }
- }
-}
+++ /dev/null
-using Microsoft.Diagnostics.DebugServices;
-using System;
-using System.Collections.Immutable;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Xml.Linq;
-
-namespace Microsoft.Diagnostics.DebugServices.UnitTests
-{
- public class TestDataWriter
- {
- public readonly XElement Root;
- public readonly XElement Target;
-
- /// <summary>
- /// Write a test data file from the target
- /// </summary>
- public TestDataWriter()
- {
- Root = new XElement("TestData");
- Root.Add(new XElement("Version", "1.0.0"));
- Target = new XElement("Target");
- Root.Add(Target);
- }
-
- public void Build(ITarget target)
- {
- AddMembers(Target, typeof(ITarget), target, nameof(ITarget.Id), nameof(ITarget.GetTempDirectory));
-
- var modulesElement = new XElement("Modules");
- Target.Add(modulesElement);
-
- var moduleService = target.Services.GetService<IModuleService>();
- string runtimeModuleName = target.GetPlatformModuleName("coreclr");
- foreach (IModule module in moduleService.EnumerateModules())
- {
- var moduleElement = new XElement("Module");
- modulesElement.Add(moduleElement);
- AddModuleMembers(moduleElement, module, runtimeModuleName);
- }
-
- var threadsElement = new XElement("Threads");
- Target.Add(threadsElement);
-
- var threadService = target.Services.GetService<IThreadService>();
- var registerIndexes = new int[] { threadService.InstructionPointerIndex, threadService.StackPointerIndex, threadService.FramePointerIndex };
- foreach (IThread thread in threadService.EnumerateThreads())
- {
- var threadElement = new XElement("Thread");
- threadsElement.Add(threadElement);
- AddMembers(threadElement, typeof(IThread), thread, nameof(IThread.ThreadIndex), nameof(IThread.GetThreadContext));
-
- var registersElement = new XElement("Registers");
- threadElement.Add(registersElement);
- foreach (int registerIndex in registerIndexes)
- {
- var registerElement = new XElement("Register");
- registersElement.Add(registerElement);
-
- if (threadService.TryGetRegisterInfo(registerIndex, out RegisterInfo info))
- {
- AddMembers(registerElement, typeof(RegisterInfo), info, nameof(Object.ToString), nameof(Object.GetHashCode));
- }
- if (thread.TryGetRegisterValue(registerIndex, out ulong value))
- {
- registerElement.Add(new XElement("Value", $"0x{value:X16}"));
- }
- }
- }
-
- var runtimesElement = new XElement("Runtimes");
- Target.Add(runtimesElement);
-
- var runtimeService = target.Services.GetService<IRuntimeService>();
- foreach (IRuntime runtime in runtimeService.EnumerateRuntimes())
- {
- var runtimeElement = new XElement("Runtime");
- runtimesElement.Add(runtimeElement);
- AddMembers(runtimeElement, typeof(IRuntime), runtime, nameof(IRuntime.GetDacFilePath), nameof(IRuntime.GetDbiFilePath));
-
- var runtimeModuleElement = new XElement("RuntimeModule");
- runtimeElement.Add(runtimeModuleElement);
- AddModuleMembers(runtimeModuleElement, runtime.RuntimeModule, symbolModuleName: null);
- }
- }
-
- public void Write(string testDataFile)
- {
- File.WriteAllText(testDataFile, Root.ToString());
- }
-
- private void AddModuleMembers(XElement element, IModule module, string symbolModuleName)
- {
- AddMembers(element, typeof(IModule), module, nameof(IModule.ModuleIndex), nameof(IModule.PdbFileInfos), nameof(IModule.VersionString));
-
- if (symbolModuleName != null && IsModuleEqual(module, symbolModuleName))
- {
- IExportSymbols exportSymbols = module.Services.GetService<IExportSymbols>();
- if (exportSymbols is not null)
- {
- XElement exportSymbolsElement = null;
-
- string symbol1 = "coreclr_initialize";
- if (exportSymbols.TryGetSymbolAddress(symbol1, out ulong offset1))
- {
- XElement symbolElement = AddExportSymbolSection();
- symbolElement.Add(new XElement("Name", symbol1));
- symbolElement.Add(new XElement("Value", ToHex(offset1)));
- }
- string symbol2 = "coreclr_execute_assembly";
- if (exportSymbols.TryGetSymbolAddress(symbol2, out ulong offset2))
- {
- XElement symbolElement = AddExportSymbolSection();
- symbolElement.Add(new XElement("Name", symbol2));
- symbolElement.Add(new XElement("Value", ToHex(offset2)));
- }
-
- XElement AddExportSymbolSection()
- {
- if (exportSymbolsElement == null)
- {
- exportSymbolsElement = new XElement("ExportSymbols");
- element.Add(exportSymbolsElement);
- }
- var symbolElement = new XElement("Symbol");
- exportSymbolsElement.Add(symbolElement);
- return symbolElement;
- }
- }
-
- IModuleSymbols moduleSymbols = module.Services.GetService<IModuleSymbols>();
- if (moduleSymbols is not null)
- {
- XElement symbolsElement = null;
-
- string symbol1 = "coreclr_initialize";
- if (moduleSymbols.TryGetSymbolAddress(symbol1, out ulong offset1))
- {
- XElement symbolElement = AddExportSymbolSection();
- symbolElement.Add(new XElement("Name", symbol1));
- symbolElement.Add(new XElement("Value", ToHex(offset1)));
- symbolElement.Add(new XElement("Displacement", "0"));
- }
-
- XElement AddExportSymbolSection()
- {
- if (symbolsElement == null)
- {
- symbolsElement = new XElement("Symbols");
- element.Add(symbolsElement);
- }
- var symbolElement = new XElement("Symbol");
- symbolsElement.Add(symbolElement);
- return symbolElement;
- }
- }
- }
- }
-
- private void AddMembers(XElement element, Type type, object instance, params string[] membersToSkip)
- {
- MemberInfo[] members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance);
- foreach (MemberInfo member in members)
- {
- if (membersToSkip.Any((skip) => member.Name == skip)) {
- continue;
- }
- string result = null;
- object memberValue = null;
- Type memberType = null;
-
- switch (member.MemberType)
- {
- case MemberTypes.Property:
- memberValue = ((PropertyInfo)member).GetValue(instance);
- memberType = ((PropertyInfo)member).PropertyType;
- break;
- case MemberTypes.Field:
- memberValue = ((FieldInfo)member).GetValue(instance);
- memberType = ((FieldInfo)member).FieldType;
- break;
- case MemberTypes.Method:
- MethodInfo methodInfo = (MethodInfo)member;
- if (!methodInfo.IsSpecialName && methodInfo.GetParameters().Length == 0 && methodInfo.ReturnType != typeof(void))
- {
- memberValue = ((MethodInfo)member).Invoke(instance, null);
- memberType = ((MethodInfo)member).ReturnType;
- }
- break;
- }
- if (memberType != null)
- {
- Type nullableType = Nullable.GetUnderlyingType(memberType);
- memberType = nullableType ?? memberType;
-
- if (nullableType != null && memberValue == null)
- {
- result = "";
- }
- else if (memberType == typeof(string))
- {
- result = (string)memberValue ?? "";
- }
- else if (memberType == typeof(bool))
- {
- result = (bool)memberValue ? "true" : "false";
- }
- else if (memberValue is ImmutableArray<byte> buildId)
- {
- if (!buildId.IsDefaultOrEmpty)
- {
- result = string.Concat(buildId.Select((b) => b.ToString("x2")));
- }
- }
- else if (memberType.IsEnum)
- {
- result = memberValue.ToString();
- }
- else if (memberType.IsPrimitive)
- {
- if (memberType == typeof(short) || memberType == typeof(int) || memberType == typeof(long))
- {
- result = memberValue.ToString();
- }
- else
- {
- int digits = Marshal.SizeOf(memberType) * 2;
- result = string.Format($"0x{{0:X{digits}}}", memberValue);
- }
- }
- else if (memberType.IsValueType || memberType == typeof(VersionData) || memberType == typeof(PdbFileInfo))
- {
- result = memberValue?.ToString();
- }
- }
- if (result != null)
- {
- element.Add(new XElement(member.Name, result));
- }
- }
- }
-
- private string ToHex<T>(T value) where T : struct
- {
- int digits = Marshal.SizeOf(typeof(T)) * 2;
- return string.Format($"0x{{0:X{digits}}}", value);
- }
-
- private bool IsModuleEqual(IModule module, string moduleName)
- {
- if (module.Target.OperatingSystem == OSPlatform.Windows) {
- return StringComparer.OrdinalIgnoreCase.Equals(Path.GetFileName(module.FileName), moduleName);
- }
- else {
- return string.Equals(Path.GetFileName(module.FileName), moduleName);
- }
- }
- }
-}
-using Microsoft.Diagnostics.Repl;
-using Microsoft.Diagnostics.Runtime;
+using Microsoft.Diagnostics.Runtime;
using Microsoft.Diagnostics.Runtime.Interop;
using Microsoft.Diagnostics.Runtime.Utilities;
using Microsoft.Diagnostics.TestHelpers;
+++ /dev/null
-using Microsoft.Diagnostics.DebugServices.Implementation;
-using Microsoft.Diagnostics.Runtime;
-using Microsoft.Diagnostics.TestHelpers;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.InteropServices;
-
-namespace Microsoft.Diagnostics.DebugServices.UnitTests
-{
- public class TestDump : TestHost, IHost
- {
- private readonly ServiceProvider _serviceProvider;
- private readonly ContextService _contextService;
- private readonly SymbolService _symbolService;
- private DataTarget _dataTarget;
- private int _targetIdFactory;
-
- public TestDump(TestConfiguration config)
- : base(config)
- {
- _serviceProvider = new ServiceProvider();
- _contextService = new ContextService(this);
- _symbolService = new SymbolService(this);
- _serviceProvider.AddService<IContextService>(_contextService);
- _serviceProvider.AddService<ISymbolService>(_symbolService);
-
- // Automatically enable symbol server support
- _symbolService.AddSymbolServer(msdl: true, symweb: false, symbolServerPath: null, authToken: null, timeoutInMinutes: 0);
- _symbolService.AddCachePath(_symbolService.DefaultSymbolCache);
- }
-
- protected override ITarget GetTarget()
- {
- _dataTarget = DataTarget.LoadDump(DumpFile);
-
- OSPlatform targetPlatform = _dataTarget.DataReader.TargetPlatform;
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
- targetPlatform = OSPlatform.OSX;
- }
- _symbolService.AddDirectoryPath(Path.GetDirectoryName(DumpFile));
- return new TargetFromDataReader(_dataTarget.DataReader, targetPlatform, this, _targetIdFactory++, DumpFile);
- }
-
- #region IHost
-
- public IServiceEvent OnShutdownEvent { get; } = new ServiceEvent();
-
- HostType IHost.HostType => HostType.DotnetDump;
-
- IServiceProvider IHost.Services => _serviceProvider;
-
- IEnumerable<ITarget> IHost.EnumerateTargets() => Target != null ? new ITarget[] { Target } : Array.Empty<ITarget>();
-
- void IHost.DestroyTarget(ITarget target)
- {
- if (target == null) {
- throw new ArgumentNullException(nameof(target));
- }
- if (target == Target)
- {
- _contextService.ClearCurrentTarget();
- if (target is IDisposable disposable) {
- disposable.Dispose();
- }
- }
- }
-
- #endregion
- }
-}
+++ /dev/null
-using Microsoft.Diagnostics.TestHelpers;
-using System.IO;
-
-namespace Microsoft.Diagnostics.DebugServices.UnitTests
-{
- public abstract class TestHost
- {
- private TestDataReader _testData;
- private ITarget _target;
-
- public readonly TestConfiguration Config;
-
- public TestHost(TestConfiguration config)
- {
- Config = config;
- }
-
- public static TestHost CreateHost(TestConfiguration config)
- {
- if (config.IsTestDbgEng())
- {
- return new TestDbgEng(config);
- }
- else
- {
- return new TestDump(config);
- }
- }
-
- public TestDataReader TestData
- {
- get
- {
- _testData ??= new TestDataReader(TestDataFile);
- return _testData;
- }
- }
-
- public ITarget Target
- {
- get
- {
- _target ??= GetTarget();
- return _target;
- }
- }
-
- public bool IsTestDbgEng => Config.AllSettings.TryGetValue("TestDbgEng", out string value) && value == "true";
-
- protected abstract ITarget GetTarget();
-
- public string DumpFile => TestConfiguration.MakeCanonicalPath(Config.AllSettings["DumpFile"]);
-
- public string TestDataFile => TestConfiguration.MakeCanonicalPath(Config.AllSettings["TestDataFile"]);
-
- public override string ToString() => DumpFile;
- }
-
- public static class TestHostExtensions
- {
- public static bool IsTestDbgEng(this TestConfiguration config) => config.AllSettings.TryGetValue("TestDbgEng", out string value) && value == "true";
- }
-}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.TestHelpers;
namespace Microsoft.Diagnostics.DebugServices.UnitTests
{