From a75e9cd3eeeb7c98656a17b007dfa20cfc9ec284 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Wed, 13 Jul 2022 16:15:45 +0000 Subject: [PATCH] Revert "[pkg/native_stack_traces] Support Mach-O dSYM debugging information." This reverts commit 08c13f173c3715fd768d768566eeccf1f9f14c3b. Reason for revert: Causing failures on simarm, simarm64, etc Original change's description: > [pkg/native_stack_traces] Support Mach-O dSYM debugging information. > > TEST=vm/dart{,_2}/use_dwarf_stack_traces_flag > > Bug: https://github.com/dart-lang/sdk/issues/43612 > Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-mac-product-x64-try,pkg-mac-release-arm64-try,vm-kernel-mac-release-arm64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try > Change-Id: Icda21bb14dcc0cf4784cea118e6ba7dd4edd35aa > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250381 > Commit-Queue: Tess Strickland > Reviewed-by: Slava Egorov # Not skipping CQ checks because original CL landed > 1 day ago. Bug: https://github.com/dart-lang/sdk/issues/43612 Change-Id: I020c29f7329e9b53a8fe0f4f4a4de4070fca0ec3 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-mac-product-x64-try,pkg-mac-release-arm64-try,vm-kernel-mac-release-arm64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251242 Reviewed-by: Ben Konyi Reviewed-by: Alexander Markov Commit-Queue: Ben Konyi --- pkg/native_stack_traces/CHANGELOG.md | 6 - pkg/native_stack_traces/bin/decode.dart | 63 +- pkg/native_stack_traces/lib/src/convert.dart | 9 +- pkg/native_stack_traces/lib/src/dwarf.dart | 135 +++-- .../lib/src/dwarf_container.dart | 30 - pkg/native_stack_traces/lib/src/elf.dart | 124 +--- pkg/native_stack_traces/lib/src/macho.dart | 548 ------------------ pkg/native_stack_traces/lib/src/reader.dart | 36 +- runtime/lib/object.cc | 7 - .../use_dwarf_stack_traces_flag_program.dart | 3 - .../use_dwarf_stack_traces_flag_test.dart | 279 +++------ .../tests/vm/dart/use_flag_test_helper.dart | 19 +- .../use_resolve_dwarf_paths_flag_test.dart | 5 +- .../use_save_debugging_info_flag_program.dart | 19 - .../use_save_debugging_info_flag_test.dart | 6 +- .../tests/vm/dart/use_strip_flag_test.dart | 7 +- .../dart/use_trace_precompiler_flag_test.dart | 7 +- .../use_dwarf_stack_traces_flag_program.dart | 3 - .../use_dwarf_stack_traces_flag_test.dart | 278 +++------ .../tests/vm/dart_2/use_flag_test_helper.dart | 19 +- .../use_resolve_dwarf_paths_flag_test.dart | 5 +- .../use_save_debugging_info_flag_program.dart | 21 - .../use_save_debugging_info_flag_test.dart | 6 +- .../tests/vm/dart_2/use_strip_flag_test.dart | 7 +- .../use_trace_precompiler_flag_test.dart | 7 +- runtime/vm/bootstrap_natives.h | 1 - sdk/lib/_internal/vm/lib/internal_patch.dart | 4 - 27 files changed, 299 insertions(+), 1355 deletions(-) delete mode 100644 pkg/native_stack_traces/lib/src/dwarf_container.dart delete mode 100644 pkg/native_stack_traces/lib/src/macho.dart delete mode 100644 runtime/tests/vm/dart/use_save_debugging_info_flag_program.dart delete mode 100644 runtime/tests/vm/dart_2/use_save_debugging_info_flag_program.dart diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md index bba7560ef6a..b96e9658324 100644 --- a/pkg/native_stack_traces/CHANGELOG.md +++ b/pkg/native_stack_traces/CHANGELOG.md @@ -1,9 +1,3 @@ -## 0.5.0 - -- Add support for parsing DWARF in Mach-O files and dSYM directories. -- Add `dump` command to replace the old `--dump_debug_file_contents` - flag to `find` and `translate`. - ## 0.4.6 - Upgrade to `package:lints` 2.0. diff --git a/pkg/native_stack_traces/bin/decode.dart b/pkg/native_stack_traces/bin/decode.dart index cb44a02d144..12c61ef7211 100644 --- a/pkg/native_stack_traces/bin/decode.dart +++ b/pkg/native_stack_traces/bin/decode.dart @@ -18,11 +18,10 @@ ArgParser _createBaseDebugParser(ArgParser parser) => parser ..addFlag('verbose', abbr: 'v', negatable: false, - help: 'Translate all frames, not just user or library code frames'); - -final ArgParser _dumpParser = ArgParser(allowTrailingOptions: true) - ..addOption('output', - abbr: 'o', help: 'Filename for generated output', valueHelp: 'FILE'); + help: 'Translate all frames, not just user or library code frames') + ..addFlag('dump_debug_file_contents', + negatable: false, + help: 'Dump all the parsed information from the debugging file'); final ArgParser _translateParser = _createBaseDebugParser(ArgParser(allowTrailingOptions: true)) @@ -49,7 +48,6 @@ final ArgParser _findParser = final ArgParser _helpParser = ArgParser(allowTrailingOptions: true); final ArgParser _argParser = ArgParser(allowTrailingOptions: true) - ..addCommand('dump', _dumpParser) ..addCommand('help', _helpParser) ..addCommand('find', _findParser) ..addCommand('translate', _translateParser) @@ -125,22 +123,12 @@ ${_argParser.usage} Options specific to the find command: ${_findParser.usage}'''; -final String _dumpUsage = ''' -Usage: decode dump [options] - -The dump command dumps the DWARF information in the given snapshot to either -standard output or a given output file. - -Options specific to the dump command: -${_dumpParser.usage}'''; - final _usages = { null: _mainUsage, '': _mainUsage, 'help': _helpUsage, 'translate': _translateUsage, 'find': _findUsage, - 'dump': _dumpUsage, }; const int _badUsageExitCode = 1; @@ -174,16 +162,15 @@ Dwarf? _loadFromFile(String? original, Function(String) usageError) { return null; } final filename = path.canonicalize(path.normalize(original)); - try { - final dwarf = Dwarf.fromFile(filename); - if (dwarf == null) { - usageError('file "$original" does not contain debugging information'); - } - return dwarf; - } on io.FileSystemException { + if (!io.File(filename).existsSync()) { usageError('debug file "$original" does not exist'); return null; } + final dwarf = Dwarf.fromFile(filename); + if (dwarf == null) { + usageError('file "$original" does not contain debugging information'); + } + return dwarf; } void find(ArgResults options) { @@ -212,6 +199,10 @@ void find(ArgResults options) { final dwarf = _loadFromFile(options['debug'], usageError); if (dwarf == null) return; + if (options['dump_debug_file_contents']) { + print(dwarf.dumpFileInfo()); + } + if ((options['vm_start'] == null) != (options['isolate_start'] == null)) { return usageError('need both VM start and isolate start'); } @@ -275,6 +266,9 @@ Future translate(ArgResults options) async { if (dwarf == null) { return; } + if (options['dump_debug_file_contents']) { + print(dwarf.dumpFileInfo()); + } final verbose = options['verbose']; final output = options['output'] != null @@ -297,27 +291,6 @@ Future translate(ArgResults options) async { await output.close(); } -Future dump(ArgResults options) async { - void usageError(String message) => errorWithUsage(message, command: 'dump'); - - if (options.rest.isEmpty) { - return usageError('must provide a path to an ELF file or dSYM directory ' - 'that contains DWARF information'); - } - final dwarf = _loadFromFile(options.rest.first, usageError); - if (dwarf == null) { - return usageError("'${options.rest.first}' contains no DWARF information"); - } - - final output = options['output'] != null - ? io.File(path.canonicalize(path.normalize(options['output']))) - .openWrite() - : io.stdout; - output.write(dwarf.dumpFileInfo()); - await output.flush(); - await output.close(); -} - Future main(List arguments) async { ArgResults options; @@ -337,7 +310,5 @@ Future main(List arguments) async { return find(options.command!); case 'translate': return await translate(options.command!); - case 'dump': - return await dump(options.command!); } } diff --git a/pkg/native_stack_traces/lib/src/convert.dart b/pkg/native_stack_traces/lib/src/convert.dart index 34c17c35584..0bc8f2470a9 100644 --- a/pkg/native_stack_traces/lib/src/convert.dart +++ b/pkg/native_stack_traces/lib/src/convert.dart @@ -178,21 +178,18 @@ Iterable collectPCOffsets(Iterable lines) sync* { class DwarfStackTraceDecoder extends StreamTransformerBase { final Dwarf _dwarf; final bool _includeInternalFrames; - StackTraceHeader? _header; DwarfStackTraceDecoder(this._dwarf, {bool includeInternalFrames = false}) : _includeInternalFrames = includeInternalFrames; - PCOffset? offsetOf(int address) => _header?.offsetOf(address); - @override Stream bind(Stream stream) async* { var depth = 0; - _header = null; + StackTraceHeader? header; await for (final line in stream) { final parsedHeader = _parseInstructionsLine(line); if (parsedHeader != null) { - _header = parsedHeader; + header = parsedHeader; depth = 0; yield line; continue; @@ -200,7 +197,7 @@ class DwarfStackTraceDecoder extends StreamTransformerBase { // If at any point we can't get appropriate information for the current // line as a stack trace line, then just pass the line through unchanged. final lineMatch = _traceLineRE.firstMatch(line); - final offset = _retrievePCOffset(_header, lineMatch); + final offset = _retrievePCOffset(header, lineMatch); final callInfo = offset?.callInfoFrom(_dwarf, includeInternalFrames: _includeInternalFrames); if (callInfo == null) { diff --git a/pkg/native_stack_traces/lib/src/dwarf.dart b/pkg/native_stack_traces/lib/src/dwarf.dart index 665f0c9bdc9..93a2af4cfb7 100644 --- a/pkg/native_stack_traces/lib/src/dwarf.dart +++ b/pkg/native_stack_traces/lib/src/dwarf.dart @@ -6,9 +6,8 @@ import 'dart:collection'; import 'dart:math'; import 'dart:typed_data'; -import 'dwarf_container.dart'; +import 'constants.dart' as constants; import 'elf.dart'; -import 'macho.dart'; import 'reader.dart'; int _initialLengthValue(Reader reader) { @@ -103,14 +102,12 @@ enum _AttributeForm { reference4, sectionOffset, string, - strp, } const _attributeForms = { 0x01: _AttributeForm.address, 0x08: _AttributeForm.string, 0x0c: _AttributeForm.flag, - 0x0e: _AttributeForm.strp, 0x0f: _AttributeForm.constant, 0x13: _AttributeForm.reference4, 0x17: _AttributeForm.sectionOffset, @@ -119,7 +116,6 @@ const _attributeForms = { const _attributeFormStrings = <_AttributeForm, String>{ _AttributeForm.address: 'DW_FORM_addr', _AttributeForm.string: 'DW_FORM_string', - _AttributeForm.strp: 'DW_FORM_strp', _AttributeForm.flag: 'DW_FORM_flag', _AttributeForm.constant: 'DW_FORM_udata', _AttributeForm.reference4: 'DW_FORM_ref4', @@ -149,9 +145,6 @@ class _Attribute { switch (form) { case _AttributeForm.string: return reader.readNullTerminatedString(); - case _AttributeForm.strp: - final offset = reader.readBytes(4); - return header.stringTable[offset]!; case _AttributeForm.flag: return reader.readByte() != 0; case _AttributeForm.address: @@ -168,7 +161,6 @@ class _Attribute { String valueToString(Object value, [CompilationUnit? unit]) { switch (form) { case _AttributeForm.string: - case _AttributeForm.strp: return value as String; case _AttributeForm.flag: return value.toString(); @@ -439,16 +431,14 @@ class CompilationUnitHeader { final int addressSize; // ignore: library_private_types_in_public_api final _AbbreviationsTable abbreviations; - final DwarfContainerStringTable stringTable; CompilationUnitHeader._(this.size, this.version, this.abbreviationsOffset, - this.addressSize, this.abbreviations, this.stringTable); + this.addressSize, this.abbreviations); static CompilationUnitHeader? fromReader( Reader reader, // ignore: library_private_types_in_public_api - Map abbreviationsTables, - DwarfContainerStringTable stringTable) { + Map abbreviationsTables) { final size = _initialLengthValue(reader); // An empty unit is an ending marker. if (size == 0) return null; @@ -463,8 +453,8 @@ class CompilationUnitHeader { '0x${paddedHex(abbreviationsOffset, 4)}'); } final addressSize = reader.readByte(); - return CompilationUnitHeader._(size, version, abbreviationsOffset, - addressSize, abbreviationsTable, stringTable); + return CompilationUnitHeader._( + size, version, abbreviationsOffset, addressSize, abbreviationsTable); } void writeToStringBuffer(StringBuffer buffer) { @@ -499,10 +489,9 @@ class CompilationUnit { static CompilationUnit? fromReader( Reader reader, // ignore: library_private_types_in_public_api - Map abbreviationsTables, - DwarfContainerStringTable stringTable) { - final header = CompilationUnitHeader.fromReader( - reader, abbreviationsTables, stringTable); + Map abbreviationsTables) { + final header = + CompilationUnitHeader.fromReader(reader, abbreviationsTables); if (header == null) return null; final referenceTable = Map.fromEntries(reader.readRepeatedWithOffsets( @@ -574,11 +563,10 @@ class DebugInfo { static DebugInfo fromReader( Reader reader, // ignore: library_private_types_in_public_api - Map abbreviationsTable, - DwarfContainerStringTable stringTable) { + Map abbreviationsTable) { final units = reader - .readRepeated((r) => - CompilationUnit.fromReader(reader, abbreviationsTable, stringTable)) + .readRepeated( + (r) => CompilationUnit.fromReader(reader, abbreviationsTable)) .toList(); return DebugInfo._(units); } @@ -926,18 +914,14 @@ class LineNumberProgram { void applySpecialOpcode(int opcode) { final adjustedOpcode = opcode - header.opcodeBase; - final addrDiff = (adjustedOpcode ~/ header.lineRange) * - header.minimumInstructionLength; - final lineDiff = header.lineBase + (adjustedOpcode % header.lineRange); - state.address += addrDiff; - state.line += lineDiff; + state.address = adjustedOpcode ~/ header.lineRange; + state.line += header.lineBase + (adjustedOpcode % header.lineRange); } while (!reader.done) { final opcode = reader.readByte(); if (opcode >= header.opcodeBase) { applySpecialOpcode(opcode); - yield state.clone(); continue; } switch (opcode) { @@ -991,8 +975,7 @@ class LineNumberProgram { state.basicBlock = true; break; case 8: // DW_LNS_const_add_pc - state.address += ((255 - header.opcodeBase) ~/ header.lineRange) * - header.minimumInstructionLength; + applySpecialOpcode(255); break; case 9: // DW_LNS_fixed_advance_pc state.address += reader.readBytes(2); @@ -1217,19 +1200,12 @@ class PCOffset { other is PCOffset && offset == other.offset && section == other.section; @override - String toString() => 'PCOffset($section, 0x${offset.toRadixString(16)})'; -} - -class UnimplementedStringTable extends DwarfContainerStringTable { - @override - String? operator [](int index) { - throw 'No string table found in DWARF information'; - } + String toString() => 'PCOffset($section, $offset)'; } /// The DWARF debugging information for a Dart snapshot. class Dwarf { - final DwarfContainer _container; + final Elf _elf; final Map _abbreviationsTables; final DebugInfo _debugInfo; final LineNumberInfo _lineNumberInfo; @@ -1242,38 +1218,17 @@ class Dwarf { /// DWARF information. final int isolateStartAddress; - Dwarf._(this._container, this._abbreviationsTables, this._debugInfo, + Dwarf._(this._elf, this._abbreviationsTables, this._debugInfo, this._lineNumberInfo, this.vmStartAddress, this.isolateStartAddress); - static Dwarf fromDwarfContainer(Reader reader, DwarfContainer container) { - final abbrevReader = container.abbreviationsTableReader(reader); - final abbreviationsTables = Map.fromEntries( - abbrevReader.readRepeatedWithOffsets(_AbbreviationsTable.fromReader)); - - final lineNumberInfo = - LineNumberInfo.fromReader(container.lineNumberInfoReader(reader)); - - final stringTable = container.stringTable ?? UnimplementedStringTable(); - final debugInfo = DebugInfo.fromReader( - container.debugInfoReader(reader), abbreviationsTables, stringTable); - - return Dwarf._(container, abbreviationsTables, debugInfo, lineNumberInfo, - container.vmStartAddress, container.isolateStartAddress); - } - /// Attempts to load the DWARF debugging information from the reader. /// /// Returns a [Dwarf] object if the load succeeds, otherwise returns null. static Dwarf? fromReader(Reader reader) { + // Currently, the only DWARF-containing format we recognize is ELF. final elf = Elf.fromReader(reader); - if (elf != null) { - return Dwarf.fromDwarfContainer(reader, elf); - } - final macho = MachO.fromReader(reader); - if (macho != null) { - return Dwarf.fromDwarfContainer(reader, macho); - } - return null; + if (elf == null) return null; + return Dwarf._loadSectionsFromElf(reader, elf); } /// Attempts to load the DWARF debugging information from the given bytes. @@ -1286,12 +1241,54 @@ class Dwarf { /// /// Returns a [Dwarf] object if the load succeeds, otherwise returns null. static Dwarf? fromFile(String path) => - Dwarf.fromReader(Reader.fromFile(MachO.handleDSYM(path))); + Dwarf.fromReader(Reader.fromFile(path)); + + static Dwarf _loadSectionsFromElf(Reader reader, Elf elf) { + final abbrevSection = elf.namedSections('.debug_abbrev').single; + final abbrevReader = abbrevSection.refocusedCopy(reader); + final abbreviationsTables = Map.fromEntries( + abbrevReader.readRepeatedWithOffsets(_AbbreviationsTable.fromReader)); + + final lineNumberSection = elf.namedSections('.debug_line').single; + final lineNumberInfo = + LineNumberInfo.fromReader(lineNumberSection.refocusedCopy(reader)); + + final infoSection = elf.namedSections('.debug_info').single; + final debugInfo = DebugInfo.fromReader( + infoSection.refocusedCopy(reader), abbreviationsTables); + + final vmStartSymbol = elf.dynamicSymbolFor(constants.vmSymbolName); + if (vmStartSymbol == null) { + throw FormatException( + 'Expected a dynamic symbol with name ${constants.vmSymbolName}'); + } + final vmStartAddress = vmStartSymbol.value; + + final isolateStartSymbol = + elf.dynamicSymbolFor(constants.isolateSymbolName); + if (isolateStartSymbol == null) { + throw FormatException( + 'Expected a dynamic symbol with name ${constants.isolateSymbolName}'); + } + final isolateStartAddress = isolateStartSymbol.value; + + return Dwarf._(elf, abbreviationsTables, debugInfo, lineNumberInfo, + vmStartAddress, isolateStartAddress); + } /// The build ID for the debugging information. /// /// Returns null if there is no build ID information recorded. - String? get buildId => _container.buildId; + String? get buildId { + final sections = _elf.namedSections(constants.buildIdSectionName); + if (sections.isEmpty) return null; + final note = sections.single as Note; + if (note.type != constants.buildIdNoteType) return null; + if (note.name != constants.buildIdNoteName) return null; + return note.description + .map((i) => i.toRadixString(16).padLeft(2, '0')) + .join(); + } /// The call information for the given virtual address. There may be /// multiple [CallInfo] objects returned for a single virtual address when @@ -1305,7 +1302,7 @@ class Dwarf { {bool includeInternalFrames = false}) { var calls = _debugInfo.callInfo(_lineNumberInfo, address); if (calls == null) { - final symbol = _container.staticSymbolAt(address); + final symbol = _elf.staticSymbolAt(address); if (symbol != null) { final offset = address - symbol.value; calls = [StubCallInfo(name: symbol.name, offset: offset)]; @@ -1361,7 +1358,7 @@ class Dwarf { String dumpFileInfo() { final buffer = StringBuffer(); - _container.writeToStringBuffer(buffer); + _elf.writeToStringBuffer(buffer); buffer.writeln(); writeToStringBuffer(buffer); return buffer.toString(); diff --git a/pkg/native_stack_traces/lib/src/dwarf_container.dart b/pkg/native_stack_traces/lib/src/dwarf_container.dart deleted file mode 100644 index feb3b573412..00000000000 --- a/pkg/native_stack_traces/lib/src/dwarf_container.dart +++ /dev/null @@ -1,30 +0,0 @@ -// 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 'reader.dart'; - -abstract class DwarfContainerStringTable { - String? operator [](int index); -} - -abstract class DwarfContainerSymbol { - int get value; - String get name; -} - -abstract class DwarfContainer { - Reader debugInfoReader(Reader containerReader); - Reader lineNumberInfoReader(Reader containerReader); - Reader abbreviationsTableReader(Reader containerReader); - DwarfContainerSymbol? staticSymbolAt(int address); - - int get vmStartAddress; - int get isolateStartAddress; - - String? get buildId; - - DwarfContainerStringTable? get stringTable; - - void writeToStringBuffer(StringBuffer buffer); -} diff --git a/pkg/native_stack_traces/lib/src/elf.dart b/pkg/native_stack_traces/lib/src/elf.dart index 4d69efc8dc2..c29b1e412c6 100644 --- a/pkg/native_stack_traces/lib/src/elf.dart +++ b/pkg/native_stack_traces/lib/src/elf.dart @@ -6,8 +6,6 @@ import 'dart:typed_data'; -import 'constants.dart' as constants; -import 'dwarf_container.dart'; import 'reader.dart'; int _readElfBytes(Reader reader, int bytes, int alignment) { @@ -92,12 +90,10 @@ class ElfHeader { this.sectionHeaderStringsIndex); static ElfHeader? fromReader(Reader reader) { - final start = reader.offset; final fileSize = reader.length; for (final sigByte in _ELFMAG.codeUnits) { if (reader.readByte() != sigByte) { - reader.seek(start, absolute: true); return null; } } @@ -270,11 +266,7 @@ class ProgramHeaderEntry { static const _PT_NULL = 0; static const _PT_LOAD = 1; static const _PT_DYNAMIC = 2; - static const _PT_NOTE = 4; static const _PT_PHDR = 6; - static const _PT_GNU_EH_FRAME = 0x6474e550; - static const _PT_GNU_STACK = 0x6474e551; - static const _PT_GNU_RELRO = 0x6474e552; ProgramHeaderEntry._(this.type, this.flags, this.offset, this.vaddr, this.paddr, this.filesz, this.memsz, this.align, this.wordSize); @@ -304,11 +296,7 @@ class ProgramHeaderEntry { _PT_NULL: 'PT_NULL', _PT_LOAD: 'PT_LOAD', _PT_DYNAMIC: 'PT_DYNAMIC', - _PT_NOTE: 'PT_NOTE', _PT_PHDR: 'PT_PHDR', - _PT_GNU_EH_FRAME: 'PT_GNU_EH_FRAME', - _PT_GNU_STACK: 'PT_GNU_STACK', - _PT_GNU_RELRO: 'PT_GNU_RELRO', }; static String _typeToString(int type) => @@ -658,7 +646,7 @@ class Note extends Section { } /// A map from table offsets to strings, used to store names of ELF objects. -class StringTable extends Section implements DwarfContainerStringTable { +class StringTable extends Section { final Map _entries; StringTable._(entry, this._entries) : super._(entry); @@ -670,22 +658,8 @@ class StringTable extends Section implements DwarfContainerStringTable { return StringTable._(entry, entries); } - @override - String? operator [](int index) { - // Fast case: Index is for the start of a null terminated string. - if (_entries.containsKey(index)) { - return _entries[index]; - } - // We can index into null terminated string entries for suffixes of - // that string, so do a linear search to find the appropriate entry. - for (final kv in _entries.entries) { - final start = index - kv.key; - if (start >= 0 && start <= kv.value.length) { - return kv.value.substring(start); - } - } - return null; - } + String? operator [](int index) => _entries[index]; + bool containsKey(int index) => _entries.containsKey(index); @override void writeToStringBuffer(StringBuffer buffer) { @@ -706,7 +680,6 @@ class StringTable extends Section implements DwarfContainerStringTable { enum SymbolBinding { STB_LOCAL, STB_GLOBAL, - STB_WEAK, } enum SymbolType { @@ -723,17 +696,15 @@ enum SymbolVisibility { } /// A symbol in an ELF file, which names a portion of the virtual address space. -class Symbol implements DwarfContainerSymbol { +class Symbol { final int nameIndex; final int info; final int other; final int sectionIndex; - @override final int value; final int size; final int _wordSize; - @override - late final String name; + late String name; Symbol._(this.nameIndex, this.info, this.other, this.sectionIndex, this.value, this.size, this._wordSize); @@ -760,6 +731,14 @@ class Symbol implements DwarfContainerSymbol { nameIndex, info, other, sectionIndex, value, size, wordSize); } + void _cacheNameFromStringTable(StringTable table) { + final nameFromTable = table[nameIndex]; + if (nameFromTable == null) { + throw FormatException('Index $nameIndex not found in string table'); + } + name = nameFromTable; + } + SymbolBinding get bind => SymbolBinding.values[info >> 4]; SymbolType get type => SymbolType.values[info & 0x0f]; SymbolVisibility get visibility => SymbolVisibility.values[other & 0x03]; @@ -776,9 +755,6 @@ class Symbol implements DwarfContainerSymbol { case SymbolBinding.STB_LOCAL: buffer.write(' a local'); break; - case SymbolBinding.STB_WEAK: - buffer.write(' a weak'); - break; } switch (visibility) { case SymbolVisibility.STV_DEFAULT: @@ -828,13 +804,8 @@ class SymbolTable extends Section { void _cacheNames(StringTable stringTable) { _nameCache.clear(); for (final symbol in _entries) { - final index = symbol.nameIndex; - final name = stringTable[index]; - if (name == null) { - throw FormatException('Index $index not found in string table'); - } - symbol.name = name; - _nameCache[name] = symbol; + symbol._cacheNameFromStringTable(stringTable); + _nameCache[symbol.name] = symbol; } } @@ -961,7 +932,7 @@ class DynamicTable extends Section { } /// Information parsed from an Executable and Linking Format (ELF) file. -class Elf implements DwarfContainer { +class Elf { final ElfHeader _header; final ProgramHeader _programHeader; final SectionHeader _sectionHeader; @@ -1013,24 +984,16 @@ class Elf implements DwarfContainer { /// Reverse lookup of the static symbol that contains the given virtual /// address. Returns null if no static symbol matching the address is found. - @override Symbol? staticSymbolAt(int address) { - Symbol? bestSym; for (final section in namedSections('.symtab')) { final table = section as SymbolTable; for (final symbol in table.values) { final start = symbol.value; - if (start > address) continue; - // If given a non-zero extent of a symbol, make sure the address is - // within the extent. - if (symbol.size > 0 && (start + symbol.size <= address)) continue; - // Pick the symbol with a start closest to the given address. - if (bestSym == null || (bestSym.value < start)) { - bestSym = symbol; - } + final end = start + symbol.size; + if (start <= address && address < end) return symbol; } } - return bestSym; + return null; } /// Creates an [Elf] from the data pointed to by [reader]. @@ -1114,55 +1077,6 @@ class Elf implements DwarfContainer { header, programHeader, sectionHeader, sections, sectionsByName); } - @override - Reader abbreviationsTableReader(Reader containerReader) => - namedSections('.debug_abbrev').single.refocusedCopy(containerReader); - - @override - Reader lineNumberInfoReader(Reader containerReader) => - namedSections('.debug_line').single.refocusedCopy(containerReader); - - @override - Reader debugInfoReader(Reader containerReader) => - namedSections('.debug_info').single.refocusedCopy(containerReader); - - @override - int get vmStartAddress { - final vmStartSymbol = dynamicSymbolFor(constants.vmSymbolName); - if (vmStartSymbol == null) { - throw FormatException( - 'Expected a dynamic symbol with name ${constants.vmSymbolName}'); - } - return vmStartSymbol.value; - } - - @override - int get isolateStartAddress { - final isolateStartSymbol = dynamicSymbolFor(constants.isolateSymbolName); - if (isolateStartSymbol == null) { - throw FormatException( - 'Expected a dynamic symbol with name ${constants.isolateSymbolName}'); - } - return isolateStartSymbol.value; - } - - @override - String? get buildId { - final sections = namedSections(constants.buildIdSectionName); - if (sections.isEmpty) return null; - final note = sections.single as Note; - if (note.type != constants.buildIdNoteType) return null; - if (note.name != constants.buildIdNoteName) return null; - return note.description - .map((i) => i.toRadixString(16).padLeft(2, '0')) - .join(); - } - - // Currently we don't handle DWARF uses of string tables in ELF files. - @override - DwarfContainerStringTable? get stringTable => null; - - @override void writeToStringBuffer(StringBuffer buffer) { buffer ..writeln('-----------------------------------------------------') diff --git a/pkg/native_stack_traces/lib/src/macho.dart b/pkg/native_stack_traces/lib/src/macho.dart deleted file mode 100644 index d46a5076dc7..00000000000 --- a/pkg/native_stack_traces/lib/src/macho.dart +++ /dev/null @@ -1,548 +0,0 @@ -// 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. - -// ignore_for_file: constant_identifier_names - -import 'dart:typed_data'; - -import 'package:path/path.dart' as path; - -import 'constants.dart' as constants; -import 'dwarf_container.dart'; -import 'reader.dart'; - -int _readMachOUint8(Reader reader) => reader.readByte(signed: false); - -int _readMachOUint16(Reader reader) => reader.readBytes(2, signed: false); - -int _readMachOUint32(Reader reader) => reader.readBytes(4, signed: false); - -int _readMachOUword(Reader reader) => - reader.readBytes(reader.wordSize, signed: false); - -class StringTable implements DwarfContainerStringTable { - final Map _stringsByOffset; - - StringTable._(this._stringsByOffset); - - static StringTable fromReader(Reader reader) => StringTable._(Map.fromEntries( - reader.readRepeatedWithOffsets((r) => r.readNullTerminatedString()))); - - @override - String? operator [](int index) { - // Fast case: Index is for the start of a null terminated string. - if (_stringsByOffset.containsKey(index)) { - return _stringsByOffset[index]; - } - // We can index into null terminated string entries for suffixes of - // that string, so do a linear search to find the appropriate entry. - for (final kv in _stringsByOffset.entries) { - final start = index - kv.key; - if (start >= 0 && start <= kv.value.length) { - return kv.value.substring(start); - } - } - return null; - } - - void writeToStringBuffer(StringBuffer buffer) { - for (final k in _stringsByOffset.keys) { - buffer - ..write(k.toString().padLeft(8, ' ')) - ..write(' => ') - ..writeln(_stringsByOffset[k]); - } - } - - @override - String toString() { - final buffer = StringBuffer(); - writeToStringBuffer(buffer); - return buffer.toString(); - } -} - -class Symbol implements DwarfContainerSymbol { - final int index; - final int type; - final int sect; - final int desc; - @override - final int value; - @override - late final String name; - - Symbol._(this.index, this.type, this.sect, this.desc, this.value); - - static Symbol fromReader(Reader reader) { - final index = _readMachOUint32(reader); - final type = _readMachOUint8(reader); - final sect = _readMachOUint8(reader); - final desc = _readMachOUint16(reader); - final value = _readMachOUword(reader); - return Symbol._(index, type, sect, desc, value); - } -} - -class SymbolTable { - final Map _symbols; - - SymbolTable._(this._symbols); - - static SymbolTable fromReader( - Reader reader, int nsyms, StringTable stringTable) { - final symbols = {}; - for (int i = 0; i < nsyms; i++) { - final symbol = Symbol.fromReader(reader); - final index = symbol.index; - final name = stringTable[index]; - if (name == null) { - throw FormatException('Index $index not found in string table'); - } - symbol.name = name; - symbols[name] = symbol; - } - return SymbolTable._(symbols); - } - - Iterable get keys => _symbols.keys; - Iterable get values => _symbols.values; - - Symbol? operator [](String name) => _symbols[name]; - - bool containsKey(String name) => _symbols.containsKey(name); -} - -class LoadCommand { - final int cmd; - final int cmdsize; - - LoadCommand._(this.cmd, this.cmdsize); - - static const LC_SEGMENT = 0x1; - static const LC_SYMTAB = 0x2; - static const LC_SEGMENT_64 = 0x19; - - static LoadCommand fromReader(Reader reader) { - final start = reader.offset; // cmdsize includes size of cmd and cmdsize. - final cmd = _readMachOUint32(reader); - final cmdsize = _readMachOUint32(reader); - LoadCommand command = LoadCommand._(cmd, cmdsize); - switch (cmd) { - case LC_SEGMENT: - case LC_SEGMENT_64: - command = SegmentCommand.fromReader(reader, cmd, cmdsize); - break; - case LC_SYMTAB: - command = SymbolTableCommand.fromReader(reader, cmd, cmdsize); - break; - default: - break; - } - reader.seek(start + cmdsize, absolute: true); - return command; - } - - void writeToStringBuffer(StringBuffer buffer) { - buffer - ..write('Uninterpreted command 0x') - ..write(cmd.toRadixString(16)) - ..write(' of size ') - ..writeln(cmdsize); - } - - @override - String toString() { - StringBuffer buffer = StringBuffer(); - writeToStringBuffer(buffer); - return buffer.toString(); - } -} - -class SegmentCommand extends LoadCommand { - final String segname; - final int vmaddr; - final int vmsize; - final int fileoff; - final int filesize; - final int maxprot; - final int initprot; - final int nsects; - final int flags; - final Map sections; - - SegmentCommand._( - int cmd, - int cmdsize, - this.segname, - this.vmaddr, - this.vmsize, - this.fileoff, - this.filesize, - this.maxprot, - this.initprot, - this.nsects, - this.flags, - this.sections) - : super._(cmd, cmdsize); - - static SegmentCommand fromReader(Reader reader, int cmd, int cmdsize) { - final segname = reader.readFixedLengthNullTerminatedString(16); - final vmaddr = _readMachOUword(reader); - final vmsize = _readMachOUword(reader); - final fileoff = _readMachOUword(reader); - final filesize = _readMachOUword(reader); - final maxprot = _readMachOUint32(reader); - final initprot = _readMachOUint32(reader); - final nsects = _readMachOUint32(reader); - final flags = _readMachOUint32(reader); - final sections = {}; - for (int i = 0; i < nsects; i++) { - final section = Section.fromReader(reader); - sections[section.sectname] = section; - } - return SegmentCommand._(cmd, cmdsize, segname, vmaddr, vmsize, fileoff, - filesize, maxprot, initprot, nsects, flags, sections); - } - - @override - void writeToStringBuffer(StringBuffer buffer) { - buffer - ..write('Segment "') - ..write(segname) - ..write('" of size ') - ..write(filesize) - ..write(' at offset 0x') - ..writeln(fileoff.toRadixString(16)); - buffer.writeln('Sections:'); - for (final section in sections.values) { - section.writeToStringBuffer(buffer); - buffer.writeln(); - } - } -} - -class Section { - String sectname; - String segname; - int addr; - int size; - int offset; - int align; - int reloff; - int nreloc; - int flags; - int reserved1; - int reserved2; - int? reserved3; - - Section._( - this.sectname, - this.segname, - this.addr, - this.size, - this.offset, - this.align, - this.reloff, - this.nreloc, - this.flags, - this.reserved1, - this.reserved2, - this.reserved3); - - static Section fromReader(Reader reader) { - final sectname = reader.readFixedLengthNullTerminatedString(16); - final segname = reader.readFixedLengthNullTerminatedString(16); - final addr = _readMachOUword(reader); - final size = _readMachOUword(reader); - final offset = _readMachOUint32(reader); - final align = _readMachOUint32(reader); - final reloff = _readMachOUint32(reader); - final nreloc = _readMachOUint32(reader); - final flags = _readMachOUint32(reader); - final reserved1 = _readMachOUint32(reader); - final reserved2 = _readMachOUint32(reader); - final reserved3 = (reader.wordSize == 8) ? _readMachOUint32(reader) : null; - return Section._(sectname, segname, addr, size, offset, align, reloff, - nreloc, flags, reserved1, reserved2, reserved3); - } - - Reader refocus(Reader reader) => reader.refocusedCopy(offset, size); - - void writeToStringBuffer(StringBuffer buffer) { - buffer - ..write('Section "') - ..write(sectname) - ..write('" of size ') - ..write(size) - ..write(' at offset 0x') - ..write(paddedHex(offset, 4)); - } - - @override - String toString() { - StringBuffer buffer = StringBuffer(); - writeToStringBuffer(buffer); - return buffer.toString(); - } -} - -class SymbolTableCommand extends LoadCommand { - final int _symoff; - final int _nsyms; - final int _stroff; - final int _strsize; - - SymbolTableCommand._(int cmd, int cmdsize, this._symoff, this._nsyms, - this._stroff, this._strsize) - : super._(cmd, cmdsize); - - static SymbolTableCommand fromReader(Reader reader, int cmd, int cmdsize) { - final symoff = _readMachOUint32(reader); - final nsyms = _readMachOUint32(reader); - final stroff = _readMachOUint32(reader); - final strsize = _readMachOUint32(reader); - return SymbolTableCommand._(cmd, cmdsize, symoff, nsyms, stroff, strsize); - } - - SymbolTable load(Reader reader) { - final stringTable = - StringTable.fromReader(reader.refocusedCopy(_stroff, _strsize)); - return SymbolTable.fromReader( - reader.refocusedCopy(_symoff), _nsyms, stringTable); - } - - @override - void writeToStringBuffer(StringBuffer buffer) { - buffer - ..write('Symbol table with ') - ..write(_nsyms) - ..write(' symbols of size ') - ..writeln(cmdsize); - } -} - -class MachOHeader { - final int magic; - final int cputype; - final int cpusubtype; - final int filetype; - final int ncmds; - final int sizeofcmds; - final int flags; - final int? reserved; - final int size; - - MachOHeader._(this.magic, this.cputype, this.cpusubtype, this.filetype, - this.ncmds, this.sizeofcmds, this.flags, this.reserved, this.size); - - static const _MH_MAGIC = 0xfeedface; - static const _MH_CIGAM = 0xcefaedfe; - static const _MH_MAGIC_64 = 0xfeedfacf; - static const _MH_CIGAM_64 = 0xcffaedfe; - - static MachOHeader? fromReader(Reader reader) { - final start = reader.offset; - // Initially assume host endianness. - reader.endian = Endian.host; - final magic = _readMachOUint32(reader); - if (magic == _MH_MAGIC || magic == _MH_CIGAM) { - reader.wordSize = 4; - } else if (magic == _MH_MAGIC_64 || magic == _MH_CIGAM_64) { - reader.wordSize = 8; - } else { - // Not an expected magic value, so not a supported Mach-O file. - return null; - } - if (magic == _MH_CIGAM || magic == _MH_CIGAM_64) { - reader.endian = Endian.host == Endian.big ? Endian.little : Endian.big; - } - final cputype = _readMachOUint32(reader); - final cpusubtype = _readMachOUint32(reader); - final filetype = _readMachOUint32(reader); - final ncmds = _readMachOUint32(reader); - final sizeofcmds = _readMachOUint32(reader); - final flags = _readMachOUint32(reader); - final reserved = reader.wordSize == 8 ? _readMachOUint32(reader) : null; - final size = reader.offset - start; - return MachOHeader._(magic, cputype, cpusubtype, filetype, ncmds, - sizeofcmds, flags, reserved, size); - } - - void writeToStringBuffer(StringBuffer buffer) { - buffer - ..write('Magic: 0x') - ..writeln(paddedHex(magic, 4)); - buffer - ..write('Cpu Type: 0x') - ..writeln(paddedHex(cputype, 4)); - buffer - ..write('Cpu Subtype: 0x') - ..writeln(paddedHex(cpusubtype, 4)); - buffer - ..write('Filetype: 0x') - ..writeln(paddedHex(filetype, 4)); - buffer - ..write('Number of commands: ') - ..writeln(ncmds); - buffer - ..write('Size of commands: ') - ..writeln(sizeofcmds); - buffer - ..write('Flags: 0x') - ..writeln(paddedHex(flags, 4)); - if (reserved != null) { - buffer - ..write('Reserved: 0x') - ..writeln(paddedHex(reserved!, 4)); - } - } - - @override - String toString() { - final buffer = StringBuffer(); - writeToStringBuffer(buffer); - return buffer.toString(); - } -} - -class MachO implements DwarfContainer { - final MachOHeader _header; - final List _commands; - final SymbolTable _symbolTable; - final SegmentCommand _dwarfSegment; - final StringTable? _dwarfStringTable; - - MachO._(this._header, this._commands, this._symbolTable, this._dwarfSegment, - this._dwarfStringTable); - - static MachO? fromReader(Reader machOReader) { - // MachO files contain absolute offsets from the start of the file, so - // make sure we have a reader that a) makes no assumptions about the - // endianness or word size, since we'll read those in the header and b) - // has an internal offset of 0 so absolute offsets can be used directly. - final reader = Reader.fromTypedData(ByteData.sublistView(machOReader.bdata, - machOReader.bdata.offsetInBytes + machOReader.offset)); - final header = MachOHeader.fromReader(reader); - if (header == null) return null; - - final commandReader = - reader.refocusedCopy(reader.offset, header.sizeofcmds); - final commands = - List.of(commandReader.readRepeated(LoadCommand.fromReader)); - assert(commands.length == header.ncmds); - - final symbolTable = - commands.whereType().single.load(reader); - - final dwarfSegment = commands - .whereType() - .firstWhere((sc) => sc!.segname == '__DWARF', orElse: () => null); - if (dwarfSegment == null) { - print("No DWARF information in Mach-O file"); - return null; - } - - final dwarfStringTableSection = dwarfSegment.sections['__debug_str']; - StringTable? dwarfStringTable; - if (dwarfStringTableSection != null) { - dwarfStringTable = - StringTable.fromReader(dwarfStringTableSection.refocus(reader)); - } - - // Set the wordSize and endian of the original reader before returning. - machOReader.wordSize = reader.wordSize; - machOReader.endian = reader.endian; - - return MachO._( - header, commands, symbolTable, dwarfSegment, dwarfStringTable); - } - - static String handleDSYM(String fileName) { - if (!fileName.endsWith('.dSYM')) { - return fileName; - } - var baseName = path.basename(fileName); - baseName = baseName.substring(0, baseName.length - '.dSYM'.length); - return path.join(fileName, 'Contents', 'Resources', 'DWARF', baseName); - } - - static MachO? fromFile(String fileName) => - MachO.fromReader(Reader.fromFile(MachO.handleDSYM(fileName))); - - @override - Reader abbreviationsTableReader(Reader reader) => - _dwarfSegment.sections['__debug_abbrev']!.refocus(reader); - @override - Reader lineNumberInfoReader(Reader reader) => - _dwarfSegment.sections['__debug_line']!.refocus(reader); - @override - Reader debugInfoReader(Reader reader) => - _dwarfSegment.sections['__debug_info']!.refocus(reader); - - @override - int get vmStartAddress { - if (!_symbolTable.containsKey(constants.vmSymbolName)) { - throw FormatException( - 'Expected a dynamic symbol with name ${constants.vmSymbolName}'); - } - return _symbolTable[constants.vmSymbolName]!.value; - } - - @override - int get isolateStartAddress { - if (!_symbolTable.containsKey(constants.isolateSymbolName)) { - throw FormatException( - 'Expected a dynamic symbol with name ${constants.isolateSymbolName}'); - } - return _symbolTable[constants.isolateSymbolName]!.value; - } - - @override - String? get buildId => null; - - @override - DwarfContainerStringTable? get stringTable => _dwarfStringTable; - - @override - Symbol? staticSymbolAt(int address) { - Symbol? bestSym; - for (final symbol in _symbolTable.values) { - if (symbol.value > address) continue; - // Pick the symbol with a value closest to the given address. - if (bestSym == null || (bestSym.value < symbol.value)) { - bestSym = symbol; - } - } - return bestSym; - } - - @override - void writeToStringBuffer(StringBuffer buffer) { - buffer - ..writeln('----------------------------------------') - ..writeln(' Header') - ..writeln('----------------------------------------') - ..writeln(''); - _header.writeToStringBuffer(buffer); - buffer - ..writeln('') - ..writeln('') - ..writeln('----------------------------------------') - ..writeln(' Load commands') - ..writeln('----------------------------------------') - ..writeln(''); - for (final command in _commands) { - command.writeToStringBuffer(buffer); - buffer.writeln(''); - } - } - - @override - String toString() { - final buffer = StringBuffer(); - writeToStringBuffer(buffer); - return buffer.toString(); - } -} diff --git a/pkg/native_stack_traces/lib/src/reader.dart b/pkg/native_stack_traces/lib/src/reader.dart index 99148d307b4..815887878ae 100644 --- a/pkg/native_stack_traces/lib/src/reader.dart +++ b/pkg/native_stack_traces/lib/src/reader.dart @@ -37,15 +37,9 @@ class Reader { bdata = ByteData.sublistView(File(path).readAsBytesSync()); /// Returns a reader focused on a different portion of the underlying buffer. - /// If size is not provided, then the new reader extends to the end of the - /// buffer. - Reader refocusedCopy(int pos, [int? size]) { + Reader refocusedCopy(int pos, int size) { assert(pos >= 0 && pos < bdata.buffer.lengthInBytes); - if (size != null) { - assert(size >= 0 && (pos + size) <= bdata.buffer.lengthInBytes); - } else { - size = bdata.buffer.lengthInBytes - pos; - } + assert(size >= 0 && (pos + size) <= bdata.buffer.lengthInBytes); return Reader.fromTypedData(ByteData.view(bdata.buffer, pos, size), wordSize: _wordSize, endian: _endian); } @@ -57,7 +51,7 @@ class Reader { void seek(int offset, {bool absolute = false}) { final newOffset = (absolute ? 0 : _offset) + offset; - assert(newOffset >= 0 && newOffset <= bdata.lengthInBytes); + assert(newOffset >= 0 && newOffset < bdata.lengthInBytes); _offset = newOffset; } @@ -91,26 +85,16 @@ class Reader { int readByte({bool signed = false}) => readBytes(1, signed: signed); int readWord() => readBytes(wordSize); - String readNullTerminatedString({int? maxSize}) { - final start = _offset; - int end = maxSize != null ? _offset + maxSize : bdata.lengthInBytes; - for (; _offset < end; _offset++) { - if (bdata.getUint8(_offset) == 0) { - end = _offset; - _offset++; // Move reader past null terminator. - break; + String readNullTerminatedString() { + final start = bdata.offsetInBytes + _offset; + for (var i = 0; _offset + i < bdata.lengthInBytes; i++) { + if (bdata.getUint8(_offset + i) == 0) { + _offset += i + 1; + return String.fromCharCodes(bdata.buffer.asUint8List(start, i)); } } return String.fromCharCodes( - bdata.buffer.asUint8List(bdata.offsetInBytes + start, end - start)); - } - - String readFixedLengthNullTerminatedString(int maxSize) { - final start = _offset; - final str = readNullTerminatedString(maxSize: maxSize); - // Ensure reader points past fixed space, not at end of string within it. - _offset = start + maxSize; - return str; + bdata.buffer.asUint8List(start, bdata.lengthInBytes - _offset)); } int readLEB128EncodedInteger({bool signed = false}) { diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc index 13534d8de3f..31d6f2557d8 100644 --- a/runtime/lib/object.cc +++ b/runtime/lib/object.cc @@ -318,13 +318,6 @@ DEFINE_NATIVE_ENTRY(Internal_deoptimizeFunctionsOnStack, 0, 0) { return Object::null(); } -DEFINE_NATIVE_ENTRY(Internal_randomAddressInsideAllocateObjectStub, 0, 0) { - auto& stub = Code::Handle( - zone, isolate->group()->object_store()->allocate_object_stub()); - const uword random_offset = isolate->random()->NextUInt32() % stub.Size(); - return Smi::New(stub.EntryPoint() + random_offset); -} - static bool ExtractInterfaceTypeArgs(Zone* zone, const Class& instance_cls, const TypeArguments& instance_type_args, diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_program.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_program.dart index 9d291b367fa..c422f18a08f 100644 --- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_program.dart +++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_program.dart @@ -4,8 +4,6 @@ // Test that the full stacktrace in an error object matches the stacktrace // handed to the catch clause. -import 'dart:_internal' show VMInternalsForTesting; - import "package:expect/expect.dart"; class C { @@ -17,6 +15,5 @@ bar(c) => c * 4; foo(c) => bar(c); main() { - print(VMInternalsForTesting.randomAddressInsideAllocateObjectStub()); var a = foo(new C()); } diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart index b85c9927621..d8cb33f0cd8 100644 --- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart +++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart @@ -6,8 +6,9 @@ // compile-time will be used at runtime (irrespective if other values were // passed to the runtime). +// OtherResources=use_dwarf_stack_traces_flag_program.dart + import "dart:async"; -import "dart:convert"; import "dart:io"; import 'package:expect/expect.dart'; @@ -37,11 +38,8 @@ main(List args) async { } await withTempDir('dwarf-flag-test', (String tempDir) async { - // We have to use the program in its original location so it can use - // the dart:_internal library (as opposed to adding it as an OtherResources - // option to the test). - final script = path.join(sdkDir, 'runtime', 'tests', 'vm', 'dart', - 'use_dwarf_stack_traces_flag_program.dart'); + final cwDir = path.dirname(Platform.script.toFilePath()); + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. @@ -78,252 +76,123 @@ main(List args) async { ]); // Run the resulting Dwarf-AOT compiled script. - - final output1 = await runTestProgram(aotRuntime, - ['--dwarf-stack-traces-mode', scriptDwarfSnapshot, scriptDill]); - final output2 = await runTestProgram(aotRuntime, [ + final dwarfTrace1 = await runError(aotRuntime, [ + '--dwarf-stack-traces-mode', + scriptDwarfSnapshot, + scriptDill, + ]); + final dwarfTrace2 = await runError(aotRuntime, [ '--no-dwarf-stack-traces-mode', scriptDwarfSnapshot, - scriptDill + scriptDill, ]); // Run the resulting non-Dwarf-AOT compiled script. - final nonDwarfTrace1 = (await runTestProgram(aotRuntime, [ + final nonDwarfTrace1 = await runError(aotRuntime, [ '--dwarf-stack-traces-mode', scriptNonDwarfSnapshot, scriptDill, - ])) - .trace; - final nonDwarfTrace2 = (await runTestProgram(aotRuntime, [ + ]); + final nonDwarfTrace2 = await runError(aotRuntime, [ '--no-dwarf-stack-traces-mode', scriptNonDwarfSnapshot, scriptDill, - ])) - .trace; + ]); // Ensure the result is based off the flag passed to gen_snapshot, not // the one passed to the runtime. Expect.deepEquals(nonDwarfTrace1, nonDwarfTrace2); - // Check with DWARF from separate debugging information. - await compareTraces(nonDwarfTrace1, output1, output2, scriptDwarfDebugInfo); - // Check with DWARF in generated snapshot. - await compareTraces(nonDwarfTrace1, output1, output2, scriptDwarfSnapshot); + // For DWARF stack traces, we can't guarantee that the stack traces are + // textually equal on all platforms, but if we retrieve the PC offsets + // out of the stack trace, those should be equal. + final tracePCOffsets1 = collectPCOffsets(dwarfTrace1); + final tracePCOffsets2 = collectPCOffsets(dwarfTrace2); + Expect.deepEquals(tracePCOffsets1, tracePCOffsets2); - if (Platform.isLinux || Platform.isMacOS) { - final scriptAssembly = path.join(tempDir, 'dwarf_assembly.S'); - final scriptDwarfAssemblyDebugInfo = - path.join(tempDir, 'dwarf_assembly_info.so'); - final scriptDwarfAssemblySnapshot = - path.join(tempDir, 'dwarf_assembly.so'); - // We get a separate .dSYM bundle on MacOS. - final scriptDwarfAssemblyDebugSnapshot = - scriptDwarfAssemblySnapshot + (Platform.isMacOS ? '.dSYM' : ''); + // Check that translating the DWARF stack trace (without internal frames) + // matches the symbolic stack trace. + final dwarf = Dwarf.fromFile(scriptDwarfDebugInfo)!; - await run(genSnapshot, [ - // We test --dwarf-stack-traces-mode, not --dwarf-stack-traces, because - // the latter is a handler that sets the former and also may change - // other flags. This way, we limit the difference between the two - // snapshots and also directly test the flag saved as a VM global flag. - '--dwarf-stack-traces-mode', - '--save-debugging-info=$scriptDwarfAssemblyDebugInfo', - '--snapshot-kind=app-aot-assembly', - '--assembly=$scriptAssembly', - scriptDill, - ]); - - await assembleSnapshot(scriptAssembly, scriptDwarfAssemblySnapshot, - debug: true); - - // Run the resulting Dwarf-AOT compiled script. - final assemblyOutput1 = await runTestProgram(aotRuntime, [ - '--dwarf-stack-traces-mode', - scriptDwarfAssemblySnapshot, - scriptDill, - ]); - final assemblyOutput2 = await runTestProgram(aotRuntime, [ - '--no-dwarf-stack-traces-mode', - scriptDwarfAssemblySnapshot, - scriptDill, - ]); - - // Check with DWARF in assembled snapshot. - await compareTraces(nonDwarfTrace1, assemblyOutput1, assemblyOutput2, - scriptDwarfAssemblyDebugSnapshot, - fromAssembly: true); - // Check with DWARF from separate debugging information. - await compareTraces(nonDwarfTrace1, assemblyOutput1, assemblyOutput2, - scriptDwarfAssemblyDebugInfo, - fromAssembly: true); - } - }); -} - -class DwarfTestOutput { - final List trace; - final int allocateObjectAddress; - - DwarfTestOutput(this.trace, this.allocateObjectAddress); -} - -Future compareTraces(List nonDwarfTrace, DwarfTestOutput output1, - DwarfTestOutput output2, String dwarfPath, - {bool fromAssembly = false}) async { - // For DWARF stack traces, we can't guarantee that the stack traces are - // textually equal on all platforms, but if we retrieve the PC offsets - // out of the stack trace, those should be equal. - final tracePCOffsets1 = collectPCOffsets(output1.trace); - final tracePCOffsets2 = collectPCOffsets(output2.trace); - Expect.deepEquals(tracePCOffsets1, tracePCOffsets2); - - // Check that translating the DWARF stack trace (without internal frames) - // matches the symbolic stack trace. - print("Reading DWARF info from ${dwarfPath}"); - final dwarf = Dwarf.fromFile(dwarfPath); - Expect.isNotNull(dwarf); - - // Check that build IDs match for traces from running ELF snapshots. - if (!fromAssembly) { - Expect.isNotNull(dwarf!.buildId); + // Check that build IDs match for traces. + Expect.isNotNull(dwarf.buildId); print('Dwarf build ID: "${dwarf.buildId!}"'); // We should never generate an all-zero build ID. Expect.notEquals(dwarf.buildId, "00000000000000000000000000000000"); // This is a common failure case as well, when HashBitsContainer ends up // hashing over seemingly empty sections. Expect.notEquals(dwarf.buildId, "01000000010000000100000001000000"); - final buildId1 = buildId(output1.trace); + final buildId1 = buildId(dwarfTrace1); Expect.isFalse(buildId1.isEmpty); print('Trace 1 build ID: "${buildId1}"'); Expect.equals(dwarf.buildId, buildId1); - final buildId2 = buildId(output2.trace); + final buildId2 = buildId(dwarfTrace2); Expect.isFalse(buildId2.isEmpty); print('Trace 2 build ID: "${buildId2}"'); Expect.equals(dwarf.buildId, buildId2); - } - final decoder = DwarfStackTraceDecoder(dwarf!); - // Run on the second trace just for the side effect of getting the header - // information so we can get the PC offset of the output address. - await Stream.fromIterable(output2.trace).transform(decoder).toList(); - final allocateObjectPCOffset2 = - decoder.offsetOf(output2.allocateObjectAddress); - final translatedDwarfTrace1 = - await Stream.fromIterable(output1.trace).transform(decoder).toList(); - final allocateObjectPCOffset1 = - decoder.offsetOf(output1.allocateObjectAddress); + final translatedDwarfTrace1 = await Stream.fromIterable(dwarfTrace1) + .transform(DwarfStackTraceDecoder(dwarf)) + .toList(); - print('Offset of first stub address ' - '0x${output1.allocateObjectAddress.toRadixString(16)}' - ' is $allocateObjectPCOffset1'); - print('Offset of second stub address ' - '0x${output2.allocateObjectAddress.toRadixString(16)}' - ' is $allocateObjectPCOffset2'); + final translatedStackFrames = onlySymbolicFrameLines(translatedDwarfTrace1); + final originalStackFrames = onlySymbolicFrameLines(nonDwarfTrace1); - Expect.isNotNull(allocateObjectPCOffset1); - Expect.isNotNull(allocateObjectPCOffset2); - final allocateObjectRelocatedAddress1 = - dwarf.virtualAddressOf(allocateObjectPCOffset1!); - final allocateObjectRelocatedAddress2 = - dwarf.virtualAddressOf(allocateObjectPCOffset2!); + print('Stack frames from translated non-symbolic stack trace:'); + translatedStackFrames.forEach(print); + print(''); - final allocateObjectCallInfo1 = dwarf.callInfoFor( - allocateObjectRelocatedAddress1, - includeInternalFrames: true); - final allocateObjectCallInfo2 = dwarf.callInfoFor( - allocateObjectRelocatedAddress2, - includeInternalFrames: true); + print('Stack frames from original symbolic stack trace:'); + originalStackFrames.forEach(print); + print(''); - Expect.isNotNull(allocateObjectCallInfo1); - Expect.isNotNull(allocateObjectCallInfo2); - Expect.equals(allocateObjectCallInfo1!.length, 1); - Expect.equals(allocateObjectCallInfo2!.length, 1); - Expect.isTrue( - allocateObjectCallInfo1.first is StubCallInfo, 'is not a StubCall'); - Expect.isTrue( - allocateObjectCallInfo2.first is StubCallInfo, 'is not a StubCall'); - final stubCall1 = allocateObjectCallInfo1.first as StubCallInfo; - final stubCall2 = allocateObjectCallInfo2.first as StubCallInfo; - Expect.equals(stubCall1.name, stubCall2.name); - Expect.contains('AllocateObject', stubCall1.name); - Expect.contains('AllocateObject', stubCall2.name); + Expect.isTrue(translatedStackFrames.length > 0); + Expect.isTrue(originalStackFrames.length > 0); - print("Successfully matched AllocateObject stub addresses"); - print(""); + // In symbolic mode, we don't store column information to avoid an increase + // in size of CodeStackMaps. Thus, we need to strip any columns from the + // translated non-symbolic stack to compare them via equality. + final columnStrippedTranslated = removeColumns(translatedStackFrames); - final translatedStackFrames = onlySymbolicFrameLines(translatedDwarfTrace1); - final originalStackFrames = onlySymbolicFrameLines(nonDwarfTrace); + print('Stack frames from translated non-symbolic stack trace, no columns:'); + columnStrippedTranslated.forEach(print); + print(''); - print('Stack frames from translated non-symbolic stack trace:'); - translatedStackFrames.forEach(print); - print(''); + Expect.deepEquals(columnStrippedTranslated, originalStackFrames); - print('Stack frames from original symbolic stack trace:'); - originalStackFrames.forEach(print); - print(''); + // Since we compiled directly to ELF, there should be a DSO base address + // in the stack trace header and 'virt' markers in the stack frames. - Expect.isTrue(translatedStackFrames.length > 0); - Expect.isTrue(originalStackFrames.length > 0); + // The offsets of absolute addresses from their respective DSO base + // should be the same for both traces. + final dsoBase1 = dsoBaseAddresses(dwarfTrace1).single; + final dsoBase2 = dsoBaseAddresses(dwarfTrace2).single; - // In symbolic mode, we don't store column information to avoid an increase - // in size of CodeStackMaps. Thus, we need to strip any columns from the - // translated non-symbolic stack to compare them via equality. - final columnStrippedTranslated = removeColumns(translatedStackFrames); + final absTrace1 = absoluteAddresses(dwarfTrace1); + final absTrace2 = absoluteAddresses(dwarfTrace2); - print('Stack frames from translated non-symbolic stack trace, no columns:'); - columnStrippedTranslated.forEach(print); - print(''); + final relocatedFromDso1 = absTrace1.map((a) => a - dsoBase1); + final relocatedFromDso2 = absTrace2.map((a) => a - dsoBase2); - Expect.deepEquals(columnStrippedTranslated, originalStackFrames); + Expect.deepEquals(relocatedFromDso1, relocatedFromDso2); - // Since we compiled directly to ELF, there should be a DSO base address - // in the stack trace header and 'virt' markers in the stack frames. + // The relocated addresses marked with 'virt' should match between the + // different runs, and they should also match the relocated address + // calculated from the PCOffset for each frame as well as the relocated + // address for each frame calculated using the respective DSO base. + final virtTrace1 = explicitVirtualAddresses(dwarfTrace1); + final virtTrace2 = explicitVirtualAddresses(dwarfTrace2); - // The offsets of absolute addresses from their respective DSO base - // should be the same for both traces. - final dsoBase1 = dsoBaseAddresses(output1.trace).single; - final dsoBase2 = dsoBaseAddresses(output2.trace).single; + Expect.deepEquals(virtTrace1, virtTrace2); - final absTrace1 = absoluteAddresses(output1.trace); - final absTrace2 = absoluteAddresses(output2.trace); + Expect.deepEquals( + virtTrace1, tracePCOffsets1.map((o) => o.virtualAddressIn(dwarf))); + Expect.deepEquals( + virtTrace2, tracePCOffsets2.map((o) => o.virtualAddressIn(dwarf))); - final relocatedFromDso1 = absTrace1.map((a) => a - dsoBase1); - final relocatedFromDso2 = absTrace2.map((a) => a - dsoBase2); - - Expect.deepEquals(relocatedFromDso1, relocatedFromDso2); - - // We don't print 'virt' relocated addresses when running assembled snapshots. - if (fromAssembly) return; - - // The relocated addresses marked with 'virt' should match between the - // different runs, and they should also match the relocated address - // calculated from the PCOffset for each frame as well as the relocated - // address for each frame calculated using the respective DSO base. - final virtTrace1 = explicitVirtualAddresses(output1.trace); - final virtTrace2 = explicitVirtualAddresses(output2.trace); - - Expect.deepEquals(virtTrace1, virtTrace2); - - Expect.deepEquals( - virtTrace1, tracePCOffsets1.map((o) => o.virtualAddressIn(dwarf))); - Expect.deepEquals( - virtTrace2, tracePCOffsets2.map((o) => o.virtualAddressIn(dwarf))); - - Expect.deepEquals(virtTrace1, relocatedFromDso1); - Expect.deepEquals(virtTrace2, relocatedFromDso2); -} - -Future runTestProgram( - String executable, List args) async { - final result = await runHelper(executable, args); - - if (result.exitCode == 0) { - throw 'Command did not fail with non-zero exit code'; - } - Expect.isTrue(result.stdout.isNotEmpty); - Expect.isTrue(result.stderr.isNotEmpty); - - return DwarfTestOutput( - LineSplitter.split(result.stderr).toList(), int.parse(result.stdout)); + Expect.deepEquals(virtTrace1, relocatedFromDso1); + Expect.deepEquals(virtTrace2, relocatedFromDso2); + }); } final _buildIdRE = RegExp(r"build_id: '([a-f\d]+)'"); diff --git a/runtime/tests/vm/dart/use_flag_test_helper.dart b/runtime/tests/vm/dart/use_flag_test_helper.dart index 5cd09d9ab53..431fce51195 100644 --- a/runtime/tests/vm/dart/use_flag_test_helper.dart +++ b/runtime/tests/vm/dart/use_flag_test_helper.dart @@ -38,8 +38,7 @@ String? get clangBuildToolsDir { return Directory(clangDir).existsSync() ? clangDir : null; } -Future assembleSnapshot(String assemblyPath, String snapshotPath, - {bool debug = false}) async { +Future assembleSnapshot(String assemblyPath, String snapshotPath) async { if (!Platform.isLinux && !Platform.isMacOS) { throw "Unsupported platform ${Platform.operatingSystem} for assembling"; } @@ -63,11 +62,6 @@ Future assembleSnapshot(String assemblyPath, String snapshotPath, if (Platform.isMacOS) { shared = '-dynamiclib'; - if (buildDir.endsWith('ARM64')) { - // ld: dynamic main executables must link with libSystem.dylib for - // architecture arm64 - ldFlags.add('-lSystem'); - } // Tell Mac linker to give up generating eh_frame from dwarf. ldFlags.add('-Wl,-no_compact_unwind'); } else if (buildDir.endsWith('SIMARM')) { @@ -79,9 +73,6 @@ Future assembleSnapshot(String assemblyPath, String snapshotPath, if (buildDir.endsWith('X64') || buildDir.endsWith('SIMARM64')) { ccFlags.add('-m64'); } - if (debug) { - ccFlags.add('-g'); - } await run(cc, [ ...ccFlags, @@ -167,7 +158,9 @@ Future> runOutput(String executable, List args) async { Expect.isTrue(result.stdout.isNotEmpty); Expect.isTrue(result.stderr.isEmpty); - return LineSplitter.split(result.stdout).toList(growable: false); + return await Stream.value(result.stdout as String) + .transform(const LineSplitter()) + .toList(); } Future> runError(String executable, List args) async { @@ -179,7 +172,9 @@ Future> runError(String executable, List args) async { Expect.isTrue(result.stdout.isEmpty); Expect.isTrue(result.stderr.isNotEmpty); - return LineSplitter.split(result.stderr).toList(growable: false); + return await Stream.value(result.stderr as String) + .transform(const LineSplitter()) + .toList(); } const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES'; diff --git a/runtime/tests/vm/dart/use_resolve_dwarf_paths_flag_test.dart b/runtime/tests/vm/dart/use_resolve_dwarf_paths_flag_test.dart index 9962f9738b3..30d90d5df43 100644 --- a/runtime/tests/vm/dart/use_resolve_dwarf_paths_flag_test.dart +++ b/runtime/tests/vm/dart/use_resolve_dwarf_paths_flag_test.dart @@ -5,7 +5,7 @@ // This test checks that --resolve-dwarf-paths outputs absolute and relative // paths in DWARF information. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:async"; import "dart:io"; @@ -38,8 +38,7 @@ main(List args) async { await withTempDir('dwarf-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/tests/vm/dart/use_save_debugging_info_flag_program.dart b/runtime/tests/vm/dart/use_save_debugging_info_flag_program.dart deleted file mode 100644 index 51d3c4bd491..00000000000 --- a/runtime/tests/vm/dart/use_save_debugging_info_flag_program.dart +++ /dev/null @@ -1,19 +0,0 @@ -// 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. -// Test that the full stacktrace in an error object matches the stacktrace -// handed to the catch clause. - -import "package:expect/expect.dart"; - -class C { - // operator*(o) is missing to trigger a noSuchMethodError when a C object - // is used in the multiplication below. -} - -bar(c) => c * 4; -foo(c) => bar(c); - -main() { - var a = foo(new C()); -} diff --git a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart index f79ada16747..2bd55a27c01 100644 --- a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart +++ b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart @@ -6,7 +6,7 @@ // for stripped ELF output, and that using the debugging information to look // up stripped stack trace information matches the non-stripped version. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:io"; import "dart:math"; @@ -41,8 +41,8 @@ main(List args) async { await withTempDir('save-debug-info-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + // We can just reuse the program for the use_dwarf_stack_traces test. + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/tests/vm/dart/use_strip_flag_test.dart b/runtime/tests/vm/dart/use_strip_flag_test.dart index 6dbd53b0c41..5025c84e170 100644 --- a/runtime/tests/vm/dart/use_strip_flag_test.dart +++ b/runtime/tests/vm/dart/use_strip_flag_test.dart @@ -6,7 +6,7 @@ // ELF and assembly output. This test is currently very weak, in that it just // checks that the stripped version is strictly smaller than the unstripped one. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:io"; @@ -34,9 +34,8 @@ main(List args) async { await withTempDir('strip-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - // We can just reuse the program for the use_save_debugging_info_flag test. - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + // We can just reuse the program for the use_dwarf_stack_traces test. + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/tests/vm/dart/use_trace_precompiler_flag_test.dart b/runtime/tests/vm/dart/use_trace_precompiler_flag_test.dart index 8d9123fe58f..7ea2d60de37 100644 --- a/runtime/tests/vm/dart/use_trace_precompiler_flag_test.dart +++ b/runtime/tests/vm/dart/use_trace_precompiler_flag_test.dart @@ -5,7 +5,7 @@ // This test ensures that --trace-precompiler runs without issue and prints // valid JSON for reasons to retain objects. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:convert"; import "dart:io"; @@ -37,9 +37,8 @@ main(List args) async { await withTempDir('trace-precompiler-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - // We can just reuse the program for the use_save_debugging_info_flag test. - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + // We can just reuse the program for the use_dwarf_stack_traces test. + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_program.dart b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_program.dart index 8e6838fefa4..a4e399ed0fa 100644 --- a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_program.dart +++ b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_program.dart @@ -6,8 +6,6 @@ // @dart = 2.9 -import 'dart:_internal' show VMInternalsForTesting; - import "package:expect/expect.dart"; class C { @@ -19,6 +17,5 @@ bar(c) => c * 4; foo(c) => bar(c); main() { - print(VMInternalsForTesting.randomAddressInsideAllocateObjectStub()); var a = foo(new C()); } diff --git a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart index 7bd3716dfa9..5850250145f 100644 --- a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart +++ b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart @@ -8,8 +8,9 @@ // compile-time will be used at runtime (irrespective if other values were // passed to the runtime). +// OtherResources=use_dwarf_stack_traces_flag_program.dart + import "dart:async"; -import "dart:convert"; import "dart:io"; import 'package:expect/expect.dart'; @@ -39,11 +40,8 @@ main(List args) async { } await withTempDir('dwarf-flag-test', (String tempDir) async { - // We have to use the program in its original location so it can use - // the dart:_internal library (as opposed to adding it as an OtherResources - // option to the test). - final script = path.join(sdkDir, 'runtime', 'tests', 'vm', 'dart', - 'use_dwarf_stack_traces_flag_program.dart'); + final cwDir = path.dirname(Platform.script.toFilePath()); + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. @@ -80,112 +78,46 @@ main(List args) async { ]); // Run the resulting Dwarf-AOT compiled script. - - final output1 = await runTestProgram(aotRuntime, - ['--dwarf-stack-traces-mode', scriptDwarfSnapshot, scriptDill]); - final output2 = await runTestProgram(aotRuntime, [ + final dwarfTrace1 = await runError(aotRuntime, [ + '--dwarf-stack-traces-mode', + scriptDwarfSnapshot, + scriptDill, + ]); + final dwarfTrace2 = await runError(aotRuntime, [ '--no-dwarf-stack-traces-mode', scriptDwarfSnapshot, - scriptDill + scriptDill, ]); // Run the resulting non-Dwarf-AOT compiled script. - final nonDwarfTrace1 = (await runTestProgram(aotRuntime, [ + final nonDwarfTrace1 = await runError(aotRuntime, [ '--dwarf-stack-traces-mode', scriptNonDwarfSnapshot, scriptDill, - ])) - .trace; - final nonDwarfTrace2 = (await runTestProgram(aotRuntime, [ + ]); + final nonDwarfTrace2 = await runError(aotRuntime, [ '--no-dwarf-stack-traces-mode', scriptNonDwarfSnapshot, scriptDill, - ])) - .trace; + ]); // Ensure the result is based off the flag passed to gen_snapshot, not // the one passed to the runtime. Expect.deepEquals(nonDwarfTrace1, nonDwarfTrace2); - // Check with DWARF from separate debugging information. - await compareTraces(nonDwarfTrace1, output1, output2, scriptDwarfDebugInfo); - // Check with DWARF in generated snapshot. - await compareTraces(nonDwarfTrace1, output1, output2, scriptDwarfSnapshot); + // For DWARF stack traces, we can't guarantee that the stack traces are + // textually equal on all platforms, but if we retrieve the PC offsets + // out of the stack trace, those should be equal. + final tracePCOffsets1 = collectPCOffsets(dwarfTrace1); + final tracePCOffsets2 = collectPCOffsets(dwarfTrace2); + Expect.deepEquals(tracePCOffsets1, tracePCOffsets2); - if (Platform.isLinux || Platform.isMacOS) { - final scriptAssembly = path.join(tempDir, 'dwarf_assembly.S'); - final scriptDwarfAssemblyDebugInfo = - path.join(tempDir, 'dwarf_assembly_info.so'); - final scriptDwarfAssemblySnapshot = - path.join(tempDir, 'dwarf_assembly.so'); - // We get a separate .dSYM bundle on MacOS. - final scriptDwarfAssemblyDebugSnapshot = - scriptDwarfAssemblySnapshot + (Platform.isMacOS ? '.dSYM' : ''); + // Check that translating the DWARF stack trace (without internal frames) + // matches the symbolic stack trace. + final dwarf = Dwarf.fromFile(scriptDwarfDebugInfo); + Expect.isNotNull(dwarf); - await run(genSnapshot, [ - // We test --dwarf-stack-traces-mode, not --dwarf-stack-traces, because - // the latter is a handler that sets the former and also may change - // other flags. This way, we limit the difference between the two - // snapshots and also directly test the flag saved as a VM global flag. - '--dwarf-stack-traces-mode', - '--save-debugging-info=$scriptDwarfAssemblyDebugInfo', - '--snapshot-kind=app-aot-assembly', - '--assembly=$scriptAssembly', - scriptDill, - ]); - - await assembleSnapshot(scriptAssembly, scriptDwarfAssemblySnapshot, - debug: true); - - // Run the resulting Dwarf-AOT compiled script. - final assemblyOutput1 = await runTestProgram(aotRuntime, [ - '--dwarf-stack-traces-mode', - scriptDwarfAssemblySnapshot, - scriptDill, - ]); - final assemblyOutput2 = await runTestProgram(aotRuntime, [ - '--no-dwarf-stack-traces-mode', - scriptDwarfAssemblySnapshot, - scriptDill, - ]); - - // Check with DWARF in assembled snapshot. - await compareTraces(nonDwarfTrace1, assemblyOutput1, assemblyOutput2, - scriptDwarfAssemblyDebugSnapshot, - fromAssembly: true); - // Check with DWARF from separate debugging information. - await compareTraces(nonDwarfTrace1, assemblyOutput1, assemblyOutput2, - scriptDwarfAssemblyDebugInfo, - fromAssembly: true); - } - }); -} - -class DwarfTestOutput { - final List trace; - final int allocateObjectAddress; - - DwarfTestOutput(this.trace, this.allocateObjectAddress); -} - -Future compareTraces(List nonDwarfTrace, DwarfTestOutput output1, - DwarfTestOutput output2, String dwarfPath, - {bool fromAssembly = false}) async { - // For DWARF stack traces, we can't guarantee that the stack traces are - // textually equal on all platforms, but if we retrieve the PC offsets - // out of the stack trace, those should be equal. - final tracePCOffsets1 = collectPCOffsets(output1.trace); - final tracePCOffsets2 = collectPCOffsets(output2.trace); - Expect.deepEquals(tracePCOffsets1, tracePCOffsets2); - - // Check that translating the DWARF stack trace (without internal frames) - // matches the symbolic stack trace. - print("Reading DWARF info from ${dwarfPath}"); - final dwarf = Dwarf.fromFile(dwarfPath); - Expect.isNotNull(dwarf); - - // Check that build IDs match for traces from running ELF snapshots. - if (!fromAssembly) { + // Check that build IDs match for traces. Expect.isNotNull(dwarf.buildId); print('Dwarf build ID: "${dwarf.buildId}"'); // We should never generate an all-zero build ID. @@ -193,139 +125,77 @@ Future compareTraces(List nonDwarfTrace, DwarfTestOutput output1, // This is a common failure case as well, when HashBitsContainer ends up // hashing over seemingly empty sections. Expect.notEquals(dwarf.buildId, "01000000010000000100000001000000"); - final buildId1 = buildId(output1.trace); + final buildId1 = buildId(dwarfTrace1); Expect.isFalse(buildId1.isEmpty); print('Trace 1 build ID: "${buildId1}"'); Expect.equals(dwarf.buildId, buildId1); - final buildId2 = buildId(output2.trace); + final buildId2 = buildId(dwarfTrace2); Expect.isFalse(buildId2.isEmpty); print('Trace 2 build ID: "${buildId2}"'); Expect.equals(dwarf.buildId, buildId2); - } - final decoder = DwarfStackTraceDecoder(dwarf); - // Run on the second trace just for the side effect of getting the header - // information so we can get the PC offset of the output address. - await Stream.fromIterable(output2.trace).transform(decoder).toList(); - final allocateObjectPCOffset2 = - decoder.offsetOf(output2.allocateObjectAddress); - final translatedDwarfTrace1 = - await Stream.fromIterable(output1.trace).transform(decoder).toList(); - final allocateObjectPCOffset1 = - decoder.offsetOf(output1.allocateObjectAddress); + final translatedDwarfTrace1 = await Stream.fromIterable(dwarfTrace1) + .transform(DwarfStackTraceDecoder(dwarf)) + .toList(); - print('Offset of first stub address ' - '0x${output1.allocateObjectAddress.toRadixString(16)}' - ' is $allocateObjectPCOffset1'); - print('Offset of second stub address ' - '0x${output2.allocateObjectAddress.toRadixString(16)}' - ' is $allocateObjectPCOffset2'); + final translatedStackFrames = onlySymbolicFrameLines(translatedDwarfTrace1); + final originalStackFrames = onlySymbolicFrameLines(nonDwarfTrace1); - Expect.isNotNull(allocateObjectPCOffset1); - Expect.isNotNull(allocateObjectPCOffset2); - final allocateObjectRelocatedAddress1 = - dwarf.virtualAddressOf(allocateObjectPCOffset1); - final allocateObjectRelocatedAddress2 = - dwarf.virtualAddressOf(allocateObjectPCOffset2); + print('Stack frames from translated non-symbolic stack trace:'); + translatedStackFrames.forEach(print); + print(''); - final allocateObjectCallInfo1 = dwarf.callInfoFor( - allocateObjectRelocatedAddress1, - includeInternalFrames: true); - final allocateObjectCallInfo2 = dwarf.callInfoFor( - allocateObjectRelocatedAddress2, - includeInternalFrames: true); + print('Stack frames from original symbolic stack trace:'); + originalStackFrames.forEach(print); + print(''); - Expect.isNotNull(allocateObjectCallInfo1); - Expect.isNotNull(allocateObjectCallInfo2); - Expect.equals(allocateObjectCallInfo1.length, 1); - Expect.equals(allocateObjectCallInfo2.length, 1); - Expect.isTrue( - allocateObjectCallInfo1.first is StubCallInfo, 'is not a StubCall'); - Expect.isTrue( - allocateObjectCallInfo2.first is StubCallInfo, 'is not a StubCall'); - final stubCall1 = allocateObjectCallInfo1.first as StubCallInfo; - final stubCall2 = allocateObjectCallInfo2.first as StubCallInfo; - Expect.equals(stubCall1.name, stubCall2.name); - Expect.contains('AllocateObject', stubCall1.name); - Expect.contains('AllocateObject', stubCall2.name); + Expect.isTrue(translatedStackFrames.length > 0); + Expect.isTrue(originalStackFrames.length > 0); - print("Successfully matched AllocateObject stub addresses"); - print(""); + // In symbolic mode, we don't store column information to avoid an increase + // in size of CodeStackMaps. Thus, we need to strip any columns from the + // translated non-symbolic stack to compare them via equality. + final columnStrippedTranslated = removeColumns(translatedStackFrames); - final translatedStackFrames = onlySymbolicFrameLines(translatedDwarfTrace1); - final originalStackFrames = onlySymbolicFrameLines(nonDwarfTrace); + print('Stack frames from translated non-symbolic stack trace, no columns:'); + columnStrippedTranslated.forEach(print); + print(''); - print('Stack frames from translated non-symbolic stack trace:'); - translatedStackFrames.forEach(print); - print(''); + Expect.deepEquals(columnStrippedTranslated, originalStackFrames); - print('Stack frames from original symbolic stack trace:'); - originalStackFrames.forEach(print); - print(''); + // Since we compiled directly to ELF, there should be a DSO base address + // in the stack trace header and 'virt' markers in the stack frames. - Expect.isTrue(translatedStackFrames.length > 0); - Expect.isTrue(originalStackFrames.length > 0); + // The offsets of absolute addresses from their respective DSO base + // should be the same for both traces. + final dsoBase1 = dsoBaseAddresses(dwarfTrace1).single; + final dsoBase2 = dsoBaseAddresses(dwarfTrace2).single; - // In symbolic mode, we don't store column information to avoid an increase - // in size of CodeStackMaps. Thus, we need to strip any columns from the - // translated non-symbolic stack to compare them via equality. - final columnStrippedTranslated = removeColumns(translatedStackFrames); + final absTrace1 = absoluteAddresses(dwarfTrace1); + final absTrace2 = absoluteAddresses(dwarfTrace2); - print('Stack frames from translated non-symbolic stack trace, no columns:'); - columnStrippedTranslated.forEach(print); - print(''); + final relocatedFromDso1 = absTrace1.map((a) => a - dsoBase1); + final relocatedFromDso2 = absTrace2.map((a) => a - dsoBase2); - Expect.deepEquals(columnStrippedTranslated, originalStackFrames); + Expect.deepEquals(relocatedFromDso1, relocatedFromDso2); - // Since we compiled directly to ELF, there should be a DSO base address - // in the stack trace header and 'virt' markers in the stack frames. + // The relocated addresses marked with 'virt' should match between the + // different runs, and they should also match the relocated address + // calculated from the PCOffset for each frame as well as the relocated + // address for each frame calculated using the respective DSO base. + final virtTrace1 = explicitVirtualAddresses(dwarfTrace1); + final virtTrace2 = explicitVirtualAddresses(dwarfTrace2); - // The offsets of absolute addresses from their respective DSO base - // should be the same for both traces. - final dsoBase1 = dsoBaseAddresses(output1.trace).single; - final dsoBase2 = dsoBaseAddresses(output2.trace).single; + Expect.deepEquals(virtTrace1, virtTrace2); - final absTrace1 = absoluteAddresses(output1.trace); - final absTrace2 = absoluteAddresses(output2.trace); + Expect.deepEquals( + virtTrace1, tracePCOffsets1.map((o) => o.virtualAddressIn(dwarf))); + Expect.deepEquals( + virtTrace2, tracePCOffsets2.map((o) => o.virtualAddressIn(dwarf))); - final relocatedFromDso1 = absTrace1.map((a) => a - dsoBase1); - final relocatedFromDso2 = absTrace2.map((a) => a - dsoBase2); - - Expect.deepEquals(relocatedFromDso1, relocatedFromDso2); - - // We don't print 'virt' relocated addresses when running assembled snapshots. - if (fromAssembly) return; - - // The relocated addresses marked with 'virt' should match between the - // different runs, and they should also match the relocated address - // calculated from the PCOffset for each frame as well as the relocated - // address for each frame calculated using the respective DSO base. - final virtTrace1 = explicitVirtualAddresses(output1.trace); - final virtTrace2 = explicitVirtualAddresses(output2.trace); - - Expect.deepEquals(virtTrace1, virtTrace2); - - Expect.deepEquals( - virtTrace1, tracePCOffsets1.map((o) => o.virtualAddressIn(dwarf))); - Expect.deepEquals( - virtTrace2, tracePCOffsets2.map((o) => o.virtualAddressIn(dwarf))); - - Expect.deepEquals(virtTrace1, relocatedFromDso1); - Expect.deepEquals(virtTrace2, relocatedFromDso2); -} - -Future runTestProgram( - String executable, List args) async { - final result = await runHelper(executable, args); - - if (result.exitCode == 0) { - throw 'Command did not fail with non-zero exit code'; - } - Expect.isTrue(result.stdout.isNotEmpty); - Expect.isTrue(result.stderr.isNotEmpty); - - return DwarfTestOutput( - LineSplitter.split(result.stderr).toList(), int.parse(result.stdout)); + Expect.deepEquals(virtTrace1, relocatedFromDso1); + Expect.deepEquals(virtTrace2, relocatedFromDso2); + }); } final _buildIdRE = RegExp(r"build_id: '([a-f\d]+)'"); diff --git a/runtime/tests/vm/dart_2/use_flag_test_helper.dart b/runtime/tests/vm/dart_2/use_flag_test_helper.dart index 8fdf89bad54..b408f42adb5 100644 --- a/runtime/tests/vm/dart_2/use_flag_test_helper.dart +++ b/runtime/tests/vm/dart_2/use_flag_test_helper.dart @@ -40,8 +40,7 @@ String get clangBuildToolsDir { return Directory(clangDir).existsSync() ? clangDir : null; } -Future assembleSnapshot(String assemblyPath, String snapshotPath, - {bool debug = false}) async { +Future assembleSnapshot(String assemblyPath, String snapshotPath) async { if (!Platform.isLinux && !Platform.isMacOS) { throw "Unsupported platform ${Platform.operatingSystem} for assembling"; } @@ -64,11 +63,6 @@ Future assembleSnapshot(String assemblyPath, String snapshotPath, if (Platform.isMacOS) { shared = '-dynamiclib'; - if (buildDir.endsWith('ARM64')) { - // ld: dynamic main executables must link with libSystem.dylib for - // architecture arm64 - ldFlags.add('-lSystem'); - } // Tell Mac linker to give up generating eh_frame from dwarf. ldFlags.add('-Wl,-no_compact_unwind'); } else if (buildDir.endsWith('SIMARM')) { @@ -80,9 +74,6 @@ Future assembleSnapshot(String assemblyPath, String snapshotPath, if (buildDir.endsWith('X64') || buildDir.endsWith('SIMARM64')) { ccFlags.add('-m64'); } - if (debug) { - ccFlags.add('-g'); - } await run(cc, [ ...ccFlags, @@ -167,7 +158,9 @@ Future> runOutput(String executable, List args) async { Expect.isTrue(result.stdout.isNotEmpty); Expect.isTrue(result.stderr.isEmpty); - return LineSplitter.split(result.stdout).toList(growable: false); + return await Stream.value(result.stdout as String) + .transform(const LineSplitter()) + .toList(); } Future> runError(String executable, List args) async { @@ -179,7 +172,9 @@ Future> runError(String executable, List args) async { Expect.isTrue(result.stdout.isEmpty); Expect.isTrue(result.stderr.isNotEmpty); - return LineSplitter.split(result.stderr).toList(growable: false); + return await Stream.value(result.stderr as String) + .transform(const LineSplitter()) + .toList(); } const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES'; diff --git a/runtime/tests/vm/dart_2/use_resolve_dwarf_paths_flag_test.dart b/runtime/tests/vm/dart_2/use_resolve_dwarf_paths_flag_test.dart index 1f9df8d2506..cd0ed3943d5 100644 --- a/runtime/tests/vm/dart_2/use_resolve_dwarf_paths_flag_test.dart +++ b/runtime/tests/vm/dart_2/use_resolve_dwarf_paths_flag_test.dart @@ -7,7 +7,7 @@ // This test checks that --resolve-dwarf-paths outputs absolute and relative // paths in DWARF information. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:async"; import "dart:io"; @@ -40,8 +40,7 @@ main(List args) async { await withTempDir('dwarf-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_program.dart b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_program.dart deleted file mode 100644 index c01efdbb09b..00000000000 --- a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_program.dart +++ /dev/null @@ -1,21 +0,0 @@ -// 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. -// Test that the full stacktrace in an error object matches the stacktrace -// handed to the catch clause. - -// @dart = 2.9 - -import "package:expect/expect.dart"; - -class C { - // operator*(o) is missing to trigger a noSuchMethodError when a C object - // is used in the multiplication below. -} - -bar(c) => c * 4; -foo(c) => bar(c); - -main() { - var a = foo(new C()); -} diff --git a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart index ddee6b2ed66..db548f44ab4 100644 --- a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart +++ b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart @@ -8,7 +8,7 @@ // for stripped ELF output, and that using the debugging information to look // up stripped stack trace information matches the non-stripped version. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:io"; import "dart:math"; @@ -43,8 +43,8 @@ main(List args) async { await withTempDir('save-debug-info-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + // We can just reuse the program for the use_dwarf_stack_traces test. + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/tests/vm/dart_2/use_strip_flag_test.dart b/runtime/tests/vm/dart_2/use_strip_flag_test.dart index e145e86c007..a552ea0b9af 100644 --- a/runtime/tests/vm/dart_2/use_strip_flag_test.dart +++ b/runtime/tests/vm/dart_2/use_strip_flag_test.dart @@ -8,7 +8,7 @@ // ELF and assembly output. This test is currently very weak, in that it just // checks that the stripped version is strictly smaller than the unstripped one. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:io"; @@ -36,9 +36,8 @@ main(List args) async { await withTempDir('strip-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - // We can just reuse the program for the use_save_debugging_info_flag test. - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + // We can just reuse the program for the use_dwarf_stack_traces test. + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/tests/vm/dart_2/use_trace_precompiler_flag_test.dart b/runtime/tests/vm/dart_2/use_trace_precompiler_flag_test.dart index 0cb879aff38..4a054e875f5 100644 --- a/runtime/tests/vm/dart_2/use_trace_precompiler_flag_test.dart +++ b/runtime/tests/vm/dart_2/use_trace_precompiler_flag_test.dart @@ -7,7 +7,7 @@ // This test ensures that --trace-precompiler runs without issue and prints // valid JSON for reasons to retain objects. -// OtherResources=use_save_debugging_info_flag_program.dart +// OtherResources=use_dwarf_stack_traces_flag_program.dart import "dart:convert"; import "dart:io"; @@ -39,9 +39,8 @@ main(List args) async { await withTempDir('trace-precompiler-flag-test', (String tempDir) async { final cwDir = path.dirname(Platform.script.toFilePath()); - // We can just reuse the program for the use_save_debugging_info_flag test. - final script = - path.join(cwDir, 'use_save_debugging_info_flag_program.dart'); + // We can just reuse the program for the use_dwarf_stack_traces test. + final script = path.join(cwDir, 'use_dwarf_stack_traces_flag_program.dart'); final scriptDill = path.join(tempDir, 'flag_program.dill'); // Compile script to Kernel IR. diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index e9bc548d64a..6e826798142 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -330,7 +330,6 @@ namespace dart { V(Internal_writeIntoOneByteString, 3) \ V(Internal_writeIntoTwoByteString, 3) \ V(Internal_deoptimizeFunctionsOnStack, 0) \ - V(Internal_randomAddressInsideAllocateObjectStub, 0) \ V(InvocationMirror_unpackTypeArguments, 2) \ V(NoSuchMethodError_existingMethodSignature, 3) \ V(Uri_isWindowsPlatform, 0) \ diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart index 934db516e3d..c02b2f80903 100644 --- a/sdk/lib/_internal/vm/lib/internal_patch.dart +++ b/sdk/lib/_internal/vm/lib/internal_patch.dart @@ -179,10 +179,6 @@ abstract class VMInternalsForTesting { @pragma("vm:external-name", "Internal_deoptimizeFunctionsOnStack") external static void deoptimizeFunctionsOnStack(); - - // Used to verify that PC addresses in stubs can be named using DWARF info. - @pragma("vm:external-name", "Internal_randomAddressInsideAllocateObjectStub") - external static int randomAddressInsideAllocateObjectStub(); } @patch