From e077fd833174ceb0e50542fbc5f77755145894dd Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Mon, 12 Dec 2022 12:22:31 -0300 Subject: [PATCH] backport 78651 --- .../debugger/BrowserDebugProxy/DebugStore.cs | 31 +++++ .../debugger/BrowserDebugProxy/MonoProxy.cs | 2 +- .../BrowserDebugProxy/MonoSDBHelper.cs | 49 ++++++-- .../debugger/DebuggerTestSuite/AsyncTests.cs | 72 +++++++++++ .../debugger-test-vb/debugger-test-vb.vb | 30 +++++ .../debugger-test-vb/debugger-test-vb.vbproj | 8 ++ .../debugger-test/debugger-async-test.cs | 114 ++++++++++++++++++ .../tests/debugger-test/debugger-test.csproj | 2 + 8 files changed, 297 insertions(+), 11 deletions(-) create mode 100644 src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vb create mode 100644 src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vbproj diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 6d6fa4c4036caf..bd887b10c98314 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -350,6 +350,7 @@ internal sealed class MethodInfo private ParameterInfo[] _parametersInfo; public int KickOffMethod { get; } internal bool IsCompilerGenerated { get; } + private readonly AsyncScopeDebugInformation[] _asyncScopes; public MethodInfo(AssemblyInfo assembly, string methodName, int methodToken, TypeInfo type, MethodAttributes attrs) { @@ -361,6 +362,7 @@ public MethodInfo(AssemblyInfo assembly, string methodName, int methodToken, Typ this.TypeInfo = type; TypeInfo.Methods.Add(this); assembly.Methods[methodToken] = this; + _asyncScopes = Array.Empty(); } public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader) @@ -447,7 +449,34 @@ public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, DebuggerAttrInfo.ClearInsignificantAttrFlags(); } if (pdbMetadataReader != null) + { localScopes = pdbMetadataReader.GetLocalScopes(methodDefHandle); + byte[] scopeDebugInformation = + (from cdiHandle in pdbMetadataReader.GetCustomDebugInformation(methodDefHandle) + let cdi = pdbMetadataReader.GetCustomDebugInformation(cdiHandle) + where pdbMetadataReader.GetGuid(cdi.Kind) == PortableCustomDebugInfoKinds.StateMachineHoistedLocalScopes + select pdbMetadataReader.GetBlobBytes(cdi.Value)).FirstOrDefault(); + + if (scopeDebugInformation != null) + { + _asyncScopes = new AsyncScopeDebugInformation[scopeDebugInformation.Length / 8]; + for (int i = 0; i < _asyncScopes.Length; i++) + { + int scopeOffset = BitConverter.ToInt32(scopeDebugInformation, i * 8); + int scopeLen = BitConverter.ToInt32(scopeDebugInformation, (i * 8) + 4); + _asyncScopes[i] = new AsyncScopeDebugInformation(scopeOffset, scopeOffset + scopeLen); + } + } + + _asyncScopes ??= Array.Empty(); + } + } + + public bool ContainsAsyncScope(int oneBasedIdx, int offset) + { + int arrIdx = oneBasedIdx - 1; + return arrIdx >= 0 && arrIdx < _asyncScopes.Length && + offset >= _asyncScopes[arrIdx].StartOffset && offset <= _asyncScopes[arrIdx].EndOffset; } public ParameterInfo[] GetParametersInfo() @@ -617,6 +646,8 @@ public override int GetHashCode(MethodInfo loc) return loc.Source.Id; } } + + private record struct AsyncScopeDebugInformation(int StartOffset, int EndOffset); } internal sealed class ParameterInfo diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index cf4bb725cdc348..05cf7436e5ef84 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -1424,7 +1424,7 @@ internal async Task GetScopeProperties(SessionId msg_id, int scopeId, Ca VarInfo[] varIds = scope.Method.Info.GetLiveVarsAt(scope.Location.IlLocation.Offset); - var values = await context.SdbAgent.StackFrameGetValues(scope.Method, context.ThreadId, scopeId, varIds, token); + var values = await context.SdbAgent.StackFrameGetValues(scope.Method, context.ThreadId, scopeId, varIds, scope.Location.IlLocation.Offset, token); if (values != null) { if (values == null || values.Count == 0) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 8ff9f8ae9af99e..8afc297a2ef32d 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -786,8 +786,10 @@ internal sealed class MonoSDBHelper private DebugStore store; private SessionId sessionId; - private readonly ILogger logger; - private static readonly Regex regexForAsyncLocals = new (@"\<([^)]*)\>", RegexOptions.Singleline); + internal readonly ILogger logger; + private static readonly Regex regexForAsyncLocals = new(@"\<([^)]*)\>([^)]*)([_][_])([0-9]*)", RegexOptions.Singleline); //5__1 + private static readonly Regex regexForVBAsyncLocals = new(@"\$VB\$ResumableLocal_([^)]*)\$([0-9]*)", RegexOptions.Singleline); //$VB$ResumableLocal_testVbScope$2 + private static readonly Regex regexForVBAsyncMethodName = new(@"VB\$StateMachine_([0-9]*)_([^)]*)", RegexOptions.Singleline); //VB$StateMachine_2_RunVBScope private static readonly Regex regexForAsyncMethodName = new (@"\<([^>]*)\>([d][_][_])([0-9]*)", RegexOptions.Compiled); private static readonly Regex regexForGenericArgs = new (@"[`][0-9]+", RegexOptions.Compiled); private static readonly Regex regexForNestedLeftRightAngleBrackets = new ("^(((?'Open'<)[^<>]*)+((?'Close-Open'>)[^<>]*)+)*(?(Open)(?!))[^<>]*", RegexOptions.Compiled); @@ -1270,6 +1272,14 @@ public async Task GetPrettyMethodName(int methodId, bool isAnonymous, Ca if (anonymousMethodId.LastIndexOf('_') >= 0) anonymousMethodId = klassName.Substring(klassName.LastIndexOf('_') + 1); } + else if (klassName.StartsWith("VB$")) + { + var match = regexForVBAsyncMethodName.Match(klassName); + if (match.Success) + ret = ret.Insert(0, match.Groups[2].Value); + else + ret = ret.Insert(0, klassName); + } else { var matchOnClassName = regexForNestedLeftRightAngleBrackets.Match(klassName); @@ -1911,7 +1921,7 @@ private static bool IsClosureReferenceField (string fieldName) fieldName.StartsWith ("<>8__", StringComparison.Ordinal); } - public async Task GetHoistedLocalVariables(int objectId, IEnumerable asyncLocals, CancellationToken token) + public async Task GetHoistedLocalVariables(MethodInfoWithDebugInformation method, int objectId, IEnumerable asyncLocals, int offset, CancellationToken token) { JArray asyncLocalsFull = new JArray(); List objectsAlreadyRead = new(); @@ -1922,7 +1932,6 @@ public async Task GetHoistedLocalVariables(int objectId, IEnumerable GetHoistedLocalVariables(int objectId, IEnumerable", StringComparison.Ordinal)) //examples: <>t__builder, <>1__state { @@ -1945,18 +1955,37 @@ public async Task GetHoistedLocalVariables(int objectId, IEnumerable StackFrameGetValues(MethodInfoWithDebugInformation method, int thread_id, int frame_id, VarInfo[] varIds, CancellationToken token) + public async Task StackFrameGetValues(MethodInfoWithDebugInformation method, int thread_id, int frame_id, VarInfo[] varIds, int offset, CancellationToken token) { using var commandParamsWriter = new MonoBinaryWriter(); commandParamsWriter.Write(thread_id); @@ -1973,7 +2002,7 @@ public async Task StackFrameGetValues(MethodInfoWithDebugInformation met retDebuggerCmdReader.ReadByte(); //ignore type var objectId = retDebuggerCmdReader.ReadInt32(); GetMembersResult asyncProxyMembers = await MemberObjectsExplorer.GetObjectMemberValues(this, objectId, GetObjectCommandOptions.WithProperties, token, includeStatic: true); - var asyncLocals = await GetHoistedLocalVariables(objectId, asyncProxyMembers.Flatten(), token); + var asyncLocals = await GetHoistedLocalVariables(method, objectId, asyncProxyMembers.Flatten(), offset, token); return asyncLocals; } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AsyncTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AsyncTests.cs index 17d29d62bab844..96dc8f25c07f9e 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/AsyncTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/AsyncTests.cs @@ -81,5 +81,77 @@ public async Task AsyncLocalsInNestedContinueWithStaticBlock() => await CheckIns ncs_dt0 = TDateTime(new DateTime(3412, 4, 6, 8, 0, 2)) }, "locals"); }); + + [Theory] + [InlineData("Run", 246, 16, 252, 16, "RunCSharpScope")] + [InlineData("RunContinueWith", 277, 20, 283, 20, "RunContinueWithSameVariableName")] + [InlineData("RunNestedContinueWith", 309, 24, 315, 24, "RunNestedContinueWithSameVariableName.AnonymousMethod__1")] + [InlineData("RunNonAsyncMethod", 334, 16, 340, 16, "RunNonAsyncMethodSameVariableName")] + public async Task InspectLocalsWithSameNameInDifferentScopesInAsyncMethod_CSharp(string method_to_run, int line1, int col1, int line2, int col2, string func_to_pause) + => await InspectLocalsWithSameNameInDifferentScopesInAsyncMethod( + $"[debugger-test] DebuggerTests.AsyncTests.VariablesWithSameNameDifferentScopes:{method_to_run}", + "dotnet://debugger-test.dll/debugger-async-test.cs", + line1, + col1, + line2, + col2, + $"DebuggerTests.AsyncTests.VariablesWithSameNameDifferentScopes.{func_to_pause}", + "testCSharpScope"); + + [Theory] + [InlineData("[debugger-test-vb] DebuggerTestVB.TestVbScope:Run", 14, 12, 22, 12, "DebuggerTestVB.TestVbScope.RunVBScope", "testVbScope")] + public async Task InspectLocalsWithSameNameInDifferentScopesInAsyncMethod_VB(string method_to_run, int line1, int col1, int line2, int col2, string func_to_pause, string variable_to_inspect) + => await InspectLocalsWithSameNameInDifferentScopesInAsyncMethod( + method_to_run, + "dotnet://debugger-test-vb.dll/debugger-test-vb.vb", + line1, + col1, + line2, + col2, + func_to_pause, + variable_to_inspect); + + private async Task InspectLocalsWithSameNameInDifferentScopesInAsyncMethod(string method_to_run, string source_to_pause, int line1, int col1, int line2, int col2, string func_to_pause, string variable_to_inspect) + { + var expression = $"{{ invoke_static_method('{method_to_run}'); }}"; + + await EvaluateAndCheck( + "window.setTimeout(function() {" + expression + "; }, 1);", + source_to_pause, line1, col1, + func_to_pause, + locals_fn: async (locals) => + { + await CheckString(locals, variable_to_inspect, "hello"); + await CheckString(locals, "onlyInFirstScope", "only-in-first-scope"); + Assert.False(locals.Any(jt => jt["name"]?.Value() == "onlyInSecondScope")); + } + ); + await StepAndCheck(StepKind.Resume, source_to_pause, line2, col2, func_to_pause, + locals_fn: async (locals) => + { + await CheckString(locals, variable_to_inspect, "hi"); + await CheckString(locals, "onlyInSecondScope", "only-in-second-scope"); + Assert.False(locals.Any(jt => jt["name"]?.Value() == "onlyInFirstScope")); + } + ); + } + + [Fact] + public async Task InspectLocalsInAsyncVBMethod() + { + var expression = $"{{ invoke_static_method('[debugger-test-vb] DebuggerTestVB.TestVbScope:Run'); }}"; + + await EvaluateAndCheck( + "window.setTimeout(function() {" + expression + "; }, 1);", + "dotnet://debugger-test-vb.dll/debugger-test-vb.vb", 14, 12, + "DebuggerTestVB.TestVbScope.RunVBScope", + locals_fn: async (locals) => + { + await CheckString(locals, "testVbScope", "hello"); + CheckNumber(locals, "a", 10); + CheckNumber(locals, "data", 10); + } + ); + } } } diff --git a/src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vb b/src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vb new file mode 100644 index 00000000000000..4ca35c3281173e --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vb @@ -0,0 +1,30 @@ +Public Class TestVbScope + Public Shared Async Function Run() As Task + Await RunVBScope(10) + Await RunVBScope(1000) + End Function + + Public Shared Async Function RunVBScope(data As Integer) As Task(Of Integer) + Dim a As Integer + a = 10 + If data < 999 Then + Dim testVbScope As String + Dim onlyInFirstScope As String + testVbScope = "hello" + onlyInFirstScope = "only-in-first-scope" + System.Diagnostics.Debugger.Break() + Await Task.Delay(1) + Return data + Else + Dim testVbScope As String + Dim onlyInSecondScope As String + testVbScope = "hi" + onlyInSecondScope = "only-in-second-scope" + System.Diagnostics.Debugger.Break() + Await Task.Delay(1) + Return data + End If + + End Function + +End Class diff --git a/src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vbproj b/src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vbproj new file mode 100644 index 00000000000000..c54512c75cdfcd --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-vb/debugger-test-vb.vbproj @@ -0,0 +1,8 @@ + + + + DebuggerTestVB + net7.0 + + + diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs index da30a3132967ed..3c103d3d6f489d 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-async-test.cs @@ -230,4 +230,118 @@ await Task.Delay(300).ContinueWith(t2 => } } + public class VariablesWithSameNameDifferentScopes + { + public static async Task Run() + { + await RunCSharpScope(10); + await RunCSharpScope(1000); + } + + public static async Task RunCSharpScope(int number) + { + await Task.Delay(1); + if (number < 999) + { + string testCSharpScope = "hello"; string onlyInFirstScope = "only-in-first-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + else + { + string testCSharpScope = "hi"; string onlyInSecondScope = "only-in-second-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + } + + public static async Task RunContinueWith() + { + await RunContinueWithSameVariableName(10); + await RunContinueWithSameVariableName(1000); + } + + public static async Task RunNestedContinueWith() + { + await RunNestedContinueWithSameVariableName(10); + await RunNestedContinueWithSameVariableName(1000); + } + + public static async Task RunContinueWithSameVariableName(int number) + { + await Task.Delay(500).ContinueWith(async t => + { + await Task.Delay(1); + if (number < 999) + { + var testCSharpScope = new String("hello"); string onlyInFirstScope = "only-in-first-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + else + { + var testCSharpScope = new String("hi"); string onlyInSecondScope = "only-in-second-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + }); + Console.WriteLine ($"done with this method"); + } + + public static async Task RunNestedContinueWithSameVariableName(int number) + { + await Task.Delay(500).ContinueWith(async t => + { + if (number < 999) + { + var testCSharpScope = new String("hello_out"); string onlyInFirstScope = "only-in-first-scope_out"; + Console.WriteLine(testCSharpScope); + } + else + { + var testCSharpScope = new String("hi_out"); string onlyInSecondScope = "only-in-second-scope_out"; + Console.WriteLine(testCSharpScope); + } + await Task.Delay(300).ContinueWith(t2 => + { + if (number < 999) + { + var testCSharpScope = new String("hello"); string onlyInFirstScope = "only-in-first-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + else + { + var testCSharpScope = new String("hi"); string onlyInSecondScope = "only-in-second-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + }); + }); + Console.WriteLine ($"done with this method"); + } + + public static void RunNonAsyncMethod() + { + RunNonAsyncMethodSameVariableName(10); + RunNonAsyncMethodSameVariableName(1000); + } + + public static string RunNonAsyncMethodSameVariableName(int number) + { + if (number < 999) + { + var testCSharpScope = new String("hello"); string onlyInFirstScope = "only-in-first-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + else + { + var testCSharpScope = new String("hi"); string onlyInSecondScope = "only-in-second-scope"; + System.Diagnostics.Debugger.Break(); + return testCSharpScope; + } + } + } + } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj index a9d8b0e66ecdc0..fb946fd185b227 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj @@ -27,6 +27,7 @@ + @@ -61,6 +62,7 @@ +