Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
69 changes: 44 additions & 25 deletions src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -841,23 +841,23 @@ internal sealed class AssemblyInfo
private readonly List<SourceFile> sources = new List<SourceFile>();
internal string Url { get; }
//The caller must keep the PEReader alive and undisposed throughout the lifetime of the metadata reader
private readonly IDisposable peReaderOrWebcilReader;
internal MetadataReader asmMetadataReader { get; }
private IDisposable peReaderOrWebcilReader;
internal MetadataReader asmMetadataReader { get; set; }
internal MetadataReader pdbMetadataReader { get; set; }

internal List<Tuple<MetadataReader, MetadataReader>> enCMetadataReader = new List<Tuple<MetadataReader, MetadataReader>>();
private int debugId;
internal int PdbAge { get; }
internal System.Guid PdbGuid { get; }
internal bool IsPortableCodeView { get; }
internal string PdbName { get; }
internal bool CodeViewInformationAvailable { get; }
internal int PdbAge { get; set; }
internal System.Guid PdbGuid { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make these private set.

internal bool IsPortableCodeView { get; set; }
internal string PdbName { get; set; }
internal bool CodeViewInformationAvailable { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we ever make use of these setters? It does not look like a necessary change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property seems to be never read at all.

public bool TriedToLoadSymbolsOnDemand { get; set; }

private readonly Dictionary<int, SourceFile> _documentIdToSourceFileTable = new Dictionary<int, SourceFile>();
public PdbChecksum[] PdbChecksums { get; }
public PdbChecksum[] PdbChecksums { get; set; }

public static AssemblyInfo FromBytes(MonoProxy monoProxy, SessionId sessionId, byte[] assembly, byte[] pdb, ILogger logger, CancellationToken token)
public void LoadInfoFromBytes(MonoProxy monoProxy, SessionId sessionId, byte[] assembly, byte[] pdb, CancellationToken token)
{
// First try to read it as a PE file, otherwise try it as a WebCIL file
using var asmStream = new MemoryStream(assembly);
Expand All @@ -866,50 +866,63 @@ public static AssemblyInfo FromBytes(MonoProxy monoProxy, SessionId sessionId, b
var peReader = new PEReader(asmStream);
if (!peReader.HasMetadata)
throw new BadImageFormatException();
return FromPEReader(monoProxy, sessionId, peReader, pdb, logger, token);
FromPEReader(monoProxy, sessionId, peReader, pdb, logger, token);
}
catch (BadImageFormatException)
{
// This is a WebAssembly file
asmStream.Seek(0, SeekOrigin.Begin);
var webcilReader = new WebcilReader(asmStream);
return FromWebcilReader(monoProxy, sessionId, webcilReader, pdb, logger, token);
FromWebcilReader(monoProxy, sessionId, webcilReader, pdb, logger, token);
}
}

public static AssemblyInfo FromBytes(MonoProxy monoProxy, SessionId sessionId, byte[] assembly, byte[] pdb, ILogger logger, CancellationToken token)
{
// First try to read it as a PE file, otherwise try it as a WebCIL file
using var asmStream = new MemoryStream(assembly);
var assemblyInfo = new AssemblyInfo(logger);
assemblyInfo.LoadInfoFromBytes(monoProxy, sessionId, assembly, pdb, token);
return assemblyInfo;
}

public static AssemblyInfo WithoutDebugInfo(ILogger logger)
{
return new AssemblyInfo(logger);
}

public static AssemblyInfo WithoutDebugInfo(string name, ILogger logger)
{
var ret = new AssemblyInfo(logger);
ret.Name = name;
return ret;
}

private AssemblyInfo(ILogger logger)
{
debugId = -1;
this.id = Interlocked.Increment(ref next_id);
this.logger = logger;
}
private static AssemblyInfo FromPEReader(MonoProxy monoProxy, SessionId sessionId, PEReader peReader, byte[] pdb, ILogger logger, CancellationToken token)
private void FromPEReader(MonoProxy monoProxy, SessionId sessionId, PEReader peReader, byte[] pdb, ILogger logger, CancellationToken token)
{

var debugProvider = new PortableExecutableDebugMetadataProvider(peReader);

var asmMetadataReader = PEReaderExtensions.GetMetadataReader(peReader);
string name = ReadAssemblyName(asmMetadataReader);
var summary = MetadataDebugSummary.Create(monoProxy, sessionId, name, debugProvider, pdb, token);

var assemblyInfo = new AssemblyInfo(peReader, name, asmMetadataReader, summary, logger);
return assemblyInfo;
LoadAssemblyInfo(peReader, name, asmMetadataReader, summary, logger);
}
private static AssemblyInfo FromWebcilReader(MonoProxy monoProxy, SessionId sessionId, WebcilReader wcReader, byte[] pdb, ILogger logger, CancellationToken token)
private void FromWebcilReader(MonoProxy monoProxy, SessionId sessionId, WebcilReader wcReader, byte[] pdb, ILogger logger, CancellationToken token)
{
var debugProvider = new WebcilDebugMetadataProvider(wcReader);
var asmMetadataReader = wcReader.GetMetadataReader();
string name = ReadAssemblyName(asmMetadataReader);

var summary = MetadataDebugSummary.Create(monoProxy, sessionId, name, debugProvider, pdb, token);

var assemblyInfo = new AssemblyInfo(wcReader, name, asmMetadataReader, summary, logger);
return assemblyInfo;
LoadAssemblyInfo(wcReader, name, asmMetadataReader, summary, logger);
}

private static string ReadAssemblyName(MetadataReader asmMetadataReader)
Expand All @@ -918,8 +931,7 @@ private static string ReadAssemblyName(MetadataReader asmMetadataReader)
return asmDef.GetAssemblyName().Name + ".dll";
}

private unsafe AssemblyInfo(IDisposable owningReader, string name, MetadataReader asmMetadataReader, MetadataDebugSummary summary, ILogger logger)
: this(logger)
private unsafe void LoadAssemblyInfo(IDisposable owningReader, string name, MetadataReader asmMetadataReader, MetadataDebugSummary summary, ILogger logger)
{
peReaderOrWebcilReader = owningReader;
var codeViewData = summary.CodeViewData;
Expand Down Expand Up @@ -1187,7 +1199,7 @@ public TypeInfo CreateTypeInfo(string typeName, int typeToken)
public Dictionary<string, TypeInfo> TypesByName { get; } = new();
public Dictionary<int, TypeInfo> TypesByToken { get; } = new();
public int Id => id;
public string Name { get; }
public string Name { get; set; }
public bool HasSymbols => pdbMetadataReader != null;

// "System.Threading", instead of "System.Threading, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Expand All @@ -1210,12 +1222,17 @@ public TypeInfo GetTypeByName(string name)
return res;
}

internal async Task LoadPDBFromSymbolServer(DebugStore debugStore, CancellationToken token)
internal async Task LoadPDBFromSymbolServer(MonoProxy proxy, MonoSDBHelper sdbHelper, SessionId id, DebugStore debugStore, CancellationToken token)
{
try
{
if (TriedToLoadSymbolsOnDemand)
return;
if (asmMetadataReader is null) //it means that the assembly was not loaded before because JMC was enabled
{
var ret = await sdbHelper.GetBytesFromAssemblyAndPdb(Name, false, token);
LoadInfoFromBytes(proxy, id, ret[0], ret[1], token);
}
var pdbName = Path.GetFileName(PdbName);
var pdbGuid = PdbGuid.ToString("N").ToUpperInvariant() + (IsPortableCodeView ? "FFFFFFFF" : PdbAge);
var key = $"{pdbName}/{pdbGuid}/{pdbName}";
Expand Down Expand Up @@ -1599,7 +1616,7 @@ public async IAsyncEnumerable<SourceFile> Load(SessionId id, string[] loaded_fil
new DebugItem
{
Url = file_name,
Data = context.SdbAgent.GetBytesFromAssemblyAndPdb(Path.GetFileName(unescapedFileName), token)
Data = context.SdbAgent.GetBytesFromAssemblyAndPdb(Path.GetFileName(unescapedFileName), false, token)
});
}
catch (Exception e)
Expand All @@ -1617,6 +1634,8 @@ public async IAsyncEnumerable<SourceFile> Load(SessionId id, string[] loaded_fil
byte[][] bytes = await step.Data.ConfigureAwait(false);
if (bytes[0] == null)
{
var unescapedFileName = Uri.UnescapeDataString(step.Url);
assemblies.Add(AssemblyInfo.WithoutDebugInfo(Path.GetFileName(unescapedFileName), logger));
logger.LogDebug($"Bytes from assembly {step.Url} is NULL");
continue;
}
Expand Down Expand Up @@ -1645,7 +1664,7 @@ public async IAsyncEnumerable<SourceFile> Load(SessionId id, string[] loaded_fil

public SourceFile GetFileById(SourceId id) => AllSources().SingleOrDefault(f => f.SourceId.Equals(id));

public AssemblyInfo GetAssemblyByName(string name) => assemblies.FirstOrDefault(a => a.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
public AssemblyInfo GetAssemblyByName(string name) => assemblies.FirstOrDefault(a => Path.GetFileNameWithoutExtension(a.Name).Equals(Path.GetFileNameWithoutExtension(name), StringComparison.InvariantCultureIgnoreCase));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path.GetFileNameWithoutExtension(name) - this will be unnecessarily called for every assembly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why unnecessarily? It can be comparing .dll with .exe or .dll with .webcil.


/*
V8 uses zero based indexing for both line and column.
Expand Down Expand Up @@ -1812,7 +1831,7 @@ internal async Task ReloadAllPDBsFromSymbolServersAndSendSources(MonoProxy monoP
foreach (var asm in assemblies.Where(asm => asm.pdbMetadataReader == null))
{
asm.TriedToLoadSymbolsOnDemand = false; //force to load again because added another symbol server
await asm.LoadPDBFromSymbolServer(this, token);
await asm.LoadPDBFromSymbolServer(monoProxy, context.SdbAgent, id, this, token);
foreach (var source in asm.Sources)
await monoProxy.OnSourceFileAdded(id, source, context, token);
}
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,6 @@ internal async Task<Result> GetMethodLocation(MessageId id, JObject args, Cancel

// GetAssemblyByName seems to work on file names
AssemblyInfo assembly = store.GetAssemblyByName(aname);
assembly ??= store.GetAssemblyByName(aname + ".exe");
assembly ??= store.GetAssemblyByName(aname + ".dll");
if (assembly == null)
{
Expand Down
24 changes: 22 additions & 2 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ internal enum CmdAssembly {
GetTypeFromToken = 11,
GetMethodFromToken = 12,
HasDebugInfo = 13,
HasDebugInfoLoaded = 18
}

internal enum CmdModule {
Expand Down Expand Up @@ -878,6 +879,14 @@ public async Task<AssemblyInfo> GetAssemblyInfo(int assemblyId, CancellationToke
logger.LogDebug($"Created assembly without debug information: {assemblyName}");
}
}
else
{
if (asm.asmMetadataReader is null && proxy.JustMyCode) //load on demand
{
var bytes = await GetBytesFromAssemblyAndPdb(asm.Name, true, token);
asm.LoadInfoFromBytes(proxy, sessionId, bytes[0], bytes[1], token);
}
}
asm.SetDebugId(assemblyId);
assemblies[assemblyId] = asm;
return asm;
Expand Down Expand Up @@ -1909,7 +1918,7 @@ public async Task<string> InvokeToStringAsync(IEnumerable<int> typeIds, bool isV
foreach (var methodId in methodIds)
{
var methodInfoFromRuntime = await GetMethodInfo(methodId, token);
if (methodInfoFromRuntime.Info.GetParametersInfo().Length > 0)
if (methodInfoFromRuntime != null && methodInfoFromRuntime.Info.GetParametersInfo().Length > 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be if (methodInfoFromRuntime?.Info?.GetParametersInfo()?.Length > 0)

continue;
var retMethod = await InvokeMethod(objectId, methodId, isValueType, token);
return retMethod["value"]?["value"].Value<string>();
Expand Down Expand Up @@ -2475,8 +2484,19 @@ public async Task<bool> ApplyUpdates(int moduleId, string dmeta, string dil, str
return true;
}

public async Task<byte[][]> GetBytesFromAssemblyAndPdb(string assemblyName, CancellationToken token)
public async Task<bool> HasDebugInfoLoadedByRuntime(string assemblyName, CancellationToken token)
{
var assemblyId = await GetAssemblyId(assemblyName, token);
using var commandParamsWriter1 = new MonoBinaryWriter();
commandParamsWriter1.Write(assemblyId);
using var retDebuggerCmdReader1 = await SendDebuggerAgentCommand(CmdAssembly.HasDebugInfoLoaded, commandParamsWriter1, token);
return retDebuggerCmdReader1.ReadByte() == 1;
}

public async Task<byte[][]> GetBytesFromAssemblyAndPdb(string assemblyName, bool ignoreJMC, CancellationToken token)
{
if (!ignoreJMC && proxy.JustMyCode && !(await HasDebugInfoLoadedByRuntime(assemblyName, token)))
return new byte[2][];
using var commandParamsWriter = new MonoBinaryWriter();
byte[] assembly_buf = null;
byte[] pdb_buf = null;
Expand Down