Skip to content
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

[debugger][wasm] Added support for non user code attribute #63876

Merged
merged 15 commits into from
Jan 20, 2022
Merged
Show file tree
Hide file tree
Changes from 14 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
33 changes: 24 additions & 9 deletions src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,7 @@ internal class MethodInfo
internal LocalScopeHandleCollection localScopes;
public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0;
public int IsAsync { get; set; }
public bool IsHiddenFromDebugger { get; }
public bool HasStepThroughAttribute { get; }
public DebuggerAttributesInfo DebuggerAttrInfo { get; set; }
public TypeInfo TypeInfo { get; }

public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader)
Expand Down Expand Up @@ -371,23 +370,32 @@ public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle,
StartLocation = new SourceLocation(this, start);
EndLocation = new SourceLocation(this, end);

DebuggerAttrInfo = new DebuggerAttributesInfo();
foreach (var cattr in methodDef.GetCustomAttributes())
{
var ctorHandle = asmMetadataReader.GetCustomAttribute(cattr).Constructor;
if (ctorHandle.Kind == HandleKind.MemberReference)
{
var container = asmMetadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent;
var name = asmMetadataReader.GetString(asmMetadataReader.GetTypeReference((TypeReferenceHandle)container).Name);
if (name == "DebuggerHiddenAttribute")
var stopSearch = true;
switch (name)
{
IsHiddenFromDebugger = true;
break;
case "DebuggerHiddenAttribute":
DebuggerAttrInfo.HasDebuggerHidden = true;
break;
case "DebuggerStepThroughAttribute":
DebuggerAttrInfo.HasStepThrough = true;
break;
case "DebuggerNonUserCodeAttribute":
DebuggerAttrInfo.HasNonUserCode = true;
break;
default:
stopSearch = false;
break;
}
if (name == "DebuggerStepThroughAttribute")
{
HasStepThroughAttribute = true;
if (stopSearch)
break;
}

}
}
Expand Down Expand Up @@ -478,6 +486,13 @@ public VarInfo[] GetLiveVarsAt(int offset)
}

public override string ToString() => "MethodInfo(" + Name + ")";

public class DebuggerAttributesInfo
{
public bool HasDebuggerHidden { get; internal set; }
public bool HasStepThrough { get; internal set; }
public bool HasNonUserCode { get; internal set; }
}
}

internal class TypeInfo
Expand Down
9 changes: 6 additions & 3 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -853,9 +853,12 @@ private async Task<bool> SendCallStack(SessionId sessionId, ExecutionContext con
if (shouldReturn)
return true;

if (j == 0 && (method?.Info.HasStepThroughAttribute == true || method?.Info.IsHiddenFromDebugger == true))
if (j == 0 &&
(method?.Info.DebuggerAttrInfo.HasStepThrough == true ||
method?.Info.DebuggerAttrInfo.HasDebuggerHidden == true ||
(method?.Info.DebuggerAttrInfo.HasNonUserCode == true && JustMyCode)))
{
if (method.Info.IsHiddenFromDebugger)
if (method.Info.DebuggerAttrInfo.HasDebuggerHidden)
{
if (event_kind == EventKind.Step)
context.IsSkippingHiddenMethod = true;
Expand Down Expand Up @@ -1443,7 +1446,7 @@ private async Task SetBreakpoint(SessionId sessionId, DebugStore store, Breakpoi
{
SourceLocation loc = sourceId.First();
req.Method = loc.IlLocation.Method;
if (req.Method.IsHiddenFromDebugger)
if (req.Method.DebuggerAttrInfo.HasDebuggerHidden)
continue;

Breakpoint bp = await SetMonoBreakpoint(sessionId, req.Id, loc, req.Condition, token);
Expand Down
140 changes: 107 additions & 33 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -763,74 +763,148 @@ public async Task StepThroughAttributeStepInWithBp(bool justMyCodeEnabled)
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task StepThroughAttributeResumeWithBp(bool justMyCodeEnabled)
[InlineData(false, "RunStepThrough")]
[InlineData(true, "RunStepThrough")]
[InlineData(true, "RunNonUserCode")]
[InlineData(false, "RunNonUserCode")]
public async Task StepThroughOrNonUserCodeAttributeStepInNoBp(bool justMyCodeEnabled, string evalFunName)
{
var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 1);
var bp1_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "NotStopOnJustMyCode", 1);
var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 2);
var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 1);
if (justMyCodeEnabled)
await SetJustMyCode(true);

var init_location = await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerAttribute:RunStepThrough'); }, 1);",
$"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);",
"dotnet://debugger-test.dll/debugger-test.cs",
bp_init.Value["locations"][0]["lineNumber"].Value<int>(),
bp_init.Value["locations"][0]["columnNumber"].Value<int>(),
"RunStepThrough"
evalFunName
);
var (funcName, line, col) = (evalFunName, 868, 8);
if (evalFunName == "RunNonUserCode")
(funcName, line, col) = justMyCodeEnabled ? (evalFunName, 888, 8) : ("NonUserCodeBp", 873, 4);
await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line, col, funcName);
}

[Theory]
[InlineData(false, "RunStepThrough", "StepThrougBp")]
[InlineData(true, "RunStepThrough", "StepThrougBp")]
[InlineData(true, "RunNonUserCode", "NonUserCodeBp")]
[InlineData(false, "RunNonUserCode", "NonUserCodeBp")]
public async Task StepThroughOrNonUserCodeAttributeStepInWithBp(bool justMyCodeEnabled, string evalFunName, string decoratedFunName)
{
var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 1);
var init_location = await EvaluateAndCheck(
$"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);",
"dotnet://debugger-test.dll/debugger-test.cs",
bp_init.Value["locations"][0]["lineNumber"].Value<int>(),
bp_init.Value["locations"][0]["columnNumber"].Value<int>(),
evalFunName
);

if (justMyCodeEnabled)
{
await SetJustMyCode(true);
var line = (evalFunName == "RunNonUserCode") ? 888 : 868;
await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line, 8, evalFunName);
}
else
{
var line1 = bp1_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
var function_name1 = "NotStopOnJustMyCode";
await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line1, 8, function_name1);
var (finalFunName, line3, col) = (decoratedFunName, 873, 4);
if (evalFunName == "RunStepThrough")
{
var bp1_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", decoratedFunName, 1);
var bp2_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", decoratedFunName, 3);
(finalFunName, line3, col) = (evalFunName, 867, 8);
var line1 = bp1_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
var line2 = bp2_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line1, col, decoratedFunName);
await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line2, col, decoratedFunName);
}
await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line3, col, finalFunName);
}
}

[Theory]
[InlineData(false, "RunStepThrough", "StepThrougBp")]
[InlineData(true, "RunStepThrough", "StepThrougBp")]
[InlineData(true, "RunNonUserCode", "NonUserCodeBp")]
[InlineData(false, "RunNonUserCode", "NonUserCodeBp")]
public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeEnabled, string evalFunName, string decoratedFunName)
{
var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 1);
var init_location = await EvaluateAndCheck(
$"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);",
"dotnet://debugger-test.dll/debugger-test.cs",
bp_init.Value["locations"][0]["lineNumber"].Value<int>(),
bp_init.Value["locations"][0]["columnNumber"].Value<int>(),
evalFunName
);

if (justMyCodeEnabled)
await SetJustMyCode(true);
else
{
var bp1_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", decoratedFunName, 1);
var line1 = bp1_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line1, 8, decoratedFunName);
}
var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 2);
var line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
var function_name2 = "RunStepThrough";
await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line2, 8, function_name2);
await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line2, 8, evalFunName);
}

[Theory]
[InlineData(false, "Debugger.resume")]
[InlineData(false, "Debugger.stepInto")]
[InlineData(true, "Debugger.stepInto")]
[InlineData(true, "Debugger.resume")]
public async Task StepThroughAttributeWithUserBp(bool justMyCodeEnabled, string debuggingFunction)
{
var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 2);
var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 3);
[InlineData(false, "Debugger.resume", "RunStepThrough", "StepThrougUserBp")]
[InlineData(false, "Debugger.stepInto", "RunStepThrough", "StepThrougUserBp")]
[InlineData(true, "Debugger.stepInto", "RunStepThrough", null)]
[InlineData(true, "Debugger.resume", "RunStepThrough", null)]
[InlineData(true, "Debugger.stepInto", "RunNonUserCode", null)]
[InlineData(true, "Debugger.resume", "RunNonUserCode", null)]
[InlineData(false, "Debugger.stepInto", "RunNonUserCode", "NonUserCodeUserBp")]
[InlineData(false, "Debugger.resume", "RunNonUserCode", "NonUserCodeUserBp")]
public async Task StepThroughOrNonUserCodAttributeWithUserBp(bool justMyCodeEnabled, string debuggingFunction, string evalFunName, string decoratedFunName)
{
var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 2);
var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 3);

var init_location = await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerAttribute:RunStepThrough'); }, 1);",
$"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);",
"dotnet://debugger-test.dll/debugger-test.cs",
bp_init.Value["locations"][0]["lineNumber"].Value<int>(),
bp_init.Value["locations"][0]["columnNumber"].Value<int>(),
"RunStepThrough"
evalFunName
);

int line1, line2;
int line1, line2;
var (col1, col2) = (8, 4);
string function_name1, function_name2;

if (justMyCodeEnabled)
{
await SetJustMyCode(true);
line1 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>() - 1;
function_name1 = "RunStepThrough";
line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
function_name2 = "RunStepThrough";
function_name1 = function_name2 = evalFunName;
}
else
{
line1 = 862;
function_name1 = "NotStopOnJustMyCodeUserBp";
line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
function_name2 = "RunStepThrough";
if (debuggingFunction == "Debugger.stepInto" && evalFunName == "RunNonUserCode")
{
(line1, col1) = (881, 4);
(line2, col2) = (882, 8);
function_name1 = function_name2 = decoratedFunName;
}
else
{
line1 = evalFunName == "RunNonUserCode" ? 882 : 862;
function_name1 = decoratedFunName;
line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value<int>();
function_name2 = evalFunName;
}
}
await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line1, 8, function_name1);
await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line2, 4, function_name2);
await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line1, col1, function_name1);
await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line2, col2, function_name2);
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ public async Task InspectLocalsUsingClassFromLibraryUsingDebugTypeFull()

await EvaluateAndCheck(
"window.setTimeout(function() {" + expression + "; }, 1);",
"dotnet://debugger-test.dll/debugger-test.cs", 880, 8,
"dotnet://debugger-test.dll/debugger-test.cs", 900, 8,
"CallToEvaluateLocal",
wait_for_event_fn: async (pause_location) =>
{
Expand Down
28 changes: 24 additions & 4 deletions src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -850,23 +850,43 @@ public static void RunDebuggerBreak()
}

[System.Diagnostics.DebuggerStepThroughAttribute]
public static void NotStopOnJustMyCode()
public static void StepThrougBp()
{
var a = 0;
currentCount++;
var b = 1;
}

[System.Diagnostics.DebuggerStepThroughAttribute]
public static void NotStopOnJustMyCodeUserBp()
public static void StepThrougUserBp()
{
System.Diagnostics.Debugger.Break();
}

public static void RunStepThrough()
{
NotStopOnJustMyCode();
NotStopOnJustMyCodeUserBp();
StepThrougBp();
StepThrougUserBp();
}

[System.Diagnostics.DebuggerNonUserCode]
public static void NonUserCodeBp()
{
var a = 0;
currentCount++;
var b = 1;
}

[System.Diagnostics.DebuggerNonUserCode]
public static void NonUserCodeUserBp()
{
System.Diagnostics.Debugger.Break();
}

public static void RunNonUserCode()
{
NonUserCodeBp();
NonUserCodeUserBp();
}
}

Expand Down