Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/5.0] [wasm] Download Symbols from microsoft symbol server #42901

Merged
merged 1 commit into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/mono/wasm/debugger/BrowserDebugHost/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;

namespace Microsoft.WebAssembly.Diagnostics
{
Expand Down Expand Up @@ -154,7 +155,10 @@ async Task ConnectProxy(HttpContext context)
{
using var loggerFactory = LoggerFactory.Create(
builder => builder.AddConsole().AddFilter(null, LogLevel.Information));
var proxy = new DebuggerProxy(loggerFactory);

context.Request.Query.TryGetValue("urlSymbolServer", out StringValues urlSymbolServerList);
var proxy = new DebuggerProxy(loggerFactory, urlSymbolServerList.ToList());

var ideSocket = await context.WebSockets.AcceptWebSocketAsync();

await proxy.Run(endpoint, ideSocket);
Expand Down
19 changes: 18 additions & 1 deletion src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ class AssemblyInfo
Dictionary<string, TypeInfo> typesByName = new Dictionary<string, TypeInfo>();
readonly List<SourceFile> sources = new List<SourceFile>();
internal string Url { get; }
public bool TriedToLoadSymbolsOnDemand { get; set; }

public AssemblyInfo(IAssemblyResolver resolver, string url, byte[] assembly, byte[] pdb)
{
Expand Down Expand Up @@ -446,7 +447,23 @@ public AssemblyInfo(ILogger logger)
this.logger = logger;
}

void Populate()
public ModuleDefinition Image => image;

public void ClearDebugInfo()
{
foreach (var type in image.GetTypes())
{
var typeInfo = new TypeInfo(this, type);
typesByName[type.FullName] = typeInfo;

foreach (var method in type.Methods)
{
method.DebugInformation = null;
}
}
}

public void Populate()
{
ProcessSourceLink();

Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
Expand All @@ -16,9 +17,9 @@ public class DebuggerProxy
{
private readonly MonoProxy proxy;

public DebuggerProxy(ILoggerFactory loggerFactory)
public DebuggerProxy(ILoggerFactory loggerFactory, IList<string> urlSymbolServerList)
{
proxy = new MonoProxy(loggerFactory);
proxy = new MonoProxy(loggerFactory, urlSymbolServerList);
}

public Task Run(Uri browserUri, WebSocket ideSocket)
Expand Down
109 changes: 108 additions & 1 deletion src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,26 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Mono.Cecil.Cil;
using Mono.Cecil.Pdb;
using Mono.Cecil;
using System.Net.Http;

namespace Microsoft.WebAssembly.Diagnostics
{

internal class MonoProxy : DevToolsProxy
{
IList<string> urlSymbolServerList;
static HttpClient client = new HttpClient();
HashSet<SessionId> sessions = new HashSet<SessionId>();
Dictionary<SessionId, ExecutionContext> contexts = new Dictionary<SessionId, ExecutionContext>();

public MonoProxy(ILoggerFactory loggerFactory, bool hideWebDriver = true) : base(loggerFactory) { this.hideWebDriver = hideWebDriver; }
public MonoProxy(ILoggerFactory loggerFactory, IList<string> urlSymbolServerList, bool hideWebDriver = true) : base(loggerFactory)
{
this.hideWebDriver = hideWebDriver;
this.urlSymbolServerList = urlSymbolServerList ?? new List<string>();
}

readonly bool hideWebDriver;

Expand Down Expand Up @@ -360,6 +370,15 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
}

// Protocol extensions
case "DotnetDebugger.addSymbolServerUrl":
{
string url = args["url"]?.Value<string>();
if (!String.IsNullOrEmpty(url) && !urlSymbolServerList.Contains(url))
urlSymbolServerList.Add(url);
SendResponse(id, Result.OkFromObject(new { }), token);
return true;
}

case "DotnetDebugger.getMethodLocation":
{
Console.WriteLine("set-breakpoint-by-method: " + id + " " + args);
Expand Down Expand Up @@ -562,6 +581,19 @@ async Task<bool> OnPause(SessionId sessionId, JObject args, CancellationToken to

var method = asm.GetMethodByToken(method_token);

if (method == null && !asm.Image.HasSymbols)
{
try
{
method = await LoadSymbolsOnDemand(asm, method_token, sessionId, token);
}
catch (Exception e)
{
Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name} exception: {e.ToString()}");
continue;
}
}

if (method == null)
{
Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
Expand Down Expand Up @@ -639,6 +671,81 @@ async Task<bool> OnPause(SessionId sessionId, JObject args, CancellationToken to
return true;
}

async Task<MethodInfo> LoadSymbolsOnDemand(AssemblyInfo asm, uint method_token, SessionId sessionId, CancellationToken token)
{
var context = GetContext(sessionId);
if (asm.TriedToLoadSymbolsOnDemand)
return null;
asm.TriedToLoadSymbolsOnDemand = true;
ImageDebugHeader header = asm.Image.GetDebugHeader();

for (var i = 0; i < header.Entries.Length; i++)
{
var entry = header.Entries[i];
if (entry.Directory.Type != ImageDebugType.CodeView)
{
continue;
}

var data = entry.Data;

if (data.Length < 24)
return null;

var pdbSignature = (data[0]
| (data[1] << 8)
| (data[2] << 16)
| (data[3] << 24));

if (pdbSignature != 0x53445352) // "SDSR" mono/metadata/debug-mono-ppdb.c#L101
return null;

var buffer = new byte[16];
Buffer.BlockCopy(data, 4, buffer, 0, 16);

var pdbAge = (data[20]
| (data[21] << 8)
| (data[22] << 16)
| (data[23] << 24));

var pdbGuid = new Guid(buffer);
var buffer2 = new byte[(data.Length - 24) - 1];
Buffer.BlockCopy(data, 24, buffer2, 0, (data.Length - 24) - 1);
var pdbName = System.Text.Encoding.UTF8.GetString(buffer2, 0, buffer2.Length);
pdbName = Path.GetFileName(pdbName);

foreach (var urlSymbolServer in urlSymbolServerList)
{
var downloadURL = $"{urlSymbolServer}/{pdbName}/{pdbGuid.ToString("N").ToUpper() + pdbAge}/{pdbName}";

try
{
using HttpResponseMessage response = await client.GetAsync(downloadURL);
using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync();
var portablePdbReaderProvider = new PdbReaderProvider();
var symbolReader = portablePdbReaderProvider.GetSymbolReader(asm.Image, streamToReadFrom);
asm.ClearDebugInfo(); //workaround while cecil PR #686 is not merged
asm.Image.ReadSymbols(symbolReader);
asm.Populate();
foreach (var source in asm.Sources)
{
var scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData));
SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
}
return asm.GetMethodByToken(method_token);
}
catch (Exception e)
{
Log("info", $"Unable to load symbols on demand exception: {e.ToString()} url:{downloadURL} assembly: {asm.Name}");
}
}
break;
}

Log("info", "Unable to load symbols on demand assembly: {asm.Name}");
return null;
}

async Task OnDefaultContext(SessionId sessionId, ExecutionContext context, CancellationToken token)
{
Log("verbose", "Default context created, clearing state and sending events");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, Func

using var loggerFactory = LoggerFactory.Create(
builder => builder.AddConsole().AddFilter(null, LogLevel.Information));
var proxy = new DebuggerProxy(loggerFactory);
var proxy = new DebuggerProxy(loggerFactory, null);
var browserUri = new Uri(con_str);
var ideSocket = await context.WebSockets.AcceptWebSocketAsync();

Expand Down