[vm/compiler] Reland "Add --save-debugging-info flag to gen_snapshot."
Removes an unnecessary change to the names of type testing stubs that allowed name collisions to happen. Old commit message: The flag can be used when creating AOT snapshots. The resulting file can be used with package:vm/dwarf/convert.dart to convert DWARF-based stack traces to stack traces with function, file, and line number information. Currently the saved file will be an ELF file with DWARF debugging information, but this is subject to change in the future. To avoid being affected by any changes in format, read the DWARF information from the file using Dwarf.fromFile() in package:vm/dwarf/dwarf.dart. Also adds --dwarf-stack-traces to the VM global flag list, so its value at compilation will be read out of snapshots by the precompiled runtime. Fixes https://github.com/dart-lang/sdk/issues/39512, which was due to a missing compiler::target:: prefix for Instructions::HeaderSize() in BlobImageWriter::WriteText(). Exposes the information suggested in https://github.com/dart-lang/sdk/issues/39490 so that package:vm/dwarf can appropriately convert absolute PC addresses to the correct virtual address for a given DWARF line number program. Bug: https://github.com/dart-lang/sdk/issues/35851 Change-Id: I6723449dceb0b89c054a1f1e70e7acd7749baf14 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-precomp-mac-release-simarm64-try,vm-kernel-precomp-win-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128730 Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
578e22fd10
commit
089aeedf56
@@ -7,8 +7,8 @@ import "dart:convert";
|
||||
import "dart:io" as io;
|
||||
|
||||
import 'package:args/args.dart' show ArgParser, ArgResults;
|
||||
import 'package:vm/elf/convert.dart';
|
||||
import 'package:vm/elf/dwarf.dart';
|
||||
import 'package:vm/dwarf/convert.dart';
|
||||
import 'package:vm/dwarf/dwarf.dart';
|
||||
|
||||
final ArgParser _argParser = new ArgParser(allowTrailingOptions: true)
|
||||
..addOption('elf',
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import "dart:async";
|
||||
import "dart:math";
|
||||
|
||||
import "dwarf.dart";
|
||||
|
||||
String _stackTracePiece(CallInfo call, int depth) => "#${depth}\t${call}";
|
||||
|
||||
final _traceStart = 'Warning: This VM has been configured to produce '
|
||||
'stack traces that violate the Dart standard.';
|
||||
final _traceInstructionsStartRE = RegExp(r'isolate_instructions: ([0-9a-f]+) '
|
||||
r'vm_instructions: ([0-9a-f]+)$');
|
||||
final _traceLineRE =
|
||||
RegExp(r' #(\d{2}) abs ([0-9a-f]+)(?: virt [0-9a-f]+)? (.*)$');
|
||||
|
||||
enum InstructionSection { vm, isolate }
|
||||
|
||||
class PCOffset {
|
||||
final int offset;
|
||||
final InstructionSection section;
|
||||
|
||||
PCOffset(this.offset, this.section);
|
||||
|
||||
int virtualAddress(Dwarf dwarf) {
|
||||
switch (section) {
|
||||
case InstructionSection.vm:
|
||||
return dwarf.convertToVMVirtualAddress(offset);
|
||||
case InstructionSection.isolate:
|
||||
return dwarf.convertToIsolateVirtualAddress(offset);
|
||||
}
|
||||
}
|
||||
|
||||
int get hashCode => offset.hashCode;
|
||||
|
||||
bool operator ==(Object other) {
|
||||
return other is PCOffset &&
|
||||
offset == other.offset &&
|
||||
section == other.section;
|
||||
}
|
||||
}
|
||||
|
||||
class StackTraceHeader {
|
||||
final int _isolateStart;
|
||||
final int _vmStart;
|
||||
|
||||
StackTraceHeader(this._isolateStart, this._vmStart);
|
||||
|
||||
factory StackTraceHeader.fromMatch(Match match) {
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
final isolateAddr = int.parse("0x" + match[1]);
|
||||
final vmAddr = int.parse("0x" + match[2]);
|
||||
return StackTraceHeader(isolateAddr, vmAddr);
|
||||
}
|
||||
|
||||
PCOffset convertAbsoluteAddress(int address) {
|
||||
int isolateOffset = address - _isolateStart;
|
||||
int vmOffset = address - _vmStart;
|
||||
if (vmOffset > 0 && vmOffset == min(vmOffset, isolateOffset)) {
|
||||
return PCOffset(vmOffset, InstructionSection.vm);
|
||||
} else {
|
||||
return PCOffset(isolateOffset, InstructionSection.isolate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PCOffset retrievePCOffset(StackTraceHeader header, Match match) {
|
||||
assert(header != null && match != null);
|
||||
final address = int.parse("0x" + match[2]);
|
||||
return header.convertAbsoluteAddress(address);
|
||||
}
|
||||
|
||||
// Returns the [PCOffset] for each frame's absolute PC address if [lines]
|
||||
// contains one or more DWARF stack traces.
|
||||
Iterable<PCOffset> collectPCOffsets(Iterable<String> lines) {
|
||||
final ret = <PCOffset>[];
|
||||
StackTraceHeader header = null;
|
||||
for (var line in lines) {
|
||||
if (line.endsWith(_traceStart)) {
|
||||
header = null;
|
||||
}
|
||||
final startMatch = _traceInstructionsStartRE.firstMatch(line);
|
||||
if (startMatch != null) {
|
||||
header = StackTraceHeader.fromMatch(startMatch);
|
||||
continue;
|
||||
}
|
||||
final lineMatch = _traceLineRE.firstMatch(line);
|
||||
if (lineMatch != null) {
|
||||
ret.add(retrievePCOffset(header, lineMatch));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Scans a stream of lines for Dart DWARF-based stack traces (i.e., Dart stack
|
||||
// traces where the frame entries include PC addresses). For each stack frame
|
||||
// found, the transformer attempts to locate a function name, file name and line
|
||||
// number using the provided DWARF information.
|
||||
//
|
||||
// If no information is found, or the line is not a stack frame, the line is
|
||||
// output to the sink unchanged.
|
||||
//
|
||||
// If the located information corresponds to Dart internals, the frame will be
|
||||
// dropped.
|
||||
//
|
||||
// Otherwise, at least one altered stack frame is generated and replaces the
|
||||
// stack frame portion of the original line. If the PC address corresponds to
|
||||
// inlined code, then multiple stack frames may be generated. When multiple
|
||||
// stack frames are generated, only the first replaces the stack frame portion
|
||||
// of the original line, and the remaining frames are separately output.
|
||||
class DwarfStackTraceDecoder extends StreamTransformerBase<String, String> {
|
||||
final Dwarf _dwarf;
|
||||
final bool includeInternalFrames;
|
||||
|
||||
DwarfStackTraceDecoder(this._dwarf, {this.includeInternalFrames = false});
|
||||
|
||||
Stream<String> bind(Stream<String> stream) => Stream<String>.eventTransformed(
|
||||
stream,
|
||||
(sink) => _DwarfStackTraceEventSink(sink, _dwarf,
|
||||
includeInternalFrames: includeInternalFrames));
|
||||
}
|
||||
|
||||
class _DwarfStackTraceEventSink implements EventSink<String> {
|
||||
final EventSink<String> _sink;
|
||||
final Dwarf _dwarf;
|
||||
final bool includeInternalFrames;
|
||||
int _cachedDepth = 0;
|
||||
StackTraceHeader _cachedHeader = null;
|
||||
|
||||
_DwarfStackTraceEventSink(this._sink, this._dwarf,
|
||||
{this.includeInternalFrames = false});
|
||||
|
||||
void close() => _sink.close();
|
||||
void addError(Object e, [StackTrace st]) => _sink.addError(e, st);
|
||||
Future addStream(Stream<String> stream) => stream.forEach(add);
|
||||
|
||||
void add(String line) {
|
||||
// Reset any stack-related state when we see the start of a new
|
||||
// stacktrace.
|
||||
if (line.endsWith(_traceStart)) {
|
||||
_cachedDepth = 0;
|
||||
_cachedHeader = null;
|
||||
}
|
||||
final startMatch = _traceInstructionsStartRE.firstMatch(line);
|
||||
if (startMatch != null) {
|
||||
_cachedHeader = StackTraceHeader.fromMatch(startMatch);
|
||||
_sink.add(line);
|
||||
return;
|
||||
}
|
||||
final lineMatch = _traceLineRE.firstMatch(line);
|
||||
if (lineMatch == null) {
|
||||
_sink.add(line);
|
||||
return;
|
||||
}
|
||||
final location =
|
||||
retrievePCOffset(_cachedHeader, lineMatch).virtualAddress(_dwarf);
|
||||
final callInfo = _dwarf
|
||||
.callInfo(location, includeInternalFrames: includeInternalFrames)
|
||||
?.toList();
|
||||
if (callInfo == null) {
|
||||
// If we can't get appropriate information for the stack trace line,
|
||||
// then just return the line unchanged.
|
||||
_sink.add(line);
|
||||
return;
|
||||
} else if (callInfo.isEmpty) {
|
||||
// No lines to output (as this corresponds to Dart internals).
|
||||
return;
|
||||
}
|
||||
_sink.add(line.substring(0, lineMatch.start) +
|
||||
_stackTracePiece(callInfo.first, _cachedDepth++));
|
||||
for (int i = 1; i < callInfo.length; i++) {
|
||||
_sink.add(_stackTracePiece(callInfo[i], _cachedDepth++));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -969,6 +969,8 @@ class Dwarf {
|
||||
Map<int, _AbbreviationsTable> abbreviationTables;
|
||||
DebugInfo debugInfo;
|
||||
LineNumberInfo lineNumberInfo;
|
||||
int vmStartAddress;
|
||||
int isolateStartAddress;
|
||||
|
||||
Dwarf.fromElf(Elf this.elf) {
|
||||
_loadSections();
|
||||
@@ -980,7 +982,7 @@ class Dwarf {
|
||||
}
|
||||
|
||||
void _loadSections() {
|
||||
final abbrevSection = elf.namedSection(".debug_abbrev");
|
||||
final abbrevSection = elf.namedSection(".debug_abbrev").first;
|
||||
abbreviationTables = <int, _AbbreviationsTable>{};
|
||||
var abbreviationOffset = 0;
|
||||
while (abbreviationOffset < abbrevSection.reader.length) {
|
||||
@@ -991,11 +993,21 @@ class Dwarf {
|
||||
}
|
||||
assert(abbreviationOffset == abbrevSection.reader.length);
|
||||
|
||||
final lineNumberSection = elf.namedSection(".debug_line");
|
||||
final lineNumberSection = elf.namedSection(".debug_line").first;
|
||||
lineNumberInfo = LineNumberInfo.fromReader(lineNumberSection.reader);
|
||||
|
||||
final infoSection = elf.namedSection(".debug_info");
|
||||
final infoSection = elf.namedSection(".debug_info").first;
|
||||
debugInfo = DebugInfo.fromReader(infoSection.reader, this);
|
||||
|
||||
final textSegments = elf.namedSection(".text");
|
||||
if (textSegments.length != 2) {
|
||||
throw FormatException(
|
||||
"Expected two text segments for VM and isolate instructions");
|
||||
}
|
||||
|
||||
final textAddresses = textSegments.map((s) => s.headerEntry.addr).toList();
|
||||
vmStartAddress = textAddresses[0];
|
||||
isolateStartAddress = textAddresses[1];
|
||||
}
|
||||
|
||||
Iterable<CallInfo> callInfo(int address,
|
||||
@@ -1007,6 +1019,14 @@ class Dwarf {
|
||||
return calls;
|
||||
}
|
||||
|
||||
int convertToVMVirtualAddress(int textOffset) {
|
||||
return textOffset + vmStartAddress;
|
||||
}
|
||||
|
||||
int convertToIsolateVirtualAddress(int textOffset) {
|
||||
return textOffset + isolateStartAddress;
|
||||
}
|
||||
|
||||
String toString() =>
|
||||
"DWARF debugging information:\n\n" +
|
||||
abbreviationTables
|
||||
@@ -415,7 +415,7 @@ class SectionHeader {
|
||||
// for the other section header entries.
|
||||
final nameTableEntry = _readSectionHeaderEntry(stringsIndex);
|
||||
assert(nameTableEntry.type == SectionHeaderEntry._SHT_STRTAB);
|
||||
nameTable = StringTable.fromReader(
|
||||
nameTable = StringTable(nameTableEntry,
|
||||
reader.refocus(nameTableEntry.offset, nameTableEntry.size));
|
||||
nameTableEntry.setName(nameTable);
|
||||
|
||||
@@ -444,19 +444,16 @@ class SectionHeader {
|
||||
|
||||
class Section {
|
||||
final Reader reader;
|
||||
final SectionHeaderEntry headerEntry;
|
||||
|
||||
Section.fromReader(Reader this.reader);
|
||||
Section(this.headerEntry, this.reader);
|
||||
|
||||
factory Section.fromEntryAndReader(SectionHeaderEntry entry, Reader reader) {
|
||||
switch (entry.type) {
|
||||
case SectionHeaderEntry._SHT_NULL:
|
||||
return NullSection.fromReader(reader.refocus(entry.offset, 0));
|
||||
case SectionHeaderEntry._SHT_NOBITS:
|
||||
return NoBits.fromReader(reader.refocus(entry.offset, 0));
|
||||
case SectionHeaderEntry._SHT_STRTAB:
|
||||
return StringTable.fromReader(reader.refocus(entry.offset, entry.size));
|
||||
return StringTable(entry, reader);
|
||||
default:
|
||||
return Section.fromReader(reader.refocus(entry.offset, entry.size));
|
||||
return Section(entry, reader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,22 +461,10 @@ class Section {
|
||||
String toString() => "an unparsed section of ${length} bytes\n";
|
||||
}
|
||||
|
||||
class NullSection extends Section {
|
||||
NullSection.fromReader(Reader reader) : super.fromReader(reader);
|
||||
|
||||
String toString() => "a null section\n";
|
||||
}
|
||||
|
||||
class NoBits extends Section {
|
||||
NoBits.fromReader(Reader reader) : super.fromReader(reader);
|
||||
|
||||
String toString() => "a section with no bits in file\n";
|
||||
}
|
||||
|
||||
class StringTable extends Section {
|
||||
final _entries = Map<int, String>();
|
||||
|
||||
StringTable.fromReader(Reader reader) : super.fromReader(reader) {
|
||||
StringTable(SectionHeaderEntry entry, Reader reader) : super(entry, reader) {
|
||||
while (!reader.done) {
|
||||
_entries[reader.offset] = reader.readNullTerminatedString();
|
||||
}
|
||||
@@ -524,13 +509,17 @@ class Elf {
|
||||
return ret;
|
||||
}
|
||||
|
||||
Section namedSection(String name) {
|
||||
Iterable<Section> namedSection(String name) {
|
||||
final ret = <Section>[];
|
||||
for (var entry in sections.keys) {
|
||||
if (entry.name == name) {
|
||||
return sections[entry];
|
||||
ret.add(sections[entry]);
|
||||
}
|
||||
}
|
||||
throw FormatException("No section named $name found in ELF file");
|
||||
if (ret.isEmpty) {
|
||||
throw FormatException("No section named $name found in ELF file");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _read() {
|
||||
@@ -554,7 +543,8 @@ class Elf {
|
||||
if (i == header.sectionHeaderStringsIndex) {
|
||||
sections[entry] = sectionHeader.nameTable;
|
||||
} else {
|
||||
sections[entry] = Section.fromEntryAndReader(entry, reader.copy());
|
||||
sections[entry] = Section.fromEntryAndReader(
|
||||
entry, reader.refocus(entry.offset, entry.size));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import "dart:async";
|
||||
|
||||
import "dwarf.dart";
|
||||
|
||||
String _stackTracePiece(CallInfo call, int depth) => "#${depth}\t${call}";
|
||||
|
||||
final _traceLineRE = RegExp(r' #(\d{2}) pc ([0-9a-f]+) (.*)$');
|
||||
|
||||
Iterable<int> collectPCAddresses(Iterable<String> lines) {
|
||||
final ret = <int>[];
|
||||
for (var line in lines) {
|
||||
final match = _traceLineRE.firstMatch(line);
|
||||
if (match == null) continue;
|
||||
ret.add(int.parse("0x" + match[2]));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Scans a stream of lines for Dart DWARF-based stack traces (i.e., Dart stack
|
||||
// traces where the frame entries include PC addresses). For each stack frame
|
||||
// found, the transformer attempts to locate a function name, file name and line
|
||||
// number using the provided DWARF information.
|
||||
//
|
||||
// If no information is found, or the line is not a stack frame, the line is
|
||||
// output to the sink unchanged.
|
||||
//
|
||||
// If the located information corresponds to Dart internals, the frame will be
|
||||
// dropped.
|
||||
//
|
||||
// Otherwise, at least one altered stack frame is generated and replaces the
|
||||
// stack frame portion of the original line. If the PC address corresponds to
|
||||
// inlined code, then multiple stack frames may be generated. When multiple
|
||||
// stack frames are generated, only the first replaces the stack frame portion
|
||||
// of the original line, and the remaining frames are separately output.
|
||||
class DwarfStackTraceDecoder extends StreamTransformerBase<String, String> {
|
||||
final Dwarf _dwarf;
|
||||
final bool includeInternalFrames;
|
||||
|
||||
DwarfStackTraceDecoder(this._dwarf, {this.includeInternalFrames = false});
|
||||
|
||||
Stream<String> bind(Stream<String> stream) => Stream<String>.eventTransformed(
|
||||
stream,
|
||||
(sink) => _DwarfStackTraceEventSink(sink, _dwarf,
|
||||
includeInternalFrames: includeInternalFrames));
|
||||
}
|
||||
|
||||
class _DwarfStackTraceEventSink implements EventSink<String> {
|
||||
final EventSink<String> _sink;
|
||||
final Dwarf _dwarf;
|
||||
final bool includeInternalFrames;
|
||||
int _cachedDepth = 0;
|
||||
|
||||
_DwarfStackTraceEventSink(this._sink, this._dwarf,
|
||||
{this.includeInternalFrames = false});
|
||||
|
||||
void close() => _sink.close();
|
||||
void addError(Object e, [StackTrace st]) => _sink.addError(e, st);
|
||||
Future addStream(Stream<String> stream) => stream.forEach(add);
|
||||
|
||||
void add(String line) {
|
||||
final match = _traceLineRE.firstMatch(line);
|
||||
if (match == null) {
|
||||
_sink.add(line);
|
||||
return;
|
||||
}
|
||||
// We don't use the original frame depths because we may elide frames.
|
||||
// If we match a stack frame with a depth of 0, then we're starting a
|
||||
// new stack frame.
|
||||
if (int.parse(match[1]) == 0) {
|
||||
_cachedDepth = 0;
|
||||
}
|
||||
final location = int.parse("0x" + match[2]);
|
||||
final callInfo = _dwarf
|
||||
.callInfo(location, includeInternalFrames: includeInternalFrames)
|
||||
?.toList();
|
||||
if (callInfo == null) {
|
||||
// If we can't get appropriate information for the stack trace line,
|
||||
// then just return the line unchanged.
|
||||
_sink.add(line);
|
||||
return;
|
||||
} else if (callInfo.isEmpty) {
|
||||
// No lines to output (as this corresponds to Dart internals).
|
||||
return;
|
||||
}
|
||||
_sink.add(line.substring(0, match.start) +
|
||||
_stackTracePiece(callInfo.first, _cachedDepth++));
|
||||
for (int i = 1; i < callInfo.length; i++) {
|
||||
_sink.add(_stackTracePiece(callInfo[i], _cachedDepth++));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,7 @@ static const char* kSnapshotKindNames[] = {
|
||||
V(elf, elf_filename) \
|
||||
V(load_compilation_trace, load_compilation_trace_filename) \
|
||||
V(load_type_feedback, load_type_feedback_filename) \
|
||||
V(save_debugging_info, debugging_info_filename) \
|
||||
V(save_obfuscation_map, obfuscation_map_filename)
|
||||
|
||||
#define BOOL_OPTIONS_LIST(V) \
|
||||
@@ -171,6 +172,7 @@ static void PrintUsage() {
|
||||
"as a static or dynamic library: \n"
|
||||
"--snapshot_kind=app-aot-assembly \n"
|
||||
"--assembly=<output-file> \n"
|
||||
"[--save-debugging-info=<debug-filename>] \n"
|
||||
"[--obfuscate] \n"
|
||||
"[--save-obfuscation-map=<map-filename>] \n"
|
||||
"<dart-kernel-file> \n"
|
||||
@@ -180,6 +182,7 @@ static void PrintUsage() {
|
||||
"--elf=<output-file> \n"
|
||||
"[--strip] \n"
|
||||
"[--obfuscate] \n"
|
||||
"[--save-debugging-info=<debug-filename>] \n"
|
||||
"[--save-obfuscation-map=<map-filename>] \n"
|
||||
"<dart-kernel-file> \n"
|
||||
" \n"
|
||||
@@ -333,8 +336,8 @@ static int ParseArguments(int argc,
|
||||
|
||||
if (!obfuscate && obfuscation_map_filename != NULL) {
|
||||
Syslog::PrintErr(
|
||||
"--obfuscation_map=<...> should only be specified when obfuscation is "
|
||||
"enabled by --obfuscate flag.\n\n");
|
||||
"--save-obfuscation_map=<...> should only be specified when "
|
||||
"obfuscation is enabled by the --obfuscate flag.\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -344,6 +347,20 @@ static int ParseArguments(int argc,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (debugging_info_filename != nullptr &&
|
||||
!IsSnapshottingForPrecompilation()) {
|
||||
Syslog::PrintErr(
|
||||
"--save-debugging-info=<...> can only be enabled when building an AOT "
|
||||
"snapshot.\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strip && snapshot_kind != kAppAOTElf) {
|
||||
Syslog::PrintErr(
|
||||
"Stripping can only be enabled when building an ELF AOT snapshot.\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -613,15 +630,23 @@ static void CreateAndWritePrecompiledSnapshot() {
|
||||
result = Dart_CreateAppAOTSnapshotAsAssembly(StreamingWriteCallback, file);
|
||||
CHECK_RESULT(result);
|
||||
} else if (snapshot_kind == kAppAOTElf) {
|
||||
if (strip) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: Generating ELF library without DWARF debugging"
|
||||
" information.\n");
|
||||
}
|
||||
File* file = OpenFile(elf_filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
result =
|
||||
Dart_CreateAppAOTSnapshotAsElf(StreamingWriteCallback, file, strip);
|
||||
if (debugging_info_filename != nullptr) {
|
||||
File* debug_file = OpenFile(debugging_info_filename);
|
||||
RefCntReleaseScope<File> rsd(debug_file);
|
||||
result = Dart_CreateAppAOTSnapshotAsElf(StreamingWriteCallback, file,
|
||||
strip, debug_file);
|
||||
} else {
|
||||
if (strip) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: Generating ELF library without DWARF debugging"
|
||||
" information.\n");
|
||||
}
|
||||
result =
|
||||
Dart_CreateAppAOTSnapshotAsElf(StreamingWriteCallback, file, strip,
|
||||
/*debug_callback_data=*/nullptr);
|
||||
}
|
||||
CHECK_RESULT(result);
|
||||
} else if (snapshot_kind == kAppAOTBlobs) {
|
||||
Syslog::PrintErr(
|
||||
@@ -678,6 +703,17 @@ static void CreateAndWritePrecompiledSnapshot() {
|
||||
CHECK_RESULT(result);
|
||||
WriteFile(obfuscation_map_filename, buffer, size);
|
||||
}
|
||||
|
||||
// Output separate debugging information if not generating ELF (otherwise we
|
||||
// have already generated it as part of that process).
|
||||
if (debugging_info_filename != nullptr && snapshot_kind != kAppAOTElf) {
|
||||
File* debug_file = OpenFile(debugging_info_filename);
|
||||
RefCntReleaseScope<File> rsd(debug_file);
|
||||
result = Dart_CreateAppAOTSnapshotAsElf(StreamingWriteCallback,
|
||||
/*callback_data=*/nullptr,
|
||||
/*strip=*/false, debug_file);
|
||||
CHECK_RESULT(result);
|
||||
}
|
||||
}
|
||||
|
||||
static Dart_QualifiedFunctionName no_entry_points[] = {
|
||||
|
||||
@@ -3419,12 +3419,16 @@ Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
||||
*
|
||||
* The callback will be invoked one or more times to provide the binary output.
|
||||
*
|
||||
* If debug_callback_data is provided, debug_callback_data will be used with
|
||||
* the callback to provide separate debugging information.
|
||||
*
|
||||
* \return A valid handle if no error occurs during the operation.
|
||||
*/
|
||||
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
|
||||
Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
void* callback_data,
|
||||
bool stripped);
|
||||
bool stripped,
|
||||
void* debug_callback_data);
|
||||
|
||||
/**
|
||||
* Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes
|
||||
|
||||
@@ -138,6 +138,7 @@ static const intptr_t SHT_PROGBITS = 1;
|
||||
static const intptr_t SHT_SYMTAB = 2;
|
||||
static const intptr_t SHT_STRTAB = 3;
|
||||
static const intptr_t SHT_HASH = 5;
|
||||
static const intptr_t SHT_NOBITS = 8;
|
||||
static const intptr_t SHT_DYNAMIC = 6;
|
||||
static const intptr_t SHT_DYNSYM = 11;
|
||||
|
||||
@@ -153,10 +154,12 @@ static const intptr_t PT_LOAD = 1;
|
||||
static const intptr_t PT_DYNAMIC = 2;
|
||||
static const intptr_t PT_PHDR = 6;
|
||||
|
||||
static const intptr_t STB_LOCAL = 0;
|
||||
static const intptr_t STB_GLOBAL = 1;
|
||||
|
||||
static const intptr_t STT_OBJECT = 1; // I.e., data.
|
||||
static const intptr_t STT_FUNC = 2;
|
||||
static const intptr_t STT_SECTION = 3;
|
||||
|
||||
static const intptr_t DT_NULL = 0;
|
||||
static const intptr_t DT_HASH = 4;
|
||||
|
||||
@@ -15,9 +15,7 @@ import 'package:kernel/kernel.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:vm/metadata/bytecode.dart' show BytecodeMetadataRepository;
|
||||
|
||||
import 'use_bare_instructions_flag_test.dart' show run, withTempDir;
|
||||
|
||||
const platformFilename = 'vm_platform_strong.dill';
|
||||
import 'use_flag_test_helper.dart';
|
||||
|
||||
Future main(List<String> args) async {
|
||||
final buildDir = path.dirname(Platform.resolvedExecutable);
|
||||
@@ -28,20 +26,16 @@ Future main(List<String> args) async {
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
print('Skipping test due missing "$platformFilename".');
|
||||
print('Skipping test due to missing "${path.basename(platformDill)}".');
|
||||
return;
|
||||
}
|
||||
|
||||
final platformDill = path.join(buildDir, platformFilename);
|
||||
await withTempDir((String tempDir) async {
|
||||
await withTempDir('product-aot-kernel-test', (String tempDir) async {
|
||||
final helloFile = path.join(tempDir, 'hello.dart');
|
||||
final helloDillFile = path.join(tempDir, 'hello.dart.dill');
|
||||
|
||||
// Compile script to Kernel IR.
|
||||
await File(helloFile).writeAsString('main() => print("Hello");');
|
||||
final genKernel = Platform.isWindows
|
||||
? "pkg\\vm\\tool\\gen_kernel.bat"
|
||||
: 'pkg/vm/tool/gen_kernel';
|
||||
await run(genKernel, <String>[
|
||||
'--aot',
|
||||
'--platform=$platformDill',
|
||||
|
||||
@@ -12,8 +12,10 @@ import "dart:io";
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'use_flag_test_helper.dart';
|
||||
|
||||
main(List<String> args) async {
|
||||
if (!Platform.executable.endsWith("dart_precompiled_runtime")) {
|
||||
if (!isAOTRuntime) {
|
||||
return; // Running in JIT: AOT binaries not available.
|
||||
}
|
||||
|
||||
@@ -21,18 +23,12 @@ main(List<String> args) async {
|
||||
return; // SDK tree and dart_bootstrap not available on the test device.
|
||||
}
|
||||
|
||||
final buildDir = path.dirname(Platform.executable);
|
||||
final sdkDir = path.dirname(path.dirname(buildDir));
|
||||
final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
|
||||
final genSnapshot = path.join(buildDir, 'gen_snapshot');
|
||||
final aotRuntime = path.join(buildDir, 'dart_precompiled_runtime');
|
||||
|
||||
await withTempDir((String tempDir) async {
|
||||
await withTempDir('bare-flag-test', (String tempDir) async {
|
||||
final script = path.join(sdkDir, 'pkg/kernel/bin/dump.dart');
|
||||
final scriptDill = path.join(tempDir, 'kernel_dump.dill');
|
||||
|
||||
// Compile script to Kernel IR.
|
||||
await run('pkg/vm/tool/gen_kernel', <String>[
|
||||
await run(genKernel, <String>[
|
||||
'--aot',
|
||||
'--platform=$platformDill',
|
||||
'-o',
|
||||
@@ -105,32 +101,3 @@ main(List<String> args) async {
|
||||
Future<String> readFile(String file) {
|
||||
return new File(file).readAsString();
|
||||
}
|
||||
|
||||
Future run(String executable, List<String> args) async {
|
||||
print('Running $executable ${args.join(' ')}');
|
||||
|
||||
final result = await Process.run(executable, args);
|
||||
final String stdout = result.stdout;
|
||||
final String stderr = result.stderr;
|
||||
if (stdout.isNotEmpty) {
|
||||
print('stdout:');
|
||||
print(stdout);
|
||||
}
|
||||
if (stderr.isNotEmpty) {
|
||||
print('stderr:');
|
||||
print(stderr);
|
||||
}
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
throw 'Command failed with non-zero exit code (was ${result.exitCode})';
|
||||
}
|
||||
}
|
||||
|
||||
withTempDir(Future fun(String dir)) async {
|
||||
final tempDir = Directory.systemTemp.createTempSync('bare-flag-test');
|
||||
try {
|
||||
await fun(tempDir.path);
|
||||
} finally {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
// 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());
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// This test ensures that the flag for --dwarf-stack-traces given at AOT
|
||||
// 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:io";
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:vm/dwarf/convert.dart';
|
||||
|
||||
import 'use_flag_test_helper.dart';
|
||||
|
||||
main(List<String> args) async {
|
||||
if (!isAOTRuntime) {
|
||||
return; // Running in JIT: AOT binaries not available.
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
return; // SDK tree and dart_bootstrap not available on the test device.
|
||||
}
|
||||
|
||||
// These are the tools we need to be available to run on a given platform:
|
||||
if (!await testExecutable(genSnapshot)) {
|
||||
throw "Cannot run test as $genSnapshot not available";
|
||||
}
|
||||
if (!await testExecutable(aotRuntime)) {
|
||||
throw "Cannot run test as $aotRuntime not available";
|
||||
}
|
||||
if (!File(platformDill).existsSync()) {
|
||||
throw "Cannot run test as $platformDill does not exist";
|
||||
}
|
||||
|
||||
await withTempDir('dwarf-flag-test', (String tempDir) async {
|
||||
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.
|
||||
await run(genKernel, <String>[
|
||||
'--aot',
|
||||
'--platform=$platformDill',
|
||||
'-o',
|
||||
scriptDill,
|
||||
script,
|
||||
]);
|
||||
|
||||
// Run the AOT compiler with/without Dwarf stack traces.
|
||||
final scriptDwarfSnapshot = path.join(tempDir, 'dwarf.so');
|
||||
final scriptNonDwarfSnapshot = path.join(tempDir, 'non_dwarf.so');
|
||||
await Future.wait(<Future>[
|
||||
run(genSnapshot, <String>[
|
||||
'--dwarf-stack-traces',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptDwarfSnapshot',
|
||||
scriptDill,
|
||||
]),
|
||||
run(genSnapshot, <String>[
|
||||
'--no-dwarf-stack-traces',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptNonDwarfSnapshot',
|
||||
scriptDill,
|
||||
]),
|
||||
]);
|
||||
|
||||
// Run the resulting Dwarf-AOT compiled script.
|
||||
final dwarfOut1 = await runError(aotRuntime, <String>[
|
||||
'--dwarf-stack-traces',
|
||||
scriptDwarfSnapshot,
|
||||
scriptDill,
|
||||
]);
|
||||
final dwarfTrace1 = cleanStacktrace(dwarfOut1);
|
||||
final dwarfOut2 = await runError(aotRuntime, <String>[
|
||||
'--no-dwarf-stack-traces',
|
||||
scriptDwarfSnapshot,
|
||||
scriptDill,
|
||||
]);
|
||||
final dwarfTrace2 = cleanStacktrace(dwarfOut2);
|
||||
|
||||
// Run the resulting non-Dwarf-AOT compiled script.
|
||||
final nonDwarfTrace1 = await runError(aotRuntime, <String>[
|
||||
'--dwarf-stack-traces',
|
||||
scriptNonDwarfSnapshot,
|
||||
scriptDill,
|
||||
]);
|
||||
final nonDwarfTrace2 = await runError(aotRuntime, <String>[
|
||||
'--no-dwarf-stack-traces',
|
||||
scriptNonDwarfSnapshot,
|
||||
scriptDill,
|
||||
]);
|
||||
|
||||
// Ensure the result is based off the flag passed to gen_snapshot, not
|
||||
// the one passed to the runtime.
|
||||
Expect.deepEquals(nonDwarfTrace1, nonDwarfTrace2);
|
||||
|
||||
// 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.
|
||||
Expect.deepEquals(
|
||||
collectPCOffsets(dwarfTrace1), collectPCOffsets(dwarfTrace2));
|
||||
});
|
||||
}
|
||||
|
||||
Iterable<String> cleanStacktrace(Iterable<String> lines) {
|
||||
// For DWARF stack traces, the pid/tid, if output, will vary over runs.
|
||||
return lines.where((line) => !line.startsWith('pid'));
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
final isAOTRuntime = path.basenameWithoutExtension(Platform.executable) ==
|
||||
'dart_precompiled_runtime';
|
||||
final buildDir = path.dirname(Platform.executable);
|
||||
final sdkDir = path.dirname(path.dirname(buildDir));
|
||||
final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
|
||||
final genKernel = path.join(sdkDir, 'pkg', 'vm', 'tool',
|
||||
'gen_kernel' + (Platform.isWindows ? '.bat' : ''));
|
||||
final _genSnapshotBase = 'gen_snapshot' + (Platform.isWindows ? '.exe' : '');
|
||||
// Slight hack to work around issue that gen_snapshot for simarm_x64 is not
|
||||
// in the same subdirectory as dart_precompiled_runtime (${MODE}SIMARM), but
|
||||
// instead it's in ${MODE}SIMARM_X64.
|
||||
final genSnapshot = File(path.join(buildDir, _genSnapshotBase)).existsSync()
|
||||
? path.join(buildDir, _genSnapshotBase)
|
||||
: path.join(buildDir + '_X64', _genSnapshotBase);
|
||||
final aotRuntime = path.join(
|
||||
buildDir, 'dart_precompiled_runtime' + (Platform.isWindows ? '.exe' : ''));
|
||||
|
||||
Future<ProcessResult> runHelper(String executable, List<String> args) async {
|
||||
print('Running $executable ${args.join(' ')}');
|
||||
|
||||
final result = await Process.run(executable, args);
|
||||
if (result.stdout.isNotEmpty) {
|
||||
print('Subcommand stdout:');
|
||||
print(result.stdout);
|
||||
}
|
||||
if (result.stderr.isNotEmpty) {
|
||||
print('Subcommand stderr:');
|
||||
print(result.stderr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> testExecutable(String executable) async {
|
||||
try {
|
||||
final result = await runHelper(executable, <String>['--version']);
|
||||
return result.exitCode == 0;
|
||||
} on ProcessException catch (e) {
|
||||
print('Got process exception: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> run(String executable, List<String> args) async {
|
||||
final result = await runHelper(executable, args);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
throw 'Command failed with unexpected exit code (was ${result.exitCode})';
|
||||
}
|
||||
}
|
||||
|
||||
Future<Iterable<String>> runOutput(String executable, List<String> args) async {
|
||||
final result = await runHelper(executable, args);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
throw 'Command failed with unexpected exit code (was ${result.exitCode})';
|
||||
}
|
||||
Expect.isTrue(result.stdout.isNotEmpty);
|
||||
Expect.isTrue(result.stderr.isEmpty);
|
||||
|
||||
return result.stdout.split(RegExp(r'[\r\n]'));
|
||||
}
|
||||
|
||||
Future<Iterable<String>> runError(String executable, List<String> 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.isEmpty);
|
||||
Expect.isTrue(result.stderr.isNotEmpty);
|
||||
|
||||
return result.stderr.split(RegExp(r'[\r\n]'));
|
||||
}
|
||||
|
||||
Future<void> withTempDir(String name, Future<void> fun(String dir)) async {
|
||||
final tempDir = Directory.systemTemp.createTempSync(name);
|
||||
try {
|
||||
await fun(tempDir.path);
|
||||
} finally {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// This test ensures that the AOT compiler can generate debugging information
|
||||
// for stripped ELF output, and that using the debugging information to look
|
||||
// up stripped stack trace information matches the non-stripped version.
|
||||
|
||||
// OtherResources=use_dwarf_stack_traces_flag_program.dart
|
||||
|
||||
import "dart:io";
|
||||
import "dart:math";
|
||||
import "dart:typed_data";
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:vm/dwarf/convert.dart';
|
||||
import 'package:vm/dwarf/dwarf.dart';
|
||||
|
||||
import 'use_flag_test_helper.dart';
|
||||
|
||||
main(List<String> args) async {
|
||||
if (!isAOTRuntime) {
|
||||
return; // Running in JIT: AOT binaries not available.
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
return; // SDK tree and dart_bootstrap not available on the test device.
|
||||
}
|
||||
|
||||
// These are the tools we need to be available to run on a given platform:
|
||||
if (!await testExecutable(genSnapshot)) {
|
||||
throw "Cannot run test as $genSnapshot not available";
|
||||
}
|
||||
if (!await testExecutable(aotRuntime)) {
|
||||
throw "Cannot run test as $aotRuntime not available";
|
||||
}
|
||||
if (!File(platformDill).existsSync()) {
|
||||
throw "Cannot run test as $platformDill does not exist";
|
||||
}
|
||||
|
||||
await withTempDir('save-debug-info-flag-test', (String tempDir) async {
|
||||
final cwDir = path.dirname(Platform.script.toFilePath());
|
||||
// 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.
|
||||
await run(genKernel, <String>[
|
||||
'--aot',
|
||||
'--platform=$platformDill',
|
||||
'-o',
|
||||
scriptDill,
|
||||
script,
|
||||
]);
|
||||
|
||||
// Run the AOT compiler with Dwarf stack traces, once without stripping,
|
||||
// once with stripping, and once with stripping and saving debugging
|
||||
// information.
|
||||
final scriptWholeSnapshot = path.join(tempDir, 'whole.so');
|
||||
await run(genSnapshot, <String>[
|
||||
'--dwarf-stack-traces',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptWholeSnapshot',
|
||||
scriptDill,
|
||||
]);
|
||||
|
||||
final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
|
||||
await run(genSnapshot, <String>[
|
||||
'--dwarf-stack-traces',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptStrippedOnlySnapshot',
|
||||
'--strip',
|
||||
scriptDill,
|
||||
]);
|
||||
|
||||
final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
|
||||
final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
|
||||
await run(genSnapshot, <String>[
|
||||
'--dwarf-stack-traces',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptStrippedSnapshot',
|
||||
'--strip',
|
||||
'--save-debugging-info=$scriptDebuggingInfo',
|
||||
scriptDill,
|
||||
]);
|
||||
|
||||
// Run the resulting scripts, saving the stack traces.
|
||||
final wholeTrace = await runError(aotRuntime, <String>[
|
||||
scriptWholeSnapshot,
|
||||
scriptDill,
|
||||
]);
|
||||
final wholeOffsets = collectPCOffsets(wholeTrace);
|
||||
|
||||
final strippedOnlyTrace = await runError(aotRuntime, <String>[
|
||||
scriptStrippedOnlySnapshot,
|
||||
scriptDill,
|
||||
]);
|
||||
final strippedOnlyOffsets = collectPCOffsets(strippedOnlyTrace);
|
||||
|
||||
final strippedTrace = await runError(aotRuntime, <String>[
|
||||
scriptStrippedSnapshot,
|
||||
scriptDill,
|
||||
]);
|
||||
final strippedOffsets = collectPCOffsets(strippedTrace);
|
||||
|
||||
if (Platform.isWindows) {
|
||||
// TODO(dartbug.com/35274): After this point, we make sure that we get
|
||||
// the same offsets from the DWARF stack traces. On Windows, we currently
|
||||
// aren't guaranteed to get the same offset in DWARF stack traces from
|
||||
// different runs because we aren't using the native loader for dynamic
|
||||
// libraries (as the Windows one does not understand ELF). Instead, we
|
||||
// fall back onto our own ELF loader, and the DWARF stack trace output
|
||||
// only prints relative PC addresses for dynamically loaded libraries.
|
||||
return;
|
||||
}
|
||||
|
||||
// The retrieved offsets should be the same for all runs.
|
||||
Expect.deepEquals(wholeOffsets, strippedOffsets);
|
||||
Expect.deepEquals(strippedOnlyOffsets, strippedOffsets);
|
||||
|
||||
// Stripped output should not change when --save-debugging-info is used.
|
||||
compareSnapshots(scriptStrippedOnlySnapshot, scriptStrippedSnapshot);
|
||||
|
||||
final stackTraceWithTerminators = strippedTrace.map((String s) => s + "\n");
|
||||
print("\nOriginal stack trace:");
|
||||
print(stackTraceWithTerminators.join());
|
||||
|
||||
final debugDwarf = Dwarf.fromFile(scriptDebuggingInfo);
|
||||
final wholeDwarf = Dwarf.fromFile(scriptWholeSnapshot);
|
||||
|
||||
final fromDebug = await Stream.fromIterable(stackTraceWithTerminators)
|
||||
.transform(DwarfStackTraceDecoder(debugDwarf))
|
||||
.toList();
|
||||
print("\nStack trace converted using separate debugging info:");
|
||||
print(fromDebug.join());
|
||||
|
||||
final fromWhole = await Stream.fromIterable(stackTraceWithTerminators)
|
||||
.transform(DwarfStackTraceDecoder(wholeDwarf))
|
||||
.toList();
|
||||
print("\nStack trace converted using unstripped ELF file:");
|
||||
print(fromWhole.join());
|
||||
|
||||
Expect.deepEquals(fromDebug, fromWhole);
|
||||
});
|
||||
}
|
||||
|
||||
void compareSnapshots(String file1, String file2) {
|
||||
final bytes1 = File(file1).readAsBytesSync();
|
||||
final bytes2 = File(file2).readAsBytesSync();
|
||||
final diff = diffBinary(bytes1, bytes2);
|
||||
if (diff.isNotEmpty) {
|
||||
print("\nFound differences between $file1 and $file2:");
|
||||
printDiff(diff);
|
||||
}
|
||||
Expect.equals(bytes1.length, bytes2.length);
|
||||
Expect.equals(0, diff.length);
|
||||
}
|
||||
|
||||
Map<int, List<int>> diffBinary(Uint8List bytes1, Uint8List bytes2) {
|
||||
final ret = Map<int, List<int>>();
|
||||
final len = min(bytes1.length, bytes2.length);
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (bytes1[i] != bytes2[i]) {
|
||||
ret[i] = <int>[bytes1[i], bytes2[i]];
|
||||
}
|
||||
}
|
||||
if (bytes1.length > len) {
|
||||
for (var i = len; i < bytes1.length; i++) {
|
||||
ret[i] = <int>[bytes1[i], -1];
|
||||
}
|
||||
} else if (bytes2.length > len) {
|
||||
for (var i = len; i < bytes2.length; i++) {
|
||||
ret[i] = <int>[-1, bytes2[i]];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void printDiff(Map<int, List<int>> map, [int maxOutput = 100]) {
|
||||
int lines = 0;
|
||||
for (var index in map.keys) {
|
||||
final pair = map[index];
|
||||
if (pair[0] == -1) {
|
||||
print('$index: <>, ${pair[1]}');
|
||||
lines++;
|
||||
} else if (pair[1] == -1) {
|
||||
print('$index: ${pair[0]}, <>');
|
||||
lines++;
|
||||
} else {
|
||||
print('$index: ${pair[0]}, ${pair[1]}');
|
||||
lines++;
|
||||
}
|
||||
if (lines >= maxOutput) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-10
@@ -6177,7 +6177,8 @@ Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
||||
DART_EXPORT Dart_Handle
|
||||
Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
void* callback_data,
|
||||
bool strip) {
|
||||
bool strip,
|
||||
void* debug_callback_data) {
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
return Api::NewError("AOT compilation is not supported on IA32.");
|
||||
#elif !defined(DART_PRECOMPILER)
|
||||
@@ -6195,11 +6196,20 @@ Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
uint8_t* isolate_snapshot_data_buffer = nullptr;
|
||||
uint8_t* isolate_snapshot_instructions_buffer = nullptr;
|
||||
|
||||
StreamingWriteStream elf_stream(2 * MB, callback, callback_data);
|
||||
const bool generate_elf = callback_data != nullptr;
|
||||
const bool generate_debug = debug_callback_data != nullptr;
|
||||
|
||||
Elf* elf = new (Z) Elf(Z, &elf_stream);
|
||||
const intptr_t kInitialSize = 2 * MB;
|
||||
StreamingWriteStream elf_stream(generate_elf ? kInitialSize : 0, callback,
|
||||
callback_data);
|
||||
const intptr_t kInitialDebugSize = generate_debug ? 1 * MB : 0;
|
||||
StreamingWriteStream debug_stream(kInitialDebugSize, callback,
|
||||
debug_callback_data);
|
||||
|
||||
Elf* elf = new (Z) Elf(Z, generate_elf ? &elf_stream : nullptr, strip,
|
||||
generate_debug ? &debug_stream : nullptr);
|
||||
Dwarf* dwarf = nullptr;
|
||||
if (!strip) {
|
||||
if (!strip || generate_debug) {
|
||||
dwarf = new (Z) Dwarf(Z, nullptr, elf);
|
||||
}
|
||||
|
||||
@@ -6210,11 +6220,11 @@ Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
elf->AddBSSData("_kDartBSSData", sizeof(compiler::target::uword));
|
||||
|
||||
BlobImageWriter vm_image_writer(T, &vm_snapshot_instructions_buffer,
|
||||
ApiReallocate, /* initial_size= */ 2 * MB,
|
||||
bss_base, elf, dwarf);
|
||||
BlobImageWriter isolate_image_writer(
|
||||
T, &isolate_snapshot_instructions_buffer, ApiReallocate,
|
||||
/* initial_size= */ 2 * MB, bss_base, elf, dwarf);
|
||||
ApiReallocate, kInitialSize, bss_base, elf,
|
||||
dwarf);
|
||||
BlobImageWriter isolate_image_writer(T, &isolate_snapshot_instructions_buffer,
|
||||
ApiReallocate, kInitialSize, bss_base,
|
||||
elf, dwarf);
|
||||
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data_buffer,
|
||||
&isolate_snapshot_data_buffer, ApiReallocate,
|
||||
&vm_image_writer, &isolate_image_writer);
|
||||
@@ -6224,7 +6234,7 @@ Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
writer.VmIsolateSnapshotSize());
|
||||
elf->AddROData("_kDartIsolateSnapshotData", isolate_snapshot_data_buffer,
|
||||
writer.IsolateSnapshotSize());
|
||||
if (!strip) {
|
||||
if (dwarf != nullptr) {
|
||||
// TODO(rmacnak): Generate .debug_frame / .eh_frame / .arm.exidx to
|
||||
// provide unwinding information.
|
||||
dwarf->Write();
|
||||
|
||||
+10
-8
@@ -19,6 +19,8 @@ namespace dart {
|
||||
#define FORM_ADDR ".8byte"
|
||||
#endif
|
||||
|
||||
static const intptr_t kTargetWordSize = sizeof(compiler::target::kWordSize);
|
||||
|
||||
class InliningNode : public ZoneAllocated {
|
||||
public:
|
||||
InliningNode(const Function& function,
|
||||
@@ -285,9 +287,9 @@ void Dwarf::WriteCompilationUnit() {
|
||||
cu_start = position();
|
||||
}
|
||||
|
||||
u2(2); // DWARF version 2
|
||||
u4(0); // debug_abbrev_offset
|
||||
u1(sizeof(void*)); // address_size
|
||||
u2(2); // DWARF version 2
|
||||
u4(0); // debug_abbrev_offset
|
||||
u1(kTargetWordSize); // address_size
|
||||
|
||||
// Compilation Unit DIE. We describe the entire Dart program as a single
|
||||
// compilation unit. Note we write attributes in the same order we declared
|
||||
@@ -537,9 +539,9 @@ void Dwarf::WriteInliningNode(InliningNode* node,
|
||||
const char* asm_name =
|
||||
namer->AssemblyNameFor(root_code_index, *codes_[root_code_index]);
|
||||
// DW_AT_low_pc
|
||||
Print(FORM_ADDR " %s + %d\n", asm_name, node->start_pc_offset);
|
||||
Print(FORM_ADDR " %s + %" Pd32 "\n", asm_name, node->start_pc_offset);
|
||||
// DW_AT_high_pc
|
||||
Print(FORM_ADDR " %s + %d\n", asm_name, node->end_pc_offset);
|
||||
Print(FORM_ADDR " %s + %" Pd32 "\n", asm_name, node->end_pc_offset);
|
||||
} else {
|
||||
// DW_AT_low_pc
|
||||
addr(root_code_offset + node->start_pc_offset);
|
||||
@@ -732,11 +734,11 @@ void Dwarf::WriteLines() {
|
||||
// 4. Update LNP pc.
|
||||
if (previous_code_offset == -1) {
|
||||
// This variant is relocatable.
|
||||
u1(0); // This is an extended opcode
|
||||
u1(1 + sizeof(void*)); // that is 5 or 9 bytes long
|
||||
u1(0); // This is an extended opcode
|
||||
u1(1 + kTargetWordSize); // that is 5 or 9 bytes long
|
||||
u1(DW_LNE_set_address);
|
||||
if (asm_stream_) {
|
||||
Print(FORM_ADDR " %s + %d\n", asm_name, current_pc_offset);
|
||||
Print(FORM_ADDR " %s + %" Pd32 "\n", asm_name, current_pc_offset);
|
||||
} else {
|
||||
addr(current_code_offset + current_pc_offset);
|
||||
}
|
||||
|
||||
+16
-5
@@ -223,7 +223,7 @@ class Dwarf : public ZoneAllocated {
|
||||
}
|
||||
void u1(uint8_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".byte %d\n", value);
|
||||
Print(".byte %u\n", value);
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
@@ -231,7 +231,7 @@ class Dwarf : public ZoneAllocated {
|
||||
}
|
||||
void u2(uint16_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".2byte %d\n", value);
|
||||
Print(".2byte %u\n", value);
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
@@ -239,7 +239,7 @@ class Dwarf : public ZoneAllocated {
|
||||
}
|
||||
intptr_t u4(uint32_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".4byte %d\n", value);
|
||||
Print(".4byte %" Pu32 "\n", value);
|
||||
return -1;
|
||||
} else {
|
||||
intptr_t fixup = position();
|
||||
@@ -255,12 +255,23 @@ class Dwarf : public ZoneAllocated {
|
||||
memmove(bin_stream_->buffer() + position, &value, sizeof(value));
|
||||
}
|
||||
}
|
||||
void u8(uint64_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".8byte %" Pu64 "\n", value);
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
}
|
||||
}
|
||||
void addr(uword value) {
|
||||
if (asm_stream_) {
|
||||
UNREACHABLE();
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
u4(value);
|
||||
#else
|
||||
u8(value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void string(const char* cstr) { // NOLINT
|
||||
|
||||
+535
-260
File diff suppressed because it is too large
Load Diff
+90
-35
@@ -9,6 +9,7 @@
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
#include "vm/datastream.h"
|
||||
#include "vm/growable_array.h"
|
||||
#include "vm/hash_map.h"
|
||||
#include "vm/zone.h"
|
||||
|
||||
namespace dart {
|
||||
@@ -21,11 +22,14 @@ class SymbolTable;
|
||||
|
||||
class Elf : public ZoneAllocated {
|
||||
public:
|
||||
Elf(Zone* zone, StreamingWriteStream* stream);
|
||||
Elf(Zone* zone,
|
||||
StreamingWriteStream* stream,
|
||||
bool strip,
|
||||
StreamingWriteStream* debug_stream = nullptr);
|
||||
|
||||
static const intptr_t kPageSize = 4096;
|
||||
|
||||
intptr_t NextMemoryOffset() const;
|
||||
intptr_t NextMemoryOffset() const { return memory_offset_; }
|
||||
intptr_t NextSectionIndex() const;
|
||||
intptr_t AddText(const char* name, const uint8_t* bytes, intptr_t size);
|
||||
intptr_t AddROData(const char* name, const uint8_t* bytes, intptr_t size);
|
||||
@@ -37,57 +41,108 @@ class Elf : public ZoneAllocated {
|
||||
|
||||
void Finalize();
|
||||
|
||||
intptr_t position() const { return stream_->position(); }
|
||||
void WriteBytes(const uint8_t* b, intptr_t size) {
|
||||
stream_->WriteBytes(b, size);
|
||||
static void WriteBytes(StreamingWriteStream* stream,
|
||||
const uint8_t* bytes,
|
||||
intptr_t size) {
|
||||
stream->WriteBytes(bytes, size);
|
||||
}
|
||||
void WriteByte(uint8_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
static void WriteByte(StreamingWriteStream* stream, uint8_t value) {
|
||||
stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteHalf(uint16_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
static void WriteHalf(StreamingWriteStream* stream, uint16_t value) {
|
||||
stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteWord(uint32_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
static void WriteWord(StreamingWriteStream* stream, uint32_t value) {
|
||||
stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteAddr(compiler::target::uword value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
static void WriteAddr(StreamingWriteStream* stream,
|
||||
compiler::target::uword value) {
|
||||
stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteOff(compiler::target::uword value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
static void WriteOff(StreamingWriteStream* stream,
|
||||
compiler::target::uword value) {
|
||||
stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
#if defined(TARGET_ARCH_IS_64_BIT)
|
||||
void WriteXWord(uint64_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
static void WriteXWord(StreamingWriteStream* stream, uint64_t value) {
|
||||
stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
void AddSection(Section* section);
|
||||
void AddSection(Section* section, const char* name);
|
||||
void AddSegment(Section* section);
|
||||
|
||||
void ComputeFileOffsets();
|
||||
void WriteHeader();
|
||||
void WriteSectionTable();
|
||||
void WriteProgramTable();
|
||||
void WriteSections();
|
||||
intptr_t ActiveSectionsIndex(intptr_t section_index) const;
|
||||
intptr_t SectionTableIndex(intptr_t section_index) const;
|
||||
intptr_t ProgramTableSize() const;
|
||||
intptr_t SectionTableSize() const;
|
||||
|
||||
void ClearOutputInfo();
|
||||
// Checks that the output information used by the writing methods has been
|
||||
// properly constructed.
|
||||
void VerifyOutputInfo() const;
|
||||
|
||||
// Creates a new version of shstrtab_ that only copies over names of active
|
||||
// sections. Sets the contents of section_names_ to indices in the new table.
|
||||
StringTable* CreateSectionHeaderStringTable();
|
||||
// Either returns the original section or a section like the old one that
|
||||
// also accounts for what sections are currently active.
|
||||
Section* AdjustForActiveSections(Section* section);
|
||||
|
||||
// These methods return the new file offset taking into consideration the
|
||||
// alignment and size of the section.
|
||||
intptr_t PrepareDebugSection(Section* section,
|
||||
intptr_t start_offset,
|
||||
bool use_fake_info);
|
||||
intptr_t PrepareMainSection(Section* section,
|
||||
intptr_t start_offset,
|
||||
intptr_t skipped_sections);
|
||||
|
||||
// These methods set up:
|
||||
// * Various information about file offsets
|
||||
// * The number of entries in the program and section tables
|
||||
// * An array of the active sections (i.e., those in the section table).
|
||||
// * An array of the sections that will be fully output.
|
||||
// * Some arrays of information used instead of the values of their
|
||||
// corresponding Section fields when creating the section table.
|
||||
void PrepareDebugOutputInfo();
|
||||
void PrepareMainOutputInfo();
|
||||
|
||||
void WriteHeader(StreamingWriteStream* s);
|
||||
void WriteProgramTable(StreamingWriteStream* s);
|
||||
void WriteSectionTable(StreamingWriteStream* s);
|
||||
void WriteSections(StreamingWriteStream* s);
|
||||
|
||||
Zone* const zone_;
|
||||
StreamingWriteStream* stream_;
|
||||
const bool strip_;
|
||||
StreamingWriteStream* const stream_;
|
||||
StreamingWriteStream* const debug_stream_;
|
||||
GrowableArray<Section*> sections_;
|
||||
GrowableArray<Section*> segments_;
|
||||
|
||||
intptr_t memory_offset_;
|
||||
intptr_t section_table_file_offset_;
|
||||
intptr_t section_table_file_size_;
|
||||
intptr_t program_table_file_offset_;
|
||||
intptr_t program_table_file_size_;
|
||||
StringTable* shstrtab_;
|
||||
StringTable* dynstrtab_;
|
||||
SymbolTable* dynsym_;
|
||||
StringTable* strtab_;
|
||||
SymbolTable* symtab_;
|
||||
DynamicTable* dynamic_;
|
||||
intptr_t memory_offset_ = 0;
|
||||
StringTable* shstrtab_ = nullptr;
|
||||
StringTable* dynstrtab_ = nullptr;
|
||||
SymbolTable* dynsym_ = nullptr;
|
||||
StringTable* strtab_ = nullptr;
|
||||
SymbolTable* symtab_ = nullptr;
|
||||
DynamicTable* dynamic_ = nullptr;
|
||||
|
||||
// Filled out during the Prepare*OutputInfo methods and used by the Write*
|
||||
// instance methods, as these values will differ between stripped and
|
||||
// debugging outputs.
|
||||
GrowableArray<Section*> active_sections_;
|
||||
GrowableArray<Section*> output_sections_;
|
||||
IntMap<intptr_t> adjusted_indices_;
|
||||
// These should all contain entries for the sections in active_sections_.
|
||||
GrowableArray<intptr_t> file_sizes_;
|
||||
GrowableArray<intptr_t> section_names_;
|
||||
GrowableArray<intptr_t> section_types_;
|
||||
intptr_t section_table_file_offset_ = -1;
|
||||
intptr_t section_table_entry_count_ = -1;
|
||||
intptr_t program_table_file_offset_ = -1;
|
||||
intptr_t program_table_entry_count_ = -1;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -244,11 +244,12 @@ constexpr bool kDartUseBackgroundCompilation = true;
|
||||
// List of VM-global (i.e. non-isolate specific) flags.
|
||||
//
|
||||
// The value used for those flags at snapshot generation time needs to be the
|
||||
// same as during runtime.
|
||||
// same as during runtime. Currently only boolean flags are supported.
|
||||
//
|
||||
// Usage:
|
||||
// V(name, command-line-flag-name)
|
||||
#define VM_GLOBAL_FLAG_LIST(V) \
|
||||
V(dwarf_stack_traces, FLAG_dwarf_stack_traces) \
|
||||
V(use_bare_instructions, FLAG_use_bare_instructions)
|
||||
|
||||
#endif // RUNTIME_VM_FLAG_LIST_H_
|
||||
|
||||
@@ -924,7 +924,7 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||
const Code& code = *instructions_[i].code_;
|
||||
if ((elf_ != nullptr) && (dwarf_ != nullptr) && !code.IsNull()) {
|
||||
intptr_t segment_offset = instructions_blob_stream_.bytes_written() +
|
||||
Instructions::HeaderSize();
|
||||
compiler::target::Instructions::HeaderSize();
|
||||
dwarf_->AddCode(code, segment_base + segment_offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
+15
-6
@@ -22411,9 +22411,10 @@ const char* StackTrace::ToDartCString(const StackTrace& stack_trace_in) {
|
||||
|
||||
const char* StackTrace::ToDwarfCString(const StackTrace& stack_trace_in) {
|
||||
#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
StackTrace& stack_trace = StackTrace::Handle(zone, stack_trace_in.raw());
|
||||
Object& code = Object::Handle(zone);
|
||||
auto const T = Thread::Current();
|
||||
auto const zone = T->zone();
|
||||
auto& stack_trace = StackTrace::Handle(zone, stack_trace_in.raw());
|
||||
auto& code = Object::Handle(zone);
|
||||
ZoneTextBuffer buffer(zone, 1024);
|
||||
|
||||
// The Dart standard requires the output of StackTrace.toString to include
|
||||
@@ -22429,6 +22430,14 @@ const char* StackTrace::ToDwarfCString(const StackTrace& stack_trace_in) {
|
||||
OSThread* thread = OSThread::Current();
|
||||
buffer.Printf("pid: %" Pd ", tid: %" Pd ", name %s\n", OS::ProcessId(),
|
||||
OSThread::ThreadIdToIntPtr(thread->id()), thread->name());
|
||||
auto const isolate_instructions =
|
||||
T->isolate_group()->source()->snapshot_instructions;
|
||||
auto const vm_instructions =
|
||||
Dart::vm_isolate()->group()->source()->snapshot_instructions;
|
||||
buffer.Printf("isolate_instructions: %" Px "",
|
||||
reinterpret_cast<uintptr_t>(isolate_instructions));
|
||||
buffer.Printf(" vm_instructions: %" Px "\n",
|
||||
reinterpret_cast<uintptr_t>(vm_instructions));
|
||||
intptr_t frame_index = 0;
|
||||
uint32_t frame_skip = 0;
|
||||
do {
|
||||
@@ -22463,11 +22472,11 @@ const char* StackTrace::ToDwarfCString(const StackTrace& stack_trace_in) {
|
||||
if (NativeSymbolResolver::LookupSharedObject(call_addr, &dso_base,
|
||||
&dso_name)) {
|
||||
uword dso_offset = call_addr - dso_base;
|
||||
buffer.Printf(" #%02" Pd " pc %" Pp " %s\n", frame_index,
|
||||
dso_offset, dso_name);
|
||||
buffer.Printf(" #%02" Pd " abs %" Pp " virt %" Pp " %s\n",
|
||||
frame_index, call_addr, dso_offset, dso_name);
|
||||
NativeSymbolResolver::FreeSymbolName(dso_name);
|
||||
} else {
|
||||
buffer.Printf(" #%02" Pd " pc %" Pp " <unknown>\n", frame_index,
|
||||
buffer.Printf(" #%02" Pd " abs %" Pp " <unknown>\n", frame_index,
|
||||
call_addr);
|
||||
}
|
||||
frame_index++;
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
/// VMOptions=--dwarf-stack-traces
|
||||
/// VMOptions=--dwarf-stack-traces --save-debugging-info=dwarf.so
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:unittest/unittest.dart';
|
||||
import 'package:vm/elf/convert.dart';
|
||||
import 'package:vm/elf/dwarf.dart';
|
||||
import 'package:vm/elf/elf.dart';
|
||||
import 'package:vm/dwarf/convert.dart';
|
||||
import 'package:vm/dwarf/dwarf.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
@pragma("vm:prefer-inline")
|
||||
@@ -40,24 +39,21 @@ Future<void> main() async {
|
||||
print("Raw stack trace:");
|
||||
print(rawStack);
|
||||
|
||||
if (Platform.isWindows) {
|
||||
// TODO(dartbug.com/39490): Remove this when we can retrieve or calculate
|
||||
// virtual addresses from DWARF stack traces on Windows.
|
||||
print("Skipping test because we are running on Windows.");
|
||||
return;
|
||||
if (!Platform.executable.endsWith("dart_precompiled_runtime")) {
|
||||
return; // Not running from an AOT compiled snapshot.
|
||||
}
|
||||
|
||||
if (!Elf.startsWithMagicNumber(Platform.script.toFilePath())) {
|
||||
print("Skipping test because we are not running from ELF.");
|
||||
return;
|
||||
if (Platform.isAndroid) {
|
||||
return; // Generated dwarf.so not available on the test device.
|
||||
}
|
||||
|
||||
final dwarf = Dwarf.fromFile(Platform.script.toFilePath());
|
||||
final dwarf = Dwarf.fromFile("dwarf.so");
|
||||
|
||||
var rawLines =
|
||||
final rawLines =
|
||||
await Stream.value(rawStack).transform(const LineSplitter()).toList();
|
||||
|
||||
final pcAddresses = collectPCAddresses(rawLines).toList();
|
||||
final pcAddresses =
|
||||
collectPCOffsets(rawLines).map((pc) => pc.virtualAddress(dwarf)).toList();
|
||||
|
||||
// We should have at least enough PC addresses to cover the frames we'll be
|
||||
// checking.
|
||||
@@ -142,20 +138,20 @@ final expectedExternalCallInfo = <List<CallInfo>>[
|
||||
CallInfo(
|
||||
function: "bar",
|
||||
filename: "dwarf_stack_trace_test.dart",
|
||||
line: 19,
|
||||
line: 18,
|
||||
inlined: true),
|
||||
// The second frame corresponds to call to foo in main.
|
||||
CallInfo(
|
||||
function: "foo",
|
||||
filename: "dwarf_stack_trace_test.dart",
|
||||
line: 25,
|
||||
line: 24,
|
||||
inlined: false)
|
||||
],
|
||||
[
|
||||
CallInfo(
|
||||
function: "main",
|
||||
filename: "dwarf_stack_trace_test.dart",
|
||||
line: 31,
|
||||
line: 30,
|
||||
inlined: false)
|
||||
],
|
||||
// No call information for the main tearoff.
|
||||
|
||||
Reference in New Issue
Block a user