Revert "[vm] Rename tests causal_stacks -> awaiter_stacks and improve harness."

This reverts commit 0c1b2722ed.

Reason for revert: Failures on AOT (stack expectations unmet) and Windows (parse errors due to Windows paths) trybots.

Original change's description:
> [vm] Rename tests causal_stacks -> awaiter_stacks and improve harness.
>
> The main difference from the previous harness is that it
> allows auto updating the expectations instead of
> maintaining them manually.
>
> TEST=ci
>
> Change-Id: I80e303d3ecbcc834ac94fa089cabe4f8834cc661
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/311400
> Reviewed-by: Alexander Markov <alexmarkov@google.com>
> Commit-Queue: Slava Egorov <vegorov@google.com>

Change-Id: I7311bf08403f4167f88f6204fde1a6fdee353f7d
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/311600
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Tess Strickland 2023-06-27 09:02:28 +00:00 committed by Commit Queue
parent 1df39e243a
commit 516f238aa6
31 changed files with 2409 additions and 2615 deletions

View file

@ -2,7 +2,7 @@
// 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.
// This test is derived from `runtime/tests/vm/dart/awaiter_stacks/utils.dart`.
// This test is derived from `runtime/tests/vm/dart/causal_stacks/utils.dart`.
import 'dart:async';

View file

@ -5,7 +5,7 @@
"../vm/dart/byte_array_test.dart",
"../vm/dart/callee_side_type_checks_test.dart",
"../vm/dart/catch_entry_state_test.dart",
"../vm/dart/awaiter_stacks/sync_async_start_pkg_test_test.dart",
"../vm/dart/causal_stacks/sync_async_start_pkg_test_test.dart",
"../vm/dart/deferred_loading_and_weak_serialization_references_test.dart",
"../vm/dart/deferred_loading_call_modes_test.dart",
"../vm/dart/deopt/allocate_array_test.dart",
@ -3386,7 +3386,7 @@
"../vm/dart_2/byte_array_test.dart",
"../vm/dart_2/callee_side_type_checks_test.dart",
"../vm/dart_2/catch_entry_state_test.dart",
"../vm/dart_2/awaiter_stacks/sync_async_start_pkg_test_test.dart",
"../vm/dart_2/causal_stacks/sync_async_start_pkg_test_test.dart",
"../vm/dart_2/deferred_loading_and_weak_serialization_references_test.dart",
"../vm/dart_2/deferred_loading_call_modes_test.dart",
"../vm/dart_2/deopt/allocate_array_test.dart",

View file

@ -1,712 +0,0 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
import 'dart:async';
import 'package:expect/expect.dart';
import 'harness.dart' as harness;
// Test functions:
Future<void> throwSync() {
throw 'throw from throwSync';
}
Future<void> throwAsync() async {
await 0;
throw 'throw from throwAsync';
}
// ----
// 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();
}
// ----
// Scenario: None of the async functions yielded before the throw:
// ----
Future<void> noYields() async {
await noYields2();
}
Future<void> noYields2() async {
await noYields3();
}
Future<void> noYields3() async {
throwSync();
}
// ----
// 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();
}
// ----
// Scenario: Non-async frame:
// ----
Future<void> syncSuffix() async {
await syncSuffix2();
}
Future<void> syncSuffix2() async {
await 0;
await syncSuffix3();
}
Future<void> syncSuffix3() {
return throwAsync();
}
// ----
// 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());
// ----
// Scenario: async*:
// ----
Future awaitEveryAsyncStarThrowSync() async {
await for (Future v in asyncStarThrowSync()) {
await v;
}
}
Stream<Future> asyncStarThrowSync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield throwSync();
}
}
Future awaitEveryAsyncStarThrowAsync() async {
await for (Future v in asyncStarThrowAsync()) {
await v;
}
}
Stream<Future> asyncStarThrowAsync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield Future.value(i);
await throwAsync();
}
}
Future listenAsyncStarThrowAsync() async {
final _output = [];
// Listening to an async* doesn't create the usual await-for StreamIterator.
StreamSubscription ss = asyncStarThrowAsync().listen((Future f) {
_output.add('unique value');
});
await ss.asFuture();
if (_output.length == 44) {
print(_output);
}
}
// ----
// Scenario: All async functions yielded and we run in a custom zone with a
// custom error handler.
// ----
Future<void> customErrorZone() async {
final completer = Completer<void>();
runZonedGuarded(() async {
await allYield();
completer.complete(null);
}, (e, s) {
completer.completeError(e, s);
});
return completer.future;
}
// ----
// Scenario: Future.timeout:
// ----
Future awaitTimeout() async {
await (throwAsync().timeout(Duration(seconds: 1)));
}
// ----
// Scenario: Future.wait:
// ----
Future awaitWait() async {
await Future.wait([
throwAsync(),
() async {
await Future.value();
}()
]);
}
// ----
// Scenario: Future.whenComplete:
// ----
Future futureSyncWhenComplete() {
return Future.sync(throwAsync).whenComplete(() => 'nop');
}
// ----
// Scenario: Future.then:
// ----
Future futureThen() {
return Future.value(0).then((value) {
throwSync();
}).then(_doSomething);
}
void _doSomething(_) {
Expect.fail('Should not reach doSomething');
}
Future<void> doTestAwait(Future f()) async {
await f();
Expect.fail('No exception thrown!');
}
Future<void> doTestAwaitThen(Future f()) async {
// Passing (e) {} to then() can cause the closure instructions to be
// deduped, changing the stack trace to the deduped owner, so we
// duplicate the Expect.fail() call in the closure.
await f().then((e) => Expect.fail('No exception thrown!'));
}
Future<void> doTestAwaitCatchError(Future f()) async {
late Object error;
late StackTrace stackTrace;
await f().catchError((e, s) {
error = e;
stackTrace = s;
});
return Future.error(error, stackTrace);
}
Future<void> main(List<String> args) async {
if (harness.shouldSkip()) {
return;
}
harness.configure(currentExpectations);
final tests = [
allYield,
noYields,
mixedYields,
syncSuffix,
nonAsyncNoStack,
awaitEveryAsyncStarThrowSync,
awaitEveryAsyncStarThrowAsync,
listenAsyncStarThrowAsync,
customErrorZone,
awaitTimeout,
awaitWait,
futureSyncWhenComplete,
futureThen,
];
for (var test in tests) {
await harness.runTest(() => doTestAwait(test));
await harness.runTest(() => doTestAwaitThen(test));
await harness.runTest(() => doTestAwaitCatchError(test));
}
harness.updateExpectations();
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 doTestAwait (%test%)
<asynchronous suspension>
#5 runTest (harness.dart)
<asynchronous suspension>
#6 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 doTestAwaitThen (%test%)
<asynchronous suspension>
#5 runTest (harness.dart)
<asynchronous suspension>
#6 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#5 runTest (harness.dart)
<asynchronous suspension>
#6 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 noYields3 (%test%)
#2 noYields2 (%test%)
#3 noYields (%test%)
#4 doTestAwait (%test%)
#5 main.<anonymous closure> (%test%)
#6 runTest (harness.dart)
#7 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 noYields3 (%test%)
#2 noYields2 (%test%)
#3 noYields (%test%)
#4 doTestAwaitThen (%test%)
#5 main.<anonymous closure> (%test%)
#6 runTest (harness.dart)
#7 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 noYields3 (%test%)
#2 noYields2 (%test%)
#3 noYields (%test%)
#4 doTestAwaitCatchError (%test%)
#5 main.<anonymous closure> (%test%)
#6 runTest (harness.dart)
#7 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 mixedYields2 (%test%)
<asynchronous suspension>
#2 mixedYields (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 mixedYields2 (%test%)
<asynchronous suspension>
#2 mixedYields (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 mixedYields2 (%test%)
<asynchronous suspension>
#2 mixedYields (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 syncSuffix2 (%test%)
<asynchronous suspension>
#2 syncSuffix (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 syncSuffix2 (%test%)
<asynchronous suspension>
#2 syncSuffix (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 syncSuffix2 (%test%)
<asynchronous suspension>
#2 syncSuffix (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 nonAsyncNoStack1 (%test%)
<asynchronous suspension>
#2 nonAsyncNoStack (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 nonAsyncNoStack1 (%test%)
<asynchronous suspension>
#2 nonAsyncNoStack (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 nonAsyncNoStack1 (%test%)
<asynchronous suspension>
#2 nonAsyncNoStack (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 asyncStarThrowSync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowSync (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 asyncStarThrowSync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowSync (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 asyncStarThrowSync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowSync (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowAsync (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowAsync (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowAsync (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 listenAsyncStarThrowAsync.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 listenAsyncStarThrowAsync.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 listenAsyncStarThrowAsync.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 customErrorZone.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 customErrorZone.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 customErrorZone.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.timeout.<anonymous closure> (future_impl.dart)
<asynchronous suspension>
#2 awaitTimeout (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.timeout.<anonymous closure> (future_impl.dart)
<asynchronous suspension>
#2 awaitTimeout (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.timeout.<anonymous closure> (future_impl.dart)
<asynchronous suspension>
#2 awaitTimeout (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.wait.<anonymous closure> (future.dart)
<asynchronous suspension>
#2 awaitWait (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.wait.<anonymous closure> (future.dart)
<asynchronous suspension>
#2 awaitWait (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.wait.<anonymous closure> (future.dart)
<asynchronous suspension>
#2 awaitWait (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 doTestAwait (%test%)
<asynchronous suspension>
#2 runTest (harness.dart)
<asynchronous suspension>
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 doTestAwaitThen (%test%)
<asynchronous suspension>
#2 runTest (harness.dart)
<asynchronous suspension>
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#2 runTest (harness.dart)
<asynchronous suspension>
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 futureThen.<anonymous closure> (%test%)
<asynchronous suspension>
#2 doTestAwait (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 futureThen.<anonymous closure> (%test%)
<asynchronous suspension>
#2 doTestAwaitThen (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 futureThen.<anonymous closure> (%test%)
<asynchronous suspension>
#2 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,98 +0,0 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
import 'dart:async';
import 'package:expect/expect.dart';
import 'harness.dart' as harness;
String effectOrder = '';
StackTrace? stackAfterYield = null;
void emit(String m) => effectOrder += m;
main() async {
if (harness.shouldSkip()) {
return;
}
harness.configure(currentExpectations);
await harness.runTest(() async {
emit('1');
await for (final value in produce()) {
emit('5');
Expect.equals('|value|', value);
}
emit('8');
Expect.equals('12345678', effectOrder);
effectOrder = '';
emit('1');
await for (final value in produceYieldStar()) {
emit('5');
Expect.equals('|value|', value);
break;
}
emit('6');
Expect.equals('123456', effectOrder);
return Future.error('error', stackAfterYield!);
});
harness.updateExpectations();
}
Stream<dynamic> produce() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('7');
}
Stream produceInner() async* {
emit('3');
yield '|value|';
emit('6');
stackAfterYield = StackTrace.current;
}
Stream<dynamic> produceYieldStar() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('x');
}
Stream produceInnerYieldStar() async* {
emit('3');
yield* Stream.fromIterable(['|value|', '|value2|']);
emit('x');
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 produceInner (%test%)
<asynchronous suspension>
#1 produce (%test%)
<asynchronous suspension>
#2 main.<anonymous closure> (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,236 +0,0 @@
// Copyright (c) 2023, 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 'package:path/path.dart' as path;
import 'package:expect/expect.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
//
// Test framework
//
class _ParsedFrame {
const _ParsedFrame();
static _ParsedFrame parse(String frame) {
if (frame == '<asynchronous suspension>') {
return const _AsynchronousGap();
} else {
return _DartFrame.parse(frame);
}
}
}
class _DartFrame extends _ParsedFrame {
final int no;
final String symbol;
final String location;
final int? lineNo;
_DartFrame({
required this.no,
required this.symbol,
required this.location,
required this.lineNo,
});
static final _pattern = RegExp(
r'^#(?<no>\d+)\s+(?<symbol>[^(]+)(\((?<location>(\w+:)?[^:]+)(:(?<line>\d+)(:(?<column>\d+))?)?\))?$');
static _DartFrame parse(String frame) {
final match = _pattern.firstMatch(frame);
if (match == null) {
throw 'Failed to parse: $frame';
}
final no = int.parse(match.namedGroup('no')!);
final symbol = match.namedGroup('symbol')!.trim();
var location = match.namedGroup('location')!;
if (location.endsWith('_test.dart')) {
location = '%test%';
}
final lineNo =
location.endsWith('utils.dart') || location.endsWith('tests.dart')
? match.namedGroup('line')
: null;
return _DartFrame(
no: no,
symbol: symbol,
location: location.split('/').last,
lineNo: lineNo != null ? int.parse(lineNo) : null,
);
}
@override
String toString() =>
'#$no $symbol ($location${lineNo != null ? ':$lineNo' : ''})';
@override
bool operator ==(Object other) {
if (other is! _DartFrame) {
return false;
}
return no == other.no &&
symbol == other.symbol &&
location == other.location &&
lineNo == other.lineNo;
}
}
class _AsynchronousGap extends _ParsedFrame {
const _AsynchronousGap();
@override
String toString() => '<asynchronous suspension>';
}
final _lineRE = RegExp(r'^(?:#(?<number>\d+)|<asynchronous suspension>)');
Future<List<_ParsedFrame>> _parseStack(String text) async {
if (text.contains('*** *** ***')) {
// Looks like DWARF stack traces mode.
text = await Stream.fromIterable(text.split('\n'))
.transform(DwarfStackTraceDecoder(_dwarf!))
.where(_lineRE.hasMatch)
.join('\n');
}
return text
.split('\n')
.map((l) => l.trim())
.where((l) => l.isNotEmpty)
.map(_ParsedFrame.parse)
.toList();
}
const _updatingExpectations = bool.fromEnvironment('update.expectations');
final _updatedExpectations = <String>[];
late final List<String> _currentExpectations;
var _testIndex = 0;
late final Dwarf? _dwarf;
void configure(List<String> currentExpectations,
{String debugInfoFilename = 'debug.so'}) {
if (debugInfoFilename != null) {
try {
final testCompilationDir = Platform.environment['TEST_COMPILATION_DIR'];
if (testCompilationDir != null) {
debugInfoFilename = path.join(testCompilationDir, debugInfoFilename);
}
_dwarf = Dwarf.fromFile(debugInfoFilename)!;
} on FileSystemException {
// We're not running in precompiled mode, so the file doesn't exist and
// we can continue normally.
}
}
_currentExpectations = currentExpectations;
}
Future<void> runTest(Future<void> Function() body) async {
try {
await body();
} catch (e, st) {
await checkExpectedStack(st);
}
}
Future<void> checkExpectedStack(StackTrace st) async {
final expectedFramesString = _testIndex < _currentExpectations.length
? _currentExpectations[_testIndex]
: '';
final stackTraceString = st.toString();
final gotFrames = await _parseStack(stackTraceString);
final normalizedStack = gotFrames.join('\n');
if (_updatingExpectations) {
_updatedExpectations.add(normalizedStack);
} else {
if (normalizedStack != expectedFramesString) {
final expectedFrames = await _parseStack(expectedFramesString);
final isDwarfMode = stackTraceString.contains('*** *** ***');
print('''
STACK TRACE MISMATCH -----------------
GOT:
$normalizedStack
EXPECTED:
$expectedFramesString
--------------------------------------
To regenate expectations run:
\$ ${Platform.executable} -Dupdate.expectations=true ${Platform.script}
--------------------------------------
''');
if (isDwarfMode) {
print('''
--------------------------------------
RAW STACK:
$st
--------------------------------------
''');
}
Expect.equals(
expectedFrames.length, gotFrames.length, 'wrong number of frames');
for (var i = 0; i < expectedFrames.length; i++) {
final expectedFrame = expectedFrames[i];
final gotFrame = gotFrames[i];
if (expectedFrame == gotFrame) {
continue;
}
if (expectedFrame is _DartFrame && gotFrame is _DartFrame) {
Expect.equals(expectedFrame.symbol, gotFrame.symbol,
'at frame #$i mismatched function name');
Expect.equals(expectedFrame.location, gotFrame.location,
'at frame #$i mismatched location');
Expect.equals(expectedFrame.lineNo, gotFrame.lineNo,
'at frame #$i mismatched line location');
}
Expect.equals(expectedFrame, gotFrame);
}
}
}
_testIndex++;
}
void updateExpectations([String? expectationsFile]) {
if (!_updatingExpectations) {
return;
}
final sourceFilePath = expectationsFile != null
? path.join(path.dirname(Platform.script.toFilePath()), expectationsFile)
: Platform.script.toFilePath();
final sourceFile = File(sourceFilePath);
final source = sourceFile.readAsStringSync();
final expectationsStart = source.lastIndexOf('// CURRENT EXPECTATIONS BEGIN');
final updatedExpectationsString =
[for (var s in _updatedExpectations) '"""\n$s"""'].join(",\n");
final newSource = source.substring(0, expectationsStart) +
"""
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [${updatedExpectationsString}];
// CURRENT EXPECTATIONS END
""";
sourceFile.writeAsStringSync(newSource);
print('updated expectations in ${sourceFile}!');
}
// Check if we are running with obfuscation but without DWARF stack traces
// then we don't have a way to deobfuscate the stack trace.
bool shouldSkip() {
final stack = StackTrace.current.toString();
return !stack.contains('shouldSkip') && !stack.contains('*** ***');
}

View file

@ -1,57 +0,0 @@
// Copyright (c) 2023, 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.
//
// This test ensures that "pkg:stack_trace" (used by "pkg:test") doesn't break
// when lazy async stacks are enabled by dropping frames below a synchronous
// start to an async function.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
import 'dart:async';
import 'package:test/test.dart';
import 'harness.dart' as harness;
Future<StackTrace> firstMethod() async {
return await secondMethod();
}
Future<StackTrace> secondMethod() async {
return StackTrace.current;
}
void main() {
if (harness.shouldSkip()) {
return;
}
setUpAll(() => harness.configure(currentExpectations));
test("Stacktrace includes sync-starts.", () async {
final st = await firstMethod();
await harness.checkExpectedStack(st);
});
tearDownAll(() => harness.updateExpectations());
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 secondMethod (%test%)
#1 firstMethod (%test%)
#2 main.<anonymous closure> (%test%)
#3 Declarer.test.<anonymous closure>.<anonymous closure> (declarer.dart)
<asynchronous suspension>
#4 Declarer.test.<anonymous closure> (declarer.dart)
<asynchronous suspension>
#5 Invoker._waitForOutstandingCallbacks.<anonymous closure> (invoker.dart)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,102 +0,0 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
import 'dart:async';
import 'package:expect/expect.dart';
import 'harness.dart' as harness;
bool barRunning = false;
Future<void> foo() async {}
Future<void> bar() async {
try {
barRunning = true;
await foo();
} finally {
barRunning = false;
}
}
Future<void> runTest() {
final Zone testZone = Zone.current.fork(
specification: ZoneSpecification(
registerUnaryCallback: _registerUnaryCallback,
registerBinaryCallback: _registerBinaryCallback));
return testZone.run(bar);
}
StackTrace? registerUnaryCallbackStackTrace;
StackTrace? registerBinaryCallbackStackTrace;
ZoneUnaryCallback<R, T> _registerUnaryCallback<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T) f) {
final stackTrace = StackTrace.current;
print('registerUnaryCallback got stack trace:');
print(stackTrace);
if (barRunning) {
Expect.isNull(registerUnaryCallbackStackTrace);
registerUnaryCallbackStackTrace = stackTrace;
}
return parent.registerUnaryCallback(zone, f);
}
ZoneBinaryCallback<R, T1, T2> _registerBinaryCallback<R, T1, T2>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T1, T2) f) {
final stackTrace = StackTrace.current;
print('registerBinaryCallback got stack trace:');
print(stackTrace);
if (barRunning) {
Expect.isNull(registerBinaryCallbackStackTrace);
registerBinaryCallbackStackTrace = stackTrace;
}
return parent.registerBinaryCallback(zone, f);
}
Future<void> main() async {
if (harness.shouldSkip()) {
return;
}
harness.configure(currentExpectations);
await runTest();
await harness.checkExpectedStack(registerUnaryCallbackStackTrace!);
await harness.checkExpectedStack(registerBinaryCallbackStackTrace!);
harness.updateExpectations();
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 _registerUnaryCallback (%test%)
#1 _CustomZone.registerUnaryCallback (zone.dart)
#2 bar (%test%)
#3 _rootRun (zone.dart)
#4 _CustomZone.run (zone.dart)
#5 runTest (%test%)
#6 main (%test%)
#7 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#8 _RawReceivePort._handleMessage (isolate_patch.dart)""",
"""
#0 _registerBinaryCallback (%test%)
#1 _CustomZone.registerBinaryCallback (zone.dart)
#2 bar (%test%)
#3 _rootRun (zone.dart)
#4 _CustomZone.run (zone.dart)
#5 runTest (%test%)
#6 main (%test%)
#7 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#8 _RawReceivePort._handleMessage (isolate_patch.dart)"""
];
// CURRENT EXPECTATIONS END

View file

@ -0,0 +1,17 @@
// Copyright (c) 2020, 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=--dwarf-stack-traces --save-debugging-info=async_lazy_debug.so
import 'dart:async';
import 'dart:io';
import 'utils.dart';
Future<void> main(List<String> args) async {
// We won't have access to the debugging info file on Android.
if (Platform.isAndroid) return;
await doTestsLazy('async_lazy_debug.so');
}

View file

@ -0,0 +1,9 @@
// 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 'utils.dart';
Future<void> main(List<String> args) async => await doTestsLazy();

View file

@ -0,0 +1,75 @@
// Copyright (c) 2022, 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 'package:expect/expect.dart';
import 'utils.dart' show assertStack;
String effectOrder = '';
StackTrace? stackAfterYield = null;
void emit(String m) => effectOrder += m;
main() async {
emit('1');
await for (final value in produce()) {
emit('5');
Expect.equals('|value|', value);
}
emit('8');
Expect.equals('12345678', effectOrder);
assertStack(const <String>[
r'^#0 produceInner .*$',
r'^<asynchronous suspension>$',
r'^#1 produce .*$',
r'^<asynchronous suspension>$',
r'^#2 main .*$',
r'^<asynchronous suspension>$',
], stackAfterYield!);
effectOrder = '';
emit('1');
await for (final value in produceYieldStar()) {
emit('5');
Expect.equals('|value|', value);
break;
}
emit('6');
Expect.equals('123456', effectOrder);
}
Stream<dynamic> produce() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('7');
}
Stream produceInner() async* {
emit('3');
yield '|value|';
emit('6');
stackAfterYield = StackTrace.current;
}
Stream<dynamic> produceYieldStar() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('x');
}
Stream produceInnerYieldStar() async* {
emit('3');
yield* Stream.fromIterable(['|value|', '|value2|']);
emit('x');
}

View file

@ -0,0 +1,27 @@
// 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.
//
// This test ensures that "pkg:stack_trace" (used by "pkg:test") doesn't break
// when lazy async stacks are enabled by dropping frames below a synchronous
// start to an async function.
import "package:test/test.dart";
import "package:stack_trace/src/stack_zone_specification.dart";
import 'dart:async';
void main() {
test("Stacktrace includes sync-starts.", () async {
final st = await firstMethod();
expect("$st", allOf([contains("firstMethod"), contains("secondMethod")]));
});
}
Future<StackTrace> firstMethod() async {
return await secondMethod();
}
Future<StackTrace> secondMethod() async {
return StackTrace.current;
}

View file

@ -0,0 +1,865 @@
// 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:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:expect/expect.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
// Test functions:
Future<void> throwSync() {
throw 'throw from throwSync';
}
Future<void> throwAsync() async {
await 0;
throw 'throw from throwAsync';
}
// ----
// 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();
}
// ----
// Scenario: None of the async functions yielded before the throw:
// ----
Future<void> noYields() async {
await noYields2();
}
Future<void> noYields2() async {
await noYields3();
}
Future<void> noYields3() async {
throwSync();
}
// ----
// 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();
}
// ----
// Scenario: Non-async frame:
// ----
Future<void> syncSuffix() async {
await syncSuffix2();
}
Future<void> syncSuffix2() async {
await 0;
await syncSuffix3();
}
Future<void> syncSuffix3() {
return throwAsync();
}
// ----
// 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());
// ----
// Scenario: async*:
// ----
Future awaitEveryAsyncStarThrowSync() async {
await for (Future v in asyncStarThrowSync()) {
await v;
}
}
Stream<Future> asyncStarThrowSync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield throwSync();
}
}
Future awaitEveryAsyncStarThrowAsync() async {
await for (Future v in asyncStarThrowAsync()) {
await v;
}
}
Stream<Future> asyncStarThrowAsync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield Future.value(i);
await throwAsync();
}
}
Future listenAsyncStarThrowAsync() async {
// Listening to an async* doesn't create the usual await-for StreamIterator.
StreamSubscription ss = asyncStarThrowAsync().listen((Future f) {});
await ss.asFuture();
}
// ----
// Scenario: All async functions yielded and we run in a custom zone with a
// custom error handler.
// ----
Future<void> customErrorZone() async {
final completer = Completer<void>();
runZonedGuarded(() async {
await allYield();
completer.complete(null);
}, (e, s) {
completer.completeError(e, s);
});
return completer.future;
}
// ----
// Scenario: Future.timeout:
// ----
Future awaitTimeout() async {
await (throwAsync().timeout(Duration(seconds: 1)));
}
// ----
// Scenario: Future.wait:
// ----
Future awaitWait() async {
await Future.wait([
throwAsync(),
() async {
await Future.value();
}()
]);
}
// ----
// Scenario: Future.whenComplete:
// ----
Future futureSyncWhenComplete() {
return Future.sync(throwAsync).whenComplete(() => 'nop');
}
// ----
// Scenario: Future.then:
// ----
Future futureThen() {
return Future.value(0).then((value) {
throwSync();
});
}
// Helpers:
// Marker to tell the matcher to ignore the rest of the stack.
const IGNORE_REMAINING_STACK = '#@ IGNORE_REMAINING_STACK #@';
// We want lines that either start with a frame index or an async gap marker.
final _lineRE = RegExp(r'^(?:#(?<number>\d+)|<asynchronous suspension>)');
Future<void> assertStack(List<String> expects, StackTrace stackTrace,
[String? debugInfoFilename]) async {
final original = await Stream.value(stackTrace.toString())
.transform(const LineSplitter())
.toList();
var frames = original;
// Use the DWARF stack decoder if we're running in --dwarf-stack-traces mode
// and in precompiled mode (otherwise --dwarf-stack-traces has no effect).
bool usingDwarf = false;
if (debugInfoFilename != null) {
try {
final dwarf = Dwarf.fromFile(debugInfoFilename)!;
usingDwarf = true;
frames = await Stream.fromIterable(original)
.transform(DwarfStackTraceDecoder(dwarf))
.where(_lineRE.hasMatch)
.toList();
} on FileSystemException {
// We're not running in precompiled mode, so the file doesn't exist and
// we can continue normally.
}
}
void printFrameInformation() {
print('RegExps for expected stack:');
expects.forEach((s) => print('"${s}"'));
print('');
if (usingDwarf) {
print('Non-symbolic actual stack:');
original.forEach(print);
print('');
}
print('Actual stack:');
frames.forEach(print);
print('');
}
for (int i = 0; i < expects.length; i++) {
try {
Expect.isTrue(i < frames.length,
'Expected at least ${expects.length} frames, found ${frames.length}');
} on ExpectException {
// On failed expect, print full stack for reference.
printFrameInformation();
print('Expected line ${i + 1} to be ${expects[i]} but was missing');
rethrow;
}
// If we encounter this special marker we ignore the rest of the stack.
if (expects[i] == IGNORE_REMAINING_STACK) {
return;
}
try {
Expect.isTrue(RegExp(expects[i]).hasMatch(frames[i]));
} on ExpectException {
// On failed expect, print full stack for reference.
printFrameInformation();
print('Expected line ${i + 1} to be `${expects[i]}` '
'but was `${frames[i]}`');
rethrow;
}
}
try {
Expect.equals(expects.length, frames.length);
} on ExpectException {
// On failed expect, print full stack for reference.
printFrameInformation();
rethrow;
}
}
Future<void> doTestAwait(Future f(), List<String> expectedStack,
[String? debugInfoFilename]) async {
// Caller catches exception.
try {
await f();
Expect.fail('No exception thrown!');
} on String catch (e, s) {
return assertStack(expectedStack, s, debugInfoFilename);
}
}
Future<void> doTestAwaitThen(Future f(), List<String> expectedStack,
[String? debugInfoFilename]) async {
// Caller catches but a then is set.
try {
// Passing (e) {} to then() can cause the closure instructions to be
// deduped, changing the stack trace to the deduped owner, so we
// duplicate the Expect.fail() call in the closure.
await f().then((e) => Expect.fail('No exception thrown!'));
Expect.fail('No exception thrown!');
} on String catch (e, s) {
return assertStack(expectedStack, s, debugInfoFilename);
}
}
Future<void> doTestAwaitCatchError(Future f(), List<String> expectedStack,
[String? debugInfoFilename]) async {
// Caller doesn't catch, but we have a catchError set.
late StackTrace stackTrace;
await f().catchError((e, s) {
stackTrace = s;
});
return assertStack(expectedStack, stackTrace, debugInfoFilename);
}
// ----
// Test "Suites":
// ----
Future<void> doTestsLazy([String? debugInfoFilename]) async {
// allYield
{
final allYieldExpected = const <String>[
r'^#0 throwSync \(.*/utils.dart:16(:3)?\)$',
r'^#1 allYield3 \(.*/utils.dart:39(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#2 allYield2 \(.*/utils.dart:34(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#3 allYield \(.*/utils.dart:29(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
allYield,
allYieldExpected +
const <String>[
r'^#4 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
allYield,
allYieldExpected +
const <String>[
r'^#4 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
allYield,
allYieldExpected +
const <String>[
r'^#4 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// noYields
{
final noYieldsExpected = const <String>[
r'^#0 throwSync \(.*/utils.dart:16(:3)?\)$',
r'^#1 noYields3 \(.*/utils.dart:54(:3)?\)$',
r'^#2 noYields2 \(.*/utils.dart:50(:9)?\)$',
r'^#3 noYields \(.*/utils.dart:46(:9)?\)$',
];
await doTestAwait(
noYields,
noYieldsExpected +
const <String>[
r'^#4 doTestAwait ',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
noYields,
noYieldsExpected +
const <String>[
r'^#4 doTestAwaitThen ',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
noYields,
noYieldsExpected +
const <String>[
r'^#4 doTestAwaitCatchError ',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// mixedYields
{
final mixedYieldsExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 mixedYields2 \(.*/utils.dart:66(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#2 mixedYields \(.*/utils.dart:61(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
mixedYields,
mixedYieldsExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
mixedYields,
mixedYieldsExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
mixedYields,
mixedYieldsExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// syncSuffix
{
final syncSuffixExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 syncSuffix2 \(.*/utils.dart:82(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#2 syncSuffix \(.*/utils.dart:77(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
syncSuffix,
syncSuffixExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
syncSuffix,
syncSuffixExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
syncSuffix,
syncSuffixExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// nonAsyncNoStack
{
final nonAsyncNoStackExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 nonAsyncNoStack1 \(.*/utils.dart:95(:36)?\)$',
r'^<asynchronous suspension>$',
r'^#2 nonAsyncNoStack \(.*/utils.dart:93(:35)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
nonAsyncNoStack,
nonAsyncNoStackExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
nonAsyncNoStack,
nonAsyncNoStackExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
nonAsyncNoStack,
nonAsyncNoStackExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// awaitEveryAsyncStarThrowSync
{
final asyncStarThrowSyncExpected = const <String>[
r'^#0 throwSync \(.+/utils.dart:16(:3)?\)$',
r'^#1 asyncStarThrowSync \(.+/utils.dart:112(:11)?\)$',
r'^<asynchronous suspension>$',
r'^#2 awaitEveryAsyncStarThrowSync \(.+/utils.dart:104(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitEveryAsyncStarThrowSync,
asyncStarThrowSyncExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitEveryAsyncStarThrowSync,
asyncStarThrowSyncExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitEveryAsyncStarThrowSync,
asyncStarThrowSyncExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// awaitEveryAsyncStarThrowAsync
{
final asyncStarThrowAsyncExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 asyncStarThrowAsync \(.*/utils.dart:126(:5)?\)$',
r'^<asynchronous suspension>$',
r'^#2 awaitEveryAsyncStarThrowAsync \(.+/utils.dart:117(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitEveryAsyncStarThrowAsync,
asyncStarThrowAsyncExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitEveryAsyncStarThrowAsync,
asyncStarThrowAsyncExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitEveryAsyncStarThrowAsync,
asyncStarThrowAsyncExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// listenAsyncStarThrowAsync
{
final listenAsyncStartExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 asyncStarThrowAsync \(.*/utils.dart:126(:5)?\)$',
r'^<asynchronous suspension>$',
r'^#2 listenAsyncStarThrowAsync.<anonymous closure> \(.+/utils.dart:132(:56)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
listenAsyncStarThrowAsync, listenAsyncStartExpected, debugInfoFilename);
await doTestAwaitThen(
listenAsyncStarThrowAsync, listenAsyncStartExpected, debugInfoFilename);
await doTestAwaitCatchError(
listenAsyncStarThrowAsync, listenAsyncStartExpected, debugInfoFilename);
}
// customErrorZone
{
final customErrorZoneExpected = const <String>[
r'#0 throwSync \(.*/utils.dart:16(:3)?\)$',
r'#1 allYield3 \(.*/utils.dart:39(:3)?\)$',
r'<asynchronous suspension>$',
r'#2 allYield2 \(.*/utils.dart:34(:3)?\)$',
r'<asynchronous suspension>$',
r'#3 allYield \(.*/utils.dart:29(:3)?\)$',
r'<asynchronous suspension>$',
r'#4 customErrorZone.<anonymous closure> \(.*/utils.dart:144(:5)?\)$',
r'<asynchronous suspension>$',
];
await doTestAwait(
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitThen(
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitCatchError(
customErrorZone, customErrorZoneExpected, debugInfoFilename);
}
// awaitTimeout
{
final awaitTimeoutExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 Future.timeout.<anonymous closure> \(dart:async/future_impl.dart',
r'^<asynchronous suspension>$',
r'^#2 awaitTimeout ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// awaitWait
{
final awaitWaitExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 Future.wait.<anonymous closure> \(dart:async/future.dart',
r'^<asynchronous suspension>$',
r'^#2 awaitWait ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitWait,
awaitWaitExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitWait,
awaitWaitExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitWait,
awaitWaitExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// futureSyncWhenComplete
{
final expected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
futureSyncWhenComplete,
expected +
const <String>[
r'^#1 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#2 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#3 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
futureSyncWhenComplete,
expected +
const <String>[
r'^#1 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#2 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#3 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
futureSyncWhenComplete,
expected +
const <String>[
r'^#1 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#2 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#3 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// futureThen
{
final expected = const <String>[
r'^#0 throwSync \(.*/utils.dart:16(:3)?\)$',
r'^#1 futureThen.<anonymous closure> ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
futureThen,
expected +
const <String>[
r'^#2 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#3 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#4 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
futureThen,
expected +
const <String>[
r'^#2 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#3 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#4 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
futureThen,
expected +
const <String>[
r'^#2 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#3 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#4 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
}

View file

@ -0,0 +1,84 @@
// Copyright (c) 2022, 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 'package:expect/expect.dart';
const String scriptName = 'zone_callback_stack_traces_test.dart';
Future<void> foo() async {}
Future<void> bar() async {
await foo();
}
Future<void> runTest() {
final Zone testZone = Zone.current.fork(
specification: ZoneSpecification(
registerUnaryCallback: _registerUnaryCallback,
registerBinaryCallback: _registerBinaryCallback));
return testZone.run(bar);
}
StackTrace? registerUnaryCallbackStackTrace;
StackTrace? registerBinaryCallbackStackTrace;
ZoneUnaryCallback<R, T> _registerUnaryCallback<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T) f) {
final stackTrace = StackTrace.current;
print('registerUnaryCallback got stack trace:');
print(stackTrace);
if (stackTrace.toString().contains('bar')) {
Expect.isNull(registerUnaryCallbackStackTrace);
registerUnaryCallbackStackTrace = stackTrace;
}
return parent.registerUnaryCallback(zone, f);
}
ZoneBinaryCallback<R, T1, T2> _registerBinaryCallback<R, T1, T2>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T1, T2) f) {
final stackTrace = StackTrace.current;
print('registerBinaryCallback got stack trace:');
print(stackTrace);
if (stackTrace.toString().contains('bar')) {
Expect.isNull(registerBinaryCallbackStackTrace);
registerBinaryCallbackStackTrace = stackTrace;
}
return parent.registerBinaryCallback(zone, f);
}
void verifyStackTrace(List<String> expected, StackTrace stackTrace) {
final List<String> actual = stackTrace
.toString()
.split('\n')
.where((entry) => entry.contains(scriptName))
.toList();
print('Expected:\n${expected.join('\n')}');
print('Actual:\n${actual.join('\n')}');
Expect.equals(expected.length, actual.length);
for (int i = 0; i < expected.length; ++i) {
if (!RegExp(expected[i]).hasMatch(actual[i])) {
Expect.fail("Stack trace entry $i doesn't match:\n"
" expected: ${expected[i]}\n actual: ${actual[i]}");
}
}
}
main() async {
await runTest();
verifyStackTrace([
r'^#\d+ _registerUnaryCallback \(.*zone_callback_stack_traces_test.dart:30(:33)?\)$',
r'^#\d+ bar \(.*zone_callback_stack_traces_test.dart:14(:3)?\)$',
r'^#\d+ runTest \(.*zone_callback_stack_traces_test.dart:22(:19)?\)$',
r'^#\d+ main \(.*zone_callback_stack_traces_test.dart:70(:9)?\)$',
], registerUnaryCallbackStackTrace!);
verifyStackTrace([
r'^#\d+ _registerBinaryCallback \(.*zone_callback_stack_traces_test.dart:42(:33)?\)$',
r'^#\d+ bar \(.*zone_callback_stack_traces_test.dart:14(:3)?\)$',
r'^#\d+ runTest \(.*zone_callback_stack_traces_test.dart:22(:19)?\)$',
r'^#\d+ main \(.*zone_callback_stack_traces_test.dart:70(:9)?\)$',
], registerBinaryCallbackStackTrace!);
}

View file

@ -1,14 +1,8 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
import 'awaiter_stacks/harness.dart' as harness;
import 'causal_stacks/utils.dart' show assertStack, IGNORE_REMAINING_STACK;
class A {
void takesA(A a) {
@ -24,9 +18,7 @@ class B extends A {
StackTrace? trace = null;
void main() async {
harness.configure(currentExpectations);
void main() {
A a = new A();
A b = new B();
try {
@ -34,17 +26,9 @@ void main() async {
} catch (e, st) {
trace = st;
}
await harness.checkExpectedStack(trace!);
harness.updateExpectations();
assertStack(const <String>[
r'^#0 B.takesA \(.*/checked_parameter_assert_assignable_stacktrace_test.dart:14(:27)?\)$',
r'^#1 main \(.*/checked_parameter_assert_assignable_stacktrace_test.dart:25(:7)?\)$',
IGNORE_REMAINING_STACK,
], trace!);
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 B.takesA (%test%)
#1 main (%test%)
#2 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#3 _RawReceivePort._handleMessage (isolate_patch.dart)"""
];
// CURRENT EXPECTATIONS END

View file

@ -2,32 +2,57 @@
// 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 'awaiter_stacks/harness.dart' as harness;
import 'causal_stacks/utils.dart';
main() async {
harness.configure(currentExpectations);
StackTrace trace = StackTrace.empty;
A.visible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 new A.visible',
r'^#2 main',
IGNORE_REMAINING_STACK,
], trace);
A.invisible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 main',
IGNORE_REMAINING_STACK,
], trace);
visible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 visible',
r'^#2 main',
IGNORE_REMAINING_STACK,
], trace);
invisible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 main',
IGNORE_REMAINING_STACK,
], trace);
visibleClosure(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 visibleClosure.visibleInner',
r'^#2 visibleClosure',
r'^#3 main',
IGNORE_REMAINING_STACK,
], trace);
invisibleClosure(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
harness.updateExpectations();
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 invisibleClosure',
r'^#2 main',
IGNORE_REMAINING_STACK,
], trace);
}
class A {
@ -62,38 +87,3 @@ void invisibleClosure(void Function() fun) {
invisibleInner();
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 main.<anonymous closure> (%test%)
#1 new A.visible (%test%)
#2 main (%test%)
#3 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#4 _RawReceivePort._handleMessage (isolate_patch.dart)""",
"""
#0 main.<anonymous closure> (%test%)
#1 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 visible (%test%)
#2 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 visibleClosure.visibleInner (%test%)
#2 visibleClosure (%test%)
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 invisibleClosure (%test%)
#2 main (%test%)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,46 +1,95 @@
// Copyright (c) 2021, 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.
//
// This test checks that --resolve-dwarf-paths outputs absolute and relative
// paths in DWARF information.
//
// VMOptions=--dwarf-stack-traces --resolve-dwarf-paths --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// OtherResources=use_save_debugging_info_flag_program.dart
import "dart:async";
import "dart:io";
import 'package:expect/expect.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
import 'package:path/path.dart' as p;
import 'package:path/path.dart' as path;
import 'use_flag_test_helper.dart';
main(List<String> args) async {
if (!isAOTRuntime) {
return; // Running in JIT: AOT binaries not available.
}
if (Platform.isAndroid) {
return;
return; // SDK tree and dart_bootstrap not available on the test device.
}
final isDwarfStackTraces = StackTrace.current.toString().contains('*** ***');
if (!isDwarfStackTraces) {
return;
// These are the tools we need to be available to run on a given platform:
if (!await testExecutable(genSnapshot)) {
throw "Cannot run test as $genSnapshot not available";
}
if (!await testExecutable(dartPrecompiledRuntime)) {
throw "Cannot run test as $dartPrecompiledRuntime not available";
}
if (!File(platformDill).existsSync()) {
throw "Cannot run test as $platformDill does not exist";
}
final dwarfPath =
p.join(Platform.environment['TEST_COMPILATION_DIR']!, 'debug.so');
final dwarf = Dwarf.fromFile(dwarfPath)!;
runTests(obfuscate: false);
runTests(obfuscate: true);
}
final stack = StackTrace.current.toString();
print(stack);
final offsets = collectPCOffsets(stack.split('\n'));
print(offsets);
checkDwarfInfo(dwarf, offsets);
void runTests({required bool obfuscate}) async {
final pathSuffix = obfuscate ? 'obfuscated' : 'cleartext';
await withTempDir('dwarf-flag-test-$pathSuffix', (String tempDir) async {
final cwDir = path.dirname(Platform.script.toFilePath());
final script =
path.join(cwDir, 'use_save_debugging_info_flag_program.dart');
final scriptDill = path.join(tempDir, 'flag_program.dill');
// Compile script to Kernel IR.
await run(genKernel, <String>[
'--aot',
'--platform=$platformDill',
'-o',
scriptDill,
script,
]);
final scriptDwarfSnapshot = path.join(tempDir, 'dwarf.so');
await run(genSnapshot, <String>[
if (obfuscate) ...[
'--obfuscate',
'--save-obfuscation-map=${path.join(tempDir, 'obfuscation.map')}',
],
'--resolve-dwarf-paths',
'--dwarf-stack-traces-mode',
'--snapshot-kind=app-aot-elf',
'--elf=$scriptDwarfSnapshot',
scriptDill,
]);
// Run the resulting Dwarf-AOT compiled script.
final dwarfTrace = await runError(dartPrecompiledRuntime, <String>[
scriptDwarfSnapshot,
scriptDill,
]);
final tracePCOffsets = collectPCOffsets(dwarfTrace);
// Check that translating the DWARF stack trace (without internal frames)
// matches the symbolic stack trace.
final dwarf = Dwarf.fromFile(scriptDwarfSnapshot);
Expect.isNotNull(dwarf);
checkDwarfInfo(dwarf!, tracePCOffsets);
});
}
void checkDwarfInfo(Dwarf dwarf, Iterable<PCOffset> offsets) {
final filenames = <String>{};
for (final offset in offsets) {
final callInfo = offset.callInfoFrom(dwarf, includeInternalFrames: true);
final callInfo = offset.callInfoFrom(dwarf);
Expect.isNotNull(callInfo);
Expect.isNotEmpty(callInfo!);
for (final e in callInfo) {
@ -48,7 +97,7 @@ void checkDwarfInfo(Dwarf dwarf, Iterable<PCOffset> offsets) {
final entry = e as DartCallInfo;
var filename = entry.filename;
if (!filename.startsWith('/')) {
filename = p.join(sdkDir, filename);
filename = path.join(sdkDir, filename);
}
if (filenames.add(filename)) {
Expect.isTrue(
@ -61,6 +110,4 @@ void checkDwarfInfo(Dwarf dwarf, Iterable<PCOffset> offsets) {
print('- ${filename}');
}
Expect.isNotEmpty(filenames);
Expect.isNotEmpty(filenames
.where((p) => p.endsWith('use_resolve_dwarf_paths_flag_test.dart')));
}

View file

@ -1,714 +0,0 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// @dart=2.9
import 'dart:async';
import 'package:expect/expect.dart';
import 'harness.dart' as harness;
// Test functions:
Future<void> throwSync() {
throw 'throw from throwSync';
}
Future<void> throwAsync() async {
await 0;
throw 'throw from throwAsync';
}
// ----
// 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();
}
// ----
// Scenario: None of the async functions yielded before the throw:
// ----
Future<void> noYields() async {
await noYields2();
}
Future<void> noYields2() async {
await noYields3();
}
Future<void> noYields3() async {
throwSync();
}
// ----
// 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();
}
// ----
// Scenario: Non-async frame:
// ----
Future<void> syncSuffix() async {
await syncSuffix2();
}
Future<void> syncSuffix2() async {
await 0;
await syncSuffix3();
}
Future<void> syncSuffix3() {
return throwAsync();
}
// ----
// 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());
// ----
// Scenario: async*:
// ----
Future awaitEveryAsyncStarThrowSync() async {
await for (Future v in asyncStarThrowSync()) {
await v;
}
}
Stream<Future> asyncStarThrowSync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield throwSync();
}
}
Future awaitEveryAsyncStarThrowAsync() async {
await for (Future v in asyncStarThrowAsync()) {
await v;
}
}
Stream<Future> asyncStarThrowAsync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield Future.value(i);
await throwAsync();
}
}
Future listenAsyncStarThrowAsync() async {
final _output = [];
// Listening to an async* doesn't create the usual await-for StreamIterator.
StreamSubscription ss = asyncStarThrowAsync().listen((Future f) {
_output.add('unique value');
});
await ss.asFuture();
if (_output.length == 44) {
print(_output);
}
}
// ----
// Scenario: All async functions yielded and we run in a custom zone with a
// custom error handler.
// ----
Future<void> customErrorZone() async {
final completer = Completer<void>();
runZonedGuarded(() async {
await allYield();
completer.complete(null);
}, (e, s) {
completer.completeError(e, s);
});
return completer.future;
}
// ----
// Scenario: Future.timeout:
// ----
Future awaitTimeout() async {
await (throwAsync().timeout(Duration(seconds: 1)));
}
// ----
// Scenario: Future.wait:
// ----
Future awaitWait() async {
await Future.wait([
throwAsync(),
() async {
await Future.value();
}()
]);
}
// ----
// Scenario: Future.whenComplete:
// ----
Future futureSyncWhenComplete() {
return Future.sync(throwAsync).whenComplete(() => 'nop');
}
// ----
// Scenario: Future.then:
// ----
Future futureThen() {
return Future.value(0).then((value) {
throwSync();
}).then(_doSomething);
}
void _doSomething(_) {
Expect.fail('Should not reach doSomething');
}
Future<void> doTestAwait(Future f()) async {
await f();
Expect.fail('No exception thrown!');
}
Future<void> doTestAwaitThen(Future f()) async {
// Passing (e) {} to then() can cause the closure instructions to be
// deduped, changing the stack trace to the deduped owner, so we
// duplicate the Expect.fail() call in the closure.
await f().then((e) => Expect.fail('No exception thrown!'));
}
Future<void> doTestAwaitCatchError(Future f()) async {
Object error;
StackTrace stackTrace;
await f().catchError((e, s) {
error = e;
stackTrace = s;
});
return Future.error(error, stackTrace);
}
Future<void> main(List<String> args) async {
if (harness.shouldSkip()) {
return;
}
harness.configure(currentExpectations);
final tests = [
allYield,
noYields,
mixedYields,
syncSuffix,
nonAsyncNoStack,
awaitEveryAsyncStarThrowSync,
awaitEveryAsyncStarThrowAsync,
listenAsyncStarThrowAsync,
customErrorZone,
awaitTimeout,
awaitWait,
futureSyncWhenComplete,
futureThen,
];
for (var test in tests) {
await harness.runTest(() => doTestAwait(test));
await harness.runTest(() => doTestAwaitThen(test));
await harness.runTest(() => doTestAwaitCatchError(test));
}
harness.updateExpectations();
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 doTestAwait (%test%)
<asynchronous suspension>
#5 runTest (harness.dart)
<asynchronous suspension>
#6 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 doTestAwaitThen (%test%)
<asynchronous suspension>
#5 runTest (harness.dart)
<asynchronous suspension>
#6 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#5 runTest (harness.dart)
<asynchronous suspension>
#6 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 noYields3 (%test%)
#2 noYields2 (%test%)
#3 noYields (%test%)
#4 doTestAwait (%test%)
#5 main.<anonymous closure> (%test%)
#6 runTest (harness.dart)
#7 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 noYields3 (%test%)
#2 noYields2 (%test%)
#3 noYields (%test%)
#4 doTestAwaitThen (%test%)
#5 main.<anonymous closure> (%test%)
#6 runTest (harness.dart)
#7 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 noYields3 (%test%)
#2 noYields2 (%test%)
#3 noYields (%test%)
#4 doTestAwaitCatchError (%test%)
#5 main.<anonymous closure> (%test%)
#6 runTest (harness.dart)
#7 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 mixedYields2 (%test%)
<asynchronous suspension>
#2 mixedYields (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 mixedYields2 (%test%)
<asynchronous suspension>
#2 mixedYields (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 mixedYields2 (%test%)
<asynchronous suspension>
#2 mixedYields (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 syncSuffix2 (%test%)
<asynchronous suspension>
#2 syncSuffix (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 syncSuffix2 (%test%)
<asynchronous suspension>
#2 syncSuffix (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 syncSuffix2 (%test%)
<asynchronous suspension>
#2 syncSuffix (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 nonAsyncNoStack1 (%test%)
<asynchronous suspension>
#2 nonAsyncNoStack (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 nonAsyncNoStack1 (%test%)
<asynchronous suspension>
#2 nonAsyncNoStack (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 nonAsyncNoStack1 (%test%)
<asynchronous suspension>
#2 nonAsyncNoStack (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 asyncStarThrowSync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowSync (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 asyncStarThrowSync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowSync (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 asyncStarThrowSync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowSync (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowAsync (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowAsync (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 awaitEveryAsyncStarThrowAsync (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 listenAsyncStarThrowAsync.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 listenAsyncStarThrowAsync.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 asyncStarThrowAsync (%test%)
<asynchronous suspension>
#2 listenAsyncStarThrowAsync.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 customErrorZone.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 customErrorZone.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 allYield3 (%test%)
<asynchronous suspension>
#2 allYield2 (%test%)
<asynchronous suspension>
#3 allYield (%test%)
<asynchronous suspension>
#4 customErrorZone.<anonymous closure> (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.timeout.<anonymous closure> (future_impl.dart)
<asynchronous suspension>
#2 awaitTimeout (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.timeout.<anonymous closure> (future_impl.dart)
<asynchronous suspension>
#2 awaitTimeout (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.timeout.<anonymous closure> (future_impl.dart)
<asynchronous suspension>
#2 awaitTimeout (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.wait.<anonymous closure> (future.dart)
<asynchronous suspension>
#2 awaitWait (%test%)
<asynchronous suspension>
#3 doTestAwait (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.wait.<anonymous closure> (future.dart)
<asynchronous suspension>
#2 awaitWait (%test%)
<asynchronous suspension>
#3 doTestAwaitThen (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 Future.wait.<anonymous closure> (future.dart)
<asynchronous suspension>
#2 awaitWait (%test%)
<asynchronous suspension>
#3 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#4 runTest (harness.dart)
<asynchronous suspension>
#5 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 doTestAwait (%test%)
<asynchronous suspension>
#2 runTest (harness.dart)
<asynchronous suspension>
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 doTestAwaitThen (%test%)
<asynchronous suspension>
#2 runTest (harness.dart)
<asynchronous suspension>
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 throwAsync (%test%)
<asynchronous suspension>
#1 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#2 runTest (harness.dart)
<asynchronous suspension>
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 futureThen.<anonymous closure> (%test%)
<asynchronous suspension>
#2 doTestAwait (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 futureThen.<anonymous closure> (%test%)
<asynchronous suspension>
#2 doTestAwaitThen (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>""",
"""
#0 throwSync (%test%)
#1 futureThen.<anonymous closure> (%test%)
<asynchronous suspension>
#2 doTestAwaitCatchError (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,100 +0,0 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// @dart=2.9
import 'dart:async';
import 'package:expect/expect.dart';
import 'harness.dart' as harness;
String effectOrder = '';
StackTrace stackAfterYield = null;
void emit(String m) => effectOrder += m;
main() async {
if (harness.shouldSkip()) {
return;
}
harness.configure(currentExpectations);
await harness.runTest(() async {
emit('1');
await for (final value in produce()) {
emit('5');
Expect.equals('|value|', value);
}
emit('8');
Expect.equals('12345678', effectOrder);
effectOrder = '';
emit('1');
await for (final value in produceYieldStar()) {
emit('5');
Expect.equals('|value|', value);
break;
}
emit('6');
Expect.equals('123456', effectOrder);
return Future.error('error', stackAfterYield);
});
harness.updateExpectations();
}
Stream<dynamic> produce() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('7');
}
Stream produceInner() async* {
emit('3');
yield '|value|';
emit('6');
stackAfterYield = StackTrace.current;
}
Stream<dynamic> produceYieldStar() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('x');
}
Stream produceInnerYieldStar() async* {
emit('3');
yield* Stream.fromIterable(['|value|', '|value2|']);
emit('x');
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 produceInner (%test%)
<asynchronous suspension>
#1 produce (%test%)
<asynchronous suspension>
#2 main.<anonymous closure> (%test%)
<asynchronous suspension>
#3 runTest (harness.dart)
<asynchronous suspension>
#4 main (%test%)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,237 +0,0 @@
// Copyright (c) 2023, 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.
// @dart=2.9
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:expect/expect.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
//
// Test framework
//
class _ParsedFrame {
const _ParsedFrame();
static _ParsedFrame parse(String frame) {
if (frame == '<asynchronous suspension>') {
return const _AsynchronousGap();
} else {
return _DartFrame.parse(frame);
}
}
}
class _DartFrame extends _ParsedFrame {
final int no;
final String symbol;
final String location;
final int lineNo;
_DartFrame({
this.no,
this.symbol,
this.location,
this.lineNo,
});
static final _pattern = RegExp(
r'^#(?<no>\d+)\s+(?<symbol>[^(]+)(\((?<location>(\w+:)?[^:]+)(:(?<line>\d+)(:(?<column>\d+))?)?\))?$');
static _DartFrame parse(String frame) {
final match = _pattern.firstMatch(frame);
if (match == null) {
throw 'Failed to parse: $frame';
}
final no = int.parse(match.namedGroup('no'));
final symbol = match.namedGroup('symbol').trim();
var location = match.namedGroup('location');
if (location.endsWith('_test.dart')) {
location = '%test%';
}
final lineNo =
location.endsWith('utils.dart') || location.endsWith('tests.dart')
? match.namedGroup('line')
: null;
return _DartFrame(
no: no,
symbol: symbol,
location: location.split('/').last,
lineNo: lineNo != null ? int.parse(lineNo) : null,
);
}
@override
String toString() =>
'#$no $symbol ($location${lineNo != null ? ':$lineNo' : ''})';
@override
bool operator ==(Object other) {
if (other is _DartFrame) {
return no == other.no &&
symbol == other.symbol &&
location == other.location &&
lineNo == other.lineNo;
}
return false;
}
}
class _AsynchronousGap extends _ParsedFrame {
const _AsynchronousGap();
@override
String toString() => '<asynchronous suspension>';
}
final _lineRE = RegExp(r'^(?:#(?<number>\d+)|<asynchronous suspension>)');
Future<List<_ParsedFrame>> _parseStack(String text) async {
if (text.contains('*** *** ***')) {
// Looks like DWARF stack traces mode.
text = await Stream.fromIterable(text.split('\n'))
.transform(DwarfStackTraceDecoder(_dwarf))
.where(_lineRE.hasMatch)
.join('\n');
}
return text
.split('\n')
.map((l) => l.trim())
.where((l) => l.isNotEmpty)
.map(_ParsedFrame.parse)
.toList();
}
const _updatingExpectations = bool.fromEnvironment('update.expectations');
final _updatedExpectations = <String>[];
List<String> _currentExpectations;
var _testIndex = 0;
Dwarf _dwarf;
void configure(List<String> currentExpectations,
{String debugInfoFilename = 'debug.so'}) {
if (debugInfoFilename != null) {
try {
final testCompilationDir = Platform.environment['TEST_COMPILATION_DIR'];
if (testCompilationDir != null) {
debugInfoFilename = path.join(testCompilationDir, debugInfoFilename);
}
_dwarf = Dwarf.fromFile(debugInfoFilename);
} on FileSystemException {
// We're not running in precompiled mode, so the file doesn't exist and
// we can continue normally.
}
}
_currentExpectations = currentExpectations;
}
Future<void> runTest(Future<void> Function() body) async {
try {
await body();
} catch (e, st) {
await checkExpectedStack(st);
}
}
Future<void> checkExpectedStack(StackTrace st) async {
final expectedFramesString = _testIndex < _currentExpectations.length
? _currentExpectations[_testIndex]
: '';
final stackTraceString = st.toString();
final gotFrames = await _parseStack(stackTraceString);
final normalizedStack = gotFrames.join('\n');
if (_updatingExpectations) {
_updatedExpectations.add(normalizedStack);
} else {
if (normalizedStack != expectedFramesString) {
final expectedFrames = await _parseStack(expectedFramesString);
final isDwarfMode = stackTraceString.contains('*** *** ***');
print('''
STACK TRACE MISMATCH -----------------
GOT:
$normalizedStack
EXPECTED:
$expectedFramesString
--------------------------------------
To regenate expectations run:
\$ ${Platform.executable} -Dupdate.expectations=true ${Platform.script}
--------------------------------------
''');
if (isDwarfMode) {
print('''
--------------------------------------
RAW STACK:
$st
--------------------------------------
''');
}
Expect.equals(
expectedFrames.length, gotFrames.length, 'wrong number of frames');
for (var i = 0; i < expectedFrames.length; i++) {
final expectedFrame = expectedFrames[i];
final gotFrame = gotFrames[i];
if (expectedFrame == gotFrame) {
continue;
}
if (expectedFrame is _DartFrame && gotFrame is _DartFrame) {
Expect.equals(expectedFrame.symbol, gotFrame.symbol,
'at frame #$i mismatched function name');
Expect.equals(expectedFrame.location, gotFrame.location,
'at frame #$i mismatched location');
Expect.equals(expectedFrame.lineNo, gotFrame.lineNo,
'at frame #$i mismatched line location');
}
Expect.equals(expectedFrame, gotFrame);
}
}
}
_testIndex++;
}
void updateExpectations([String expectationsFile]) {
if (!_updatingExpectations) {
return;
}
final sourceFilePath = expectationsFile != null
? path.join(path.dirname(Platform.script.toFilePath()), expectationsFile)
: Platform.script.toFilePath();
final sourceFile = File(sourceFilePath);
final source = sourceFile.readAsStringSync();
final expectationsStart = source.lastIndexOf('// CURRENT EXPECTATIONS BEGIN');
final updatedExpectationsString =
[for (var s in _updatedExpectations) '"""\n$s"""'].join(",\n");
final newSource = source.substring(0, expectationsStart) +
"""
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [${updatedExpectationsString}];
// CURRENT EXPECTATIONS END
""";
sourceFile.writeAsStringSync(newSource);
print('updated expectations in ${sourceFile}!');
}
// Check if we are running with obfuscation but without DWARF stack traces
// then we don't have a way to deobfuscate the stack trace.
bool shouldSkip() {
final stack = StackTrace.current.toString();
return !stack.contains('shouldSkip') && !stack.contains('*** ***');
}

View file

@ -1,59 +0,0 @@
// Copyright (c) 2023, 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.
//
// This test ensures that "pkg:stack_trace" (used by "pkg:test") doesn't break
// when lazy async stacks are enabled by dropping frames below a synchronous
// start to an async function.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// @dart=2.9
import 'dart:async';
import 'package:test/test.dart';
import 'harness.dart' as harness;
Future<StackTrace> firstMethod() async {
return await secondMethod();
}
Future<StackTrace> secondMethod() async {
return StackTrace.current;
}
void main() {
if (harness.shouldSkip()) {
return;
}
setUpAll(() => harness.configure(currentExpectations));
test("Stacktrace includes sync-starts.", () async {
final st = await firstMethod();
await harness.checkExpectedStack(st);
});
tearDownAll(() => harness.updateExpectations());
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 secondMethod (%test%)
#1 firstMethod (%test%)
#2 main.<anonymous closure> (%test%)
#3 Declarer.test.<anonymous closure>.<anonymous closure> (declarer.dart)
<asynchronous suspension>
#4 Declarer.test.<anonymous closure> (declarer.dart)
<asynchronous suspension>
#5 Invoker._waitForOutstandingCallbacks.<anonymous closure> (invoker.dart)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,104 +0,0 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// @dart=2.9
import 'dart:async';
import 'package:expect/expect.dart';
import 'harness.dart' as harness;
bool barRunning = false;
Future<void> foo() async {}
Future<void> bar() async {
try {
barRunning = true;
await foo();
} finally {
barRunning = false;
}
}
Future<void> runTest() {
final Zone testZone = Zone.current.fork(
specification: ZoneSpecification(
registerUnaryCallback: _registerUnaryCallback,
registerBinaryCallback: _registerBinaryCallback));
return testZone.run(bar);
}
StackTrace registerUnaryCallbackStackTrace;
StackTrace registerBinaryCallbackStackTrace;
ZoneUnaryCallback<R, T> _registerUnaryCallback<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T) f) {
final stackTrace = StackTrace.current;
print('registerUnaryCallback got stack trace:');
print(stackTrace);
if (barRunning) {
Expect.isNull(registerUnaryCallbackStackTrace);
registerUnaryCallbackStackTrace = stackTrace;
}
return parent.registerUnaryCallback(zone, f);
}
ZoneBinaryCallback<R, T1, T2> _registerBinaryCallback<R, T1, T2>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T1, T2) f) {
final stackTrace = StackTrace.current;
print('registerBinaryCallback got stack trace:');
print(stackTrace);
if (barRunning) {
Expect.isNull(registerBinaryCallbackStackTrace);
registerBinaryCallbackStackTrace = stackTrace;
}
return parent.registerBinaryCallback(zone, f);
}
Future<void> main() async {
if (harness.shouldSkip()) {
return;
}
harness.configure(currentExpectations);
await runTest();
await harness.checkExpectedStack(registerUnaryCallbackStackTrace);
await harness.checkExpectedStack(registerBinaryCallbackStackTrace);
harness.updateExpectations();
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 _registerUnaryCallback (%test%)
#1 _CustomZone.registerUnaryCallback (zone.dart)
#2 bar (%test%)
#3 _rootRun (zone.dart)
#4 _CustomZone.run (zone.dart)
#5 runTest (%test%)
#6 main (%test%)
#7 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#8 _RawReceivePort._handleMessage (isolate_patch.dart)""",
"""
#0 _registerBinaryCallback (%test%)
#1 _CustomZone.registerBinaryCallback (zone.dart)
#2 bar (%test%)
#3 _rootRun (zone.dart)
#4 _CustomZone.run (zone.dart)
#5 runTest (%test%)
#6 main (%test%)
#7 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#8 _RawReceivePort._handleMessage (isolate_patch.dart)"""
];
// CURRENT EXPECTATIONS END

View file

@ -0,0 +1,19 @@
// Copyright (c) 2020, 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=--dwarf-stack-traces --save-debugging-info=async_lazy_debug.so
// @dart = 2.9
import 'dart:async';
import 'dart:io';
import 'utils.dart';
Future<void> main(List<String> args) async {
// We won't have access to the debugging info file on Android.
if (Platform.isAndroid) return;
await doTestsLazy('async_lazy_debug.so');
}

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.
// @dart = 2.9
import 'dart:async';
import 'utils.dart';
Future<void> main(List<String> args) async => await doTestsLazy();

View file

@ -0,0 +1,76 @@
// Copyright (c) 2022, 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.
// @dart = 2.9
import 'dart:async';
import 'package:expect/expect.dart';
import 'utils.dart' show assertStack;
String effectOrder = '';
StackTrace stackAfterYield = null;
void emit(String m) => effectOrder += m;
main() async {
emit('1');
await for (final value in produce()) {
emit('5');
Expect.equals('|value|', value);
}
emit('8');
Expect.equals('12345678', effectOrder);
assertStack(const <String>[
r'^#0 produceInner .*$',
r'^<asynchronous suspension>$',
r'^#1 produce .*$',
r'^<asynchronous suspension>$',
r'^#2 main .*$',
r'^<asynchronous suspension>$',
], stackAfterYield);
effectOrder = '';
emit('1');
await for (final value in produceYieldStar()) {
emit('5');
Expect.equals('|value|', value);
break;
}
emit('6');
Expect.equals('123456', effectOrder);
}
Stream<dynamic> produce() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('7');
}
Stream produceInner() async* {
emit('3');
yield '|value|';
emit('6');
stackAfterYield = StackTrace.current;
}
Stream<dynamic> produceYieldStar() async* {
emit('2');
await for (String response in produceInner()) {
emit('4');
yield response;
}
emit('x');
}
Stream produceInnerYieldStar() async* {
emit('3');
yield* Stream.fromIterable(['|value|', '|value2|']);
emit('x');
}

View file

@ -0,0 +1,29 @@
// 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.
//
// This test ensures that "pkg:stack_trace" (used by "pkg:test") doesn't break
// when lazy async stacks are enabled by dropping frames below a synchronous
// start to an async function.
// @dart = 2.9
import "package:test/test.dart";
import "package:stack_trace/src/stack_zone_specification.dart";
import 'dart:async';
void main() {
test("Stacktrace includes sync-starts.", () async {
final st = await firstMethod();
expect("$st", allOf([contains("firstMethod"), contains("secondMethod")]));
});
}
Future<StackTrace> firstMethod() async {
return await secondMethod();
}
Future<StackTrace> secondMethod() async {
return StackTrace.current;
}

View file

@ -0,0 +1,867 @@
// 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.
// @dart = 2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:expect/expect.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
// Test functions:
Future<void> throwSync() {
throw 'throw from throwSync';
}
Future<void> throwAsync() async {
await 0;
throw 'throw from throwAsync';
}
// ----
// 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();
}
// ----
// Scenario: None of the async functions yielded before the throw:
// ----
Future<void> noYields() async {
await noYields2();
}
Future<void> noYields2() async {
await noYields3();
}
Future<void> noYields3() async {
throwSync();
}
// ----
// 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();
}
// ----
// Scenario: Non-async frame:
// ----
Future<void> syncSuffix() async {
await syncSuffix2();
}
Future<void> syncSuffix2() async {
await 0;
await syncSuffix3();
}
Future<void> syncSuffix3() {
return throwAsync();
}
// ----
// 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());
// ----
// Scenario: async*:
// ----
Future awaitEveryAsyncStarThrowSync() async {
await for (Future v in asyncStarThrowSync()) {
await v;
}
}
Stream<Future> asyncStarThrowSync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield throwSync();
}
}
Future awaitEveryAsyncStarThrowAsync() async {
await for (Future v in asyncStarThrowAsync()) {
await v;
}
}
Stream<Future> asyncStarThrowAsync() async* {
for (int i = 0; i < 2; i++) {
await i;
yield Future.value(i);
await throwAsync();
}
}
Future listenAsyncStarThrowAsync() async {
// Listening to an async* doesn't create the usual await-for StreamIterator.
StreamSubscription ss = asyncStarThrowAsync().listen((Future f) => 42);
await ss.asFuture();
}
// ----
// Scenario: All async functions yielded and we run in a custom zone with a
// custom error handler.
// ----
Future<void> customErrorZone() async {
final completer = Completer<void>();
runZonedGuarded(() async {
await allYield();
completer.complete(null);
}, (e, s) {
completer.completeError(e, s);
});
return completer.future;
}
// ----
// Scenario: Future.timeout:
// ----
Future awaitTimeout() async {
await (throwAsync().timeout(Duration(seconds: 1)));
}
// ----
// Scenario: Future.wait:
// ----
Future awaitWait() async {
await Future.wait([
throwAsync(),
() async {
await Future.value();
}()
]);
}
// ----
// Scenario: Future.whenComplete:
// ----
Future futureSyncWhenComplete() {
return Future.sync(throwAsync).whenComplete(() => 'nop');
}
// ----
// Scenario: Future.then:
// ----
Future futureThen() {
return Future.value(0).then((value) {
throwSync();
});
}
// Helpers:
// Marker to tell the matcher to ignore the rest of the stack.
const IGNORE_REMAINING_STACK = '#@ IGNORE_REMAINING_STACK #@';
// We want lines that either start with a frame index or an async gap marker.
final _lineRE = RegExp(r'^(?:#(?<number>\d+)|<asynchronous suspension>)');
Future<void> assertStack(List<String> expects, StackTrace stackTrace,
[String debugInfoFilename]) async {
final original = await Stream.value(stackTrace.toString())
.transform(const LineSplitter())
.toList();
var frames = original;
// Use the DWARF stack decoder if we're running in --dwarf-stack-traces mode
// and in precompiled mode (otherwise --dwarf-stack-traces has no effect).
bool usingDwarf = false;
if (debugInfoFilename != null) {
try {
final dwarf = Dwarf.fromFile(debugInfoFilename);
usingDwarf = true;
frames = await Stream.fromIterable(original)
.transform(DwarfStackTraceDecoder(dwarf))
.where(_lineRE.hasMatch)
.toList();
} on FileSystemException {
// We're not running in precompiled mode, so the file doesn't exist and
// we can continue normally.
}
}
void printFrameInformation() {
print('RegExps for expected stack:');
expects.forEach((s) => print('"${s}"'));
print('');
if (usingDwarf) {
print('Non-symbolic actual stack:');
original.forEach(print);
print('');
}
print('Actual stack:');
frames.forEach(print);
print('');
}
for (int i = 0; i < expects.length; i++) {
try {
Expect.isTrue(i < frames.length,
'Expected at least ${expects.length} frames, found ${frames.length}');
} on ExpectException {
// On failed expect, print full stack for reference.
printFrameInformation();
print('Expected line ${i + 1} to be ${expects[i]} but was missing');
rethrow;
}
// If we encounter this special marker we ignore the rest of the stack.
if (expects[i] == IGNORE_REMAINING_STACK) {
return;
}
try {
Expect.isTrue(RegExp(expects[i]).hasMatch(frames[i]));
} on ExpectException {
// On failed expect, print full stack for reference.
printFrameInformation();
print('Expected line ${i + 1} to be `${expects[i]}` '
'but was `${frames[i]}`');
rethrow;
}
}
try {
Expect.equals(expects.length, frames.length);
} on ExpectException {
// On failed expect, print full stack for reference.
printFrameInformation();
rethrow;
}
}
Future<void> doTestAwait(Future f(), List<String> expectedStack,
[String debugInfoFilename]) async {
// Caller catches exception.
try {
await f();
Expect.fail('No exception thrown!');
} on String catch (e, s) {
return assertStack(expectedStack, s, debugInfoFilename);
}
}
Future<void> doTestAwaitThen(Future f(), List<String> expectedStack,
[String debugInfoFilename]) async {
// Caller catches but a then is set.
try {
// Passing (e) {} to then() can cause the closure instructions to be
// deduped, changing the stack trace to the deduped owner, so we
// duplicate the Expect.fail() call in the closure.
await f().then((e) => Expect.fail('No exception thrown!'));
Expect.fail('No exception thrown!');
} on String catch (e, s) {
return assertStack(expectedStack, s, debugInfoFilename);
}
}
Future<void> doTestAwaitCatchError(Future f(), List<String> expectedStack,
[String debugInfoFilename]) async {
// Caller doesn't catch, but we have a catchError set.
StackTrace stackTrace;
await f().catchError((e, s) {
stackTrace = s;
});
return assertStack(expectedStack, stackTrace, debugInfoFilename);
}
// ----
// Test "Suites":
// ----
Future<void> doTestsLazy([String debugInfoFilename]) async {
// allYield
{
final allYieldExpected = const <String>[
r'^#0 throwSync \(.*/utils.dart:18(:3)?\)$',
r'^#1 allYield3 \(.*/utils.dart:41(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#2 allYield2 \(.*/utils.dart:36(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#3 allYield \(.*/utils.dart:31(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
allYield,
allYieldExpected +
const <String>[
r'^#4 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
allYield,
allYieldExpected +
const <String>[
r'^#4 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
allYield,
allYieldExpected +
const <String>[
r'^#4 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// noYields
{
final noYieldsExpected = const <String>[
r'^#0 throwSync \(.*/utils.dart:18(:3)?\)$',
r'^#1 noYields3 \(.*/utils.dart:56(:3)?\)$',
r'^#2 noYields2 \(.*/utils.dart:52(:9)?\)$',
r'^#3 noYields \(.*/utils.dart:48(:9)?\)$',
];
await doTestAwait(
noYields,
noYieldsExpected +
const <String>[
r'^#4 doTestAwait ',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
noYields,
noYieldsExpected +
const <String>[
r'^#4 doTestAwaitThen ',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
noYields,
noYieldsExpected +
const <String>[
r'^#4 doTestAwaitCatchError ',
r'^#5 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#6 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// mixedYields
{
final mixedYieldsExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 mixedYields2 \(.*/utils.dart:68(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#2 mixedYields \(.*/utils.dart:63(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
mixedYields,
mixedYieldsExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
mixedYields,
mixedYieldsExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
mixedYields,
mixedYieldsExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// syncSuffix
{
final syncSuffixExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 syncSuffix2 \(.*/utils.dart:84(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#2 syncSuffix \(.*/utils.dart:79(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
syncSuffix,
syncSuffixExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
syncSuffix,
syncSuffixExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
syncSuffix,
syncSuffixExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// nonAsyncNoStack
{
final nonAsyncNoStackExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 nonAsyncNoStack1 \(.*/utils.dart:97(:36)?\)$',
r'^<asynchronous suspension>$',
r'^#2 nonAsyncNoStack \(.*/utils.dart:95(:35)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
nonAsyncNoStack,
nonAsyncNoStackExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
nonAsyncNoStack,
nonAsyncNoStackExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
nonAsyncNoStack,
nonAsyncNoStackExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// awaitEveryAsyncStarThrowSync
{
final asyncStarThrowSyncExpected = const <String>[
r'^#0 throwSync \(.+/utils.dart:18(:3)?\)$',
r'^#1 asyncStarThrowSync \(.+/utils.dart:114(:11)?\)$',
r'^<asynchronous suspension>$',
r'^#2 awaitEveryAsyncStarThrowSync \(.+/utils.dart:106(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitEveryAsyncStarThrowSync,
asyncStarThrowSyncExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitEveryAsyncStarThrowSync,
asyncStarThrowSyncExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitEveryAsyncStarThrowSync,
asyncStarThrowSyncExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// awaitEveryAsyncStarThrowAsync
{
final asyncStarThrowAsyncExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 asyncStarThrowAsync \(.*/utils.dart:128(:5)?\)$',
r'^<asynchronous suspension>$',
r'^#2 awaitEveryAsyncStarThrowAsync \(.+/utils.dart:119(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitEveryAsyncStarThrowAsync,
asyncStarThrowAsyncExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitEveryAsyncStarThrowAsync,
asyncStarThrowAsyncExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitEveryAsyncStarThrowAsync,
asyncStarThrowAsyncExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// listenAsyncStarThrowAsync
{
final listenAsyncStartExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 asyncStarThrowAsync \(.*/utils.dart:128(:5)?\)$',
r'^<asynchronous suspension>$',
r'^#2 listenAsyncStarThrowAsync.<anonymous closure> \(.+/utils.dart:134(:56)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
listenAsyncStarThrowAsync, listenAsyncStartExpected, debugInfoFilename);
await doTestAwaitThen(
listenAsyncStarThrowAsync, listenAsyncStartExpected, debugInfoFilename);
await doTestAwaitCatchError(
listenAsyncStarThrowAsync, listenAsyncStartExpected, debugInfoFilename);
}
// customErrorZone
{
final customErrorZoneExpected = const <String>[
r'#0 throwSync \(.*/utils.dart:18(:3)?\)$',
r'#1 allYield3 \(.*/utils.dart:41(:3)?\)$',
r'<asynchronous suspension>$',
r'#2 allYield2 \(.*/utils.dart:36(:3)?\)$',
r'<asynchronous suspension>$',
r'#3 allYield \(.*/utils.dart:31(:3)?\)$',
r'<asynchronous suspension>$',
r'#4 customErrorZone.<anonymous closure> \(.*/utils.dart:146(:5)?\)$',
r'<asynchronous suspension>$',
];
await doTestAwait(
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitThen(
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitCatchError(
customErrorZone, customErrorZoneExpected, debugInfoFilename);
}
// awaitTimeout
{
final awaitTimeoutExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 Future.timeout.<anonymous closure> \(dart:async/future_impl.dart',
r'^<asynchronous suspension>$',
r'^#2 awaitTimeout ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// awaitWait
{
final awaitWaitExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 Future.wait.<anonymous closure> \(dart:async/future.dart',
r'^<asynchronous suspension>$',
r'^#2 awaitWait ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitWait,
awaitWaitExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitWait,
awaitWaitExpected +
const <String>[
r'^#3 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitWait,
awaitWaitExpected +
const <String>[
r'^#3 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// futureSyncWhenComplete
{
final expected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:23(:3)?\)$',
r'^<asynchronous suspension>$',
];
await doTestAwait(
futureSyncWhenComplete,
expected +
const <String>[
r'^#1 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#2 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#3 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
futureSyncWhenComplete,
expected +
const <String>[
r'^#1 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#2 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#3 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
futureSyncWhenComplete,
expected +
const <String>[
r'^#1 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#2 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#3 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
// futureThen
{
final expected = const <String>[
r'^#0 throwSync \(.*/utils.dart:18(:3)?\)$',
r'^#1 futureThen.<anonymous closure> ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
futureThen,
expected +
const <String>[
r'^#2 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#3 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#4 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
futureThen,
expected +
const <String>[
r'^#2 doTestAwaitThen ',
r'^<asynchronous suspension>$',
r'^#3 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#4 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
futureThen,
expected +
const <String>[
r'^#2 doTestAwaitCatchError ',
r'^<asynchronous suspension>$',
r'^#3 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#4 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
}
}

View file

@ -0,0 +1,86 @@
// Copyright (c) 2022, 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.
// @dart = 2.9
import 'dart:async';
import 'package:expect/expect.dart';
const String scriptName = 'zone_callback_stack_traces_test.dart';
Future<void> foo() async {}
Future<void> bar() async {
await foo();
}
Future<void> runTest() {
final Zone testZone = Zone.current.fork(
specification: ZoneSpecification(
registerUnaryCallback: _registerUnaryCallback,
registerBinaryCallback: _registerBinaryCallback));
return testZone.run(bar);
}
StackTrace registerUnaryCallbackStackTrace;
StackTrace registerBinaryCallbackStackTrace;
ZoneUnaryCallback<R, T> _registerUnaryCallback<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T) f) {
final stackTrace = StackTrace.current;
print('registerUnaryCallback got stack trace:');
print(stackTrace);
if (stackTrace.toString().contains('bar')) {
Expect.isNull(registerUnaryCallbackStackTrace);
registerUnaryCallbackStackTrace = stackTrace;
}
return parent.registerUnaryCallback(zone, f);
}
ZoneBinaryCallback<R, T1, T2> _registerBinaryCallback<R, T1, T2>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T1, T2) f) {
final stackTrace = StackTrace.current;
print('registerBinaryCallback got stack trace:');
print(stackTrace);
if (stackTrace.toString().contains('bar')) {
Expect.isNull(registerBinaryCallbackStackTrace);
registerBinaryCallbackStackTrace = stackTrace;
}
return parent.registerBinaryCallback(zone, f);
}
void verifyStackTrace(List<String> expected, StackTrace stackTrace) {
final List<String> actual = stackTrace
.toString()
.split('\n')
.where((entry) => entry.contains(scriptName))
.toList();
print('Expected:\n${expected.join('\n')}');
print('Actual:\n${actual.join('\n')}');
Expect.equals(expected.length, actual.length);
for (int i = 0; i < expected.length; ++i) {
if (!RegExp(expected[i]).hasMatch(actual[i])) {
Expect.fail("Stack trace entry $i doesn't match:\n"
" expected: ${expected[i]}\n actual: ${actual[i]}");
}
}
}
main() async {
await runTest();
verifyStackTrace([
r'^#\d+ _registerUnaryCallback \(.*zone_callback_stack_traces_test.dart:32(:33)?\)$',
r'^#\d+ bar \(.*zone_callback_stack_traces_test.dart:16(:3)?\)$',
r'^#\d+ runTest \(.*zone_callback_stack_traces_test.dart:24(:19)?\)$',
r'^#\d+ main \(.*zone_callback_stack_traces_test.dart:72(:9)?\)$',
], registerUnaryCallbackStackTrace);
verifyStackTrace([
r'^#\d+ _registerBinaryCallback \(.*zone_callback_stack_traces_test.dart:44(:33)?\)$',
r'^#\d+ bar \(.*zone_callback_stack_traces_test.dart:16(:3)?\)$',
r'^#\d+ runTest \(.*zone_callback_stack_traces_test.dart:24(:19)?\)$',
r'^#\d+ main \(.*zone_callback_stack_traces_test.dart:72(:9)?\)$',
], registerBinaryCallbackStackTrace);
}

View file

@ -1,16 +1,10 @@
// Copyright (c) 2023, 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.
//
// Note: we pass --save-debugging-info=* without --dwarf-stack-traces to
// make this test pass on vm-aot-dwarf-* builders.
//
// VMOptions=--save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// VMOptions=--dwarf-stack-traces --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// @dart=2.9
// @dart = 2.9
import 'awaiter_stacks/harness.dart' as harness;
import 'causal_stacks/utils.dart' show assertStack, IGNORE_REMAINING_STACK;
class A {
void takesA(A a) {
@ -26,9 +20,7 @@ class B extends A {
StackTrace trace = null;
void main() async {
harness.configure(currentExpectations);
void main() {
A a = new A();
A b = new B();
try {
@ -36,17 +28,9 @@ void main() async {
} catch (e, st) {
trace = st;
}
await harness.checkExpectedStack(trace);
harness.updateExpectations();
assertStack(const <String>[
r'^#0 B.takesA \(.*/checked_parameter_assert_assignable_stacktrace_test.dart:16(:27)?\)$',
r'^#1 main \(.*/checked_parameter_assert_assignable_stacktrace_test.dart:27(:7)?\)$',
IGNORE_REMAINING_STACK,
], trace);
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 B.takesA (%test%)
#1 main (%test%)
#2 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#3 _RawReceivePort._handleMessage (isolate_patch.dart)"""
];
// CURRENT EXPECTATIONS END

View file

@ -4,31 +4,57 @@
// @dart = 2.9
import 'awaiter_stacks/harness.dart' as harness;
import 'causal_stacks/utils.dart';
main() async {
harness.configure(currentExpectations);
StackTrace trace = StackTrace.empty;
A.visible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 new A.visible',
r'^#2 main',
IGNORE_REMAINING_STACK,
], trace);
A.invisible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 main',
IGNORE_REMAINING_STACK,
], trace);
visible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 visible',
r'^#2 main',
IGNORE_REMAINING_STACK,
], trace);
invisible(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 main',
IGNORE_REMAINING_STACK,
], trace);
visibleClosure(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 visibleClosure.visibleInner',
r'^#2 visibleClosure',
r'^#3 main',
IGNORE_REMAINING_STACK,
], trace);
invisibleClosure(() => trace = StackTrace.current);
await harness.checkExpectedStack(trace);
harness.updateExpectations();
await assertStack([
r'^#0 main.<anonymous closure>',
r'^#1 invisibleClosure',
r'^#2 main',
IGNORE_REMAINING_STACK,
], trace);
}
class A {
@ -63,38 +89,3 @@ void invisibleClosure(void Function() fun) {
invisibleInner();
}
// CURRENT EXPECTATIONS BEGIN
final currentExpectations = [
"""
#0 main.<anonymous closure> (%test%)
#1 new A.visible (%test%)
#2 main (%test%)
#3 _delayEntrypointInvocation.<anonymous closure> (isolate_patch.dart)
#4 _RawReceivePort._handleMessage (isolate_patch.dart)""",
"""
#0 main.<anonymous closure> (%test%)
#1 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 visible (%test%)
#2 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 visibleClosure.visibleInner (%test%)
#2 visibleClosure (%test%)
#3 main (%test%)
<asynchronous suspension>""",
"""
#0 main.<anonymous closure> (%test%)
#1 invisibleClosure (%test%)
#2 main (%test%)
<asynchronous suspension>"""
];
// CURRENT EXPECTATIONS END

View file

@ -1,49 +1,99 @@
// Copyright (c) 2021, 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.
//
// @dart = 2.9
// This test checks that --resolve-dwarf-paths outputs absolute and relative
// paths in DWARF information.
//
// VMOptions=--dwarf-stack-traces --resolve-dwarf-paths --save-debugging-info=$TEST_COMPILATION_DIR/debug.so
// @dart=2.9
// OtherResources=use_save_debugging_info_flag_program.dart
import "dart:async";
import "dart:io";
import 'package:expect/expect.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
import 'package:path/path.dart' as p;
import 'package:path/path.dart' as path;
import 'use_flag_test_helper.dart';
main(List<String> args) async {
if (!isAOTRuntime) {
return; // Running in JIT: AOT binaries not available.
}
if (Platform.isAndroid) {
return;
return; // SDK tree and dart_bootstrap not available on the test device.
}
final isDwarfStackTraces = StackTrace.current.toString().contains('*** ***');
if (!isDwarfStackTraces) {
return;
// These are the tools we need to be available to run on a given platform:
if (!await testExecutable(genSnapshot)) {
throw "Cannot run test as $genSnapshot not available";
}
if (!await testExecutable(dartPrecompiledRuntime)) {
throw "Cannot run test as $dartPrecompiledRuntime not available";
}
if (!File(platformDill).existsSync()) {
throw "Cannot run test as $platformDill does not exist";
}
final dwarfPath =
p.join(Platform.environment['TEST_COMPILATION_DIR'], 'debug.so');
final dwarf = Dwarf.fromFile(dwarfPath);
runTests(obfuscate: false);
runTests(obfuscate: true);
}
final stack = StackTrace.current.toString();
print(stack);
final offsets = collectPCOffsets(stack.split('\n'));
print(offsets);
checkDwarfInfo(dwarf, offsets);
void runTests({bool obfuscate}) async {
final pathSuffix = obfuscate ? 'obfuscated' : 'cleartext';
await withTempDir('dwarf-flag-test-$pathSuffix', (String tempDir) async {
final cwDir = path.dirname(Platform.script.toFilePath());
final script =
path.join(cwDir, 'use_save_debugging_info_flag_program.dart');
final scriptDill = path.join(tempDir, 'flag_program.dill');
// Compile script to Kernel IR.
await run(genKernel, <String>[
'--no-sound-null-safety',
'--aot',
'--platform=$platformDill',
'-o',
scriptDill,
script,
]);
final scriptDwarfSnapshot = path.join(tempDir, 'dwarf.so');
await run(genSnapshot, <String>[
if (obfuscate) ...[
'--obfuscate',
'--save-obfuscation-map=${path.join(tempDir, 'obfuscation.map')}',
],
'--no-sound-null-safety',
'--resolve-dwarf-paths',
'--dwarf-stack-traces-mode',
'--snapshot-kind=app-aot-elf',
'--elf=$scriptDwarfSnapshot',
scriptDill,
]);
// Run the resulting Dwarf-AOT compiled script.
final dwarfTrace = await runError(dartPrecompiledRuntime, <String>[
scriptDwarfSnapshot,
scriptDill,
]);
final tracePCOffsets = collectPCOffsets(dwarfTrace);
// Check that translating the DWARF stack trace (without internal frames)
// matches the symbolic stack trace.
final dwarf = Dwarf.fromFile(scriptDwarfSnapshot);
Expect.isNotNull(dwarf);
checkDwarfInfo(dwarf, tracePCOffsets);
});
}
void checkDwarfInfo(Dwarf dwarf, Iterable<PCOffset> offsets) {
print(offsets);
final filenames = <String>{};
for (final offset in offsets) {
final callInfo = offset.callInfoFrom(dwarf, includeInternalFrames: true);
final callInfo = offset.callInfoFrom(dwarf);
Expect.isNotNull(callInfo);
Expect.isNotEmpty(callInfo);
for (final e in callInfo) {
@ -51,7 +101,7 @@ void checkDwarfInfo(Dwarf dwarf, Iterable<PCOffset> offsets) {
final entry = e as DartCallInfo;
var filename = entry.filename;
if (!filename.startsWith('/')) {
filename = p.join(sdkDir, filename);
filename = path.join(sdkDir, filename);
}
if (filenames.add(filename)) {
Expect.isTrue(
@ -64,6 +114,4 @@ void checkDwarfInfo(Dwarf dwarf, Iterable<PCOffset> offsets) {
print('- ${filename}');
}
Expect.isNotEmpty(filenames);
Expect.isNotEmpty(filenames
.where((p) => p.endsWith('use_resolve_dwarf_paths_flag_test.dart')));
}

View file

@ -141,10 +141,14 @@ dart_2/snapshot_depfile_test: SkipByDesign # Test needs to run from source
[ $compiler == dartkp ]
dart/await_type_check_with_dynamic_loading_test: SkipByDesign # Uses dart:mirrors.
dart/causal_stacks/async_throws_stack_no_causal_non_symbolic_test: SkipByDesign # --no-lazy... does nothing on precompiler.
dart/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # --no-lazy... does nothing on precompiler.
dart/finalizer/finalizer_isolate_groups_run_gc_test: SkipByDesign # Isolate.spawnUri is not supported in AOT.
dart/redirection_type_shuffling_test: SkipByDesign # Uses dart:mirrors.
dart/scavenger_abort_test: SkipSlow
dart/v8_snapshot_profile_writer_test: Pass, Slow # Can be slow due to re-invoking the precompiler.
dart_2/causal_stacks/async_throws_stack_no_causal_non_symbolic_test: SkipByDesign # --no-lazy... does nothing on precompiler.
dart_2/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # --no-lazy... does nothing on precompiler.
dart_2/redirection_type_shuffling_test: SkipByDesign # Uses dart:mirrors.
dart_2/scavenger_abort_test: SkipSlow
dart_2/v8_snapshot_profile_writer_test: Pass, Slow # Can be slow due to re-invoking the precompiler.
@ -382,10 +386,10 @@ dart/isolates/dart_api_create_lightweight_isolate_test: SkipByDesign # https://d
dart_2/isolates/dart_api_create_lightweight_isolate_test: SkipByDesign # https://dartbug.com/40579 Dart C API symbols not available.
[ $compiler == dartkp && $simulator ]
dart/awaiter_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
dart/isolates/fast_object_copy2_test*: Skip # Uses ffi which is not available on simulated architectures
dart/isolates/fast_object_copy_test*: SkipSlow
dart_2/awaiter_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
dart_2/isolates/fast_object_copy_test*: SkipSlow
[ $compiler == dartkp && ($builder_tag == tsan || $simulator) ]
@ -443,11 +447,11 @@ dart/run_appended_aot_snapshot_test: SkipByDesign # Tests the precompiled runtim
dart_2/run_appended_aot_snapshot_test: SkipByDesign # Tests the precompiled runtime.
[ $builder_tag == dwarf || $builder_tag == obfuscated ]
dart/awaiter_stacks/async_throws_stack_lazy_test: SkipByDesign # Asserts exact stacktrace output.
dart/awaiter_stacks/async_throws_stack_no_causal_test: SkipByDesign # Asserts exact stacktrace output.
dart/awaiter_stacks/flutter_regress_100441_test: SkipByDesign # Asserts exact stacktrace output.
dart/awaiter_stacks/sync_async_start_pkg_test_test: SkipByDesign # Asserts exact stacktrace output.
dart/awaiter_stacks/zone_callback_stack_traces_test: SkipByDesign # Asserts exact stacktrace output.
dart/causal_stacks/async_throws_stack_lazy_test: SkipByDesign # Asserts exact stacktrace output.
dart/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # Asserts exact stacktrace output.
dart/causal_stacks/flutter_regress_100441_test: SkipByDesign # Asserts exact stacktrace output.
dart/causal_stacks/sync_async_start_pkg_test_test: SkipByDesign # Asserts exact stacktrace output.
dart/causal_stacks/zone_callback_stack_traces_test: SkipByDesign # Asserts exact stacktrace output.
dart/checked_parameter_assert_assignable_stacktrace_test: SkipByDesign # Asserts exact stacktrace output.
dart/error_messages_in_null_checks_test: SkipByDesign # Relies symbol names in stack traces
dart/extension_names_test: SkipByDesign # Relies symbol names in stack traces
@ -457,11 +461,11 @@ dart/invisible_function_pragma_test: SkipByDesign # Relies symbol names in stack
dart/optimized_stacktrace_line_and_column_test: SkipByDesign # Relies symbol names in stack traces
dart/optimized_stacktrace_line_test: SkipByDesign # Relies symbol names in stack traces
dart/stacktrace_mixin_application_test: SkipByDesign # Relies symbol names in stack traces
dart_2/awaiter_stacks/async_throws_stack_lazy_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/awaiter_stacks/async_throws_stack_no_causal_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/awaiter_stacks/flutter_regress_100441_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/awaiter_stacks/sync_async_start_pkg_test_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/awaiter_stacks/zone_callback_stack_traces_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/causal_stacks/async_throws_stack_lazy_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/causal_stacks/flutter_regress_100441_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/causal_stacks/sync_async_start_pkg_test_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/causal_stacks/zone_callback_stack_traces_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/checked_parameter_assert_assignable_stacktrace_test: SkipByDesign # Asserts exact stacktrace output.
dart_2/error_messages_in_null_checks_test: SkipByDesign # Relies on uris / symbol names
dart_2/extension_names_test: SkipByDesign # Relies on uris / symbol names