Version 2.14.0-52.0.dev Merge commit '1dedeffc4c68485763031487d9812f8517fe2846' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index 761daab..8af538d 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@ "constraint, update this by running tools/generate_package_config.dart." ], "configVersion": 2, - "generated": "2021-04-23T17:16:06.111697", + "generated": "2021-04-27T17:23:41.776651", "generator": "tools/generate_package_config.dart", "packages": [ {
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8365209..842903d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -38,14 +38,14 @@ #### Linter -Updated the Linter to `1.3.0`, which includes: +Updated the Linter to `1.4.0`, which includes: -- updated `non_constant_identifier_names` to check local variables, for-loop - initializers and catch clauses. -- updated error range of `lines_longer_than_80_chars` to start at 80 to make - splitting easier. -- new lint: `require_trailing_commas`. -- new lint: `prefer_null_aware_method_calls`. +- `directives_ordering` now checks ordering of `package:` imports in code + outside pub packages. +- simple reachability analysis added to `use_build_context_synchronously` to + short-circuit await-discovery in terminating blocks. +- `use_build_context_synchronously` updated to recognize nullable types when + accessed from legacy libraries. ### Language @@ -5738,4 +5738,4 @@ compression. * `dart:isolate`: `Isolate.spawnUri` added the optional `packageRoot` argument, - which controls how it resolves `package:` URIs. + which controls how it resolves `package:` URIs. \ No newline at end of file
diff --git a/DEPS b/DEPS index da99e64..553bbaf 100644 --- a/DEPS +++ b/DEPS
@@ -73,7 +73,7 @@ # Revisions of /third_party/* dependencies. "args_rev": "d8fea36c10ef96797be02e3d132d572445cd86f4", - "async_rev": "376c0fe95fa6fe7a5a6c41ef7b4585085c826f89", + "async_rev": "06774f59a7cf9780e08148298d438c1043e2b063", "bazel_worker_rev": "0885637b037979afbf5bcd05fd748b309fd669c0", "benchmark_harness_rev": "c546dbd9f639f75cd2f75de8df2eb9f8ea15e8e7", "boolean_selector_rev": "665e6921ab246569420376f827bff4585dff0b14", @@ -117,14 +117,14 @@ "http_multi_server_rev" : "7aca9e87d4a68374b685334f20359320054b8d7b", "http_parser_rev": "7720bfd42a0c096734c5213478fdce92c62f0293", "http_retry_rev": "845771af7bb5ab38ab740ce4a31f3b0c7680302b", - "http_rev": "69d6064dd92470ed7ccd50a808fc789ee7716fe8", + "http_rev": "14e4fece54591436c1e3083df350b8e142067ae7", "http_throttle_tag" : "1.0.2", "icu_rev" : "81d656878ec611cb0b42d52c82e9dae93920d9ba", "idl_parser_rev": "5fb1ebf49d235b5a70c9f49047e83b0654031eb7", "intl_tag": "0.17.0-nullsafety", "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1", "json_rpc_2_rev": "5ec32a2e0e99dedcef5b3237f93167cd22c2da50", - "linter_tag": "1.3.0", + "linter_tag": "1.4.0", "logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2", "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783", "markdown_rev": "9c4beaac96d8f008078e00b027915f81b665d2de",
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md index 4a9f873..ae7d875 100644 --- a/pkg/native_stack_traces/CHANGELOG.md +++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,5 +1,12 @@ # Changelog +## 0.4.2 + +- When decoding a stack trace, frames corresponding to the functions + with DW_AT_artificial DWARF attribute are now omitted from the symbolized + stack traces. This is needed because Dart VM no longer omits invisible + functions from binary stack traces in certain cases. + ## 0.4.1 - Exported some ELF utilities in lib/elf.dart for use in Dart tests.
diff --git a/pkg/native_stack_traces/lib/src/dwarf.dart b/pkg/native_stack_traces/lib/src/dwarf.dart index e8d7ef1..71605a0 100644 --- a/pkg/native_stack_traces/lib/src/dwarf.dart +++ b/pkg/native_stack_traces/lib/src/dwarf.dart
@@ -40,6 +40,7 @@ enum _AttributeName { abstractOrigin, + artificial, callColumn, callFile, callLine, @@ -66,6 +67,7 @@ 0x20: _AttributeName.inline, 0x25: _AttributeName.producer, 0x31: _AttributeName.abstractOrigin, + 0x34: _AttributeName.artificial, 0x39: _AttributeName.declarationColumn, 0x3a: _AttributeName.declarationFile, 0x3b: _AttributeName.declarationLine, @@ -84,6 +86,7 @@ _AttributeName.inline: "DW_AT_inline", _AttributeName.producer: "DW_AT_producer", _AttributeName.abstractOrigin: "DW_AT_abstract_origin", + _AttributeName.artificial: "DW_AT_artificial", _AttributeName.declarationColumn: "DW_AT_decl_column", _AttributeName.declarationFile: "DW_AT_decl_file", _AttributeName.declarationLine: "DW_AT_decl_line", @@ -95,6 +98,7 @@ enum _AttributeForm { address, constant, + flag, reference4, sectionOffset, string, @@ -103,6 +107,7 @@ const _attributeForms = <int, _AttributeForm>{ 0x01: _AttributeForm.address, 0x08: _AttributeForm.string, + 0x0c: _AttributeForm.flag, 0x0f: _AttributeForm.constant, 0x13: _AttributeForm.reference4, 0x17: _AttributeForm.sectionOffset, @@ -111,6 +116,7 @@ const _attributeFormStrings = <_AttributeForm, String>{ _AttributeForm.address: "DW_FORM_addr", _AttributeForm.string: "DW_FORM_string", + _AttributeForm.flag: "DW_FORM_flag", _AttributeForm.constant: "DW_FORM_udata", _AttributeForm.reference4: "DW_FORM_ref4", _AttributeForm.sectionOffset: "DW_FORM_sec_offset", @@ -139,6 +145,8 @@ switch (form) { case _AttributeForm.string: return reader.readNullTerminatedString(); + case _AttributeForm.flag: + return reader.readByte() != 0; case _AttributeForm.address: return reader.readBytes(header.addressSize); case _AttributeForm.sectionOffset: @@ -154,6 +162,8 @@ switch (form) { case _AttributeForm.string: return value as String; + case _AttributeForm.flag: + return value.toString(); case _AttributeForm.address: return '0x' + paddedHex(value as int, unit?.header.addressSize ?? 0); case _AttributeForm.sectionOffset: @@ -310,6 +320,8 @@ int? get highPC => this[_AttributeName.highProgramCounter] as int?; + bool get isArtificial => (this[_AttributeName.artificial] ?? false) as bool; + bool containsPC(int virtualAddress) => (lowPC ?? 0) <= virtualAddress && virtualAddress < (highPC ?? -1); @@ -339,6 +351,7 @@ ..add(DartCallInfo( function: unit.nameOfOrigin(abstractOrigin ?? -1), inlined: inlined, + internal: isArtificial, filename: callFilename(child.callFileIndex ?? -1), line: child.callLine ?? 0, column: child.callColumn ?? 0)); @@ -353,6 +366,7 @@ DartCallInfo( function: unit.nameOfOrigin(abstractOrigin ?? -1), inlined: inlined, + internal: isArtificial, filename: filename, line: line, column: column) @@ -1057,6 +1071,7 @@ /// Represents the information for a call site located in Dart source code. class DartCallInfo extends CallInfo { final bool inlined; + final bool internal; final String function; final String filename; final int line; @@ -1064,28 +1079,32 @@ DartCallInfo( {this.inlined = false, + this.internal = false, required this.function, required this.filename, required this.line, required this.column}); @override - bool get isInternal => false; + bool get isInternal => internal; @override - int get hashCode => _hashFinish(_hashCombine( - _hashCombine( - _hashCombine( - _hashCombine( - _hashCombine(0, inlined.hashCode), function.hashCode), - filename.hashCode), - line.hashCode), - column.hashCode)); + int get hashCode { + int hash = 0; + hash = _hashCombine(hash, inlined.hashCode); + hash = _hashCombine(hash, internal.hashCode); + hash = _hashCombine(hash, function.hashCode); + hash = _hashCombine(hash, filename.hashCode); + hash = _hashCombine(hash, line.hashCode); + hash = _hashCombine(hash, column.hashCode); + return _hashFinish(hash); + } @override bool operator ==(Object other) { if (other is DartCallInfo) { return inlined == other.inlined && + internal == other.internal && function == other.function && filename == other.filename && line == other.line &&
diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml index f93e01b..d398708 100644 --- a/pkg/native_stack_traces/pubspec.yaml +++ b/pkg/native_stack_traces/pubspec.yaml
@@ -1,6 +1,6 @@ name: native_stack_traces description: Utilities for working with non-symbolic stack traces. -version: 0.4.1 +version: 0.4.2 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc index eaee574..22a4b28 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -1809,6 +1809,7 @@ cls = it.GetNextClass(); functions = cls.current_functions(); for (intptr_t j = 0; j < functions.Length(); j++) { + SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock()); function ^= functions.At(j); bool retain = possibly_retained_functions_.ContainsKey(function); if (!retain && function.HasImplicitClosureFunction()) { @@ -2767,13 +2768,8 @@ function_ = code.function(); if (functions_to_retain_.ContainsKey(function_)) { // Retain Code objects corresponding to: - // * invisible functions (to filter them from stack traces); // * async/async* closures (to construct async stacks). // * native functions (to find native implementation). - if (!function_.is_visible()) { - ++codes_with_invisible_function_; - return; - } if (function_.is_native()) { ++codes_with_native_function_; return; @@ -2833,8 +2829,6 @@ codes_with_exception_handlers_); THR_Print(" %8" Pd " Codes with pc descriptors\n", codes_with_pc_descriptors_); - THR_Print(" %8" Pd " Codes with invisible functions\n", - codes_with_invisible_function_); THR_Print(" %8" Pd " Codes with native functions\n", codes_with_native_function_); THR_Print(" %8" Pd " Codes with async closure functions\n", @@ -2869,7 +2863,6 @@ intptr_t non_function_codes_ = 0; intptr_t codes_with_exception_handlers_ = 0; intptr_t codes_with_pc_descriptors_ = 0; - intptr_t codes_with_invisible_function_ = 0; intptr_t codes_with_native_function_ = 0; intptr_t codes_with_async_closure_function_ = 0; intptr_t codes_with_dynamically_called_function_ = 0;
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc index 59d7893..8fe8ea3 100644 --- a/runtime/vm/dwarf.cc +++ b/runtime/vm/dwarf.cc
@@ -259,6 +259,8 @@ stream->uleb128(DW_FORM_addr); stream->uleb128(DW_AT_high_pc); stream->uleb128(DW_FORM_addr); + stream->uleb128(DW_AT_artificial); + stream->uleb128(DW_FORM_flag); stream->uleb128(0); stream->uleb128(0); // End of attributes. @@ -388,6 +390,8 @@ stream->OffsetFromSymbol(asm_name, 0); // DW_AT_high_pc stream->OffsetFromSymbol(asm_name, code.Size()); + // DW_AT_artificial + stream->u1(function.is_visible() ? 0 : 1); InliningNode* node = ExpandInliningTree(code); if (node != NULL) {
diff --git a/runtime/vm/dwarf.h b/runtime/vm/dwarf.h index 66851c4..ec214b6 100644 --- a/runtime/vm/dwarf.h +++ b/runtime/vm/dwarf.h
@@ -269,6 +269,7 @@ static const intptr_t DW_AT_inline = 0x20; static const intptr_t DW_AT_producer = 0x25; static const intptr_t DW_AT_abstract_origin = 0x31; + static const intptr_t DW_AT_artificial = 0x34; static const intptr_t DW_AT_decl_column = 0x39; static const intptr_t DW_AT_decl_file = 0x3a; static const intptr_t DW_AT_decl_line = 0x3b; @@ -278,6 +279,7 @@ static const intptr_t DW_FORM_addr = 0x01; static const intptr_t DW_FORM_string = 0x08; + static const intptr_t DW_FORM_flag = 0x0c; static const intptr_t DW_FORM_udata = 0x0f; static const intptr_t DW_FORM_ref4 = 0x13; static const intptr_t DW_FORM_ref_udata = 0x15;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 1d9e3ce..3c3c21a 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc
@@ -7282,22 +7282,26 @@ } ASSERT(is_native()); ASSERT(obj.IsArray()); - const Object& res = Object::Handle(Array::Cast(obj).At(1)); + const Object& res = Object::Handle(Array::Cast(obj).AtAcquire(1)); return res.IsNull() ? Function::null() : Function::Cast(res).ptr(); } void Function::set_implicit_closure_function(const Function& value) const { + DEBUG_ASSERT( + IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter()); ASSERT(!IsClosureFunction()); const Object& old_data = Object::Handle(data()); if (is_native()) { ASSERT(old_data.IsArray()); - ASSERT((Array::Cast(old_data).At(1) == Object::null()) || value.IsNull()); - Array::Cast(old_data).SetAt(1, value); + ASSERT((Array::Cast(old_data).AtAcquire(1) == Object::null()) || + value.IsNull()); + Array::Cast(old_data).SetAtRelease(1, value); } else { // Maybe this function will turn into a native later on :-/ if (old_data.IsArray()) { - ASSERT((Array::Cast(old_data).At(1) == Object::null()) || value.IsNull()); - Array::Cast(old_data).SetAt(1, value); + ASSERT((Array::Cast(old_data).AtAcquire(1) == Object::null()) || + value.IsNull()); + Array::Cast(old_data).SetAtRelease(1, value); } else { ASSERT(old_data.IsNull() || value.IsNull()); set_data(value); @@ -7424,7 +7428,7 @@ // dyn inv forwarder: Array[0] = Function target // Array[1] = TypeArguments default type args void Function::set_data(const Object& value) const { - untag()->set_data(value.ptr()); + untag()->set_data<std::memory_order_release>(value.ptr()); } void Function::set_name(const String& value) const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h index c39a387..078af85 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h
@@ -3763,7 +3763,7 @@ void set_num_optional_parameters(intptr_t value) const; // Encoded value. void set_kind_tag(uint32_t value) const; - ObjectPtr data() const { return untag()->data(); } + ObjectPtr data() const { return untag()->data<std::memory_order_acquire>(); } void set_data(const Object& value) const; static FunctionPtr New(Heap::Space space = Heap::kOld);
diff --git a/tests/standalone/dwarf_stack_trace_invisible_functions_test.dart b/tests/standalone/dwarf_stack_trace_invisible_functions_test.dart new file mode 100644 index 0000000..b7247454 --- /dev/null +++ b/tests/standalone/dwarf_stack_trace_invisible_functions_test.dart
@@ -0,0 +1,122 @@ +// 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. + +/// VMOptions=--dwarf-stack-traces --save-debugging-info=dwarf_invisible_functions.so + +import 'dart:io'; + +import 'package:native_stack_traces/native_stack_traces.dart'; +import 'package:path/path.dart' as path; + +import 'dwarf_stack_trace_test.dart' as dwarf_stack_trace_test; + +const int LINE_A = 23; +const int LINE_B = 29; +const int LINE_C = 36; +const int LINE_D = 44; +const int LINE_E = 56; + +@pragma("vm:prefer-inline") +bar() { + // Keep the 'throw' and its argument on separate lines. + throw // force linebreak with dartfmt // LINE_A + "Hello, Dwarf!"; +} + +@pragma("vm:never-inline") +foo() { + bar(); // LINE_B +} + +@pragma("vm:never-inline") +bazz(void Function() func) { + // Call through tear-off (implicit closure function) which should be + // omitted from the stack trace. + func(); // LINE_C +} + +class A<T> { + A(); + + @pragma("vm:never-inline") + void add(T x) { + bazz(foo); // LINE_D + } +} + +dynamic aa = int.parse('1') == 1 ? A() : []; + +Future<void> main() async { + String rawStack = ""; + try { + // Dynamic call to a generic-covariant method goes through the + // dynamic invocation forwarder function, which should be + // omitted from the stack trace. + aa.add(42); // LINE_E + } catch (e, st) { + rawStack = st.toString(); + } + + if (path.basenameWithoutExtension(Platform.executable) != + "dart_precompiled_runtime") { + return; // Not running from an AOT compiled snapshot. + } + + if (Platform.isAndroid) { + return; // Generated dwarf.so not available on the test device. + } + + final dwarf = Dwarf.fromFile("dwarf_invisible_functions.so")!; + + await dwarf_stack_trace_test.checkStackTrace( + rawStack, dwarf, expectedCallsInfo); +} + +final expectedCallsInfo = <List<DartCallInfo>>[ + // Frame 1: the throw in bar, which was inlined + // into foo (so we'll get information for two calls for that PC address). + [ + DartCallInfo( + function: "bar", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_A, + column: 3, + inlined: true), + DartCallInfo( + function: "foo", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_B, + column: 3, + inlined: false) + ], + // Frame 2: call to foo in bazz. + [ + DartCallInfo( + function: "bazz", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_C, + column: 3, + inlined: false) + ], + // Frame 3: call to bazz in A.method. + [ + DartCallInfo( + function: "A.add", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_D, + column: 5, + inlined: false) + ], + // Frame 4: the call to foo in main. + [ + DartCallInfo( + function: "main", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_E, + column: 8, + inlined: false) + ], + // Don't assume anything about any of the frames below the main, + // as this makes the test too brittle. +];
diff --git a/tests/standalone/dwarf_stack_trace_test.dart b/tests/standalone/dwarf_stack_trace_test.dart index 236a21e..a583d34 100644 --- a/tests/standalone/dwarf_stack_trace_test.dart +++ b/tests/standalone/dwarf_stack_trace_test.dart
@@ -86,11 +86,19 @@ Expect.isNotNull(externalCallInfo); final allCallInfo = dwarf.callInfoFor(addr, includeInternalFrames: true); Expect.isNotNull(allCallInfo); - for (final call in allCallInfo!) { + for (final call in externalCallInfo!) { Expect.isTrue(call is DartCallInfo, "got non-Dart call info ${call}"); + Expect.isFalse(call.isInternal); + Expect.isTrue(allCallInfo!.contains(call), + "External call info ${call} is not among all calls"); } - Expect.deepEquals(externalCallInfo, allCallInfo); - gotCallsInfo.add(allCallInfo.cast<DartCallInfo>().toList()); + for (final call in allCallInfo!) { + if (!call.isInternal) { + Expect.isTrue(externalCallInfo.contains(call), + "External call info ${call} is not among external calls"); + } + } + gotCallsInfo.add(externalCallInfo.cast<DartCallInfo>().toList()); } print(""); @@ -101,10 +109,13 @@ gotCallsInfo[i].forEach((frame) => print(" ${frame}")); } + // Remove empty entries which correspond to skipped internal frames. + gotCallsInfo.removeWhere((calls) => calls.isEmpty); + checkFrames(gotCallsInfo, expectedCallsInfo); final gotSymbolizedLines = await Stream.fromIterable(rawLines) - .transform(DwarfStackTraceDecoder(dwarf, includeInternalFrames: true)) + .transform(DwarfStackTraceDecoder(dwarf, includeInternalFrames: false)) .toList(); final gotSymbolizedCalls =
diff --git a/tests/standalone_2/dwarf_stack_trace_invisible_functions_test.dart b/tests/standalone_2/dwarf_stack_trace_invisible_functions_test.dart new file mode 100644 index 0000000..a54f3eb --- /dev/null +++ b/tests/standalone_2/dwarf_stack_trace_invisible_functions_test.dart
@@ -0,0 +1,122 @@ +// 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. + +/// VMOptions=--dwarf-stack-traces --save-debugging-info=dwarf_invisible_functions.so + +import 'dart:io'; + +import 'package:native_stack_traces/native_stack_traces.dart'; +import 'package:path/path.dart' as path; + +import 'dwarf_stack_trace_test.dart' as dwarf_stack_trace_test; + +const int LINE_A = 23; +const int LINE_B = 29; +const int LINE_C = 36; +const int LINE_D = 44; +const int LINE_E = 56; + +@pragma("vm:prefer-inline") +bar() { + // Keep the 'throw' and its argument on separate lines. + throw // force linebreak with dartfmt // LINE_A + "Hello, Dwarf!"; +} + +@pragma("vm:never-inline") +foo() { + bar(); // LINE_B +} + +@pragma("vm:never-inline") +bazz(void Function() func) { + // Call through tear-off (implicit closure function) which should be + // omitted from the stack trace. + func(); // LINE_C +} + +class A<T> { + A(); + + @pragma("vm:never-inline") + void add(T x) { + bazz(foo); // LINE_D + } +} + +dynamic aa = int.parse('1') == 1 ? A() : []; + +Future<void> main() async { + String rawStack = ""; + try { + // Dynamic call to a generic-covariant method goes through the + // dynamic invocation forwarder function, which should be + // omitted from the stack trace. + aa.add(42); // LINE_E + } catch (e, st) { + rawStack = st.toString(); + } + + if (path.basenameWithoutExtension(Platform.executable) != + "dart_precompiled_runtime") { + return; // Not running from an AOT compiled snapshot. + } + + if (Platform.isAndroid) { + return; // Generated dwarf.so not available on the test device. + } + + final dwarf = Dwarf.fromFile("dwarf_invisible_functions.so"); + + await dwarf_stack_trace_test.checkStackTrace( + rawStack, dwarf, expectedCallsInfo); +} + +final expectedCallsInfo = <List<DartCallInfo>>[ + // Frame 1: the throw in bar, which was inlined + // into foo (so we'll get information for two calls for that PC address). + [ + DartCallInfo( + function: "bar", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_A, + column: 3, + inlined: true), + DartCallInfo( + function: "foo", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_B, + column: 3, + inlined: false) + ], + // Frame 2: call to foo in bazz. + [ + DartCallInfo( + function: "bazz", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_C, + column: 3, + inlined: false) + ], + // Frame 3: call to bazz in A.method. + [ + DartCallInfo( + function: "A.add", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_D, + column: 5, + inlined: false) + ], + // Frame 4: the call to foo in main. + [ + DartCallInfo( + function: "main", + filename: "dwarf_stack_trace_invisible_functions_test.dart", + line: LINE_E, + column: 8, + inlined: false) + ], + // Don't assume anything about any of the frames below the main, + // as this makes the test too brittle. +];
diff --git a/tests/standalone_2/dwarf_stack_trace_test.dart b/tests/standalone_2/dwarf_stack_trace_test.dart index 6624b62..75353ee 100644 --- a/tests/standalone_2/dwarf_stack_trace_test.dart +++ b/tests/standalone_2/dwarf_stack_trace_test.dart
@@ -88,11 +88,19 @@ Expect.isNotNull(externalCallInfo); final allCallInfo = dwarf.callInfoFor(addr, includeInternalFrames: true); Expect.isNotNull(allCallInfo); - for (final call in allCallInfo) { + for (final call in externalCallInfo) { Expect.isTrue(call is DartCallInfo, "got non-Dart call info ${call}"); + Expect.isFalse(call.isInternal); + Expect.isTrue(allCallInfo.contains(call), + "External call info ${call} is not among all calls"); } - Expect.deepEquals(externalCallInfo, allCallInfo); - gotCallsInfo.add(allCallInfo.cast<DartCallInfo>().toList()); + for (final call in allCallInfo) { + if (!call.isInternal) { + Expect.isTrue(externalCallInfo.contains(call), + "External call info ${call} is not among external calls"); + } + } + gotCallsInfo.add(externalCallInfo.cast<DartCallInfo>().toList()); } print(""); @@ -103,10 +111,13 @@ gotCallsInfo[i].forEach((frame) => print(" ${frame}")); } + // Remove empty entries which correspond to skipped internal frames. + gotCallsInfo.removeWhere((calls) => calls.isEmpty); + checkFrames(gotCallsInfo, expectedCallsInfo); final gotSymbolizedLines = await Stream.fromIterable(rawLines) - .transform(DwarfStackTraceDecoder(dwarf, includeInternalFrames: true)) + .transform(DwarfStackTraceDecoder(dwarf, includeInternalFrames: false)) .toList(); final gotSymbolizedCalls =
diff --git a/tools/VERSION b/tools/VERSION index e11b2c9..f14e617 100644 --- a/tools/VERSION +++ b/tools/VERSION
@@ -27,5 +27,5 @@ MAJOR 2 MINOR 14 PATCH 0 -PRERELEASE 51 +PRERELEASE 52 PRERELEASE_PATCH 0 \ No newline at end of file