-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[wasm][debugger] Improve debugger performance based on JMC #86982
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
Changes from 5 commits
58562fb
ef3da1f
1027969
6084272
38ad5f7
898bdfc
7565f63
baf3c84
6222b1e
d50bfb4
7b59456
8b7c51a
e183804
c16349e
6becf9c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; } | ||
|
||
| internal bool IsPortableCodeView { get; set; } | ||
| internal string PdbName { get; set; } | ||
| internal bool CodeViewInformationAvailable { get; set; } | ||
|
||
| 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); | ||
|
|
@@ -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; | ||
| } | ||
thaystg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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) | ||
|
|
@@ -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; | ||
|
|
@@ -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" | ||
|
|
@@ -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}"; | ||
|
|
@@ -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) | ||
|
|
@@ -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; | ||
| } | ||
|
|
@@ -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)); | ||
|
||
|
|
||
| /* | ||
| V8 uses zero based indexing for both line and column. | ||
|
|
@@ -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); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -204,6 +204,7 @@ internal enum CmdAssembly { | |
| GetTypeFromToken = 11, | ||
| GetMethodFromToken = 12, | ||
| HasDebugInfo = 13, | ||
| HasDebugInfoLoaded = 18 | ||
| } | ||
|
|
||
| internal enum CmdModule { | ||
|
|
@@ -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; | ||
|
|
@@ -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) | ||
ilonatommy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| continue; | ||
| var retMethod = await InvokeMethod(objectId, methodId, isValueType, token); | ||
| return retMethod["value"]?["value"].Value<string>(); | ||
|
|
@@ -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) | ||
thaystg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| var assemblyId = await GetAssemblyId(assemblyName, token); | ||
| using var commandParamsWriter1 = new MonoBinaryWriter(); | ||
thaystg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.