[SDK] Adds tests for async stacktraces.

Bug: https://github.com/dart-lang/sdk/issues/37668
Change-Id: Id29704d086dbae066c8b34e347b75cd374b1ce2b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121986
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Clement Skau 2019-10-24 13:38:34 +00:00 committed by commit-bot@chromium.org
parent 0f2787c9f3
commit 17b5ddeeb8
4 changed files with 290 additions and 0 deletions

View file

@ -0,0 +1,11 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// VMOptions=--no-causal-async-stacks
import 'dart:async';
import 'utils.dart';
Future<void> main(List<String> args) async => doTestsNoCausal();

View file

@ -0,0 +1,11 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// VMOptions=--causal-async-stacks
import 'dart:async';
import 'utils.dart';
Future<void> main(List<String> args) async => doTestsCausal();

View file

@ -0,0 +1,265 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:path/path.dart' as path;
import 'package:expect/expect.dart';
import 'package:expect/matchers_lite.dart';
Matcher startsWith(String expected) {
return (Object actual) {
if (actual is String) {
Expect.equals(
expected, actual.substring(0, min(expected.length, actual.length)));
return;
}
Expect.fail('Expected String.');
};
}
void assertStack(Map expected, StackTrace stack_trace) {
final List<String> frames = stack_trace.toString().split('\n');
for (int i in expected.keys) {
expect(frames[i], startsWith(expected[i]));
}
}
Future<void> doTest(Future f(), Map<int, String> expected_stack) async {
// Caller catches exception.
try {
await f();
Expect.fail('No exception thrown!');
} on String catch (e, s) {
assertStack(expected_stack, s);
}
// Caller catches but a then is set.
try {
await f().then((e) {
// Ignore.
});
Expect.fail('No exception thrown!');
} on String catch (e, s) {
assertStack(expected_stack, s);
}
// Caller doesn't catch, but we have a catchError set.
StackTrace stack_trace;
await f().catchError((e, s) {
stack_trace = s;
});
assertStack(expected_stack, stack_trace);
}
// Test functions:
Future<void> throwSync() {
throw '';
}
Future<void> throwAsync() async {
await 0;
throw '';
}
// ----
// Scenario: All async functions yielded at least once before throw:
// ----
Future<void> allYield() async {
await 0;
await allYield2();
}
Future<void> allYield2() async {
await 0;
await allYield3();
}
Future<void> allYield3() async {
await 0;
throwSync();
}
// For: --causal-async-stacks
Map<int, String> allYieldMapCausal = {
0: '#0 throwSync ',
1: '#1 allYield3 ',
2: '<asynchronous suspension>',
3: '#2 allYield2 ',
4: '<asynchronous suspension>',
5: '#3 allYield ',
4: '<asynchronous suspension>',
// Callers, like doTest and main ..
};
// For: --no-causal-async-stacks
Map<int, String> allYieldMapNoCausal = {
0: '#0 throwSync ',
1: '#1 allYield3 ',
2: '#2 _RootZone.runUnary ',
// The rest are more Dart internal async mechanisms..
};
// ----
// Scenario: None of the async functions yieled before the throw:
// ----
Future<void> noYields() async {
await noYields2();
}
Future<void> noYields2() async {
await noYields3();
}
Future<void> noYields3() async {
throwSync();
}
// For: --causal-async-stacks
Map<int, String> noYieldsMapCausal = {
0: '#0 throwSync ',
1: '#1 noYields3 ',
2: '<asynchronous suspension>',
3: '#2 noYields2 ',
4: '<asynchronous suspension>',
5: '#3 noYields ',
4: '<asynchronous suspension>',
// Callers, like doTest and main ..
};
// For: --no-causal-async-stacks
Map<int, String> noYieldsMapNoCausal = {
0: '#0 throwSync ',
1: '#1 noYields3 ',
// Skip: _AsyncAwaitCompleter.start
3: '#3 noYields3 ',
4: '#4 noYields2 ',
// Skip: _AsyncAwaitCompleter.start
6: '#6 noYields2 ',
7: '#7 noYields ',
// Skip: _AsyncAwaitCompleter.start
9: '#9 noYields ',
// Calling functions like doTest and main ..
};
// ----
// Scenario: Mixed yielding and non-yielding frames:
// ----
Future<void> mixedYields() async {
await mixedYields2();
}
Future<void> mixedYields2() async {
await 0;
await mixedYields3();
}
Future<void> mixedYields3() async {
return throwAsync();
}
// For: --causal-async-stacks
Map<int, String> mixedYieldsMapCausal = {
0: '#0 throwAsync ',
1: '<asynchronous suspension>',
2: '#1 mixedYields3 ',
3: '<asynchronous suspension>',
4: '#2 mixedYields2 ',
5: '<asynchronous suspension>',
6: '#3 mixedYields ',
7: '<asynchronous suspension>',
// Callers, like doTest and main ..
};
// For: --no-causal-async-stacks
Map<int, String> mixedYieldsMapNoCausal = {
0: '#0 throwAsync ',
1: '#1 _RootZone.runUnary ',
// The rest are more Dart internal async mechanisms..
};
// ----
// Scenario: Non-async frame:
// ----
Future<void> syncSuffix() async {
await syncSuffix2();
}
Future<void> syncSuffix2() async {
await 0;
await syncSuffix3();
}
Future<void> syncSuffix3() {
return throwAsync();
}
// For: --causal-async-stacks
Map<int, String> syncSuffixMapCausal = {
0: '#0 throwAsync ',
1: '<asynchronous suspension>',
2: '#1 syncSuffix3 ',
3: '#2 syncSuffix2 ',
4: '<asynchronous suspension>',
5: '#3 syncSuffix ',
6: '<asynchronous suspension>',
// Callers, like doTest and main ..
};
// For: --no-causal-async-stacks
Map<int, String> syncSuffixMapNoCausal = {
0: '#0 throwAsync ',
1: '#1 _RootZone.runUnary ',
// The rest are more Dart internal async mechanisms..
};
// ----
// Scenario: Caller is non-async, has no upwards stack:
// ----
Future nonAsyncNoStack() async => await nonAsyncNoStack1();
Future nonAsyncNoStack1() async => await nonAsyncNoStack2();
Future nonAsyncNoStack2() async => Future.value(0).then((_) => throwAsync());
// For: --causal-async-stacks
Map<int, String> nonAsyncNoStackMapCausal = {
0: '#0 throwAsync ',
1: '<asynchronous suspension>',
2: '#1 nonAsyncNoStack2.<anonymous closure> ',
3: '#2 _RootZone.runUnary ',
// The rest are more Dart internal async mechanisms..
};
// For: --no-causal-async-stacks
Map<int, String> nonAsyncNoStackMapNoCausal = {
0: '#0 throwAsync ',
1: '#1 _RootZone.runUnary ',
// The rest are more Dart internal async mechanisms..
};
// ----
// Test "Suites":
// ----
Future<void> doTestsCausal() async {
await doTest(allYield, allYieldMapCausal);
await doTest(noYields, noYieldsMapCausal);
await doTest(mixedYields, mixedYieldsMapCausal);
await doTest(syncSuffix, syncSuffixMapCausal);
await doTest(nonAsyncNoStack, nonAsyncNoStackMapCausal);
}
Future<void> doTestsNoCausal() async {
await doTest(allYield, allYieldMapNoCausal);
await doTest(noYields, noYieldsMapNoCausal);
await doTest(mixedYields, mixedYieldsMapNoCausal);
await doTest(syncSuffix, syncSuffixMapNoCausal);
await doTest(nonAsyncNoStack, nonAsyncNoStackMapNoCausal);
}

View file

@ -25,6 +25,9 @@ dart/transferable_throws_oom_test: SkipByDesign # This test tries to allocate to
[ $builder_tag == crossword ]
dart/emit_aot_size_info_flag_test: SkipByDesign # The test itself cannot determine the location of gen_snapshot (only tools/test.py knows where it is).
[ $builder_tag == obfuscated ]
dart/causal_stacks/*: SkipByDesign # Asserts exact stacktrace output.
[ $builder_tag == optimization_counter_threshold ]
cc/*: Skip # Many tests want see unoptimized code running
dart/appjit*: SkipByDesign # Test needs to a particular opt-counter value