[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:
committed by
commit-bot@chromium.org
parent
8833cbed7d
commit
f2dfe3a8be
@@ -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',
|
||||
|
||||
+11
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user