Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/sdks
diff options
context:
space:
mode:
authorAnkit Jain <radical@gmail.com>2020-02-23 00:16:03 +0300
committerAnkit Jain <radical@gmail.com>2020-02-23 01:49:56 +0300
commit547e4180df3cc93d1e97a7417687591f64c96849 (patch)
treef115b2d646f5b978c2980c6dc0e436ab31c881f3 /sdks
parentd02bbe4c32cade26c9cd4e5bf53fb48792ae0315 (diff)
[wasm][debugger] Fix issue in getting locals for previous frames
`mono_wasm_get_var_info` didn't correctly reset the `data.cur_frame` required for `mono_walk_stack_with_ctx`, as the stack is walked repeatedly to get the variables.
Diffstat (limited to 'sdks')
-rw-r--r--sdks/wasm/DebuggerTestSuite/Tests.cs365
-rw-r--r--sdks/wasm/debugger-driver.html13
-rw-r--r--sdks/wasm/debugger-test.cs44
3 files changed, 411 insertions, 11 deletions
diff --git a/sdks/wasm/DebuggerTestSuite/Tests.cs b/sdks/wasm/DebuggerTestSuite/Tests.cs
index 32e591fcc40..e5ee59c8ed8 100644
--- a/sdks/wasm/DebuggerTestSuite/Tests.cs
+++ b/sdks/wasm/DebuggerTestSuite/Tests.cs
@@ -41,9 +41,12 @@ namespace DebuggerTests
void CheckLocation (string script_loc, int line, int column, Dictionary<string, string> scripts, JToken location)
{
- Assert.Equal (script_loc, scripts[location["scriptId"].Value<string>()]);
- Assert.Equal (line, location ["lineNumber"].Value<int> ());
- Assert.Equal (column, location ["columnNumber"].Value<int> ());
+ var loc_str = $"{ scripts[location["scriptId"].Value<string>()] }"
+ + $"#{ location ["lineNumber"].Value<int> () }"
+ + $"#{ location ["columnNumber"].Value<int> () }";
+
+ var expected_loc_str = $"{script_loc}#{line}#{column}";
+ Assert.Equal (expected_loc_str, loc_str);
}
[Fact]
@@ -226,9 +229,23 @@ namespace DebuggerTests
Assert.True(false, $"Could not find variable '{name}'");
}
+ void CheckString (JToken locals, string name, string value) {
+ foreach (var l in locals) {
+ if (name != l["name"]?.Value<string> ())
+ continue;
+ var val = l["value"];
+ if (value == null) {
+ Assert.Equal ("object", val ["type"]?.Value<string> ());
+ Assert.Equal ("null", val["subtype"]?.Value<string> ());
+ } else {
+ Assert.Equal ("string", val ["type"]?.Value<string> ());
+ Assert.Equal (value, val["value"]?.Value <string> ());
+ }
+ return;
+ }
+ Assert.True(false, $"Could not find variable '{name}'");
+ }
void CheckObject (JToken locals, string name, string class_name, string subtype=null) {
- Console.WriteLine ($"** Locals: {locals.ToString ()}");
- Console.WriteLine ($"\tChecking {name}");
foreach (var l in locals) {
if (name != l["name"]?.Value<string> ())
continue;
@@ -243,8 +260,6 @@ namespace DebuggerTests
}
void CheckArray (JToken locals, string name, string class_name) {
- Console.WriteLine ($"** Locals: {locals.ToString ()}");
- Console.WriteLine ($"\tChecking {name}");
foreach (var l in locals) {
if (name != l["name"]?.Value<string> ())
continue;
@@ -555,6 +570,342 @@ namespace DebuggerTests
});
}
+ [Fact]
+ public async Task InspectLocalsInPreviousFramesDuringSteppingIn2 () {
+ var insp = new Inspector ();
+ //Collect events
+ var scripts = SubscribeToScripts(insp);
+
+ await Ready();
+ await insp.Ready (async (cli, token) => {
+ var dep_cs_loc = "dotnet://Simple.Dependency.dll/dependency.cs";
+
+ var ctx = new DebugTestContext (cli, insp, token, scripts);
+ var bp1_req = JObject.FromObject(new {
+ lineNumber = 24,
+ columnNumber = 2,
+ url = dicFileToUrl[dep_cs_loc]
+ });
+
+ var bp1_res = await cli.SendCommand ("Debugger.setBreakpointByUrl", bp1_req, token);
+ Assert.True (bp1_res.IsOk);
+
+ var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs";
+
+ // Will stop in Complex.DoEvenMoreStuff
+ var wait_res = await EvaluateAndCheck (
+ "window.setTimeout(function() { invoke_use_complex (); }, 1);",
+ dep_cs_loc, 24, 2, "DoEvenMoreStuff", ctx,
+ locals_fn: (locals) => {
+ Assert.Equal (1, locals.Count());
+ CheckObject (locals, "this", "Simple.Complex");
+ }
+ );
+
+ // Check UseComplex frame
+ await CheckLocalsOnFrame (wait_res ["callFrames"][3], debugger_test_loc, 17, 2, "UseComplex", ctx,
+ test_fn: (locals_m1) => {
+ Assert.Equal (7, locals_m1.Count());
+
+ CheckNumber (locals_m1, "a", 10);
+ CheckNumber (locals_m1, "b", 20);
+ CheckObject (locals_m1, "complex", "Simple.Complex");
+ CheckNumber (locals_m1, "c", 30);
+ CheckNumber (locals_m1, "d", 50);
+ CheckNumber (locals_m1, "e", 60);
+ CheckNumber (locals_m1, "f", 0);
+ }
+ );
+
+ wait_res = await StepAndCheck (StepKind.Over, dep_cs_loc, 16, 2, "DoStuff", ctx, times: 2);
+ // Check UseComplex frame again
+ await CheckLocalsOnFrame (wait_res ["callFrames"][1], debugger_test_loc, 17, 2, "UseComplex", ctx,
+ test_fn: (locals_m1) => {
+ Assert.Equal (7, locals_m1.Count());
+
+ CheckNumber (locals_m1, "a", 10);
+ CheckNumber (locals_m1, "b", 20);
+ CheckObject (locals_m1, "complex", "Simple.Complex");
+ CheckNumber (locals_m1, "c", 30);
+ CheckNumber (locals_m1, "d", 50);
+ CheckNumber (locals_m1, "e", 60);
+ CheckNumber (locals_m1, "f", 0);
+ }
+ );
+ });
+ }
+
+ [Fact]
+ public async Task InspectLocalsInPreviousFramesDuringSteppingIn () {
+ var insp = new Inspector ();
+ //Collect events
+ var scripts = SubscribeToScripts(insp);
+
+ await Ready();
+ await insp.Ready (async (cli, token) => {
+ var ctx = new DebugTestContext (cli, insp, token, scripts);
+ var bp1_req = JObject.FromObject(new {
+ lineNumber = 100,
+ columnNumber = 3,
+ url = dicFileToUrl["dotnet://debugger-test.dll/debugger-test.cs"],
+ });
+
+ var bp1_res = await cli.SendCommand ("Debugger.setBreakpointByUrl", bp1_req, token);
+ Assert.True (bp1_res.IsOk);
+
+ var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs";
+
+ // Will stop in InnerMethod
+ var wait_res = await EvaluateAndCheck (
+ "window.setTimeout(function() { invoke_outer_method(); }, 1);",
+ debugger_test_loc, 100, 3, "InnerMethod", ctx,
+ locals_fn: (locals) => {
+ Assert.Equal (4, locals.Count());
+ CheckNumber (locals, "i", 5);
+ CheckNumber (locals, "j", 24);
+ CheckString (locals, "foo_str", "foo");
+ //CheckObject (locals, "this", "Math.NestedInMath");
+ }
+ );
+
+ // Check OuterMethod frame
+ await CheckLocalsOnFrame (wait_res ["callFrames"][1], debugger_test_loc, 76, 2, "OuterMethod", ctx,
+ test_fn: (locals_m1) => {
+ Assert.Equal (5, locals_m1.Count());
+ // FIXME: Failing test CheckNumber (locals_m1, "i", 5);
+ // FIXME: Failing test CheckString (locals_m1, "text", "Hello");
+ CheckNumber (locals_m1, "new_i", 0);
+ CheckNumber (locals_m1, "k", 0);
+ //CheckObject (locals_m1, "nim", "Math.NestedInMath");
+ }
+ );
+
+ // step back into OuterMethod
+ await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 80, 2, "OuterMethod", ctx, times: 7,
+ locals_fn: (locals) => {
+ Assert.Equal (5, locals.Count());
+
+ // FIXME: Failing test CheckNumber (locals_m1, "i", 5);
+ CheckString (locals, "text", "Hello");
+ // FIXME: Failing test CheckNumber (locals, "new_i", 24);
+ CheckNumber (locals, "k", 19);
+ //CheckObject (locals, "nim", "Math.NestedInMath");
+ }
+ );
+
+ //await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 81, 2, "OuterMethod", ctx, times: 2);
+
+ // step into InnerMethod2
+ await StepAndCheck (StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 85, 1, "InnerMethod2", ctx,
+ locals_fn: (locals) => {
+ // umm use JOBject instead of JToken?
+ Assert.Equal (3, locals.Count());
+
+ CheckString (locals, "s", "test string");
+ //out var: CheckNumber (locals, "k", 0);
+ CheckNumber (locals, "i", 24);
+ }
+ );
+
+ await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 89, 1, "InnerMethod2", ctx, times: 4,
+ locals_fn: (locals) => {
+ // umm use JOBject instead of JToken?
+ Assert.Equal (3, locals.Count());
+
+ CheckString (locals, "s", "test string");
+ // FIXME: Failing test CheckNumber (locals, "k", 34);
+ CheckNumber (locals, "i", 24);
+ }
+ );
+
+ await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 81, 2, "OuterMethod", ctx, times: 2,
+ locals_fn: (locals) => {
+ Assert.Equal (5, locals.Count());
+
+ CheckString (locals, "text", "Hello");
+ // FIXME: failing test CheckNumber (locals, "i", 5);
+ CheckNumber (locals, "new_i", 22);
+ CheckNumber (locals, "k", 34);
+ //CheckObject (locals, "nim", "Math.NestedInMath");
+ }
+ );
+ });
+ }
+
+ [Fact]
+ public async Task InspectLocalsDuringSteppingIn () {
+ var insp = new Inspector ();
+ //Collect events
+ var scripts = SubscribeToScripts(insp);
+
+ await Ready();
+ await insp.Ready (async (cli, token) => {
+ var ctx = new DebugTestContext (cli, insp, token, scripts);
+ var bp1_req = JObject.FromObject(new {
+ lineNumber = 75,
+ columnNumber = 2,
+ url = dicFileToUrl["dotnet://debugger-test.dll/debugger-test.cs"],
+ });
+
+ var bp1_res = await cli.SendCommand ("Debugger.setBreakpointByUrl", bp1_req, token);
+ Assert.True (bp1_res.IsOk);
+
+ await EvaluateAndCheck ("window.setTimeout(function() { invoke_outer_method(); }, 1);",
+ "dotnet://debugger-test.dll/debugger-test.cs", 75, 2, "OuterMethod", ctx,
+ locals_fn: (locals) => {
+ Assert.Equal (5, locals.Count());
+
+ //CheckObject (locals, "nim", "Math.NestedInMath");
+ CheckNumber (locals, "i", 0);
+ CheckNumber (locals, "k", 0);
+ CheckNumber (locals, "new_i", 0);
+ CheckString (locals, "text", null);
+ }
+ );
+
+ await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 76, 2, "OuterMethod", ctx,
+ locals_fn: (locals) => {
+ Assert.Equal (5, locals.Count());
+
+ //CheckObject (locals, "nim", "Math.NestedInMath");
+ // FIXME: Failing test CheckNumber (locals, "i", 5);
+ CheckNumber (locals, "k", 0);
+ CheckNumber (locals, "new_i", 0);
+ CheckString (locals, "text", "Hello");
+ }
+ );
+
+ // Step into InnerMethod
+ await StepAndCheck (StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 94, 2, "InnerMethod", ctx);
+ await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 98, 3, "InnerMethod", ctx, times: 5,
+ locals_fn: (locals) => {
+ Assert.Equal (4, locals.Count());
+
+ CheckNumber (locals, "i", 5);
+ CheckNumber (locals, "j", 15);
+ CheckString (locals, "foo_str", "foo");
+ //CheckObject (locals, "this", "Math.NestedInMath");
+ }
+ );
+
+ // Step back to OuterMethod
+ await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 77, 2, "OuterMethod", ctx, times: 5,
+ locals_fn: (locals) => {
+ Assert.Equal (5, locals.Count());
+
+ //CheckObject (locals, "nim", "Math.NestedInMath");
+ // FIXME: Failing test CheckNumber (locals, "i", 5);
+ CheckNumber (locals, "k", 0);
+ CheckNumber (locals, "new_i", 24);
+ CheckString (locals, "text", "Hello");
+ }
+ );
+ });
+ }
+
+ async Task<JObject> StepAndCheck (StepKind kind, string script_loc, int line, int column, string function_name, DebugTestContext ctx,
+ Func<JObject, Task> wait_for_event_fn = null, Action<JToken> locals_fn = null, int times=1)
+ {
+ for (int i = 0; i < times - 1; i ++) {
+ await SendCommandAndCheck (null, $"Debugger.step{kind.ToString ()}", ctx);
+ }
+
+ // Check for method/line etc only at the last step
+ return await SendCommandAndCheck (
+ null, $"Debugger.step{kind.ToString ()}", ctx,
+ wait_for_event_fn: async (pause_location) => {
+ await CheckLocalsOnFrame (pause_location ["callFrames"][0], script_loc, line, column, function_name, ctx);
+ if (wait_for_event_fn != null)
+ await wait_for_event_fn (pause_location);
+ },
+ locals_fn: locals_fn);
+ }
+
+ async Task<JObject> EvaluateAndCheck (string expression, string script_loc, int line, int column, string function_name, DebugTestContext ctx,
+ Func<JObject, Task> wait_for_event_fn = null, Action<JToken> locals_fn = null)
+ => await SendCommandAndCheck (
+ JObject.FromObject (new { expression = expression }),
+ "Runtime.evaluate", ctx,
+ wait_for_event_fn: async (pause_location) => {
+ await CheckLocalsOnFrame (pause_location ["callFrames"][0], script_loc, line, column, function_name, ctx);
+ if (wait_for_event_fn != null)
+ await wait_for_event_fn (pause_location);
+ },
+ locals_fn: locals_fn);
+
+ async Task<JObject> SendCommandAndCheck (JObject args, string method, DebugTestContext ctx, Func<JObject, Task> wait_for_event_fn = null,
+ Action<JToken> locals_fn = null, string waitForEvent = Inspector.PAUSE)
+ {
+ var res = await ctx.cli.SendCommand (method, args, ctx.token);
+ if (!res.IsOk) {
+ Console.WriteLine ($"Failed to run command {method} with args: {args?.ToString ()}\nresult: {res.Error.ToString ()}");
+ Assert.True (false, $"SendCommand for {method} failed with {res.Error.ToString ()}");
+ }
+
+ var wait_res = await ctx.insp.WaitFor(waitForEvent);
+
+ if (wait_for_event_fn != null)
+ await wait_for_event_fn (wait_res);
+
+ if (locals_fn != null)
+ await CheckLocalsOnFrame (wait_res ["callFrames"][0], ctx, locals_fn);
+
+ return wait_res;
+ }
+
+ async Task CheckLocalsOnFrame (JToken frame, string script_loc, int line, int column, string function_name, DebugTestContext ctx, Action<JToken> test_fn = null)
+ {
+ CheckLocation (script_loc, line, column, ctx.scripts, frame ["location"]);
+ Assert.Equal (function_name, frame ["functionName"].Value<string> ());
+
+ await CheckLocalsOnFrame (frame, ctx, test_fn);
+ }
+
+ async Task CheckLocalsOnFrame (JToken frame, DebugTestContext ctx, Action<JToken> test_fn = null)
+ {
+ var get_prop_req = JObject.FromObject (new {
+ objectId = frame["callFrameId"]
+ });
+
+ var frame_props = await ctx.cli.SendCommand ("Runtime.getProperties", get_prop_req, ctx.token);
+ if (!frame_props.IsOk)
+ Assert.True (false, $"Runtime.getProperties failed for {get_prop_req.ToString ()}");
+
+ if (test_fn == null)
+ return;
+
+ var locals = frame_props.Value ["result"];
+ try {
+ test_fn (locals);
+ } catch {
+ Console.WriteLine ($"Failed trying to check locals: {locals.ToString ()}");
+ throw;
+ }
+ }
+
//TODO add tests covering basic stepping behavior as step in/out/over
}
+
+ class DebugTestContext
+ {
+ public InspectorClient cli;
+ public Inspector insp;
+ public CancellationToken token;
+ public Dictionary<string, string> scripts;
+
+ public DebugTestContext (InspectorClient cli, Inspector insp, CancellationToken token, Dictionary<string, string> scripts)
+ {
+ this.cli = cli;
+ this.insp = insp;
+ this.token = token;
+ this.scripts = scripts;
+ }
+ }
+
+ enum StepKind
+ {
+ Into,
+ Over,
+ Out
+ }
}
diff --git a/sdks/wasm/debugger-driver.html b/sdks/wasm/debugger-driver.html
index 91aa3a24a9b..42934a154d5 100644
--- a/sdks/wasm/debugger-driver.html
+++ b/sdks/wasm/debugger-driver.html
@@ -7,14 +7,19 @@
var App = {
init: function () {
this.int_add = Module.mono_bind_static_method ("[debugger-test] Math:IntAdd");
+ this.use_complex = Module.mono_bind_static_method ("[debugger-test] Math:UseComplex");
this.delegates_test = Module.mono_bind_static_method ("[debugger-test] Math:DelegatesTest");
this.generic_types_test = Module.mono_bind_static_method ("[debugger-test] Math:GenericTypesTest");
+ this.outer_method = Module.mono_bind_static_method ("[debugger-test] Math:OuterMethod");
console.log ("ready");
},
};
function invoke_add () {
return App.int_add (10, 20);
}
+ function invoke_use_complex () {
+ return App.use_complex (10, 20);
+ }
function invoke_delegates_test () {
return App.delegates_test ();
}
@@ -22,7 +27,13 @@
return App.generic_types_test ();
}
function invoke_bad_js_test () {
- return App.non_existant ();
+ console.log ("js: In invoke_bad_js_test");
+ App.non_existant ();
+ console.log ("js: After.. shouldn't reach here");
+ }
+ function invoke_outer_method () {
+ console.log('invoke_outer_method called');
+ return App.outer_method ();
}
</script>
<script type="text/javascript" src="mono-config.js"></script>
diff --git a/sdks/wasm/debugger-test.cs b/sdks/wasm/debugger-test.cs
index c5c150ef783..18fba8e5b97 100644
--- a/sdks/wasm/debugger-test.cs
+++ b/sdks/wasm/debugger-test.cs
@@ -9,10 +9,14 @@ public class Math { //Only append content to this class as the test suite depend
return e;
}
- public static int UseComplex () {
+ public static int UseComplex (int a, int b) {
var complex = new Simple.Complex (10, "xx");
- var res = complex.DoStuff ();
- return res;
+ int c = a + b;
+ int d = c + b;
+ int e = d + a;
+ int f = 0;
+ e += complex.DoStuff ();
+ return e;
}
delegate bool IsMathNull (Math m);
@@ -63,4 +67,38 @@ public class Math { //Only append content to this class as the test suite depend
}
static bool IsMathNullDelegateTarget (Math m) => m == null;
+
+ public static void OuterMethod ()
+ {
+ Console.WriteLine ($"OuterMethod called");
+ var nim = new Math.NestedInMath ();
+ var i = 5;
+ var text = "Hello";
+ var new_i = nim.InnerMethod (i);
+ Console.WriteLine ($"i: {i}");
+ Console.WriteLine ($"-- InnerMethod returned: {new_i}, nim: {nim}, text: {text}");
+ int k = 19;
+ new_i = InnerMethod2 ("test string", new_i, out k);
+ Console.WriteLine ($"-- InnerMethod2 returned: {new_i}, and k: {k}");
+ }
+
+ static int InnerMethod2 (string s, int i, out int k)
+ {
+ k = i + 10;
+ Console.WriteLine ($"s: {s}, i: {i}, k: {k}");
+ return i - 2;
+ }
+
+ class NestedInMath
+ {
+ public int InnerMethod (int i)
+ {
+ int j = i + 10;
+ string foo_str = "foo";
+ Console.WriteLine ($"i: {i} and j: {j}, foo_str: {foo_str}");
+ j += 9;
+ Console.WriteLine ($"i: {i} and j: {j}");
+ return j;
+ }
+ }
}