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