From 17b5ddeeb863c080b3d525bf78695d2df1a7b8cf Mon Sep 17 00:00:00 2001 From: Clement Skau Date: Thu, 24 Oct 2019 13:38:34 +0000 Subject: [PATCH] [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 Reviewed-by: Martin Kustermann --- .../async_throws_stack_no_causal_test.dart | 11 + .../async_throws_stack_test.dart | 11 + .../tests/vm/dart/causal_stacks/utils.dart | 265 ++++++++++++++++++ runtime/tests/vm/vm.status | 3 + 4 files changed, 290 insertions(+) create mode 100644 runtime/tests/vm/dart/causal_stacks/async_throws_stack_no_causal_test.dart create mode 100644 runtime/tests/vm/dart/causal_stacks/async_throws_stack_test.dart create mode 100644 runtime/tests/vm/dart/causal_stacks/utils.dart diff --git a/runtime/tests/vm/dart/causal_stacks/async_throws_stack_no_causal_test.dart b/runtime/tests/vm/dart/causal_stacks/async_throws_stack_no_causal_test.dart new file mode 100644 index 00000000000..62e72dcdf83 --- /dev/null +++ b/runtime/tests/vm/dart/causal_stacks/async_throws_stack_no_causal_test.dart @@ -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 main(List args) async => doTestsNoCausal(); diff --git a/runtime/tests/vm/dart/causal_stacks/async_throws_stack_test.dart b/runtime/tests/vm/dart/causal_stacks/async_throws_stack_test.dart new file mode 100644 index 00000000000..eb1f207acf7 --- /dev/null +++ b/runtime/tests/vm/dart/causal_stacks/async_throws_stack_test.dart @@ -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 main(List args) async => doTestsCausal(); diff --git a/runtime/tests/vm/dart/causal_stacks/utils.dart b/runtime/tests/vm/dart/causal_stacks/utils.dart new file mode 100644 index 00000000000..6038e33de8f --- /dev/null +++ b/runtime/tests/vm/dart/causal_stacks/utils.dart @@ -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 frames = stack_trace.toString().split('\n'); + for (int i in expected.keys) { + expect(frames[i], startsWith(expected[i])); + } +} + +Future doTest(Future f(), Map 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 throwSync() { + throw ''; +} + +Future throwAsync() async { + await 0; + throw ''; +} + +// ---- +// Scenario: All async functions yielded at least once before throw: +// ---- +Future allYield() async { + await 0; + await allYield2(); +} + +Future allYield2() async { + await 0; + await allYield3(); +} + +Future allYield3() async { + await 0; + throwSync(); +} + +// For: --causal-async-stacks +Map allYieldMapCausal = { + 0: '#0 throwSync ', + 1: '#1 allYield3 ', + 2: '', + 3: '#2 allYield2 ', + 4: '', + 5: '#3 allYield ', + 4: '', + // Callers, like doTest and main .. +}; + +// For: --no-causal-async-stacks +Map 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 noYields() async { + await noYields2(); +} + +Future noYields2() async { + await noYields3(); +} + +Future noYields3() async { + throwSync(); +} + +// For: --causal-async-stacks +Map noYieldsMapCausal = { + 0: '#0 throwSync ', + 1: '#1 noYields3 ', + 2: '', + 3: '#2 noYields2 ', + 4: '', + 5: '#3 noYields ', + 4: '', + // Callers, like doTest and main .. +}; + +// For: --no-causal-async-stacks +Map 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 mixedYields() async { + await mixedYields2(); +} + +Future mixedYields2() async { + await 0; + await mixedYields3(); +} + +Future mixedYields3() async { + return throwAsync(); +} + +// For: --causal-async-stacks +Map mixedYieldsMapCausal = { + 0: '#0 throwAsync ', + 1: '', + 2: '#1 mixedYields3 ', + 3: '', + 4: '#2 mixedYields2 ', + 5: '', + 6: '#3 mixedYields ', + 7: '', + // Callers, like doTest and main .. +}; + +// For: --no-causal-async-stacks +Map mixedYieldsMapNoCausal = { + 0: '#0 throwAsync ', + 1: '#1 _RootZone.runUnary ', + // The rest are more Dart internal async mechanisms.. +}; + +// ---- +// Scenario: Non-async frame: +// ---- +Future syncSuffix() async { + await syncSuffix2(); +} + +Future syncSuffix2() async { + await 0; + await syncSuffix3(); +} + +Future syncSuffix3() { + return throwAsync(); +} + +// For: --causal-async-stacks +Map syncSuffixMapCausal = { + 0: '#0 throwAsync ', + 1: '', + 2: '#1 syncSuffix3 ', + 3: '#2 syncSuffix2 ', + 4: '', + 5: '#3 syncSuffix ', + 6: '', + // Callers, like doTest and main .. +}; + +// For: --no-causal-async-stacks +Map 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 nonAsyncNoStackMapCausal = { + 0: '#0 throwAsync ', + 1: '', + 2: '#1 nonAsyncNoStack2. ', + 3: '#2 _RootZone.runUnary ', + // The rest are more Dart internal async mechanisms.. +}; + +// For: --no-causal-async-stacks +Map nonAsyncNoStackMapNoCausal = { + 0: '#0 throwAsync ', + 1: '#1 _RootZone.runUnary ', + // The rest are more Dart internal async mechanisms.. +}; + +// ---- +// Test "Suites": +// ---- + +Future doTestsCausal() async { + await doTest(allYield, allYieldMapCausal); + await doTest(noYields, noYieldsMapCausal); + await doTest(mixedYields, mixedYieldsMapCausal); + await doTest(syncSuffix, syncSuffixMapCausal); + await doTest(nonAsyncNoStack, nonAsyncNoStackMapCausal); +} + +Future doTestsNoCausal() async { + await doTest(allYield, allYieldMapNoCausal); + await doTest(noYields, noYieldsMapNoCausal); + await doTest(mixedYields, mixedYieldsMapNoCausal); + await doTest(syncSuffix, syncSuffixMapNoCausal); + await doTest(nonAsyncNoStack, nonAsyncNoStackMapNoCausal); +} diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index c91d02ea116..d1c929efa99 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -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