未验证 提交 94015cd9 编写于 作者: T Thays Grazia 提交者: GitHub

[wasm][debugger] Breakpoint set in a invalid line, set it to the next available line (#73189)

* Adding more tests, including async cases. Some odd ones are broken at this time
Co-authored-by: NAnkit Jain <radical@gmail.com>
上级 e94f7ceb
......@@ -1596,7 +1596,7 @@ private static bool Match(SequencePoint sp, int line, int column)
return true;
}
public IEnumerable<SourceLocation> FindBreakpointLocations(BreakpointRequest request)
public IEnumerable<SourceLocation> FindBreakpointLocations(BreakpointRequest request, bool ifNoneFoundThenFindNext = false)
{
request.TryResolve(this);
......@@ -1606,16 +1606,64 @@ public IEnumerable<SourceLocation> FindBreakpointLocations(BreakpointRequest req
if (sourceFile == null)
yield break;
foreach (MethodInfo method in sourceFile.Methods)
List<MethodInfo> methodList = FindMethodsContainingLine(sourceFile, request.Line);
if (methodList.Count == 0)
yield break;
List<SourceLocation> locations = new List<SourceLocation>();
foreach (var method in methodList)
{
foreach (SequencePoint sequencePoint in method.DebugInformation.GetSequencePoints())
{
if (!sequencePoint.IsHidden &&
Match(sequencePoint, request.Line, request.Column) &&
sequencePoint.StartLine - 1 == request.Line &&
(request.Column == 0 || sequencePoint.StartColumn - 1 == request.Column))
{
// Found an exact match
locations.Add(new SourceLocation(method, sequencePoint));
}
}
}
if (locations.Count == 0 && ifNoneFoundThenFindNext)
{
if (!method.DebugInformation.SequencePointsBlob.IsNil)
(MethodInfo method, SequencePoint seqPoint)? closest = null;
foreach (var method in methodList)
{
foreach (SequencePoint sequencePoint in method.DebugInformation.GetSequencePoints())
{
if (!sequencePoint.IsHidden && Match(sequencePoint, request.Line, request.Column))
yield return new SourceLocation(method, sequencePoint);
if (!sequencePoint.IsHidden &&
sequencePoint.StartLine > request.Line &&
(closest is null || closest.Value.seqPoint.StartLine > sequencePoint.StartLine))
{
// sequence points in a method are ordered,
// and we found the one right after request.Line
closest = (method, sequencePoint);
// .. and now we can look for it in other methods
break;
}
}
}
if (closest is not null)
locations.Add(new SourceLocation(closest.Value.method, closest.Value.seqPoint));
}
foreach (SourceLocation loc in locations)
yield return loc;
static List<MethodInfo> FindMethodsContainingLine(SourceFile sourceFile, int line)
{
List<MethodInfo> ret = new();
foreach (MethodInfo method in sourceFile.Methods)
{
if (method.DebugInformation.SequencePointsBlob.IsNil)
continue;
if (!(method.StartLocation.Line <= line && line <= method.EndLocation.Line))
continue;
ret.Add(method);
}
return ret;
}
}
......
......@@ -1553,18 +1553,22 @@ protected async Task<DebugStore> RuntimeReady(SessionId sessionId, CancellationT
return store;
}
private static IEnumerable<IGrouping<SourceId, SourceLocation>> GetBPReqLocations(DebugStore store, BreakpointRequest req)
private static IEnumerable<IGrouping<SourceId, SourceLocation>> GetBPReqLocations(DebugStore store, BreakpointRequest req, bool ifNoneFoundThenFindNext = false)
{
var comparer = new SourceLocation.LocationComparer();
// if column is specified the frontend wants the exact matches
// and will clear the bp if it isn't close enoug
IEnumerable<IGrouping<SourceId, SourceLocation>> locations = store.FindBreakpointLocations(req)
.Distinct(comparer)
.Where(l => l.Line == req.Line && (req.Column == 0 || l.Column == req.Column))
var bpLocations = store.FindBreakpointLocations(req, ifNoneFoundThenFindNext);
IEnumerable<IGrouping<SourceId, SourceLocation>> locations = bpLocations.Distinct(comparer)
.OrderBy(l => l.Column)
.GroupBy(l => l.Id);
if (ifNoneFoundThenFindNext && !locations.Any())
{
locations = bpLocations.GroupBy(l => l.Id);
}
return locations;
}
private async Task ResetBreakpoint(SessionId msg_id, DebugStore store, MethodInfo method, CancellationToken token)
{
ExecutionContext context = GetContext(msg_id);
......@@ -1622,12 +1626,11 @@ protected async Task SetBreakpoint(SessionId sessionId, DebugStore store, Breakp
return;
}
var locations = GetBPReqLocations(store, req);
var locations = GetBPReqLocations(store, req, true);
logger.LogDebug("BP request for '{Req}' runtime ready {Context.RuntimeReady}", req, context.IsRuntimeReady);
var breakpoints = new List<Breakpoint>();
foreach (IGrouping<SourceId, SourceLocation> sourceId in locations)
{
SourceLocation loc = sourceId.First();
......
......@@ -839,7 +839,7 @@ public async Task StepOverHiddenLinesShouldResumeAtNextAvailableLineInTheMethod(
}
[ConditionalFact(nameof(RunningOnChrome))]
async Task StepOverHiddenLinesInMethodWithNoNextAvailableLineShouldResumeAtCallSite()
public async Task StepOverHiddenLinesInMethodWithNoNextAvailableLineShouldResumeAtCallSite()
{
string source_loc = "dotnet://debugger-test.dll/debugger-test.cs";
await SetBreakpoint(source_loc, 552, 8);
......@@ -852,15 +852,45 @@ async Task StepOverHiddenLinesInMethodWithNoNextAvailableLineShouldResumeAtCallS
await StepAndCheck(StepKind.Over, source_loc, 544, 4, "HiddenSequencePointTest.StepOverHiddenSP");
}
// [ConditionalFact(nameof(RunningOnChrome))]
// Issue: https://github.com/dotnet/runtime/issues/42704
async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLine()
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(539, 8, 542, 8, "StepOverHiddenSP", "HiddenSequencePointTest.StepOverHiddenSP")]
[InlineData(1272, 8, 1266, 8, "StepOverHiddenSP3", "HiddenSequencePointTest.StepOverHiddenSP3")]
public async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLine(int line_bp, int column_bp, int line_pause, int column_pause, string method_to_call, string method_name)
{
await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 539, 8);
Console.WriteLine(await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", line_bp, column_bp));
await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);",
"dotnet://debugger-test.dll/debugger-test.cs", 546, 4,
"StepOverHiddenSP2");
"window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:" + method_to_call + "'); }, 1);",
"dotnet://debugger-test.dll/debugger-test.cs", line_pause, column_pause,
method_name);
}
// [ConditionalTheory(nameof(RunningOnChrome))]
//[ActiveIssue("https://github.com/dotnet/runtime/issues/73867")]
[InlineData(184, 20, 161, 8, "HiddenLinesContainingStartOfAnAsyncBlock")]
[InlineData(206, 20, 201, 8, "HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod")]
[InlineData(224, 20, 220, 8, "HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod2")]
public async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLineAsync_PauseEarlier(int line_bp, int column_bp, int line_pause, int column_pause, string method_name)
{
await SetBreakpoint("dotnet://debugger-test.dll/debugger-async-test.cs", line_bp, column_bp);
await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerTests.AsyncTests.ContinueWithTests:RunAsyncWithLineHidden'); }, 1);",
"dotnet://debugger-test.dll/debugger-async-test.cs", line_pause, column_pause,
$"DebuggerTests.AsyncTests.ContinueWithTests.{method_name}");
}
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(112, 16, 114, 16, "HiddenLinesInAnAsyncBlock")]
[InlineData(130, 16, 133, 16, "HiddenLinesJustBeforeANestedAsyncBlock")]
[InlineData(153, 20, 155, 16, "HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod.AnonymousMethod__1")]
[InlineData(154, 20, 155, 16, "HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod.AnonymousMethod__1")]
[InlineData(170, 20, 172, 16, "HiddenLinesAtTheEndOfANestedAsyncBlockWithBreakableLineAtEndOfTheMethod.AnonymousMethod__1")]
public async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLineAsync(int line_bp, int column_bp, int line_pause, int column_pause, string method_name)
{
await SetBreakpoint("dotnet://debugger-test.dll/debugger-async-test.cs", line_bp, column_bp);
await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerTests.AsyncTests.ContinueWithTests:RunAsyncWithLineHidden'); }, 1);",
"dotnet://debugger-test.dll/debugger-async-test.cs", line_pause, column_pause,
$"DebuggerTests.AsyncTests.ContinueWithTests.{method_name}");
}
[ConditionalFact(nameof(RunningOnChrome))]
......
......@@ -94,6 +94,140 @@ public async Task NestedContinueWithInstanceAsync(string str)
Console.WriteLine ($"done with this method");
}
public static async Task RunAsyncWithLineHidden()
{
await HiddenLinesInAnAsyncBlock("foobar");
await HiddenLinesJustBeforeANestedAsyncBlock("foobar");
await HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod("foobar");
await HiddenLinesAtTheEndOfANestedAsyncBlockWithBreakableLineAtEndOfTheMethod("foobar");
await HiddenLinesContainingStartOfAnAsyncBlock("foobar");
await HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod("foobar");
await HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod2("foobar");
System.Diagnostics.Debugger.Break();
}
public static async Task HiddenLinesInAnAsyncBlock(string str)
{
await Task.Delay(500).ContinueWith(async t =>
{
#line hidden
var code = t.Status;
#line default
var ncs_dt0 = new DateTime(3412, 4, 6, 8, 0, 2);
Console.WriteLine ($"First continueWith: {code}, {ncs_dt0}"); // t, code, str, dt0
await Task.Delay(300).ContinueWith(t2 =>
{
var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8);
Console.WriteLine ($"t2: {t2.Status}, str: {str}, {ncs_dt1}, {ncs_dt0}");//t2, dt1, str, dt0
});
});
Console.WriteLine ($"done with this method");
}
static async Task HiddenLinesJustBeforeANestedAsyncBlock(string str)
{
await Task.Delay(500).ContinueWith(async t =>
{
Console.WriteLine($"First continueWith");
#line hidden
var code = t.Status; // Next line will be in the next async block? hidden line just before async block
Console.WriteLine("another line of code");
#line default
await Task.Delay(300).ContinueWith(t2 =>
{
var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8);
Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0
});
});
Console.WriteLine($"done with this method");
}
static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithNoLinesAtEndOfTheMethod(string str)
{
await Task.Delay(500).ContinueWith(async t =>
{
var code = t.Status;
Console.WriteLine($"First continueWith");
await Task.Delay(300).ContinueWith(t2 =>
{
var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8);
Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0
#line hidden
Console.WriteLine("something else"); // Next line will be in the next async block? hidden line at end of async block
#line default
});
});
}
static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithBreakableLineAtEndOfTheMethod(string str)
{
await Task.Delay(500).ContinueWith(async t =>
{
var code = t.Status;
Console.WriteLine($"First continueWith");
await Task.Delay(300).ContinueWith(t2 =>
{
var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8);
Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0
#line hidden
Console.WriteLine("something else"); // Next line will be in the next async block? hidden line at end of async block
#line default
});
});
Console.WriteLine ($"Last line..");
}
static async Task HiddenLinesContainingStartOfAnAsyncBlock(string str)
{
await Task.Delay(500).ContinueWith(async t =>
{
var code = t.Status;
Console.WriteLine($"First continueWith");
#line hidden
await Task.Delay(300).ContinueWith(t2 =>
#line default
{
var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8);
Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0
Console.WriteLine("something else"); // Next line will be in the next async block? hidden line at end of async block
});
});
Console.WriteLine($"done with this method");
}
static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod(string str)
{
await Task.Delay(500).ContinueWith(async t =>
{
var code = t.Status;
Console.WriteLine($"First continueWith");
await Task.Delay(300).ContinueWith(t2 =>
{
var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8);
Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0
#line hidden
Console.WriteLine("somethind else"); // Next line will be in the next async block? hidden line at end of async block
});
});
#line default
Console.WriteLine($"done with this method");
}
static async Task HiddenLinesAtTheEndOfANestedAsyncBlockWithWithLineDefaultOutsideTheMethod2(string str)
{
await Task.Delay(500).ContinueWith(async t =>
{
var code = t.Status;
Console.WriteLine($"First continueWith");
await Task.Delay(300).ContinueWith(t2 =>
{
var ncs_dt1 = new DateTime(4513, 4, 5, 6, 7, 8);
Console.WriteLine($"t2: {t2.Status}, str: {str}, {ncs_dt1}");//t2, dt1, str, dt0
#line hidden
Console.WriteLine("somethind else"); // Next line will be in the next async block? hidden line at end of async block
});
#line default
});
Console.WriteLine($"done with this method");
}
}
}
......@@ -532,7 +532,7 @@ public static void LoadLazyAssembly(string asm_base64, string pdb_base64)
}
}
public class HiddenSequencePointTest {
public partial class HiddenSequencePointTest {
public static void StepOverHiddenSP()
{
Console.WriteLine("first line");
......@@ -1259,3 +1259,19 @@ public static void Run()
System.Diagnostics.Debugger.Break();
}
}
public partial class HiddenSequencePointTest {
public static void StepOverHiddenSP3()
{
MethodWithHiddenLinesAtTheEnd3();
System.Diagnostics.Debugger.Break();
}
public static void MethodWithHiddenLinesAtTheEnd3()
{
Console.WriteLine ($"MethodWithHiddenLinesAtTheEnd");
#line hidden
Console.WriteLine ($"debugger shouldn't be able to step here");
}
#line default
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册