Skip to content

Commit

Permalink
Async gap fixes (flutter#96)
Browse files Browse the repository at this point in the history
* Fix async gap handling. (backpublish)

Fix an issue where an async gap at the end of a stack trace would not
get parsed correctly due to the trailing newline being `trim()`'d.

Add tests to cover this case.

* Fixes async gap handling in Trace.parse and Chain.parse (backpublish)

Co-authored-by: Clement Skau <[email protected]>
  • Loading branch information
natebosch and Clement Skau authored Nov 5, 2020
1 parent 56811db commit 96b83bd
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 6 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 1.9.6 (backpublish)

* Fix bug parsing asynchronous suspension gap markers at the end of stack
traces. (Also fixed separately in 1.10.0-nullsafety.3)
* Fix bug parsing asynchronous suspension gap markers at the end of stack
traces, when parsing with `Trace.parse` and `Chain.parse`. (Also fixed
separately in 1.10.0-nullsafety.6)

## 1.9.5

* Parse the format for `data:` URIs that the Dart VM has used since `2.2.0`.
Expand Down
6 changes: 4 additions & 2 deletions lib/src/chain.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,10 @@ class Chain implements StackTrace {
factory Chain.parse(String chain) {
if (chain.isEmpty) return Chain([]);
if (chain.contains(vmChainGap)) {
return Chain(
chain.split(vmChainGap).map((trace) => Trace.parseVM(trace)));
return Chain(chain
.split(vmChainGap)
.where((line) => line.isNotEmpty)
.map((trace) => Trace.parseVM(trace)));
}
if (!chain.contains(chainGap)) return Chain([Trace.parse(chain)]);

Expand Down
11 changes: 10 additions & 1 deletion lib/src/trace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,16 @@ class Trace implements StackTrace {
static List<Frame> _parseVM(String trace) {
// Ignore [vmChainGap]. This matches the behavior of
// `Chain.parse().toTrace()`.
var lines = trace.trim().replaceAll(vmChainGap, '').split('\n');
var lines = trace
.trim()
.replaceAll(vmChainGap, '')
.split('\n')
.where((line) => line.isNotEmpty);

if (lines.isEmpty) {
return [];
}

var frames = lines
.take(lines.length - 1)
.map((line) => Frame.parseVM(line))
Expand Down
2 changes: 1 addition & 1 deletion lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const chainGap = '===== asynchronous gap ===========================\n';

/// The line used in the string representation of VM stack chains to represent
/// the gap between traces.
const vmChainGap = '<asynchronous suspension>\n';
final vmChainGap = RegExp(r'^<asynchronous suspension>\n?$', multiLine: true);

// TODO(nweiz): When cross-platform imports work, use them to set this.
/// Whether we're running in a JS context.
Expand Down
3 changes: 1 addition & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
name: stack_trace
version: 1.9.5

version: 1.9.6
description: A package for manipulating stack traces and printing them readably.
homepage: https://github.com/dart-lang/stack_trace

Expand Down
15 changes: 15 additions & 0 deletions test/chain/chain_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ void main() {
expect(chain.traces[1].frames, isEmpty);
expect(chain.traces[2].frames, isEmpty);
});

test('parses a chain with VM gaps', () {
final chain =
Chain.parse('#1 MyClass.run (package:my_lib.dart:134:5)\n'
'<asynchronous suspension>\n'
'#2 main (file:///my_app.dart:9:3)\n'
'<asynchronous suspension>\n');
expect(chain.traces, hasLength(2));
expect(chain.traces[0].frames, hasLength(1));
expect(chain.traces[0].frames[0].toString(),
equals('package:my_lib.dart 134:5 in MyClass.run'));
expect(chain.traces[1].frames, hasLength(1));
expect(chain.traces[1].frames[0].toString(),
equals('/my_app.dart 9:3 in main'));
});
});

group('Chain.capture()', () {
Expand Down
41 changes: 41 additions & 0 deletions test/trace_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,25 @@ void main() {
equals(Uri.parse('https://dart.dev/foo/quux.dart')));
});

test('parses a package:stack_trace stack chain with end gap correctly', () {
var trace =
Trace.parse('https://dart.dev/foo/bar.dart 10:11 Foo.<fn>.bar\n'
'https://dart.dev/foo/baz.dart Foo.<fn>.bar\n'
'https://dart.dev/foo/bang.dart 10:11 Foo.<fn>.bar\n'
'https://dart.dev/foo/quux.dart Foo.<fn>.bar'
'===== asynchronous gap ===========================\n');

expect(trace.frames.length, 4);
expect(trace.frames[0].uri,
equals(Uri.parse('https://dart.dev/foo/bar.dart')));
expect(trace.frames[1].uri,
equals(Uri.parse('https://dart.dev/foo/baz.dart')));
expect(trace.frames[2].uri,
equals(Uri.parse('https://dart.dev/foo/bang.dart')));
expect(trace.frames[3].uri,
equals(Uri.parse('https://dart.dev/foo/quux.dart')));
});

test('parses a real package:stack_trace stack trace correctly', () {
var traceString = Trace.current().toString();
expect(Trace.parse(traceString).toString(), equals(traceString));
Expand All @@ -259,6 +278,28 @@ void main() {
expect(trace.frames, isEmpty);
expect(trace.toString(), equals(''));
});

test('parses trace with async gap correctly', () {
var trace = Trace.parse('#0 bop (file:///pull.dart:42:23)\n'
'<asynchronous suspension>\n'
'#1 twist (dart:the/future.dart:0:2)\n'
'#2 main (dart:my/file.dart:4:6)\n');

expect(trace.frames.length, 3);
expect(trace.frames[0].uri, equals(Uri.parse('file:///pull.dart')));
expect(trace.frames[1].uri, equals(Uri.parse('dart:the/future.dart')));
expect(trace.frames[2].uri, equals(Uri.parse('dart:my/file.dart')));
});

test('parses trace with async gap at end correctly', () {
var trace = Trace.parse('#0 bop (file:///pull.dart:42:23)\n'
'#1 twist (dart:the/future.dart:0:2)\n'
'<asynchronous suspension>\n');

expect(trace.frames.length, 2);
expect(trace.frames[0].uri, equals(Uri.parse('file:///pull.dart')));
expect(trace.frames[1].uri, equals(Uri.parse('dart:the/future.dart')));
});
});

test('.toString() nicely formats the stack trace', () {
Expand Down

0 comments on commit 96b83bd

Please sign in to comment.