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/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();
+ }
+ }
+}