From: Mike McLaughlin Date: Wed, 8 May 2019 16:41:34 +0000 (-0700) Subject: Download and register DAC with CLRMD (#248) X-Git-Tag: submit/tizen/20190813.035844~6^2^2~37 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9841919aca5fb7cfc90d2154e9914f414ec89499;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Download and register DAC with CLRMD (#248) Issue https://github.com/dotnet/diagnostics/issues/158 Add SOSInitializeByHost SOS module entry so the SOSHost code can pass the temp dir created, DAC module path downloaded for clrmd and the symbol store enabled flag. Removed ISOSHostServices and replaced initializing the SOSNetCoreCallbacks initialization as part of the call to SOSInitializeByHost. Added the "clrmodules" command as a test of a pure CLRMD command and as place to add the module version requested by some customers. Catch the DllNotFoundException exception and add "try installing libc6-dev" to exception message and documentation to work around Microsoft stretch docker image problems. Issue: https://github.com/dotnet/diagnostics/issues/201 --- diff --git a/documentation/dotnet-dump-instructions.md b/documentation/dotnet-dump-instructions.md index ead87c93c..a235104ac 100644 --- a/documentation/dotnet-dump-instructions.md +++ b/documentation/dotnet-dump-instructions.md @@ -5,7 +5,7 @@ The dotnet-dump CLI global tool is way to collect and analyze Windows and Linux ## Installing dotnet-dump -The first step is to install the dotnet-dump CLI global tool. This requires the 2.1 .NET Core SDK to be installed. If you see the error message `Tool 'dotnet-dump' is already installed`, you will need to uninstall the global tool (see below). +The first step is to install the dotnet-dump CLI global tool. This requires at least the 2.1 or greater .NET Core SDK to be installed. If you see the error message `Tool 'dotnet-dump' is already installed`, you will need to uninstall the global tool (see below). $ dotnet tool install -g dotnet-dump --version 1.0.3-preview5.19251.2 You can invoke the tool using the following command: dotnet-dump diff --git a/documentation/installing-sos-instructions.md b/documentation/installing-sos-instructions.md index f3198d6a3..ff8921e1d 100644 --- a/documentation/installing-sos-instructions.md +++ b/documentation/installing-sos-instructions.md @@ -1,7 +1,7 @@ Installing SOS on Linux and MacOS ================================= -The first step is to install the dotnet-sos CLI global tool. This requires the 2.1 .NET Core SDK to be installed. If you see the error message `Tool 'dotnet-sos' is already installed`, you will need to uninstall the global tool (see below). +The first step is to install the dotnet-sos CLI global tool. This requires at least the 2.1 or greater .NET Core SDK to be installed. If you see the error message `Tool 'dotnet-sos' is already installed`, you will need to uninstall the global tool (see below). $ dotnet tool install -g dotnet-sos --version 1.0.3-preview5.19251.2 You can invoke the tool using the following command: dotnet-sos diff --git a/src/SOS/SOS.Hosting/AssemblyResolver.cs b/src/SOS/SOS.Hosting/AssemblyResolver.cs index 8e9e07bef..7903fc8d3 100644 --- a/src/SOS/SOS.Hosting/AssemblyResolver.cs +++ b/src/SOS/SOS.Hosting/AssemblyResolver.cs @@ -12,14 +12,20 @@ namespace SOS /// /// Used to enable app-local assembly unification. /// - internal static class AssemblyResolver + public static class AssemblyResolver { + private static bool s_initialized = false; + /// /// Call to enable the assembly resolver for the current AppDomain. /// public static void Enable() { - AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + if (!s_initialized) + { + s_initialized = true; + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + } } private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) diff --git a/src/SOS/SOS.Hosting/LLDBServicesWrapper.cs b/src/SOS/SOS.Hosting/LLDBServicesWrapper.cs index 7704a9baa..b1ae1c0c5 100644 --- a/src/SOS/SOS.Hosting/LLDBServicesWrapper.cs +++ b/src/SOS/SOS.Hosting/LLDBServicesWrapper.cs @@ -23,104 +23,6 @@ namespace SOS 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); - - private delegate int GetMetadataLocatorDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string imagePath, - uint imageTimestamp, - uint imageSize, - [MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] mvid, - uint mdRva, - uint flags, - uint bufferSize, - IntPtr buffer, - IntPtr dataSize); - - #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; - public GetMetadataLocatorDelegate GetMetadataLocatorDelegate; - } - - 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, - GetMetadataLocatorDelegate = MetadataHelper.GetMetadataLocator - }; - static readonly string s_coreclrModuleName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "coreclr" : "libcoreclr.so"; readonly SOSHost _sosHost; @@ -189,10 +91,6 @@ namespace SOS builder.AddMethod(new AddModuleSymbolDelegate(AddModuleSymbol)); builder.Complete(); - builder = AddInterface(IID_SOSHostServices, validate: false); - builder.AddMethod(new GetSOSNETCoreCallbacksDelegate(GetSOSNETCoreCallbacks)); - builder.Complete(); - AddRef(); } @@ -762,30 +660,6 @@ namespace SOS #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)] @@ -1069,15 +943,5 @@ namespace SOS [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/SOSHost.cs b/src/SOS/SOS.Hosting/SOSHost.cs index 36896d6ec..579a2f72e 100644 --- a/src/SOS/SOS.Hosting/SOSHost.cs +++ b/src/SOS/SOS.Hosting/SOSHost.cs @@ -4,6 +4,7 @@ using Microsoft.Diagnostics.Runtime; using System; +using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.InteropServices; @@ -16,7 +17,118 @@ namespace SOS public sealed class SOSHost { [UnmanagedFunctionPointer(CallingConvention.StdCall)] - delegate int SOSCommandDelegate(IntPtr ILLDBServices, [In, MarshalAs(UnmanagedType.LPStr)] string args); + private delegate int SOSCommandDelegate( + IntPtr ILLDBServices, + [In, MarshalAs(UnmanagedType.LPStr)] string args); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int SOSInitializeDelegate( + [In, MarshalAs(UnmanagedType.Struct)] ref SOSNetCoreCallbacks callbacks, + int callbacksSize, + [In, MarshalAs(UnmanagedType.LPStr)] string tempDirectory, + [In, MarshalAs(UnmanagedType.LPStr)] string dacFilePath, + [In, MarshalAs(UnmanagedType.LPStr)] string dbiFilePath, + bool symbolStoreEnabled); + + private const string SOSInitialize = "SOSInitializeByHost"; + + #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); + + private delegate int GetMetadataLocatorDelegate( + [MarshalAs(UnmanagedType.LPWStr)] string imagePath, + uint imageTimestamp, + uint imageSize, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] mvid, + uint mdRva, + uint flags, + uint bufferSize, + IntPtr buffer, + IntPtr dataSize); + + #endregion + + /// + /// Pass to SOSInitializeByHost + /// + [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; + public GetMetadataLocatorDelegate GetMetadataLocatorDelegate; + } + + 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, + GetMetadataLocatorDelegate = MetadataHelper.GetMetadataLocator + }; readonly LLDBServicesWrapper _wrapper; IntPtr _sosLibrary = IntPtr.Zero; @@ -57,6 +169,69 @@ namespace SOS _wrapper = new LLDBServicesWrapper(this, dataReader, context); } + /// + /// Loads and initializes the SOS module. + /// + /// Temporary directory created to download DAC module + /// The path to DAC that CLRMD loaded or downloaded or null + /// The path to DBI (for future use) or null + public void InitializeSOSHost(string tempDirectory, string dacFilePath, string dbiFilePath) + { + if (_sosLibrary == IntPtr.Zero) + { + string sosPath = Path.Combine(SOSPath, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "sos.dll" : "libsos.so"); + try + { + _sosLibrary = DataTarget.PlatformFunctions.LoadLibrary(sosPath); + } + catch (DllNotFoundException ex) + { + // This is a workaround for the Microsoft SDK docker images. Can fail when LoadLibrary uses libdl.so to load the SOS module. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + throw new DllNotFoundException("Problem loading SOS module. Try installing libc6-dev (apt-get install libc6-dev) to work around this problem.", ex); + } + else + { + throw; + } + } + if (_sosLibrary == IntPtr.Zero) + { + throw new FileNotFoundException($"SOS module {sosPath} not found"); + } + IntPtr initializeAddress = DataTarget.PlatformFunctions.GetProcAddress(_sosLibrary, SOSInitialize); + if (initializeAddress == IntPtr.Zero) + { + throw new EntryPointNotFoundException($"Can not find SOS module initialization function: {SOSInitialize}"); + } + var initializeFunc = (SOSInitializeDelegate)Marshal.GetDelegateForFunctionPointer(initializeAddress, typeof(SOSInitializeDelegate)); + + // SOS depends on that the temp directory ends with "/". + if (!string.IsNullOrEmpty(tempDirectory) && tempDirectory[tempDirectory.Length - 1] != Path.DirectorySeparatorChar) + { + tempDirectory = tempDirectory + Path.DirectorySeparatorChar; + } + + int result = initializeFunc( + ref s_callbacks, + Marshal.SizeOf(), + tempDirectory, + dacFilePath, + dbiFilePath, + SymbolReader.IsSymbolStoreEnabled()); + + if (result != 0) + { + throw new InvalidOperationException($"SOS initialization FAILED 0x{result:X8}"); + } + } + } + + /// + /// Execute a SOS command. + /// + /// command name and arguments public void ExecuteCommand(string commandLine) { string command = "Help"; @@ -71,17 +246,15 @@ namespace SOS ExecuteCommand(command, arguments); } + /// + /// Execute a SOS command. + /// + /// just the command name + /// the command arguments and options 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"); - } - } + Debug.Assert(_sosLibrary != null); + IntPtr commandAddress = DataTarget.PlatformFunctions.GetProcAddress(_sosLibrary, command); if (commandAddress == IntPtr.Zero) { diff --git a/src/SOS/SOS.NETCore/SymbolReader.cs b/src/SOS/SOS.NETCore/SymbolReader.cs index f80a4febb..9bd04f220 100644 --- a/src/SOS/SOS.NETCore/SymbolReader.cs +++ b/src/SOS/SOS.NETCore/SymbolReader.cs @@ -238,6 +238,7 @@ namespace SOS /// /// called back for each symbol file loaded /// callback parameter + /// temp directory unique to this instance of SOS /// module path /// module base address /// module size @@ -277,41 +278,65 @@ namespace SOS // Don't download the sos binaries that come with the runtime if (moduleFileName != "SOS.NETCore.dll" && !moduleFileName.StartsWith("libsos.")) { - using (SymbolStoreFile file = GetSymbolStoreFile(key)) + string downloadFilePath = GetSymbolFile(key, tempDirectory); + if (downloadFilePath != null) { - if (file != null) - { - try - { - string downloadFilePath = file.FileName; - - // If the downloaded doesn't already exists on disk in the cache, then write it to a temporary location. - if (!File.Exists(downloadFilePath)) - { - downloadFilePath = Path.Combine(tempDirectory, moduleFileName); - - using (Stream destinationStream = File.OpenWrite(downloadFilePath)) { - file.Stream.CopyTo(destinationStream); - } - s_tracer.WriteLine("Downloaded symbol file {0}", key.FullPathName); - } - s_tracer.Information("{0}: {1}", moduleFileName, downloadFilePath); - callback(parameter, moduleFileName, downloadFilePath); - } - catch (Exception ex) when (ex is UnauthorizedAccessException || ex is DirectoryNotFoundException) - { - s_tracer.Error("{0}", ex.Message); - } - } + s_tracer.Information("{0}: {1}", moduleFileName, downloadFilePath); + callback(parameter, moduleFileName, downloadFilePath); } } } } catch (Exception ex) when (ex is BadInputFormatException || ex is InvalidVirtualAddressException) { - s_tracer.Error("Exception: {0}/{1}: {2:X16}", moduleFilePath, address); + s_tracer.Error("{0}/{1:X16}: {2}", moduleFilePath, address, ex.Message); + } + } + } + + /// + /// Download a symbol from the symbol stores/server. + /// + /// index of the file to download + /// temp directory to put the file. This directory is only created if + /// the file is NOT already in the cache and is downloaded. + /// Path to the downloaded file either in the cache or in the temp directory + public static string GetSymbolFile(SymbolStoreKey key, string tempDirectory) + { + string downloadFilePath = null; + + if (IsSymbolStoreEnabled()) + { + using (SymbolStoreFile file = GetSymbolStoreFile(key)) + { + if (file != null) + { + try + { + downloadFilePath = file.FileName; + + // If the downloaded doesn't already exists on disk in the cache, then write it to a temporary location. + if (!File.Exists(downloadFilePath)) + { + Directory.CreateDirectory(tempDirectory); + downloadFilePath = Path.Combine(tempDirectory, Path.GetFileName(key.FullPathName)); + + using (Stream destinationStream = File.OpenWrite(downloadFilePath)) { + file.Stream.CopyTo(destinationStream); + } + s_tracer.WriteLine("Downloaded symbol file {0}", key.FullPathName); + } + } + catch (Exception ex) when (ex is UnauthorizedAccessException || ex is DirectoryNotFoundException) + { + s_tracer.Error("{0}: {1}", file.FileName, ex.Message); + downloadFilePath = null; + } + } } } + + return downloadFilePath; } /// @@ -940,7 +965,7 @@ namespace SOS /// /// Returns true if symbol download has been enabled. /// - internal static bool IsSymbolStoreEnabled() + public static bool IsSymbolStoreEnabled() { return s_symbolStore != null; } diff --git a/src/SOS/Strike/Strike.vcxproj b/src/SOS/Strike/Strike.vcxproj index 060b0c401..ca82bf2b2 100644 --- a/src/SOS/Strike/Strike.vcxproj +++ b/src/SOS/Strike/Strike.vcxproj @@ -418,7 +418,6 @@ - diff --git a/src/SOS/Strike/Strike.vcxproj.filters b/src/SOS/Strike/Strike.vcxproj.filters index 164090575..cd5bb759b 100644 --- a/src/SOS/Strike/Strike.vcxproj.filters +++ b/src/SOS/Strike/Strike.vcxproj.filters @@ -57,7 +57,6 @@ inc - diff --git a/src/SOS/Strike/hostcoreclr.cpp b/src/SOS/Strike/hostcoreclr.cpp index d8ee2412e..302f0e92f 100644 --- a/src/SOS/Strike/hostcoreclr.cpp +++ b/src/SOS/Strike/hostcoreclr.cpp @@ -543,6 +543,32 @@ LPCSTR GetDbiFilePath() return g_dbiFilePath; } +/**********************************************************************\ + * Called when the managed SOS Host loads/initializes SOS. +\**********************************************************************/ +extern "C" HRESULT SOSInitializeByHost(SOSNetCoreCallbacks* callbacks, int callbacksSize, LPCSTR tempDirectory, LPCSTR dacFilePath, LPCSTR dbiFilePath, bool symbolStoreEnabled) +{ + if (memcpy_s(&g_SOSNetCoreCallbacks, sizeof(g_SOSNetCoreCallbacks), callbacks, callbacksSize) != 0) + { + return E_INVALIDARG; + } + if (tempDirectory != nullptr) + { + g_tmpPath = _strdup(tempDirectory); + } + if (dacFilePath != nullptr) + { + g_dacFilePath = _strdup(dacFilePath); + } + if (dbiFilePath != nullptr) + { + g_dbiFilePath = _strdup(dbiFilePath); + } + g_symbolStoreInitialized = symbolStoreEnabled; + g_hostingInitialized = true; + return S_OK; +} + /**********************************************************************\ * Returns true if the host runtime has already been initialized. \**********************************************************************/ @@ -561,17 +587,6 @@ 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; diff --git a/src/SOS/Strike/hostcoreclr.h b/src/SOS/Strike/hostcoreclr.h index 13c04b072..fbbcb205e 100644 --- a/src/SOS/Strike/hostcoreclr.h +++ b/src/SOS/Strike/hostcoreclr.h @@ -10,7 +10,47 @@ #ifndef __hostcoreclr_h__ #define __hostcoreclr_h__ -#include +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*); + +typedef BOOL (*GetMetadataLocatorDelegate)( + LPCWSTR imagePath, + unsigned int imageTimestamp, + unsigned int imageSize, + GUID* mvid, + unsigned int mdRva, + unsigned int flags, + unsigned int bufferSize, + PVOID pMetadata, + unsigned int* pMetadataSize +); + +struct SOSNetCoreCallbacks +{ + InitializeSymbolStoreDelegate InitializeSymbolStoreDelegate; + DisplaySymbolStoreDelegate DisplaySymbolStoreDelegate; + DisableSymbolStoreDelegate DisableSymbolStoreDelegate; + LoadNativeSymbolsDelegate LoadNativeSymbolsDelegate; + LoadSymbolsForModuleDelegate LoadSymbolsForModuleDelegate; + DisposeDelegate DisposeDelegate; + ResolveSequencePointDelegate ResolveSequencePointDelegate; + GetLineByILOffsetDelegate GetLineByILOffsetDelegate; + GetLocalVariableNameDelegate GetLocalVariableNameDelegate; + GetMetadataLocatorDelegate GetMetadataLocatorDelegate; +}; static const char *SOSManagedDllName = "SOS.NETCore"; static const char *SymbolReaderClassName = "SOS.SymbolReader"; diff --git a/src/SOS/Strike/sos.def b/src/SOS/Strike/sos.def index 81964382b..cebf92cc0 100644 --- a/src/SOS/Strike/sos.def +++ b/src/SOS/Strike/sos.def @@ -232,4 +232,6 @@ EXPORTS getCodeTypeFlags=GetCodeTypeFlags TraceToCode tracetocode=TraceToCode -#endif \ No newline at end of file +#endif + + SOSInitializeByHost diff --git a/src/SOS/Strike/sos_unixexports.src b/src/SOS/Strike/sos_unixexports.src index 732da868f..acb294055 100644 --- a/src/SOS/Strike/sos_unixexports.src +++ b/src/SOS/Strike/sos_unixexports.src @@ -53,3 +53,5 @@ ThreadState Token2EE u VerifyHeap + +SOSInitializeByHost \ No newline at end of file diff --git a/src/SOS/Strike/soshostservices.h b/src/SOS/Strike/soshostservices.h deleted file mode 100644 index d16c71cf3..000000000 --- a/src/SOS/Strike/soshostservices.h +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -//---------------------------------------------------------------------------- -// -// LLDB debugger services for sos -// -//---------------------------------------------------------------------------- - -#ifndef __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*); - -typedef BOOL (*GetMetadataLocatorDelegate)( - LPCWSTR imagePath, - unsigned int imageTimestamp, - unsigned int imageSize, - GUID* mvid, - unsigned int mdRva, - unsigned int flags, - unsigned int bufferSize, - PVOID pMetadata, - unsigned int* pMetadataSize -); - -#define SOSNetCoreCallbacksVersion 2 - -struct SOSNetCoreCallbacks -{ - InitializeSymbolStoreDelegate InitializeSymbolStoreDelegate; - DisplaySymbolStoreDelegate DisplaySymbolStoreDelegate; - DisableSymbolStoreDelegate DisableSymbolStoreDelegate; - LoadNativeSymbolsDelegate LoadNativeSymbolsDelegate; - LoadSymbolsForModuleDelegate LoadSymbolsForModuleDelegate; - DisposeDelegate DisposeDelegate; - ResolveSequencePointDelegate ResolveSequencePointDelegate; - GetLineByILOffsetDelegate GetLineByILOffsetDelegate; - GetLocalVariableNameDelegate GetLocalVariableNameDelegate; - GetMetadataLocatorDelegate GetMetadataLocatorDelegate; -}; - -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__ diff --git a/src/Tools/dotnet-dump/AnalyzeContext.cs b/src/Tools/dotnet-dump/AnalyzeContext.cs index d347a0e25..4dba57461 100644 --- a/src/Tools/dotnet-dump/AnalyzeContext.cs +++ b/src/Tools/dotnet-dump/AnalyzeContext.cs @@ -4,9 +4,16 @@ // // -------------------------------------------------------------------- using Microsoft.Diagnostics.Runtime; +using Microsoft.SymbolStore; +using Microsoft.SymbolStore.KeyGenerators; using SOS; using System; +using System.Collections.Generic; using System.CommandLine; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using System.Threading; namespace Microsoft.Diagnostic.Tools.Dump @@ -18,7 +25,10 @@ namespace Microsoft.Diagnostic.Tools.Dump { private readonly IConsole _console; private ClrRuntime _runtime; - private SOSHost _sosHost; + + private static SOSHost s_sosHost; + private static string s_tempDirectory; + private static string s_dacFilePath; public AnalyzeContext(IConsole console, DataTarget target) { @@ -43,12 +53,78 @@ namespace Microsoft.Diagnostic.Tools.Dump if (Target.ClrVersions.Count != 1) { throw new InvalidOperationException("More or less than 1 CLR version is present"); } - _runtime = Target.ClrVersions[0].CreateRuntime(); + ClrInfo clrInfo = Target.ClrVersions[0]; + string dacFilePath = GetDacFile(clrInfo); + try + { + _runtime = clrInfo.CreateRuntime(dacFilePath); + } + catch (DllNotFoundException ex) + { + // This is a workaround for the Microsoft SDK docker images. Can fail when clrmd uses libdl.so + // to create a symlink to and load the DAC module. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + throw new DllNotFoundException("Problem initializing CLRMD. Try installing libc6-dev (apt-get install libc6-dev) to work around this problem.", ex); + } + else + { + throw; + } + } } return _runtime; } } + private string GetDacFile(ClrInfo clrInfo) + { + if (s_dacFilePath == null) + { + string dac = clrInfo.LocalMatchingDac; + if (dac != null && File.Exists(dac)) + { + s_dacFilePath = dac; + } + else if (SymbolReader.IsSymbolStoreEnabled()) + { + string dacFileName = Path.GetFileName(clrInfo.LocalMatchingDac); + SymbolStoreKey key = null; + + if (clrInfo.ModuleInfo.BuildId != null) + { + IEnumerable keys = ELFFileKeyGenerator.GetKeys( + KeyTypeFlags.ClrKeys, clrInfo.ModuleInfo.FileName, clrInfo.ModuleInfo.BuildId, symbolFile: false, symbolFileName: null); + + key = keys.SingleOrDefault((k) => Path.GetFileName(k.FullPathName) == dacFileName); + } + else + { + // Use the coreclr.dll's id (timestamp/filesize) to download the the dac module. + key = PEFileKeyGenerator.GetKey(dacFileName, clrInfo.ModuleInfo.TimeStamp, clrInfo.ModuleInfo.FileSize); + } + + if (key != null) + { + if (s_tempDirectory == null) + { + int processId = Process.GetCurrentProcess().Id; + s_tempDirectory = Path.Combine(Path.GetTempPath(), "analyze" + processId.ToString()); + } + // Now download the DAC module from the symbol server + s_dacFilePath = SymbolReader.GetSymbolFile(key, s_tempDirectory); + } + } + + if (s_dacFilePath == null) + { + throw new FileNotFoundException("Could not find matching DAC for this runtime: {0}", clrInfo.ModuleInfo.FileName); + } + } + + return s_dacFilePath; + } + /// /// Returns the SOS host instance /// @@ -56,18 +132,14 @@ namespace Microsoft.Diagnostic.Tools.Dump { get { - if (_sosHost == null) { - _sosHost = new SOSHost(Target.DataReader, this); + if (s_sosHost == null) { + s_sosHost = new SOSHost(Target.DataReader, this); + s_sosHost.InitializeSOSHost(s_tempDirectory, s_dacFilePath, dbiFilePath: null); } - return _sosHost; + return s_sosHost; } } - /// - /// Delegate to invoke to exit repl - /// - public Action Exit { get; } - /// /// Current OS thread Id /// diff --git a/src/Tools/dotnet-dump/Analyzer.cs b/src/Tools/dotnet-dump/Analyzer.cs index 7cd64a695..ef80f3661 100644 --- a/src/Tools/dotnet-dump/Analyzer.cs +++ b/src/Tools/dotnet-dump/Analyzer.cs @@ -1,5 +1,6 @@ using Microsoft.Diagnostic.Repl; using Microsoft.Diagnostics.Runtime; +using SOS; using System; using System.CommandLine; using System.IO; @@ -16,6 +17,15 @@ namespace Microsoft.Diagnostic.Tools.Dump private readonly ConsoleProvider _consoleProvider; private readonly CommandProcessor _commandProcessor; + /// + /// Enable the assembly resolver to get the right SOS.NETCore version (the one + /// in the same directory as this assembly). + /// + static Analyzer() + { + AssemblyResolver.Enable(); + } + public Analyzer() { _consoleProvider = new ConsoleProvider(); @@ -34,8 +44,7 @@ namespace Microsoft.Diagnostic.Tools.Dump target = DataTarget.LoadCoreDump(dump_path.FullName); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - //target = DataTarget.LoadCrashDump(dump_path.FullName, CrashDumpReader.ClrMD); - throw new PlatformNotSupportedException("Preview build: This is not yet implemented on Windows"); + target = DataTarget.LoadCrashDump(dump_path.FullName, CrashDumpReader.ClrMD); } else { throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}"); @@ -53,8 +62,9 @@ namespace Microsoft.Diagnostic.Tools.Dump _commandProcessor.AddService(analyzeContext); // Automatically enable symbol server support on Linux and MacOS - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - analyzeContext.SOSHost.ExecuteCommand("SetSymbolServer", "-ms"); + //if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + SymbolReader.InitializeSymbolStore(logging: false, msdl: true, symweb: false, symbolServerPath: null, symbolCachePath: null, windowsSymbolPath: null); } // Run the commands from the dotnet-dump command line diff --git a/src/Tools/dotnet-dump/Commands/ClrModulesCommand.cs b/src/Tools/dotnet-dump/Commands/ClrModulesCommand.cs new file mode 100644 index 000000000..50a24cfa9 --- /dev/null +++ b/src/Tools/dotnet-dump/Commands/ClrModulesCommand.cs @@ -0,0 +1,23 @@ + +using Microsoft.Diagnostic.Repl; +using Microsoft.Diagnostics.Runtime; +using System.CommandLine; +using System.Threading.Tasks; + +namespace Microsoft.Diagnostic.Tools.Dump +{ + [Command(Name = "clrmodules", Help = "Lists the managed modules in the process.")] + public class ClrModulesCommand : CommandBase + { + public AnalyzeContext AnalyzeContext { get; set; } + + public override Task InvokeAsync() + { + foreach (ClrModule module in AnalyzeContext.Runtime.Modules) + { + WriteLine("{0:X16} {1}", module.Address, module.FileName); + } + return Task.CompletedTask; + } + } +} diff --git a/src/Tools/dotnet-dump/dotnet-dump.csproj b/src/Tools/dotnet-dump/dotnet-dump.csproj index 1eca1261b..bbf450565 100644 --- a/src/Tools/dotnet-dump/dotnet-dump.csproj +++ b/src/Tools/dotnet-dump/dotnet-dump.csproj @@ -13,12 +13,14 @@ + - + +