Skip to content

Commit

Permalink
Improve SymbolService.ParseSymbolPath support for Watson. Issue dotne…
Browse files Browse the repository at this point in the history
…t#2512. Can

now handle the various symbol paths that Watson can throw at us. Doesn't support actually calling the symbol server
dll like in the symsrv*symaudit.dll*\\server\share syntax. The dll is ignored.
  • Loading branch information
mikem8361 committed Sep 20, 2021
1 parent 4740ae6 commit 1e0c106
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 49 deletions.
Empty file modified dotnet.sh
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,8 +28,8 @@ public class SymbolService : ISymbolService
/// <summary>
/// Symbol server URLs
/// </summary>
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;
Expand Down Expand Up @@ -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<string> 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<string>())
{
AddCachePath(symbolCachePath);
AddCachePath(symbolCachePath.Trim());
}
if (symbolDirectoryPath != null)
{
AddDirectoryPath(symbolDirectoryPath);
AddDirectoryPath(symbolDirectoryPath.Trim());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/SOS/SOS.Extensions/HostServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
18 changes: 18 additions & 0 deletions src/SOS/SOS.Hosting/SymbolServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ public static class SymbolServiceExtensions
// HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
const int E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007a);

/// <summary>
/// Set the windows symbol path converting the default "srv*" to the cached public symbol server URL.
/// </summary>
/// <param name="symbolPath">The windows symbol path to translate and set</param>
/// <returns>if false, error parsing symbol path</returns>
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);
}

/// <summary>
/// Metadata locator helper for the DAC.
/// </summary>
Expand Down
8 changes: 4 additions & 4 deletions src/SOS/SOS.Hosting/SymbolServiceWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,16 @@ private bool InitializeSymbolStore(
/// <summary>
/// Parse the Windows sympath format
/// </summary>
/// <param name="windowsSymbolPath">windows symbol path</param>
/// <param name="symbolPath">windows symbol path</param>
/// <returns>if false, failure</returns>
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);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Test the service event implementation
/// </summary>
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<ITarget> 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();
}
}
}

0 comments on commit 1e0c106

Please sign in to comment.