[vm/debugger] Fixes async CollectAwaiterReturn()

The async, off-stack part of DebuggerStackTrace::CollectAwaiterReturn's
stack unwinding incorrectly looked on the stack instead of following
the Closure's awaiter chain.
This caused the async stack traces to be truncated and missing any
async frames, in turn causing ShouldPauseOnException to incorrectly
conclude no handler frame existing.
This would affect any case where the exception handling was located
around any except for the inner most awaiter.

TEST=Added regression test to pause_on_unhandled_exceptions_catcherror_test

Closes: https://github.com/dart-lang/sdk/issues/37953
Bug: https://github.com/dart-lang/sdk/issues/37953
Change-Id: I34a2b5ce3c7532032b2ef3dfbc2af7294d13a4c7
Cq-Do-Not-Cancel-Tryjobs: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/187405
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
Clement Skau
2021-02-26 06:46:36 +00:00
committed by commit-bot@chromium.org
parent 8833cbed7d
commit f2dfe3a8be
6 changed files with 42 additions and 19 deletions
+12 -1
View File
@@ -97,7 +97,7 @@ final tests = <IsolateTest>[
expect(result.frames, hasLength(10));
expect(result.asyncCausalFrames, hasLength(26));
expect(result.awaiterFrames, hasLength(2));
expect(result.awaiterFrames, hasLength(13));
expectFrames(result.frames!, [
[equals('Regular'), endsWith(' func10')],
@@ -140,6 +140,17 @@ final tests = <IsolateTest>[
expectFrames(result.awaiterFrames, [
[equals('AsyncActivation'), endsWith(' func10')],
[equals('AsyncActivation'), endsWith(' func9')],
[equals('AsyncActivation'), endsWith(' func8')],
[equals('AsyncActivation'), endsWith(' func7')],
[equals('AsyncActivation'), endsWith(' func6')],
[equals('AsyncActivation'), endsWith(' func5')],
[equals('AsyncActivation'), endsWith(' func4')],
[equals('AsyncActivation'), endsWith(' func3')],
[equals('AsyncActivation'), endsWith(' func2')],
[equals('AsyncActivation'), endsWith(' func1')],
[equals('AsyncActivation'), endsWith(' testMain')],
[equals('AsyncActivation'), endsWith(' _ServiceTesteeRunner.run')],
[equals('AsyncActivation'), endsWith(' runIsolateTests')],
]);
},
];
@@ -55,7 +55,7 @@ var tests = <IsolateTest>[
var awaiterFrames = stack['awaiterFrames'];
expect(frames.length, greaterThanOrEqualTo(20));
expect(asyncFrames.length, greaterThan(frames.length));
expect(awaiterFrames.length, 13);
expect(awaiterFrames.length, greaterThan(frames.length));
expect(stack['truncated'], false);
verifyStack(frames, [
'bar.async_op', 'foo.async_op', 'bar.async_op', 'foo.async_op',
@@ -75,7 +75,7 @@ var tests = <IsolateTest>[
expect(frames.length, fullStackLength);
expect(asyncFrames.length, fullStackLength + 1);
expect(asyncFrames.length, fullStackLength + 1);
expect(awaiterFrames.length, fullStackLength + 1);
expect(stack['truncated'], true);
verifyStack(frames, [
'bar.async_op', 'foo.async_op', 'bar.async_op', 'foo.async_op',
@@ -12,12 +12,23 @@ Future<void> throwAsync() async {
throw 'Throw from throwAsync!';
}
Future<void> nestedThrowAsync() async {
await Future.delayed(const Duration(milliseconds: 100));
await throwAsync();
}
testeeMain() async {
await throwAsync().then((v) {
print('Hello from then()!');
}).catchError((e, st) {
print('Caught in catchError: $e!');
});
// Make sure we can chain through off-stack awaiters as well.
try {
await nestedThrowAsync();
} catch (e) {
print('Caught in catch: $e!');
}
}
var tests = <IsolateTest>[
@@ -55,7 +55,7 @@ var tests = <IsolateTest>[
var awaiterFrames = stack['awaiterFrames'];
expect(frames.length, greaterThanOrEqualTo(20));
expect(asyncFrames.length, greaterThan(frames.length));
expect(awaiterFrames.length, 13);
expect(awaiterFrames.length, greaterThan(frames.length));
expect(stack['truncated'], false);
verifyStack(frames, [
'bar.async_op', 'foo.async_op', 'bar.async_op', 'foo.async_op',
@@ -75,7 +75,7 @@ var tests = <IsolateTest>[
expect(frames.length, fullStackLength);
expect(asyncFrames.length, fullStackLength + 1);
expect(asyncFrames.length, fullStackLength + 1);
expect(awaiterFrames.length, fullStackLength + 1);
expect(stack['truncated'], true);
verifyStack(frames, [
'bar.async_op', 'foo.async_op', 'bar.async_op', 'foo.async_op',
@@ -12,12 +12,23 @@ Future<void> throwAsync() async {
throw 'Throw from throwAsync!';
}
Future<void> nestedThrowAsync() async {
await Future.delayed(const Duration(milliseconds: 100));
await throwAsync();
}
testeeMain() async {
await throwAsync().then((v) {
print('Hello from then()!');
}).catchError((e, st) {
print('Caught in catchError: $e!');
});
// Make sure we can chain through off-stack awaiters as well.
try {
await nestedThrowAsync();
} catch (e) {
print('Caught in catch: $e!');
}
}
var tests = <IsolateTest>[
+4 -14
View File
@@ -1935,7 +1935,6 @@ DebuggerStackTrace* DebuggerStackTrace::CollectAwaiterReturn() {
Function& function = Function::Handle(zone);
Code& inlined_code = Code::Handle(zone);
Closure& async_activation = Closure::Handle(zone);
Object& next_async_activation = Object::Handle(zone);
Array& deopt_frame = Array::Handle(zone);
bool stack_has_async_function = false;
Closure& closure = Closure::Handle();
@@ -2045,13 +2044,10 @@ DebuggerStackTrace* DebuggerStackTrace::CollectAwaiterReturn() {
while (!async_activation.IsNull() &&
async_activation.context() != Object::null()) {
ActivationFrame* activation = new (zone) ActivationFrame(async_activation);
if (!(activation->function().IsAsyncClosure() ||
activation->function().IsAsyncGenClosure())) {
break;
if (activation->function().IsAsyncClosure() ||
activation->function().IsAsyncGenClosure()) {
activation->ExtractTokenPositionFromAsyncClosure();
}
activation->ExtractTokenPositionFromAsyncClosure();
stack_trace->AddActivation(activation);
if (FLAG_trace_debugger_stacktrace) {
OS::PrintErr(
@@ -2059,13 +2055,7 @@ DebuggerStackTrace* DebuggerStackTrace::CollectAwaiterReturn() {
"closures:\n\t%s\n",
activation->function().ToFullyQualifiedCString());
}
next_async_activation = activation->GetAsyncAwaiter(&caller_closure_finder);
if (next_async_activation.IsNull()) {
break;
}
async_activation = Closure::RawCast(next_async_activation.ptr());
async_activation = caller_closure_finder.FindCaller(async_activation);
}
return stack_trace;