diff --git a/documentation/FAQ.md b/documentation/FAQ.md index bc42c7d914..589dc107ae 100644 --- a/documentation/FAQ.md +++ b/documentation/FAQ.md @@ -1,6 +1,8 @@ Frequently Asked Questions ========================== +* `dotnet-dump analyze` running on Windows doesn't support MacOS .NET 5.0 and 6.0 core dumps. `dotnet-dump` running on MacOS does support .NET 5.0 but not 6.0 core dumps (which will be fixed in a future dotnet-dump release). MacOS .NET 6.0 core dumps generated by the runtime via [createdump](https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/xplat-minidump-generation.md#os-x) are supported by lldb/SOS running on MacOS. + * If SOS or dotnet-dump analyze commands display "UNKNOWN" for types or functions names, your core dump may not have all the managed state. Dumps created with gdb or gcore have this problem. Linux system generated core dumps need the `coredump_filter` for the process to be set to at least 0x3f. See [core](http://man7.org/linux/man-pages/man5/core.5.html) for more information. * If dump collection (`dotnet-dump collect` or `createdump`) doesn't work in a docker container, try adding the SYS\_TRACE capability with --cap-add=SYS\_PTRACE or --privileged. diff --git a/documentation/debugging-coredump.md b/documentation/debugging-coredump.md index 1fc9699f93..eb43cee166 100644 --- a/documentation/debugging-coredump.md +++ b/documentation/debugging-coredump.md @@ -72,6 +72,8 @@ Even if the core dump was not generated on this machine, the native and managed Follow the rest of the above Linux steps to set the symbol server and load native symbols. +NOTE: The following issue has been fixed with .NET 6.0 core dumps generated by the runtime (see [createdump](https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/xplat-minidump-generation.md#os-x)) with a recent version of SOS. + The MacOS lldb has a bug that prevents SOS clrstack from properly working. Because of this bug SOS can't properly match the lldb native with with the managed thread OSID displayed by `clrthreads`. The `setsostid` command is a work around for this lldb bug. This command maps the OSID from this command: ``` diff --git a/documentation/lldb/linux-instructions.md b/documentation/lldb/linux-instructions.md index 75155780f9..efb15082a6 100644 --- a/documentation/lldb/linux-instructions.md +++ b/documentation/lldb/linux-instructions.md @@ -88,15 +88,9 @@ To install the lldb packages: This installs lldb version 10.0.0. -#### Alpine 3.9 #### +#### Alpine 3.9 to 3.12 #### -Currently there is no lldb that works on Alpine. - -Issue https://github.com/dotnet/diagnostics/issues/73 - -#### Alpine 3.12 #### - -lldb 10.0 is available for this Apline version. +lldb 10.0 is available and works for these Apline versions. #### CentOS 6 #### diff --git a/dotnet.sh b/dotnet.sh old mode 100644 new mode 100755 diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs index fffb98fca3..f014c23fc2 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/SymbolService.cs @@ -8,6 +8,7 @@ using Microsoft.SymbolStore.SymbolStores; using SOS; using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.IO; @@ -27,8 +28,8 @@ public class SymbolService : ISymbolService /// /// Symbol server URLs /// - const string MsdlSymbolServer = "http://msdl.microsoft.com/download/symbols/"; - const string SymwebSymbolServer = "http://symweb.corp.microsoft.com/"; + public const string MsdlSymbolServer = "http://msdl.microsoft.com/download/symbols/"; + public const string SymwebSymbolServer = "http://symweb.corp.microsoft.com/"; private readonly IHost _host; private string _defaultSymbolCache; @@ -96,77 +97,103 @@ public bool ParseSymbolPath(string symbolPath) foreach (string path in paths.Reverse()) { - string[] parts = path.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries); - - // UNC or directory paths are ignored (paths not prefixed with srv* or cache*). + string[] parts = path.Split(new char[] { '*' }, StringSplitOptions.None); if (parts.Length > 0) { - string symbolServerPath = null; - string symbolCachePath = null; + List symbolCachePaths = new(); string symbolDirectoryPath = null; - bool msdl = false; + string symbolServerPath = null; + + void ParseServer(int start) + { + symbolServerPath = MsdlSymbolServer; + for (int i = start; i < parts.Length; i++) + { + if (string.IsNullOrEmpty(parts[i])) + { + // srv** means use default cache + if (i != (parts.Length - 1)) + { + symbolCachePaths.Add(DefaultSymbolCache); + } + } + else if (i < (parts.Length - 1)) + { + symbolCachePaths.Add(parts[i]); + } + else + { + symbolServerPath = parts[i]; + } + } + } switch (parts[0].ToLowerInvariant()) { + case "symsrv": + if (parts.Length <= 2) + { + return false; + } + // ignore symsrv.dll or other server dlls in parts[2] + ParseServer(2); + break; + case "srv": - switch (parts.Length) - { - case 1: - msdl = true; - symbolCachePath = DefaultSymbolCache; - break; - case 2: - symbolServerPath = parts[1]; - break; - case 3: - symbolCachePath = parts[1]; - symbolServerPath = parts[2]; - break; - default: - return false; + if (parts.Length <= 1) + { + return false; } + ParseServer(1); break; case "cache": - switch (parts.Length) - { - case 1: - symbolCachePath = DefaultSymbolCache; - break; - case 2: - symbolCachePath = parts[1]; - break; - default: - return false; + if (parts.Length <= 1) + { + return false; + } + else + { + for (int i = 1; i < parts.Length; i++) + { + if (string.IsNullOrEmpty(parts[i])) + { + if (i == 1) + { + symbolCachePaths.Add(DefaultSymbolCache); + } + } + else + { + symbolCachePaths.Add(parts[i]); + } + } } break; default: // Directory path search - switch (parts.Length) + if (parts.Length != 1) { - case 1: - symbolDirectoryPath = parts[0]; - break; - default: - return false; + return false; } + symbolDirectoryPath = parts[0]; break; } - if (msdl || symbolServerPath != null) + if (symbolServerPath != null) { - if (!AddSymbolServer(msdl, symweb: false, symbolServerPath, authToken: null, timeoutInMinutes: 0)) + if (!AddSymbolServer(msdl: false, symweb: false, symbolServerPath.Trim(), authToken: null, timeoutInMinutes: 0)) { return false; } } - if (symbolCachePath != null) + foreach (string symbolCachePath in symbolCachePaths.Reverse()) { - AddCachePath(symbolCachePath); + AddCachePath(symbolCachePath.Trim()); } if (symbolDirectoryPath != null) { - AddDirectoryPath(symbolDirectoryPath); + AddDirectoryPath(symbolDirectoryPath.Trim()); } } } diff --git a/src/SOS/SOS.Extensions/HostServices.cs b/src/SOS/SOS.Extensions/HostServices.cs index 7424a7ae7d..fb5e4ab587 100644 --- a/src/SOS/SOS.Extensions/HostServices.cs +++ b/src/SOS/SOS.Extensions/HostServices.cs @@ -236,7 +236,7 @@ private HResult RegisterDebuggerServices( hr = DebuggerServices.GetSymbolPath(out string symbolPath); if (hr == HResult.S_OK) { - if (!_symbolService.ParseSymbolPath(symbolPath)) + if (!_symbolService.ParseSymbolPathFixDefault(symbolPath)) { Trace.TraceError("ParseSymbolPath FAILED: {0}", symbolPath); } diff --git a/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs b/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs index 21b4a629b1..285b1cace5 100644 --- a/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs +++ b/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs @@ -21,6 +21,24 @@ public static class SymbolServiceExtensions // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) const int E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007a); + /// + /// Set the windows symbol path converting the default "srv*" to the cached public symbol server URL. + /// + /// The windows symbol path to translate and set + /// if false, error parsing symbol path + public static bool ParseSymbolPathFixDefault( + this ISymbolService symbolService, + string symbolPath) + { + // Translate dbgeng's default .sympath to what the public version actually does. Normally "srv*" + // means no caching and the server path depends on whether dbgeng is internal or public. + if (symbolPath.ToLowerInvariant() == "srv*") + { + symbolPath = "cache*;SRV*https://msdl.microsoft.com/download/symbols"; + } + return symbolService.ParseSymbolPath(symbolPath); + } + /// /// Metadata locator helper for the DAC. /// diff --git a/src/SOS/SOS.Hosting/SymbolServiceWrapper.cs b/src/SOS/SOS.Hosting/SymbolServiceWrapper.cs index a5cee29318..ea90410b6a 100644 --- a/src/SOS/SOS.Hosting/SymbolServiceWrapper.cs +++ b/src/SOS/SOS.Hosting/SymbolServiceWrapper.cs @@ -157,16 +157,16 @@ private bool InitializeSymbolStore( /// /// Parse the Windows sympath format /// - /// windows symbol path + /// windows symbol path /// if false, failure private bool ParseSymbolPath( IntPtr self, - string windowsSymbolPath) + string symbolPath) { - if (windowsSymbolPath == null) { + if (string.IsNullOrWhiteSpace(symbolPath)) { return false; } - return _symbolService.ParseSymbolPath(windowsSymbolPath); + return _symbolService.ParseSymbolPathFixDefault(symbolPath); } /// diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index 4e81058a65..7a6038376a 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -16749,6 +16749,10 @@ DECLARE_API(SetSymbolServer) else if (loadNative) { Status = LoadNativeSymbols(); + if (FAILED(Status)) + { + ExtErr("Symbol server not set\n"); + } } #endif else diff --git a/src/SOS/Strike/symbols.cpp b/src/SOS/Strike/symbols.cpp index 5076eaf3b5..1ee366f4b8 100644 --- a/src/SOS/Strike/symbols.cpp +++ b/src/SOS/Strike/symbols.cpp @@ -173,12 +173,11 @@ static void LoadNativeSymbolsCallback(void* param, const char* moduleFilePath, U \**********************************************************************/ HRESULT LoadNativeSymbols(bool runtimeOnly) { - HRESULT hr = S_OK; if (g_symbolStoreInitialized) { - hr = g_ExtServices2->LoadNativeSymbols(runtimeOnly, LoadNativeSymbolsCallback); + return g_ExtServices2->LoadNativeSymbols(runtimeOnly, LoadNativeSymbolsCallback); } - return hr; + return E_FAIL; } #endif diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index c81837dae4..67a3c294a2 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -5109,23 +5109,30 @@ GetLineByOffset( ___in ULONG cchFileName, ___in BOOL bAdjustOffsetForLineNumber /* = FALSE */) { - HRESULT Status = S_OK; + HRESULT status = S_OK; ULONG32 methodToken; ULONG32 methodOffs; // Find the image, method token and IL offset that correspond to "nativeOffset" ToRelease pModule(NULL); - IfFailRet(ConvertNativeToIlOffset(nativeOffset, bAdjustOffsetForLineNumber, &pModule, &methodToken, &methodOffs)); - + status = ConvertNativeToIlOffset(nativeOffset, bAdjustOffsetForLineNumber, &pModule, &methodToken, &methodOffs); + if (FAILED(status)) + { + ExtDbgOut("GetLineByOffset(%p): ConvertNativeToIlOffset FAILED %08x\n", nativeOffset, status); + return status; + } ToRelease pMDImport(NULL); - Status = pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport); - if (FAILED(Status)) + status = pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport); + if (FAILED(status)) { - ExtDbgOut("GetLineByOffset(%p): QueryInterface(IID_IMetaDataImport) FAILED %08x\n", nativeOffset, Status); + ExtDbgOut("GetLineByOffset(%p): QueryInterface(IID_IMetaDataImport) FAILED %08x\n", nativeOffset, status); } SymbolReader symbolReader; - IfFailRet(symbolReader.LoadSymbols(pMDImport, pModule)); - + status = symbolReader.LoadSymbols(pMDImport, pModule); + if (FAILED(status)) + { + return status; + } return symbolReader.GetLineByILOffset(methodToken, methodOffs, pLinenum, pwszFileName, cchFileName); } diff --git a/src/SOS/extensions/hostcoreclr.cpp b/src/SOS/extensions/hostcoreclr.cpp index 758a5715fd..68ab572942 100644 --- a/src/SOS/extensions/hostcoreclr.cpp +++ b/src/SOS/extensions/hostcoreclr.cpp @@ -220,19 +220,23 @@ static void AddFilesFromDirectoryToTpaList(const char* directory, std::string& t const char extension[] = ".dll"; std::string filename(find.FileName()); size_t extLen = sizeof(extension) - 1; - size_t extPos = filename.length() - extLen; - - // Check if the extension matches the one we are looking for - if ((extPos > 0) && (filename.compare(extPos, extLen, extension) == 0)) + size_t extPos = filename.length(); + if (extPos > extLen) { - std::string filenameWithoutExt(filename.substr(0, extPos)); + extPos -= extLen; - // Make sure if we have an assembly with multiple extensions present, - // we insert only one version of it. - if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) + // Check if the extension matches the one we are looking for + if (filename.compare(extPos, extLen, extension) == 0) { - addedAssemblies.insert(filenameWithoutExt); - AddFileToTpaList(directory, filename.c_str(), tpaList); + std::string filenameWithoutExt(filename.substr(0, extPos)); + + // Make sure if we have an assembly with multiple extensions present, + // we insert only one version of it. + if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) + { + addedAssemblies.insert(filenameWithoutExt); + AddFileToTpaList(directory, filename.c_str(), tpaList); + } } } } diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/SymbolServiceTests.cs b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/SymbolServiceTests.cs new file mode 100644 index 0000000000..ab083dcbbd --- /dev/null +++ b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/SymbolServiceTests.cs @@ -0,0 +1,116 @@ +// 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.Implementation; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.Diagnostics.DebugServices.UnitTests +{ + /// + /// Test the service event implementation + /// + public class SymbolServiceTests : IHost + { + public SymbolServiceTests() + { + } + + [Fact] + public void SymbolPathTests() + { + var symbolService = new SymbolService(this); + Assert.False(symbolService.ParseSymbolPath("srv")); + Assert.False(symbolService.ParseSymbolPath("cache")); + Assert.False(symbolService.ParseSymbolPath("symsrv")); + + string defaultServer = $"Server: {SymbolService.MsdlSymbolServer}"; + string defaultPath = $"Cache: {symbolService.DefaultSymbolCache} {defaultServer}"; + string localSymbolCache = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "c:\\localsymbolcache" : "/home/foo/localsymbolcache"; + + Assert.True(symbolService.ParseSymbolPath("srv*")); + Assert.Equal(defaultServer, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath("srv**")); + Assert.Equal(defaultPath, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath("symsrv*symsrv.dll*")); + Assert.Equal(defaultServer, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath("cache*;srv*")); + Assert.Equal(defaultPath, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath("srv*http://msdl.microsoft.com/download/symbols/")); + Assert.Equal(defaultServer, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath($"srv**{SymbolService.MsdlSymbolServer}")); + Assert.Equal(defaultPath, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath($"srv*{localSymbolCache}*{SymbolService.SymwebSymbolServer}")); + string testpath1 = $"Cache: {localSymbolCache} Server: {SymbolService.SymwebSymbolServer}"; + Assert.Equal(testpath1, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath($"cache*{localSymbolCache};srv*")); + string testpath2 = $"Cache: {localSymbolCache} Server: {SymbolService.MsdlSymbolServer}"; + Assert.Equal(testpath2, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath($"srv**{localSymbolCache}*http://msdl.microsoft.com/download/symbols/")); + Assert.Equal($"Cache: {symbolService.DefaultSymbolCache} Cache: {localSymbolCache} Server: http://msdl.microsoft.com/download/symbols/", symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.True(symbolService.ParseSymbolPath($"symsrv*symsrv.dll*{localSymbolCache}*\\\\server\\share")); + Assert.Equal($"Cache: {localSymbolCache} Cache: \\\\server\\share", symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + Assert.True(symbolService.ParseSymbolPath("symsrv*symsrv.dll*d:\\data\\SYM\\symcache*\\\\aw0eus0symcache.file.core.windows.net\\Symbols*http://localhost/remote200/30e07e1454924e55901d7f693f7eddf1/0/x64/4542784547/remote")); + Assert.Equal("Cache: d:\\data\\SYM\\symcache Cache: \\\\aw0eus0symcache.file.core.windows.net\\Symbols Server: http://localhost/remote200/30e07e1454924e55901d7f693f7eddf1/0/x64/4542784547/remote/", symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + } + + string symbolDirectory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "c:\\symbols\\" : "/home/foo/symbols/"; + Assert.True(symbolService.ParseSymbolPath(symbolDirectory)); + Assert.Equal($"Directory: {symbolDirectory}", symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + + string symbolDirectory2 = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "c:\\foo\\bar" : "/home/foo/bar"; + Assert.True(symbolService.ParseSymbolPath($"{symbolDirectory};{symbolDirectory2};srv*")); + Assert.Equal($"Directory: {symbolDirectory} Directory: {symbolDirectory2} " + defaultServer, symbolService.FormatSymbolStores()); + symbolService.DisableSymbolStore(); + } + + #region IHost + + public IServiceEvent OnShutdownEvent { get; } = new ServiceEvent(); + + HostType IHost.HostType => HostType.DotnetDump; + + IServiceProvider IHost.Services => throw new NotImplementedException(); + + IEnumerable IHost.EnumerateTargets() => throw new NotImplementedException(); + + void IHost.DestroyTarget(ITarget target) => throw new NotImplementedException(); + + #endregion + } + public static class SymbolServiceExtensions + { + public static string FormatSymbolStores( + this ISymbolService symbolService) + { + return symbolService.ToString().Replace(Environment.NewLine, " ").TrimEnd(); + } + } +}