From: Mike McLaughlin Date: Sun, 17 Feb 2019 20:12:13 +0000 (-0800) Subject: Hosting SOS under ClrMd for ELF dumps. X-Git-Tag: submit/tizen/20190813.035844~56^2~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4cb670b3b5c2e9f2ce5805452263b2d16fb2935c;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Hosting SOS under ClrMd for ELF dumps. --- diff --git a/diagnostics.sln b/diagnostics.sln index a4f7cac45..fa4e6d875 100644 --- a/diagnostics.sln +++ b/diagnostics.sln @@ -597,6 +597,46 @@ Global {41351955-16D5-48D7-AF4C-AF25F5FB2E78}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {41351955-16D5-48D7-AF4C-AF25F5FB2E78}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU {41351955-16D5-48D7-AF4C-AF25F5FB2E78}.RelWithDebInfo|x86.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|Any CPU.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|ARM.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|ARM.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|ARM64.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|ARM64.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|x64.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|x64.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|x86.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Checked|x86.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|ARM.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|ARM.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|ARM64.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|x64.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|x64.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|x86.ActiveCfg = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Debug|x86.Build.0 = Debug|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|Any CPU.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|ARM.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|ARM.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|ARM64.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|ARM64.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|x64.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|x64.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|x86.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.Release|x86.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|ARM64.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|ARM64.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {90CF2633-58F0-44EE-943B-D70207455F20}.Checked|Any CPU.ActiveCfg = Debug|Any CPU {90CF2633-58F0-44EE-943B-D70207455F20}.Checked|Any CPU.Build.0 = Debug|Any CPU {90CF2633-58F0-44EE-943B-D70207455F20}.Checked|ARM.ActiveCfg = Debug|Any CPU @@ -659,6 +699,7 @@ Global {20EBC3C4-917C-402D-B778-9A6E3742BF5A} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7} {1F012743-941B-4915-8C55-02097894CF3F} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7} {41351955-16D5-48D7-AF4C-AF25F5FB2E78} = {B62728C8-1267-4043-B46F-5537BBAEC692} + {ED27F39F-DF5C-4E22-87E0-EC5B5873B503} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7} {90CF2633-58F0-44EE-943B-D70207455F20} = {19FAB78C-3351-4911-8F0C-8C6056401740} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/eng/Build-Native.cmd b/eng/Build-Native.cmd index a810a095b..e847566b0 100644 --- a/eng/Build-Native.cmd +++ b/eng/Build-Native.cmd @@ -331,6 +331,13 @@ if /i "%__DoCrossArchBuild%"=="1" ( endlocal ) +REM Copy the native SOS binaries to where these tools expect for testing + +set "__dotnet_sos=%__RootBinDir%\bin\dotnet-sos\%__BuildType%\netcoreapp2.1\publish\win-%__BuildArch%" +set "__dotnet_dump=%__RootBinDir%\bin\dotnet-dump\%__BuildType%\netcoreapp2.1\publish\win-%__BuildArch%" +xcopy /y /q /i /s %__BinDir% %__dotnet_sos% +xcopy /y /q /i /s %__BinDir% %__dotnet_dump% + REM ========================================================================================= REM === REM === All builds complete! diff --git a/eng/build-native.sh b/eng/build-native.sh index deb6c858c..473915a19 100755 --- a/eng/build-native.sh +++ b/eng/build-native.sh @@ -469,6 +469,14 @@ if [ $__Build == true ]; then fi build_native "$__BuildArch" "$__IntermediatesDir" "$__ExtraCmakeArgs" + + # Copy the native SOS binaries to where these tools expect for testing + __dotnet_sos=$__RootBinDir/bin/dotnet-sos/$__BuildType/netcoreapp2.1/publish/$__DistroRid + __dotnet_dump=$__RootBinDir/bin/dotnet-dump/$__BuildType/netcoreapp2.1/publish/$__DistroRid + mkdir -p "$__dotnet_sos" + mkdir -p "$__dotnet_dump" + cp "$__BinDir"/* "$__dotnet_sos" + cp "$__BinDir"/* "$__dotnet_dump" fi # Run SOS/lldbplugin tests diff --git a/src/SOS/SOS.Hosting/Amd64Context.cs b/src/SOS/SOS.Hosting/Amd64Context.cs new file mode 100644 index 000000000..c7030aa53 --- /dev/null +++ b/src/SOS/SOS.Hosting/Amd64Context.cs @@ -0,0 +1,114 @@ +// 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.Runtime.InteropServices; + +namespace SOS +{ + [StructLayout(LayoutKind.Explicit)] + internal struct AMD64Context + { + [FieldOffset(0x0)] + public ulong P1Home; + [FieldOffset(0x8)] + public ulong P2Home; + [FieldOffset(0x10)] + public ulong P3Home; + [FieldOffset(0x18)] + public ulong P4Home; + [FieldOffset(0x20)] + public ulong P5Home; + [FieldOffset(0x28)] + public ulong P6Home; + + [FieldOffset(0x30)] + public int ContextFlags; + + [FieldOffset(0x34)] + public int MxCsr; + + [FieldOffset(0x38)] + public short SegCs; + [FieldOffset(0x3a)] + public short SegDs; + [FieldOffset(0x3c)] + public short SegEs; + [FieldOffset(0x3e)] + public short SegFs; + [FieldOffset(0x40)] + public short SegGs; + [FieldOffset(0x42)] + public short SegSs; + [FieldOffset(0x44)] + public int EFlags; + + [FieldOffset(0x48)] + public ulong Dr0; + [FieldOffset(0x50)] + public ulong Dr1; + [FieldOffset(0x58)] + public ulong Dr2; + [FieldOffset(0x60)] + public ulong Dr3; + [FieldOffset(0x68)] + public ulong Dr6; + [FieldOffset(0x70)] + public ulong Dr7; + + [FieldOffset(0x78)] + public ulong Rax; + [FieldOffset(0x80)] + public ulong Rcx; + [FieldOffset(0x88)] + public ulong Rdx; + [FieldOffset(0x90)] + public ulong Rbx; + [FieldOffset(0x98)] + public ulong Rsp; + [FieldOffset(0xa0)] + public ulong Rbp; + [FieldOffset(0xa8)] + public ulong Rsi; + [FieldOffset(0xb0)] + public ulong Rdi; + [FieldOffset(0xb8)] + public ulong R8; + [FieldOffset(0xc0)] + public ulong R9; + [FieldOffset(0xc8)] + public ulong R10; + [FieldOffset(0xd0)] + public ulong R11; + [FieldOffset(0xd8)] + public ulong R12; + [FieldOffset(0xe0)] + public ulong R13; + [FieldOffset(0xe8)] + public ulong R14; + [FieldOffset(0xf0)] + public ulong R15; + + [FieldOffset(0xf8)] + public ulong Rip; + + //[FieldOffset(0x100)] + //public XmmSaveArea FltSave; + + //[FieldOffset(0x300)] + //public VectorRegisterArea VectorRegisters; + + [FieldOffset(0x4a8)] + public ulong DebugControl; + [FieldOffset(0x4b0)] + public ulong LastBranchToRip; + [FieldOffset(0x4b8)] + public ulong LastBranchFromRip; + [FieldOffset(0x4c0)] + public ulong LastExceptionToRip; + [FieldOffset(0x4c8)] + public ulong LastExceptionFromRip; + + public static int Size => Marshal.SizeOf(typeof(AMD64Context)); + } +} \ No newline at end of file diff --git a/src/SOS/SOS.Hosting/AssemblyResolver.cs b/src/SOS/SOS.Hosting/AssemblyResolver.cs new file mode 100644 index 000000000..a6734c7e2 --- /dev/null +++ b/src/SOS/SOS.Hosting/AssemblyResolver.cs @@ -0,0 +1,102 @@ +// 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.Diagnostics; +using System.IO; +using System.Reflection; + +namespace SOS +{ + /// + /// Used to enable app-local assembly unification. + /// + internal static class AssemblyResolver + { + static AssemblyResolver() + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + } + + /// + /// Call to enable the assembly resolver for the current AppDomain. + /// + public static void Enable() + { + // intentionally empty. This is just meant to ensure the static constructor + // has run. + } + + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + // apply any existing policy + AssemblyName referenceName = new AssemblyName(AppDomain.CurrentDomain.ApplyPolicy(args.Name)); + + string fileName = referenceName.Name + ".dll"; + string assemblyPath = null; + string probingPath = null; + Assembly assm = null; + + // look next to requesting assembly + assemblyPath = args.RequestingAssembly?.Location; + if (!String.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look next to the executing assembly + assemblyPath = Assembly.GetExecutingAssembly().Location; + if (!String.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + + Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look in AppDomain base directory + probingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + Debug.WriteLine($"Considering {probingPath} based on BaseDirectory"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + + return null; + } + + /// + /// Considers a path to load for satisfying an assembly ref and loads it + /// if the file exists and version is sufficient. + /// + /// Path to consider for load + /// Minimum version to consider + /// loaded assembly + /// true if assembly was loaded + private static bool Probe(string filePath, Version minimumVersion, out Assembly assembly) + { + if (File.Exists(filePath)) + { + AssemblyName name = AssemblyName.GetAssemblyName(filePath); + + if (name.Version >= minimumVersion) + { + assembly = Assembly.LoadFile(filePath); + return true; + } + } + + assembly = null; + return false; + } + } +} diff --git a/src/SOS/SOS.Hosting/ISOSHostContext.cs b/src/SOS/SOS.Hosting/ISOSHostContext.cs new file mode 100644 index 000000000..3157a3036 --- /dev/null +++ b/src/SOS/SOS.Hosting/ISOSHostContext.cs @@ -0,0 +1,30 @@ +// 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.Threading; + +namespace SOS +{ + /// + /// Context/services provided to the SOS host. + /// + public interface ISOSHostContext + { + /// + /// Display text on the console + /// + /// message + void Write(string text); + + /// + /// Get/set the current native thread id + /// + int CurrentThreadId { get; set; } + + /// + /// Cancellation token for current operation + /// + CancellationToken CancellationToken { get; } + } +} \ No newline at end of file diff --git a/src/SOS/SOS.Hosting/LLDBServicesWrapper.cs b/src/SOS/SOS.Hosting/LLDBServicesWrapper.cs new file mode 100644 index 000000000..74670f59c --- /dev/null +++ b/src/SOS/SOS.Hosting/LLDBServicesWrapper.cs @@ -0,0 +1,1065 @@ +// 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.Interop; +using Microsoft.Diagnostics.Runtime.Utilities; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace SOS +{ + internal unsafe class LLDBServicesWrapper : COMCallableIUnknown + { + private static readonly Guid IID_ILLDBServices = new Guid("2E6C569A-9E14-4DA4-9DFC-CDB73A532566"); + private static readonly Guid IID_ILLDBServices2 = new Guid("012F32F0-33BA-4E8E-BC01-037D382D8A5E"); + private static readonly Guid IID_SOSHostServices = new Guid("D13608FB-AD14-4B49-990A-80284F934C41"); + + public IntPtr ILLDBServices { get; } + + #region SOS.NETCore function delegates + + private delegate bool InitializeSymbolStoreDelegate( + bool logging, + bool msdl, + bool symweb, + string symbolServerPath, + string symbolCachePath, + string windowsSymbolPath); + + private delegate void DisplaySymbolStoreDelegate(); + + private delegate void DisableSymbolStoreDelegate(); + + private delegate void LoadNativeSymbolsDelegate( + SymbolReader.SymbolFileCallback callback, + IntPtr parameter, + string tempDirectory, + string moduleFilePath, + ulong address, + int size, + SymbolReader.ReadMemoryDelegate readMemory); + + private delegate IntPtr LoadSymbolsForModuleDelegate( + string assemblyPath, + bool isFileLayout, + ulong loadedPeAddress, + int loadedPeSize, + ulong inMemoryPdbAddress, + int inMemoryPdbSize, + SymbolReader.ReadMemoryDelegate readMemory); + + private delegate void DisposeDelegate(IntPtr symbolReaderHandle); + + private delegate bool ResolveSequencePointDelegate( + IntPtr symbolReaderHandle, + string filePath, + int lineNumber, + out int methodToken, + out int ilOffset); + + private delegate bool GetLineByILOffsetDelegate( + IntPtr symbolReaderHandle, + int methodToken, + long ilOffset, + out int lineNumber, + out IntPtr fileName); + + private delegate bool GetLocalVariableNameDelegate( + IntPtr symbolReaderHandle, + int methodToken, + int localIndex, + out IntPtr localVarName); + + #endregion + + /// + /// Used by ISOSHostServices.GetSOSNETCoreCallbacks. + /// + [StructLayout(LayoutKind.Sequential)] + struct SOSNetCoreCallbacks + { + public InitializeSymbolStoreDelegate InitializeSymbolStoreDelegate; + public DisplaySymbolStoreDelegate DisplaySymbolStoreDelegate; + public DisableSymbolStoreDelegate DisableSymbolStoreDelegate; + public LoadNativeSymbolsDelegate LoadNativeSymbolsDelegate; + public LoadSymbolsForModuleDelegate LoadSymbolsForModuleDelegate; + public DisposeDelegate DisposeDelegate; + public ResolveSequencePointDelegate ResolveSequencePointDelegate; + public GetLineByILOffsetDelegate GetLineByILOffsetDelegate; + public GetLocalVariableNameDelegate GetLocalVariableNameDelegate; + } + + static SOSNetCoreCallbacks s_callbacks = new SOSNetCoreCallbacks { + InitializeSymbolStoreDelegate = SymbolReader.InitializeSymbolStore, + DisplaySymbolStoreDelegate = SymbolReader.DisplaySymbolStore, + DisableSymbolStoreDelegate = SymbolReader.DisableSymbolStore, + LoadNativeSymbolsDelegate = SymbolReader.LoadNativeSymbols, + LoadSymbolsForModuleDelegate = SymbolReader.LoadSymbolsForModule, + DisposeDelegate = SymbolReader.Dispose, + ResolveSequencePointDelegate = SymbolReader.ResolveSequencePoint, + GetLineByILOffsetDelegate = SymbolReader.GetLineByILOffset, + GetLocalVariableNameDelegate = SymbolReader.GetLocalVariableName, + }; + + static readonly string s_coreclrModuleName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "coreclr" : "libcoreclr.so"; + + readonly SOSHost _sosHost; + readonly IDataReader _dataReader; + readonly ISOSHostContext _context; + + /// + /// Create an instance of the service wrapper SOS uses. + /// + /// clrmd data reader instance + /// sos hosting context + public LLDBServicesWrapper(SOSHost host, IDataReader dataReader, ISOSHostContext context) + { + _sosHost = host; + _dataReader = dataReader; + _context = context; + + VTableBuilder builder = AddInterface(IID_ILLDBServices, validate: false); + + builder.AddMethod(new GetCoreClrDirectoryDelegate(GetCoreClrDirectory)); + builder.AddMethod(new GetExpressionDelegate(GetExpression)); + builder.AddMethod(new VirtualUnwindDelegate(VirtualUnwind)); + builder.AddMethod(new SetExceptionCallbackDelegate(SetExceptionCallback)); + builder.AddMethod(new ClearExceptionCallbackDelegate(ClearExceptionCallback)); + + builder.AddMethod(new GetInterruptDelegate(GetInterrupt)); + builder.AddMethod(new OutputVaListDelegate(OutputVaList)); + builder.AddMethod(new GetDebuggerTypeDelegate(GetDebuggerType)); + builder.AddMethod(new GetPageSizeDelegate(GetPageSize)); + builder.AddMethod(new GetExecutingProcessorTypeDelegate(GetExecutingProcessorType)); + builder.AddMethod(new ExecuteDelegate(Execute)); + builder.AddMethod(new GetLastEventInformationDelegate(GetLastEventInformation)); + builder.AddMethod(new DisassembleDelegate(Disassemble)); + + builder.AddMethod(new GetContextStackTraceDelegate(GetContextStackTrace)); + builder.AddMethod(new ReadVirtualDelegate(ReadVirtual)); + builder.AddMethod(new WriteVirtualDelegate(WriteVirtual)); + + builder.AddMethod(new GetSymbolOptionsDelegate(GetSymbolOptions)); + builder.AddMethod(new GetNameByOffsetDelegate(GetNameByOffset)); + builder.AddMethod(new GetNumberModulesDelegate(GetNumberModules)); + builder.AddMethod(new GetModuleByIndexDelegate(GetModuleByIndex)); + builder.AddMethod(new GetModuleByModuleNameDelegate(GetModuleByModuleName)); + builder.AddMethod(new GetModuleByOffsetDelegate(GetModuleByOffset)); + builder.AddMethod(new GetModuleNamesDelegate(GetModuleNames)); + builder.AddMethod(new GetLineByOffsetDelegate(GetLineByOffset)); + builder.AddMethod(new GetSourceFileLineOffsetsDelegate(GetSourceFileLineOffsets)); + builder.AddMethod(new FindSourceFileDelegate(FindSourceFile)); + + builder.AddMethod(new GetCurrentProcessIdDelegate(GetCurrentProcessId)); + builder.AddMethod(new GetCurrentThreadIdDelegate(GetCurrentThreadId)); + builder.AddMethod(new SetCurrentThreadIdDelegate(SetCurrentThreadId)); + builder.AddMethod(new GetCurrentThreadSystemIdDelegate(GetCurrentThreadSystemId)); + builder.AddMethod(new GetThreadIdBySystemIdDelegate(GetThreadIdBySystemId)); + builder.AddMethod(new GetThreadContextByIdDelegate(GetThreadContextById)); + + builder.AddMethod(new GetValueByNameDelegate(GetValueByName)); + builder.AddMethod(new GetInstructionOffsetDelegate(GetInstructionOffset)); + builder.AddMethod(new GetStackOffsetDelegate(GetStackOffset)); + builder.AddMethod(new GetFrameOffsetDelegate(GetFrameOffset)); + + ILLDBServices = builder.Complete(); + + builder = AddInterface(IID_ILLDBServices2, validate: false); + builder.AddMethod(new LoadNativeSymbolsDelegate2(LoadNativeSymbols2)); + builder.AddMethod(new AddModuleSymbolDelegate(AddModuleSymbol)); + builder.Complete(); + + builder = AddInterface(IID_SOSHostServices, validate: false); + builder.AddMethod(new GetSOSNETCoreCallbacksDelegate(GetSOSNETCoreCallbacks)); + builder.Complete(); + + AddRef(); + } + + #region ILLDBServices + + string GetCoreClrDirectory( + IntPtr self) + { + foreach (ModuleInfo module in _dataReader.EnumerateModules()) + { + if (string.Equals(Path.GetFileName(module.FileName), s_coreclrModuleName)) + { + return Path.GetDirectoryName(module.FileName) + Path.DirectorySeparatorChar; + } + } + return null; + } + + ulong GetExpression( + IntPtr self, + string text) + { + if (ulong.TryParse(text.Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong result)) { + return result; + } + return 0; + } + + int VirtualUnwind( + IntPtr self, + uint threadId, + uint contextSize, + byte[] context) + { + return E_NOTIMPL; + } + + int SetExceptionCallback( + IntPtr self, + PFN_EXCEPTION_CALLBACK callback) + { + return S_OK; + } + + int ClearExceptionCallback( + IntPtr self) + { + return S_OK; + } + + int GetInterrupt( + IntPtr self) + { + return _context.CancellationToken.IsCancellationRequested ? S_OK : E_FAIL; + } + + int OutputVaList( + IntPtr self, + DEBUG_OUTPUT mask, + string format, + IntPtr va_list) + { + try + { + _context.Write(format); + } + catch (OperationCanceledException) + { + // ctrl-c interrupted the command + } + return S_OK; + } + + int GetDebuggerType( + IntPtr self, + out DEBUG_CLASS debugClass, + out DEBUG_CLASS_QUALIFIER qualifier) + { + debugClass = DEBUG_CLASS.USER_WINDOWS; + qualifier = DEBUG_CLASS_QUALIFIER.USER_WINDOWS_DUMP; + return S_OK; + } + + int GetPageSize( + IntPtr self, + out uint size) + { + size = 4096; + return S_OK; + } + + int GetExecutingProcessorType( + IntPtr self, + out IMAGE_FILE_MACHINE type) + { + switch (_dataReader.GetArchitecture()) + { + case Microsoft.Diagnostics.Runtime.Architecture.Amd64: + type = IMAGE_FILE_MACHINE.AMD64; + break; + case Microsoft.Diagnostics.Runtime.Architecture.X86: + type = IMAGE_FILE_MACHINE.I386; + break; + case Microsoft.Diagnostics.Runtime.Architecture.Arm: + type = IMAGE_FILE_MACHINE.ARM; + break; + default: + type = IMAGE_FILE_MACHINE.UNKNOWN; + break; + } + return S_OK; + } + + int Execute( + IntPtr self, + DEBUG_OUTCTL outputControl, + string command, + DEBUG_EXECUTE flags) + { + return E_NOTIMPL; + } + + int GetLastEventInformation( + IntPtr self, + out uint type, + out uint processId, + out uint threadId, + IntPtr extraInformation, + uint extraInformationSize, + out uint extraInformationUsed, + string description, + uint descriptionSize, + out uint descriptionUsed) + { + type = 0; + processId = 0; + threadId = 0; + extraInformationSize = 0; + extraInformationUsed = 0; + descriptionUsed = 0; + return E_NOTIMPL; + } + + int Disassemble( + IntPtr self, + ulong offset, + DEBUG_DISASM flags, + StringBuilder buffer, + uint bufferSize, + out uint disassemblySize, + out ulong endOffset) + { + disassemblySize = 0; + endOffset = 0; + return E_NOTIMPL; + } + + int GetContextStackTrace( + IntPtr self, + IntPtr startContext, + uint startContextSize, + DEBUG_STACK_FRAME[] frames, + uint framesSize, + IntPtr frameContexts, + uint frameContextsSize, + uint frameContextsEntrySize, + IntPtr pframesFilled) + { + return E_NOTIMPL; + } + + int ReadVirtual( + IntPtr self, + ulong address, + IntPtr buffer, + int bytesRequested, + IntPtr pbytesRead) + { + if (_dataReader.ReadMemory(address, buffer, bytesRequested, out int bytesRead)) + { + if (pbytesRead != IntPtr.Zero) + { + Marshal.WriteInt32(pbytesRead, bytesRead); + } + return S_OK; + } + return E_FAIL; + } + + int WriteVirtual( + IntPtr self, + ulong address, + IntPtr buffer, + uint bytesRequested, + IntPtr pbytesWritten) + { + // This gets used by MemoryBarrier() calls in the dac, which really shouldn't matter what we do here. + if (pbytesWritten != IntPtr.Zero) { + Marshal.WriteInt32(pbytesWritten, (int)bytesRequested); + } + return S_OK; + } + + int GetSymbolOptions( + IntPtr self, + out SYMOPT options) + { + options = SYMOPT.LOAD_LINES; + return S_OK; + } + + int GetNameByOffset( + IntPtr self, + ulong offset, + StringBuilder nameBuffer, + uint nameBufferSize, + out uint nameSize, + out ulong displacement) + { + nameSize = 0; + displacement = 0; + return E_NOTIMPL; + } + + int GetNumberModules( + IntPtr self, + out uint loaded, + out uint unloaded) + { + loaded = (uint)_dataReader.EnumerateModules().Count(); + unloaded = 0; + return S_OK; + } + + int GetModuleByIndex( + IntPtr self, + uint index, + out ulong baseAddress) + { + baseAddress = 0; + + try + { + ModuleInfo module = _dataReader.EnumerateModules().ElementAt((int)index); + if (module == null) + { + return E_FAIL; + } + baseAddress = module.ImageBase; + } + catch (ArgumentOutOfRangeException) + { + return E_FAIL; + } + + return S_OK; + } + + int GetModuleByModuleName( + IntPtr self, + string name, + uint startIndex, + IntPtr pIndex, + out ulong baseAddress) + { + // The returned "index" is never used by SOS. Always passes startIndex = 0; + Debug.Assert(pIndex == IntPtr.Zero); + Debug.Assert(startIndex == 0); + + baseAddress = 0; + + foreach (ModuleInfo module in _dataReader.EnumerateModules()) + { + if (string.Equals(Path.GetFileName(module.FileName), name)) + { + baseAddress = module.ImageBase; + return S_OK; + } + } + return E_FAIL; + } + + int GetModuleByOffset( + IntPtr self, + ulong offset, + uint startIndex, + out uint index, + out ulong baseAddress) + { + index = 0; + baseAddress = 0; + return E_NOTIMPL; + } + + int GetModuleNames( + IntPtr self, + uint index, + ulong baseAddress, + StringBuilder imageNameBuffer, + uint imageNameBufferSize, + out uint imageNameSize, + StringBuilder moduleNameBuffer, + uint ModuleNameBufferSize, + out uint moduleNameSize, + StringBuilder loadedImageNameBuffer, + uint loadedImageNameBufferSize, + out uint loadedImageNameSize) + { + imageNameSize = 0; + moduleNameSize = 0; + loadedImageNameSize = 0; + return E_NOTIMPL; + } + + int GetLineByOffset( + IntPtr self, + ulong offset, + out uint line, + StringBuilder fileBuffer, + uint fileBufferSize, + out uint fileSize, + out ulong displacement) + { + line = 0; + fileSize = 0; + displacement = 0; + return E_NOTIMPL; + } + + int GetSourceFileLineOffsets( + IntPtr self, + string file, + ulong[] buffer, + uint bufferLines, + out uint fileLines) + { + fileLines = 0; + return E_NOTIMPL; + } + + int FindSourceFile( + IntPtr self, + uint startElement, + string file, + uint flags, + out uint foundElement, + StringBuilder buffer, + uint bufferSize, + out uint foundSize) + { + foundElement = 0; + foundSize = 0; + return E_NOTIMPL; + } + + int GetCurrentProcessId( + IntPtr self, + out uint id) + { + id = 0; + if (_dataReader is IDataReader2 dataReader2) { + id = dataReader2.ProcessId; + } + return S_OK; + } + + int GetCurrentThreadId( + IntPtr self, + out uint id) + { + return GetThreadIdBySystemId(self, (uint)_context.CurrentThreadId, out id); + } + + int SetCurrentThreadId( + IntPtr self, + uint id) + { + try + { + unchecked { + _context.CurrentThreadId = (int)_dataReader.EnumerateAllThreads().ElementAt((int)id); + } + } + catch (ArgumentOutOfRangeException) + { + return E_FAIL; + } + return S_OK; + } + + int GetCurrentThreadSystemId( + IntPtr self, + out uint sysId) + { + sysId = (uint)_context.CurrentThreadId; + return S_OK; + } + + int GetThreadIdBySystemId( + IntPtr self, + uint sysId, + out uint id) + { + id = 0; + if (sysId != 0) + { + foreach (uint s in _dataReader.EnumerateAllThreads()) + { + if (s == sysId) { + return S_OK; + } + id++; + } + } + return E_FAIL; + } + + int GetThreadContextById( + IntPtr self, + uint threadId, + uint contextFlags, + uint contextSize, + IntPtr context) + { + if (_dataReader.GetThreadContext(threadId, contextFlags, contextSize, context)) { + return S_OK; + } + return E_FAIL; + } + + int GetValueByName( + IntPtr self, + string name, + out ulong value) + { + return GetRegister(name, out value); + } + + int GetInstructionOffset( + IntPtr self, + out ulong offset) + { + // TODO: Support other architectures + return GetRegister("rip", out offset); + } + + int GetStackOffset( + IntPtr self, + out ulong offset) + { + // TODO: Support other architectures + return GetRegister("rsp", out offset); + } + + int GetFrameOffset( + IntPtr self, + out ulong offset) + { + // TODO: Support other architectures + return GetRegister("rbp", out offset); + } + + #endregion + + // TODO: Support other architectures + int GetRegister(string register, out ulong value) + { + value = 0; + int hr = GetCurrentThreadSystemId(IntPtr.Zero, out uint threadId); + if (hr != 0) { + return hr; + } + byte[] buffer = new byte[AMD64Context.Size]; + if (!_dataReader.GetThreadContext(threadId, uint.MaxValue, (uint)AMD64Context.Size, buffer)) + { + return E_FAIL; + } + fixed (byte* ptr = buffer) + { + AMD64Context* context = (AMD64Context*)ptr; + switch (register.ToLower()) + { + case "rax": + value = context->Rax; + break; + case "rbx": + value = context->Rbx; + break; + case "rcx": + value = context->Rcx; + break; + case "rdx": + value = context->Rdx; + break; + case "rsi": + value = context->Rsi; + break; + case "rdi": + value = context->Rdi; + break; + case "r8": + value = context->R8; + break; + case "r9": + value = context->R9; + break; + case "r10": + value = context->R10; + break; + case "r11": + value = context->R11; + break; + case "r12": + value = context->R12; + break; + case "r13": + value = context->R13; + break; + case "r14": + value = context->R14; + break; + case "r15": + value = context->R15; + break; + case "rip": + value = context->Rip; + break; + case "rsp": + value = context->Rsp; + break; + case "rbp": + value = context->Rbp; + break; + default: + return E_FAIL; + } + } + return S_OK; + } + + #region ILLDBServices2 + + int LoadNativeSymbols2( + IntPtr self, + bool runtimeOnly, + ModuleLoadCallback callback) + { + foreach (ModuleInfo module in _dataReader.EnumerateModules()) + { + callback(IntPtr.Zero, module.FileName, module.ImageBase, unchecked((int)module.FileSize)); + } + return S_OK; + } + + int AddModuleSymbol( + IntPtr self, + IntPtr parameter, + string symbolFilename) + { + return S_OK; + } + + #endregion + + #region ISOSHostServices + + int GetSOSNETCoreCallbacks( + IntPtr self, + int version, + IntPtr pCallbacks) + { + if (version < 1) + { + return E_FAIL; + } + try + { + Marshal.StructureToPtr(s_callbacks, pCallbacks, false); + } + catch (ArgumentException) + { + return E_FAIL; + } + return S_OK; + } + + #endregion + + #region ILLDBServices delegates + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.LPStr)] + private delegate string GetCoreClrDirectoryDelegate( + IntPtr self); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate ulong GetExpressionDelegate( + IntPtr self, + [In][MarshalAs(UnmanagedType.LPStr)] string text); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int VirtualUnwindDelegate( + IntPtr self, + uint threadId, + uint contextSize, + byte[] context); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int PFN_EXCEPTION_CALLBACK(LLDBServicesWrapper services); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int SetExceptionCallbackDelegate( + IntPtr self, + PFN_EXCEPTION_CALLBACK callback); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int ClearExceptionCallbackDelegate( + IntPtr self); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetInterruptDelegate( + IntPtr self); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int OutputVaListDelegate( + IntPtr self, + DEBUG_OUTPUT mask, + [In, MarshalAs(UnmanagedType.LPStr)] string format, + IntPtr va_list); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetDebuggerTypeDelegate( + IntPtr self, + out DEBUG_CLASS debugClass, + out DEBUG_CLASS_QUALIFIER qualifier); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetPageSizeDelegate( + IntPtr self, + out uint size); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetExecutingProcessorTypeDelegate( + IntPtr self, + out IMAGE_FILE_MACHINE type); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int ExecuteDelegate( + IntPtr self, + DEBUG_OUTCTL outputControl, + [In, MarshalAs(UnmanagedType.LPStr)] string command, + DEBUG_EXECUTE flags); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetLastEventInformationDelegate( + IntPtr self, + out uint type, + out uint processId, + out uint threadId, + IntPtr extraInformation, + uint extraInformationSize, + out uint extraInformationUsed, + [In][MarshalAs(UnmanagedType.LPStr)] string description, + uint descriptionSize, + out uint descriptionUsed); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int DisassembleDelegate( + IntPtr self, + ulong offset, + DEBUG_DISASM flags, + [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer, + uint bufferSize, + out uint disassemblySize, + out ulong endOffset); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetContextStackTraceDelegate( + IntPtr self, + IntPtr startContext, + uint startContextSize, + [Out, MarshalAs(UnmanagedType.LPArray)] DEBUG_STACK_FRAME[] frames, + uint framesSize, + IntPtr frameContexts, + uint frameContextsSize, + uint frameContextsEntrySize, + IntPtr pframesFilled); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int ReadVirtualDelegate( + IntPtr self, + ulong address, + IntPtr buffer, + int bytesRequested, + IntPtr pbytesRead); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int WriteVirtualDelegate( + IntPtr self, + ulong address, + IntPtr buffer, + uint bytesRequested, + IntPtr pbytesWritten); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetSymbolOptionsDelegate( + IntPtr self, + out SYMOPT options); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetNameByOffsetDelegate( + IntPtr self, + ulong offset, + [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder nameBuffer, + uint nameBufferSize, + out uint nameSize, + out ulong displacement); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetNumberModulesDelegate( + IntPtr self, + out uint loaded, + out uint unloaded); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetModuleByIndexDelegate( + IntPtr self, + uint index, + out ulong baseAddress); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetModuleByModuleNameDelegate( + IntPtr self, + [In, MarshalAs(UnmanagedType.LPStr)] string name, + uint startIndex, + IntPtr index, + out ulong baseAddress); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetModuleByOffsetDelegate( + IntPtr self, + ulong offset, + uint startIndex, + out uint index, + out ulong baseAddress); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetModuleNamesDelegate( + IntPtr self, + uint index, + ulong baseAddress, + [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder imageNameBuffer, + uint imageNameBufferSize, + out uint imageNameSize, + [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder moduleNameBuffer, + uint ModuleNameBufferSize, + out uint moduleNameSize, + [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder loadedImageNameBuffer, + uint loadedImageNameBufferSize, + out uint loadedImageNameSize); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetLineByOffsetDelegate( + IntPtr self, + ulong offset, + out uint line, + [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder fileBuffer, + uint fileBufferSize, + out uint fileSize, + out ulong displacement); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetSourceFileLineOffsetsDelegate( + IntPtr self, + [In, MarshalAs(UnmanagedType.LPStr)] string file, + [Out, MarshalAs(UnmanagedType.LPArray)] ulong[] buffer, + uint bufferLines, + out uint fileLines); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int FindSourceFileDelegate( + IntPtr self, + uint startElement, + [In, MarshalAs(UnmanagedType.LPStr)] string file, + uint flags, + out uint foundElement, + [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer, + uint bufferSize, + out uint foundSize); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetCurrentProcessIdDelegate( + IntPtr self, + out uint id); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetCurrentThreadIdDelegate( + IntPtr self, + out uint id); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int SetCurrentThreadIdDelegate( + IntPtr self, + uint id); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetCurrentThreadSystemIdDelegate( + IntPtr self, + out uint sysId); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetThreadIdBySystemIdDelegate( + IntPtr self, + uint sysId, + out uint id); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetThreadContextByIdDelegate( + IntPtr self, + uint threadId, + uint contextFlags, + uint contextSize, + IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetValueByNameDelegate( + IntPtr self, + [In, MarshalAs(UnmanagedType.LPStr)] string name, + out ulong value); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetInstructionOffsetDelegate( + IntPtr self, + out ulong offset); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetStackOffsetDelegate( + IntPtr self, + out ulong offset); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetFrameOffsetDelegate( + IntPtr self, + out ulong offset); + + #endregion + + #region ILLDBServices2 delegates + + /// + /// The LoadNativeSymbolsDelegate2 callback + /// + public delegate void ModuleLoadCallback( + IntPtr parameter, + [MarshalAs(UnmanagedType.LPStr)] string moduleFilePath, + ulong moduleAddress, + int moduleSize); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int LoadNativeSymbolsDelegate2( + IntPtr self, + bool runtimeOnly, + ModuleLoadCallback callback); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int AddModuleSymbolDelegate( + IntPtr self, + IntPtr parameter, + [MarshalAs(UnmanagedType.LPStr)] string symbolFilename); + + #endregion + + #region ISOSHostServices delegates + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetSOSNETCoreCallbacksDelegate( + IntPtr self, + int version, + IntPtr pCallbacks); + + #endregion + } +} \ No newline at end of file diff --git a/src/SOS/SOS.Hosting/SOS.Hosting.csproj b/src/SOS/SOS.Hosting/SOS.Hosting.csproj new file mode 100644 index 000000000..777fb9e6c --- /dev/null +++ b/src/SOS/SOS.Hosting/SOS.Hosting.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + SOS.Hosting + ;1591;1701 + SOS Hosting support + true + true + + + + + + + + + + + diff --git a/src/SOS/SOS.Hosting/SOSHost.cs b/src/SOS/SOS.Hosting/SOSHost.cs new file mode 100644 index 000000000..9df525ee9 --- /dev/null +++ b/src/SOS/SOS.Hosting/SOSHost.cs @@ -0,0 +1,99 @@ +// 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 System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace SOS +{ + /// + /// Helper code to hosting SOS under ClrMD + /// + public sealed class SOSHost + { + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + delegate int SOSCommandDelegate(IntPtr ILLDBServices, [In, MarshalAs(UnmanagedType.LPStr)] string args); + + readonly LLDBServicesWrapper _wrapper; + IntPtr _sosLibrary = IntPtr.Zero; + + /// + /// Enable the assembly resolver to get the right SOS.NETCore version (the one + /// in the same directory as SOS.Hosting). + /// + static SOSHost() + { + AssemblyResolver.Enable(); + } + + /// + /// The native SOS binaries path. Default is OS/architecture (RID) named directory in the same directory as this assembly. + /// + public string SOSPath { get; set; } + + /// + /// Create an instance of the hosting class + /// + public SOSHost(IDataReader dataReader, ISOSHostContext context) + { + string os = null; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + os = "win"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { + os = "linux"; + } + if (os == null) { + throw new PlatformNotSupportedException($"{RuntimeInformation.OSDescription} not supported"); + } + string architecture = RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant(); + string rid = os + "-" + architecture; + SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid); + + _wrapper = new LLDBServicesWrapper(this, dataReader, context); + } + + public void ExecuteCommand(string commandLine) + { + string command = "Help"; + string arguments = null; + + if (commandLine != null) + { + int firstSpace = commandLine.IndexOf(' '); + command = firstSpace == -1 ? commandLine : commandLine.Substring(0, firstSpace); + arguments = firstSpace == -1 ? null : commandLine.Substring(firstSpace); + } + ExecuteCommand(command, arguments); + } + + public void ExecuteCommand(string command, string arguments) + { + if (_sosLibrary == IntPtr.Zero) + { + string sosPath = Path.Combine(SOSPath, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "sos.dll" : "libsos.so"); + _sosLibrary = DataTarget.PlatformFunctions.LoadLibrary(sosPath); + if (_sosLibrary == IntPtr.Zero) + { + throw new FileNotFoundException($"SOS module {sosPath} not found"); + } + } + IntPtr commandAddress = DataTarget.PlatformFunctions.GetProcAddress(_sosLibrary, command); + if (commandAddress == IntPtr.Zero) + { + throw new EntryPointNotFoundException($"Can not find SOS command: {command}"); + } + var commandFunc = (SOSCommandDelegate)Marshal.GetDelegateForFunctionPointer(commandAddress, typeof(SOSCommandDelegate)); + + int result = commandFunc(_wrapper.ILLDBServices, arguments ?? ""); + if (result != 0) + { + throw new InvalidOperationException($"SOS command FAILED 0x{result:X8}"); + } + } + } +} diff --git a/src/SOS/SOS.NETCore/SymbolReader.cs b/src/SOS/SOS.NETCore/SymbolReader.cs index 3a098e507..1c128fc7a 100644 --- a/src/SOS/SOS.NETCore/SymbolReader.cs +++ b/src/SOS/SOS.NETCore/SymbolReader.cs @@ -18,12 +18,11 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; -using System.Text; using System.Threading; namespace SOS { - internal class SymbolReader + public class SymbolReader { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct DebugInfo @@ -145,13 +144,13 @@ namespace SOS /// Read memory callback /// /// number of bytes read or 0 for error - internal unsafe delegate int ReadMemoryDelegate(ulong address, byte* buffer, int count); + public unsafe delegate int ReadMemoryDelegate(ulong address, byte* buffer, int count); /// /// Writeline delegate for symbol store logging /// /// - internal delegate void WriteLine([MarshalAs(UnmanagedType.LPStr)] string message); + public delegate void WriteLine([MarshalAs(UnmanagedType.LPStr)] string message); /// /// The LoadNativeSymbols callback @@ -175,7 +174,7 @@ namespace SOS /// symbol cache directory path (optional) /// windows symbol path (optional) /// - internal static bool InitializeSymbolStore(bool logging, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath, string windowsSymbolPath) + public static bool InitializeSymbolStore(bool logging, bool msdl, bool symweb, string symbolServerPath, string symbolCachePath, string windowsSymbolPath) { if (s_tracer == null) { s_tracer = new Tracer(enabled: logging, enabledVerbose: logging, Console.WriteLine); @@ -203,7 +202,7 @@ namespace SOS /// /// Displays the symbol server and cache configuration /// - internal static void DisplaySymbolStore() + public static void DisplaySymbolStore() { if (s_tracer != null) { @@ -227,7 +226,7 @@ namespace SOS /// /// This function disables any symbol downloading support. /// - internal static void DisableSymbolStore() + public static void DisableSymbolStore() { s_tracer = null; s_symbolStore = null; @@ -331,7 +330,7 @@ namespace SOS /// in memory PDB address or zero /// in memory PDB size /// Symbol reader handle or zero if error - internal static IntPtr LoadSymbolsForModule(string assemblyPath, bool isFileLayout, ulong loadedPeAddress, int loadedPeSize, + public static IntPtr LoadSymbolsForModule(string assemblyPath, bool isFileLayout, ulong loadedPeAddress, int loadedPeSize, ulong inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory) { try @@ -363,7 +362,7 @@ namespace SOS /// Cleanup and dispose of symbol reader handle /// /// symbol reader handle returned by LoadSymbolsForModule - internal static void Dispose(IntPtr symbolReaderHandle) + public static void Dispose(IntPtr symbolReaderHandle) { Debug.Assert(symbolReaderHandle != IntPtr.Zero); try @@ -386,7 +385,7 @@ namespace SOS /// method token return /// IL offset return /// true if information is available - internal static bool ResolveSequencePoint(IntPtr symbolReaderHandle, string filePath, int lineNumber, out int methodToken, out int ilOffset) + public static bool ResolveSequencePoint(IntPtr symbolReaderHandle, string filePath, int lineNumber, out int methodToken, out int ilOffset) { Debug.Assert(symbolReaderHandle != IntPtr.Zero); methodToken = 0; @@ -429,7 +428,7 @@ namespace SOS /// source line number return /// source file name return /// true if information is available - internal static bool GetLineByILOffset(IntPtr symbolReaderHandle, int methodToken, long ilOffset, out int lineNumber, out IntPtr fileName) + public static bool GetLineByILOffset(IntPtr symbolReaderHandle, int methodToken, long ilOffset, out int lineNumber, out IntPtr fileName) { lineNumber = 0; fileName = IntPtr.Zero; @@ -511,7 +510,7 @@ namespace SOS /// local variable index /// local variable name return /// true if name has been found - internal static bool GetLocalVariableName(IntPtr symbolReaderHandle, int methodToken, int localIndex, out IntPtr localVarName) + public static bool GetLocalVariableName(IntPtr symbolReaderHandle, int methodToken, int localIndex, out IntPtr localVarName) { localVarName = IntPtr.Zero; @@ -946,12 +945,20 @@ namespace SOS /// stream or null private static SymbolStoreFile GetSymbolStoreFile(SymbolStoreKey key) { - // Add the default symbol cache if it hasn't already been added - if (!s_symbolCacheAdded) { - s_symbolStore = new CacheSymbolStore(s_tracer, s_symbolStore, GetDefaultSymbolCache()); - s_symbolCacheAdded = true; + try + { + // Add the default symbol cache if it hasn't already been added + if (!s_symbolCacheAdded) { + s_symbolStore = new CacheSymbolStore(s_tracer, s_symbolStore, GetDefaultSymbolCache()); + s_symbolCacheAdded = true; + } + return s_symbolStore.GetFile(key, CancellationToken.None).GetAwaiter().GetResult(); + } + catch (Exception ex) when (ex is UnauthorizedAccessException || ex is BadImageFormatException || ex is IOException) + { + s_tracer.Error("Exception: {0}", ex.ToString()); + return null; } - return s_symbolStore.GetFile(key, CancellationToken.None).GetAwaiter().GetResult(); } /// diff --git a/src/SOS/SOS.NETCore/Tracer.cs b/src/SOS/SOS.NETCore/Tracer.cs index 894598cc1..ac046d0ed 100644 --- a/src/SOS/SOS.NETCore/Tracer.cs +++ b/src/SOS/SOS.NETCore/Tracer.cs @@ -24,7 +24,9 @@ namespace SOS public void WriteLine(string message) { - m_output?.Invoke(message); + lock (this) { + m_output?.Invoke(message); + } } public void WriteLine(string format, params object[] arguments) diff --git a/src/SOS/Strike/Strike.vcxproj b/src/SOS/Strike/Strike.vcxproj index ca82bf2b2..060b0c401 100644 --- a/src/SOS/Strike/Strike.vcxproj +++ b/src/SOS/Strike/Strike.vcxproj @@ -418,6 +418,7 @@ + diff --git a/src/SOS/Strike/Strike.vcxproj.filters b/src/SOS/Strike/Strike.vcxproj.filters index cd5bb759b..164090575 100644 --- a/src/SOS/Strike/Strike.vcxproj.filters +++ b/src/SOS/Strike/Strike.vcxproj.filters @@ -57,6 +57,7 @@ inc + diff --git a/src/SOS/Strike/hostcoreclr.cpp b/src/SOS/Strike/hostcoreclr.cpp index df6e99006..462134846 100644 --- a/src/SOS/Strike/hostcoreclr.cpp +++ b/src/SOS/Strike/hostcoreclr.cpp @@ -561,6 +561,17 @@ HRESULT InitializeHosting() { return S_OK; } +#ifdef FEATURE_PAL + ToRelease hostServices(NULL); + if (SUCCEEDED(g_ExtServices->QueryInterface(__uuidof(ISOSHostServices), (void**)&hostServices))) + { + if (SUCCEEDED(hostServices->GetSOSNETCoreCallbacks(SOSNetCoreCallbacksVersion, &g_SOSNetCoreCallbacks))) + { + g_hostingInitialized = true; + return S_OK; + } + } +#endif // FEATURE_PAL coreclr_initialize_ptr initializeCoreCLR = nullptr; coreclr_create_delegate_ptr createDelegate = nullptr; std::string hostRuntimeDirectory; @@ -682,15 +693,6 @@ HRESULT InitializeHosting() return Status; } -/**********************************************************************\ - * Public entry point to set the managed callbacks (unused). -\**********************************************************************/ -extern "C" void InitializeSymbolReaderCallbacks(SOSNetCoreCallbacks sosNetCoreCallbacks) -{ - g_SOSNetCoreCallbacks = sosNetCoreCallbacks; - g_hostingInitialized = true; -} - // // Pass to managed helper code to read in-memory PEs/PDBs. // Returns the number of bytes read. diff --git a/src/SOS/Strike/hostcoreclr.h b/src/SOS/Strike/hostcoreclr.h index f6b6dfa3f..88ce27abd 100644 --- a/src/SOS/Strike/hostcoreclr.h +++ b/src/SOS/Strike/hostcoreclr.h @@ -10,36 +10,11 @@ #ifndef __hostcoreclr_h__ #define __hostcoreclr_h__ +#include + static const char *SymbolReaderDllName = "SOS.NETCore"; static const char *SymbolReaderClassName = "SOS.SymbolReader"; -typedef void (*OutputDelegate)(const char*); -typedef int (*ReadMemoryDelegate)(ULONG64, uint8_t*, int); -typedef void (*SymbolFileCallbackDelegate)(void*, const char* moduleFileName, const char* symbolFileName); - -typedef BOOL (*InitializeSymbolStoreDelegate)(BOOL, BOOL, BOOL, const char*, const char*, const char*); -typedef void (*DisplaySymbolStoreDelegate)(); -typedef void (*DisableSymbolStoreDelegate)(); -typedef void (*LoadNativeSymbolsDelegate)(SymbolFileCallbackDelegate, void*, const char*, const char*, const char*, ULONG64, int, ReadMemoryDelegate); -typedef PVOID (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); -typedef void (*DisposeDelegate)(PVOID); -typedef BOOL (*ResolveSequencePointDelegate)(PVOID, const char*, unsigned int, unsigned int*, unsigned int*); -typedef BOOL (*GetLocalVariableNameDelegate)(PVOID, int, int, BSTR*); -typedef BOOL (*GetLineByILOffsetDelegate)(PVOID, mdMethodDef, ULONG64, ULONG *, BSTR*); - -struct SOSNetCoreCallbacks -{ - InitializeSymbolStoreDelegate InitializeSymbolStoreDelegate; - DisplaySymbolStoreDelegate DisplaySymbolStoreDelegate; - DisableSymbolStoreDelegate DisableSymbolStoreDelegate; - LoadNativeSymbolsDelegate LoadNativeSymbolsDelegate; - LoadSymbolsForModuleDelegate LoadSymbolsForModuleDelegate; - DisposeDelegate DisposeDelegate; - ResolveSequencePointDelegate ResolveSequencePointDelegate; - GetLineByILOffsetDelegate GetLineByILOffsetDelegate; - GetLocalVariableNameDelegate GetLocalVariableNameDelegate; -}; - extern HMODULE g_hInstance; extern LPCSTR g_hostRuntimeDirectory; extern SOSNetCoreCallbacks g_SOSNetCoreCallbacks; diff --git a/src/SOS/Strike/sos.def b/src/SOS/Strike/sos.def index dd0d3ea1c..81964382b 100644 --- a/src/SOS/Strike/sos.def +++ b/src/SOS/Strike/sos.def @@ -232,6 +232,4 @@ EXPORTS getCodeTypeFlags=GetCodeTypeFlags TraceToCode tracetocode=TraceToCode -#endif - - InitializeSymbolReaderCallbacks \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/SOS/Strike/sos_unixexports.src b/src/SOS/Strike/sos_unixexports.src index ddfd32808..732da868f 100644 --- a/src/SOS/Strike/sos_unixexports.src +++ b/src/SOS/Strike/sos_unixexports.src @@ -53,5 +53,3 @@ ThreadState Token2EE u VerifyHeap - -InitializeSymbolReaderCallbacks \ No newline at end of file diff --git a/src/SOS/Strike/soshostservices.h b/src/SOS/Strike/soshostservices.h new file mode 100644 index 000000000..619a2e49a --- /dev/null +++ b/src/SOS/Strike/soshostservices.h @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//---------------------------------------------------------------------------- +// +// LLDB debugger services for sos +// +//---------------------------------------------------------------------------- + +#ifndef __SOSHOSTSERVICES_H__ +#define __SOSHOSTSERVICES_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct SymbolModuleInfo; + +typedef void (*OutputDelegate)(const char*); +typedef int (*ReadMemoryDelegate)(ULONG64, uint8_t*, int); +typedef void (*SymbolFileCallbackDelegate)(void*, const char* moduleFileName, const char* symbolFilePath); + +typedef BOOL (*InitializeSymbolStoreDelegate)(BOOL, BOOL, BOOL, const char*, const char*, const char*); +typedef void (*DisplaySymbolStoreDelegate)(); +typedef void (*DisableSymbolStoreDelegate)(); +typedef void (*LoadNativeSymbolsDelegate)(SymbolFileCallbackDelegate, void*, const char*, const char*, ULONG64, int, ReadMemoryDelegate); +typedef PVOID (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); +typedef void (*DisposeDelegate)(PVOID); +typedef BOOL (*ResolveSequencePointDelegate)(PVOID, const char*, unsigned int, unsigned int*, unsigned int*); +typedef BOOL (*GetLocalVariableNameDelegate)(PVOID, int, int, BSTR*); +typedef BOOL (*GetLineByILOffsetDelegate)(PVOID, mdMethodDef, ULONG64, ULONG *, BSTR*); + +#define SOSNetCoreCallbacksVersion 1 + +struct SOSNetCoreCallbacks +{ + InitializeSymbolStoreDelegate InitializeSymbolStoreDelegate; + DisplaySymbolStoreDelegate DisplaySymbolStoreDelegate; + DisableSymbolStoreDelegate DisableSymbolStoreDelegate; + LoadNativeSymbolsDelegate LoadNativeSymbolsDelegate; + LoadSymbolsForModuleDelegate LoadSymbolsForModuleDelegate; + DisposeDelegate DisposeDelegate; + ResolveSequencePointDelegate ResolveSequencePointDelegate; + GetLineByILOffsetDelegate GetLineByILOffsetDelegate; + GetLocalVariableNameDelegate GetLocalVariableNameDelegate; +}; + +MIDL_INTERFACE("D13608FB-AD14-4B49-990A-80284F934C41") +ISOSHostServices : public IUnknown +{ +public: + //---------------------------------------------------------------------------- + // ISOSHostServices + //---------------------------------------------------------------------------- + + virtual HRESULT GetSOSNETCoreCallbacks( + int version, + SOSNetCoreCallbacks* callbacks) = 0; +}; + +#ifdef __cplusplus +}; +#endif + +#endif // #ifndef __SOSHOSTSERVICES_H__