Skip to content

Commit

Permalink
[release/5.0] [wasm] Download Symbols from microsoft symbol server (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
thaystg authored Oct 2, 2020
1 parent fd61375 commit 863b458
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 6 deletions.
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

0 comments on commit 863b458

Please sign in to comment.