Reland "[vm/compiler] Add symbols for read-only data when requested."

This is a reland of commit 286326f834

Checking into https://github.com/flutter/flutter/issues/108378, it
looks like the stripped snapshot indeed is unchanged, but the .ipa
being benchmarked includes .dSYMs, which is why there was a size
regression in adding this extra debugging information.

If that changes, we can remove the flag, but for now, I've added the
flag back so these symbols are not added by default.

TEST=vm/dart{,_2}/readonly_data_symbols

Original change's description:
> [vm/compiler] Add symbols for read-only data when requested.
>
> Symbols for non-clustered objects in the read-only data section are
now added to the static symbol tables for unstripped snapshots and
separate debugging information.
>
> In DEBUG mode, the name for a non-String read-only data object also
includes the name of the parent object.
>
> TEST=vm/dart{,_2}/readonly_data_symbols
>
> Change-Id: I623b023138aeca0580bc76392882eac5686f8f50
> Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251104
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Commit-Queue: Tess Strickland <sstrickl@google.com>

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try
Change-Id: I41be1c494c4324f1f3fae648d9832772c45bfbaf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260522
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Tess Strickland 2022-09-27 16:45:53 +00:00 committed by Commit Queue
parent 8a78aaf463
commit 823934f1c5
13 changed files with 590 additions and 130 deletions

View file

@ -1,3 +1,7 @@
## 0.5.3
- Exported more ELF utilities for use in Dart tests.
## 0.5.2
- Adjusted logic for finding the DWARF MachO file in a dSYM.

View file

@ -8,4 +8,12 @@ export 'src/constants.dart'
isolateSymbolName,
vmDataSymbolName,
vmSymbolName;
export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
export 'src/elf.dart'
show
DynamicTable,
DynamicTableTag,
Elf,
Section,
Symbol,
SymbolBinding,
SymbolType;

View file

@ -777,16 +777,19 @@ class StringTable extends Section implements DwarfContainerStringTable {
}
}
/// An enumeration of recognized symbol binding values used by the ELF format.
enum SymbolBinding {
STB_LOCAL,
STB_GLOBAL,
STB_WEAK,
}
/// An enumeration of recognized symbol types used by the ELF format.
enum SymbolType {
STT_NOTYPE,
STT_OBJECT,
STT_FUNC,
STT_SECTION,
}
enum SymbolVisibility {
@ -1087,6 +1090,17 @@ class Elf extends DwarfContainer {
return null;
}
/// Returns an iterable of the symbols in the dynamic symbol table(s).
/// The ordering of the symbols is not guaranteed.
Iterable<Symbol> get dynamicSymbols sync* {
for (final section in namedSections('.dynsym')) {
final dynsym = section as SymbolTable;
for (final symbol in dynsym.values) {
yield symbol;
}
}
}
/// Reverse lookup of the static symbol that contains the given virtual
/// address. Returns null if no static symbol matching the address is found.
@override
@ -1109,6 +1123,17 @@ class Elf extends DwarfContainer {
return bestSym;
}
/// Returns an iterable of the symbols in the static symbol table(s).
/// The ordering of the symbols is not guaranteed.
Iterable<Symbol> get staticSymbols sync* {
for (final section in namedSections('.symtab')) {
final symtab = section as SymbolTable;
for (final symbol in symtab.values) {
yield symbol;
}
}
}
/// Creates an [Elf] from the data pointed to by [reader].
///
/// After succesful completion, the [endian] and [wordSize] fields of the

View file

@ -1,5 +1,5 @@
name: native_stack_traces
version: 0.5.2
version: 0.5.3
description: Utilities for working with non-symbolic stack traces.
repository: https://github.com/dart-lang/sdk/tree/main/pkg/native_stack_traces

View file

@ -0,0 +1,116 @@
// 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.
// This test checks that --add-readonly-data-symbols outputs static symbols
// for read-only data objects.
// OtherResources=use_save_debugging_info_flag_program.dart
import "dart:async";
import "dart:io";
import 'package:expect/expect.dart';
import 'package:native_stack_traces/elf.dart';
import 'package:path/path.dart' as path;
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('readonly-symbols-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 scriptDill = path.join(tempDir, 'flag_program.dill');
// Compile script to Kernel IR.
await run(genKernel, <String>[
'--aot',
'--platform=$platformDill',
'-o',
scriptDill,
script,
]);
final scriptSnapshot = path.join(tempDir, 'dwarf.so');
final scriptDebuggingInfo = path.join(tempDir, 'debug_info.so');
await run(genSnapshot, <String>[
'--add-readonly-data-symbols',
'--dwarf-stack-traces-mode',
'--save-debugging-info=$scriptDebuggingInfo',
'--snapshot-kind=app-aot-elf',
'--elf=$scriptSnapshot',
scriptDill,
]);
checkElf(scriptSnapshot);
checkElf(scriptDebuggingInfo);
});
}
void checkElf(String filename) {
// Check that the static symbol table contains entries that are not in the
// dynamic symbol table, have STB_LOCAL binding, and are of type STT_OBJECT.
final elf = Elf.fromFile(filename);
Expect.isNotNull(elf);
final dynamicSymbols = elf!.dynamicSymbols.toList();
for (final symbol in dynamicSymbols) {
// All symbol tables have an initial entry with zero-valued fields.
if (symbol.name == '') {
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
Expect.equals(SymbolType.STT_NOTYPE, symbol.type);
Expect.equals(0, symbol.value);
} else {
Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
Expect.isTrue(symbol.name.startsWith('_kDart'),
'unexpected symbol name ${symbol.name}');
}
}
final onlyStaticSymbols = elf.staticSymbols
.where((s1) => !dynamicSymbols.any((s2) => s1.name == s2.name));
Expect.isNotEmpty(onlyStaticSymbols, 'no static-only symbols');
final objectSymbols =
onlyStaticSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
Expect.isNotEmpty(objectSymbols, 'no static-only object symbols');
for (final symbol in objectSymbols) {
// Currently we only write local object symbols.
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
// All object symbols are prefixed with the type of the C++ object.
final objectType = symbol.name.substring(0, symbol.name.indexOf('_'));
switch (objectType) {
// Used for entries in the non-clustered portion of the read-only data
// section that don't correspond to a specific Dart object.
case 'RawBytes':
// Currently the only types of objects written to the non-clustered
// portion of the read-only data section.
case 'OneByteString':
case 'TwoByteString':
case 'CodeSourceMap':
case 'PcDescriptors':
case 'CompressedStackMaps':
break;
default:
Expect.fail('unexpected object type $objectType');
}
}
}

View file

@ -0,0 +1,118 @@
// 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.
// @dart = 2.9
// This test checks that --add-readonly-data-symbols outputs static symbols
// for read-only data objects.
// OtherResources=use_save_debugging_info_flag_program.dart
import "dart:async";
import "dart:io";
import 'package:expect/expect.dart';
import 'package:native_stack_traces/elf.dart';
import 'package:path/path.dart' as path;
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('readonly-symbols-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 scriptDill = path.join(tempDir, 'flag_program.dill');
// Compile script to Kernel IR.
await run(genKernel, <String>[
'--aot',
'--platform=$platformDill',
'-o',
scriptDill,
script,
]);
final scriptSnapshot = path.join(tempDir, 'dwarf.so');
final scriptDebuggingInfo = path.join(tempDir, 'debug_info.so');
await run(genSnapshot, <String>[
'--add-readonly-data-symbols',
'--dwarf-stack-traces-mode',
'--save-debugging-info=$scriptDebuggingInfo',
'--snapshot-kind=app-aot-elf',
'--elf=$scriptSnapshot',
scriptDill,
]);
checkElf(scriptSnapshot);
checkElf(scriptDebuggingInfo);
});
}
void checkElf(String filename) {
// Check that the static symbol table contains entries that are not in the
// dynamic symbol table, have STB_LOCAL binding, and are of type STT_OBJECT.
final elf = Elf.fromFile(filename);
Expect.isNotNull(elf);
final dynamicSymbols = elf.dynamicSymbols.toList();
for (final symbol in dynamicSymbols) {
// All symbol tables have an initial entry with zero-valued fields.
if (symbol.name == '') {
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
Expect.equals(SymbolType.STT_NOTYPE, symbol.type);
Expect.equals(0, symbol.value);
} else {
Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
Expect.isTrue(symbol.name.startsWith('_kDart'),
'unexpected symbol name ${symbol.name}');
}
}
final onlyStaticSymbols = elf.staticSymbols
.where((s1) => !dynamicSymbols.any((s2) => s1.name == s2.name));
Expect.isNotEmpty(onlyStaticSymbols, 'no static-only symbols');
final objectSymbols =
onlyStaticSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
Expect.isNotEmpty(objectSymbols, 'no static-only object symbols');
for (final symbol in objectSymbols) {
// Currently we only write local object symbols.
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
// All object symbols are prefixed with the type of the C++ object.
final objectType = symbol.name.substring(0, symbol.name.indexOf('_'));
switch (objectType) {
// Used for entries in the non-clustered portion of the read-only data
// section that don't correspond to a specific Dart object.
case 'RawBytes':
// Currently the only types of objects written to the non-clustered
// portion of the read-only data section.
case 'OneByteString':
case 'TwoByteString':
case 'CodeSourceMap':
case 'PcDescriptors':
case 'CompressedStackMaps':
break;
default:
Expect.fail('unexpected object type $objectType');
}
}
}

View file

@ -7501,7 +7501,11 @@ void Serializer::TraceDataOffset(uint32_t offset) {
}
uint32_t Serializer::GetDataOffset(ObjectPtr object) const {
#if defined(SNAPSHOT_BACKTRACE)
return image_writer_->GetDataOffsetFor(object, ParentOf(object));
#else
return image_writer_->GetDataOffsetFor(object);
#endif
}
intptr_t Serializer::GetDataSize() const {
@ -7606,7 +7610,16 @@ void Serializer::UnexpectedObject(ObjectPtr raw_object, const char* message) {
}
#if defined(SNAPSHOT_BACKTRACE)
ObjectPtr Serializer::ParentOf(const Object& object) {
ObjectPtr Serializer::ParentOf(ObjectPtr object) const {
for (intptr_t i = 0; i < parent_pairs_.length(); i += 2) {
if (parent_pairs_[i]->ptr() == object) {
return parent_pairs_[i + 1]->ptr();
}
}
return Object::null();
}
ObjectPtr Serializer::ParentOf(const Object& object) const {
for (intptr_t i = 0; i < parent_pairs_.length(); i += 2) {
if (parent_pairs_[i]->ptr() == object.ptr()) {
return parent_pairs_[i + 1]->ptr();

View file

@ -19,10 +19,6 @@
#include "vm/snapshot.h"
#include "vm/version.h"
#if defined(DEBUG)
#define SNAPSHOT_BACKTRACE
#endif
namespace dart {
// For full snapshots, we use a clustered snapshot format that trades longer
@ -244,7 +240,8 @@ class Serializer : public ThreadStackResource {
void UnexpectedObject(ObjectPtr object, const char* message);
#if defined(SNAPSHOT_BACKTRACE)
ObjectPtr ParentOf(const Object& object);
ObjectPtr ParentOf(ObjectPtr object) const;
ObjectPtr ParentOf(const Object& object) const;
#endif
SerializationCluster* NewClusterForClass(intptr_t cid, bool is_canonical);

View file

@ -88,6 +88,8 @@ constexpr bool FLAG_support_il_printer = false;
DISASSEMBLE_FLAGS(P, R, C, D) \
P(abort_on_oom, bool, false, \
"Abort if memory allocation fails - use only with --old-gen-heap-size") \
P(add_readonly_data_symbols, bool, false, \
"Add static symbols for objects in snapshot read-only data") \
C(async_debugger, false, false, bool, true, \
"Debugger support async functions.") \
P(background_compilation, bool, kDartUseBackgroundCompilation, \

View file

@ -179,6 +179,9 @@ ImageWriter::ImageWriter(Thread* t)
next_text_offset_(0),
objects_(),
instructions_(),
#if defined(DART_PRECOMPILER)
namer_(t->zone()),
#endif
image_type_(TagObjectTypeAsReadOnly(zone_, "Image")),
instructions_section_type_(
TagObjectTypeAsReadOnly(zone_, "InstructionsSection")),
@ -286,11 +289,20 @@ intptr_t ImageWriter::SizeInSnapshot(ObjectPtr raw_object) {
}
}
#if defined(SNAPSHOT_BACKTRACE)
uint32_t ImageWriter::GetDataOffsetFor(ObjectPtr raw_object,
ObjectPtr raw_parent) {
#else
uint32_t ImageWriter::GetDataOffsetFor(ObjectPtr raw_object) {
#endif
const intptr_t snap_size = SizeInSnapshot(raw_object);
const intptr_t offset = next_data_offset_;
next_data_offset_ += snap_size;
#if defined(SNAPSHOT_BACKTRACE)
objects_.Add(ObjectData(raw_object, raw_parent));
#else
objects_.Add(ObjectData(raw_object));
#endif
return offset;
}
@ -455,8 +467,11 @@ void ImageWriter::Write(NonStreamingWriteStream* clustered_stream, bool vm) {
heap->SetObjectId(data.insns_->ptr(), 0);
}
for (auto& data : objects_) {
if (data.is_object) {
if (data.is_object()) {
data.obj = &Object::Handle(zone_, data.raw_obj);
#if defined(SNAPSHOT_BACKTRACE)
data.parent = &Object::Handle(zone_, data.raw_parent);
#endif
}
}
@ -464,11 +479,14 @@ void ImageWriter::Write(NonStreamingWriteStream* clustered_stream, bool vm) {
// to string objects. String is used for simplicity as a bit container,
// can't use TypedData because it has an internal pointer (data_) field.
for (auto& data : objects_) {
if (!data.is_object) {
if (!data.is_object()) {
const auto bytes = data.bytes;
data.obj = &Object::Handle(
zone_, OneByteString::New(bytes.buf, bytes.length, Heap::kOld));
data.is_object = true;
#if defined(SNAPSHOT_BACKTRACE)
data.parent = &Object::null_object();
#endif
data.set_is_object(true);
String::Cast(*data.obj).Hash();
free(bytes.buf);
}
@ -489,13 +507,8 @@ void ImageWriter::Write(NonStreamingWriteStream* clustered_stream, bool vm) {
}
void ImageWriter::WriteROData(NonStreamingWriteStream* stream, bool vm) {
#if defined(DART_PRECOMPILER)
const intptr_t start_position = stream->Position();
#endif
stream->Align(ImageWriter::kRODataAlignment);
ASSERT(Utils::IsAligned(stream->Position(), kRODataAlignment));
// Heap page starts here.
intptr_t section_start = stream->Position();
stream->WriteWord(next_data_offset_); // Data length.
@ -505,20 +518,20 @@ void ImageWriter::WriteROData(NonStreamingWriteStream* stream, bool vm) {
ASSERT_EQUAL(stream->Position() - section_start, Image::kHeaderSize);
#if defined(DART_PRECOMPILER)
if (profile_writer_ != nullptr) {
const intptr_t end_position = stream->Position();
// Attribute the Image header to the artificial root.
profile_writer_->AttributeBytesTo(
V8SnapshotProfileWriter::kArtificialRootId,
end_position - start_position);
V8SnapshotProfileWriter::kArtificialRootId, Image::kHeaderSize);
}
#endif
// Heap page objects start here.
for (auto entry : objects_) {
ASSERT(entry.is_object);
ASSERT(entry.is_object());
const Object& obj = *entry.obj;
#if defined(DART_PRECOMPILER)
AutoTraceImage(obj, section_start, stream);
const char* object_name = namer_.SnapshotNameFor(entry);
#endif
auto const object_start = stream->Position();
@ -568,6 +581,9 @@ void ImageWriter::WriteROData(NonStreamingWriteStream* stream, bool vm) {
}
stream->Align(compiler::target::ObjectAlignment::kObjectAlignment);
ASSERT_EQUAL(stream->Position() - object_start, SizeInSnapshot(obj));
#if defined(DART_PRECOMPILER)
AddDataSymbol(object_name, object_start, stream->Position() - object_start);
#endif
}
}
@ -772,7 +788,6 @@ void ImageWriter::WriteText(bool vm) {
#if defined(DART_PRECOMPILER)
PcDescriptors& descriptors = PcDescriptors::Handle(zone_);
SnapshotTextObjectNamer namer(zone_);
#endif
ASSERT(offset_space_ != IdSpace::kSnapshot);
@ -782,10 +797,7 @@ void ImageWriter::WriteText(bool vm) {
ASSERT_EQUAL(data.text_offset_, text_offset);
#if defined(DART_PRECOMPILER)
// We won't add trampolines as symbols, so their name need not be unique
// across different WriteText() calls.
const char* object_name = namer.SnapshotNameFor(
is_trampoline ? i : unique_symbol_counter_++, data);
const char* object_name = namer_.SnapshotNameFor(data);
if (profile_writer_ != nullptr) {
const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset);
@ -1106,7 +1118,7 @@ void AssemblyImageWriter::Finalize() {
}
}
static void AddAssemblerIdentifier(ZoneTextBuffer* printer, const char* label) {
static void AddAssemblerIdentifier(BaseTextBuffer* printer, const char* label) {
ASSERT(label[0] != '.');
if (label[0] == 'L' && printer->length() == 0) {
// Assembler treats labels starting with `L` as local which can cause
@ -1159,47 +1171,89 @@ static void AddAssemblerIdentifier(ZoneTextBuffer* printer, const char* label) {
}
}
const char* SnapshotTextObjectNamer::SnapshotNameFor(intptr_t code_index,
const Code& code) {
ASSERT(!code.IsNull());
owner_ = code.owner();
if (owner_.IsNull()) {
insns_ = code.instructions();
const char* name = StubCode::NameOfStub(insns_.EntryPoint());
ASSERT(name != nullptr);
return OS::SCreate(zone_, "Stub_%s", name);
}
// The weak reference to the Code's owner should never have been removed via
// an intermediate serialization, since WSRs are only introduced during
// precompilation.
owner_ = WeakSerializationReference::Unwrap(owner_);
ASSERT(!owner_.IsNull());
ZoneTextBuffer printer(zone_);
if (owner_.IsClass()) {
const char* name = Class::Cast(owner_).ScrubbedNameCString();
printer.AddString("AllocationStub_");
AddAssemblerIdentifier(&printer, name);
} else if (owner_.IsAbstractType()) {
const char* name = namer_.StubNameForType(AbstractType::Cast(owner_));
printer.AddString(name);
} else if (owner_.IsFunction()) {
const char* name = Function::Cast(owner_).QualifiedScrubbedNameCString();
AddAssemblerIdentifier(&printer, name);
void ImageWriter::SnapshotTextObjectNamer::AddNonUniqueNameFor(
BaseTextBuffer* buffer,
const Object& object) {
if (object.IsCode()) {
const Code& code = Code::Cast(object);
owner_ = WeakSerializationReference::Unwrap(code.owner());
if (owner_.IsNull()) {
buffer->AddString("Stub_");
insns_ = code.instructions();
const char* name = StubCode::NameOfStub(insns_.EntryPoint());
ASSERT(name != nullptr);
buffer->AddString(name);
} else {
if (owner_.IsClass()) {
buffer->AddString("AllocationStub_");
} else if (!owner_.IsAbstractType() && !owner_.IsFunction()) {
// Double-check that we didn't get an invalid owner for the Code object.
UNREACHABLE();
}
AddNonUniqueNameFor(buffer, owner_);
}
} else if (object.IsClass()) {
const char* name = Class::Cast(object).ScrubbedNameCString();
AddAssemblerIdentifier(buffer, name);
} else if (object.IsAbstractType()) {
namer_.WriteStubNameForTypeTo(buffer, AbstractType::Cast(object));
} else if (object.IsFunction()) {
const char* name = Function::Cast(object).QualifiedScrubbedNameCString();
AddAssemblerIdentifier(buffer, name);
} else if (object.IsCompressedStackMaps()) {
buffer->AddString("CompressedStackMaps");
} else if (object.IsPcDescriptors()) {
buffer->AddString("PcDescriptors");
} else if (object.IsCodeSourceMap()) {
buffer->AddString("CodeSourceMap");
} else if (object.IsString()) {
const String& str = String::Cast(object);
if (str.IsOneByteString()) {
buffer->AddString("OneByteString");
} else if (str.IsTwoByteString()) {
buffer->AddString("TwoByteString");
}
} else {
UNREACHABLE();
}
}
printer.Printf("_%" Pd, code_index);
const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
const InstructionsData& data) {
ZoneTextBuffer printer(zone_);
if (data.trampoline_bytes != nullptr) {
printer.AddString("Trampoline");
} else {
AddNonUniqueNameFor(&printer, *data.code_);
}
printer.Printf("_%" Pd, nonce_++);
return printer.buffer();
}
const char* SnapshotTextObjectNamer::SnapshotNameFor(
intptr_t index,
const ImageWriter::InstructionsData& data) {
if (data.trampoline_bytes != nullptr) {
return OS::SCreate(zone_, "Trampoline_%" Pd "", index);
const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
const ObjectData& data) {
ASSERT(data.is_object());
ZoneTextBuffer printer(zone_);
if (data.is_original_object()) {
const Object& obj = *data.obj;
AddNonUniqueNameFor(&printer, obj);
#if defined(SNAPSHOT_BACKTRACE)
// It's less useful knowing the parent of a String than other read-only
// data objects, and this avoids us having to handle other classes
// in AddNonUniqueNameFor.
if (!obj.IsString()) {
const Object& parent = *data.parent;
if (!parent.IsNull()) {
printer.AddString("_");
AddNonUniqueNameFor(&printer, parent);
}
}
#endif
} else {
printer.AddString("RawBytes");
}
return SnapshotNameFor(index, *data.code_);
printer.Printf("_%" Pd, nonce_++);
return printer.buffer();
}
void AssemblyImageWriter::WriteBss(bool vm) {
@ -1215,12 +1269,35 @@ void AssemblyImageWriter::WriteBss(bool vm) {
void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
bool vm) {
ImageWriter::WriteROData(clustered_stream, vm);
if (!EnterSection(ProgramSection::Data, vm, ImageWriter::kRODataAlignment)) {
return;
}
WriteBytes(clustered_stream->buffer(), clustered_stream->bytes_written());
ExitSection(ProgramSection::Data, vm, clustered_stream->bytes_written());
// The clustered stream already has some data on it from the serializer, so
// make sure that the read-only objects start at the appropriate alignment
// within the stream, as we'll write the entire clustered stream to the
// assembly output (which was aligned in EnterSection).
const intptr_t start_position = clustered_stream->Position();
clustered_stream->Align(ImageWriter::kRODataAlignment);
if (profile_writer_ != nullptr) {
// Attribute any padding needed to the artificial root.
const intptr_t padding = clustered_stream->Position() - start_position;
profile_writer_->AttributeBytesTo(
V8SnapshotProfileWriter::kArtificialRootId, padding);
}
// First write the read-only data objects to the clustered stream.
ImageWriter::WriteROData(clustered_stream, vm);
// Next, write the bytes of the clustered stream (along with any symbols
// if appropriate) to the assembly output.
const uint8_t* bytes = clustered_stream->buffer();
const intptr_t len = clustered_stream->bytes_written();
intptr_t last_position = 0;
for (const auto& symbol : *current_symbols_) {
WriteBytes(bytes + last_position, symbol.offset - last_position);
assembly_stream_->Printf("%s:\n", symbol.name);
last_position = symbol.offset;
}
WriteBytes(bytes + last_position, len - last_position);
ExitSection(ProgramSection::Data, vm, len);
}
bool AssemblyImageWriter::EnterSection(ProgramSection section,
@ -1240,10 +1317,14 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section,
global_symbol = true;
break;
case ProgramSection::Data:
if (debug_elf_ != nullptr) {
current_symbols_ =
new (zone_) ZoneGrowableArray<Elf::SymbolData>(zone_, 0);
}
// We create a SymbolData array even if there is no debug_elf_ because we
// may be writing RO data symbols, and RO data is written in two steps:
// 1. Serializing the read-only data objects to the clustered stream
// 2. Writing the bytes of the clustered stream to the assembly output.
// Thus, we'll need to interleave the symbols with the cluster bytes
// during step 2.
current_symbols_ =
new (zone_) ZoneGrowableArray<Elf::SymbolData>(zone_, 0);
#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
defined(DART_TARGET_OS_FUCHSIA)
assembly_stream_->WriteString(".section .rodata\n");
@ -1381,6 +1462,13 @@ void AssemblyImageWriter::AddCodeSymbol(const Code& code,
assembly_stream_->Printf("%s:\n", symbol);
}
void AssemblyImageWriter::AddDataSymbol(const char* symbol,
intptr_t offset,
size_t size) {
if (!FLAG_add_readonly_data_symbols) return;
current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size});
}
void AssemblyImageWriter::FrameUnwindPrologue() {
// Creates DWARF's .debug_frame
// CFI = Call frame information
@ -1504,11 +1592,22 @@ void BlobImageWriter::WriteBss(bool vm) {
void BlobImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
bool vm) {
ImageWriter::WriteROData(clustered_stream, vm);
current_section_stream_ = clustered_stream;
#if defined(DART_PRECOMPILER)
const intptr_t start_position = clustered_stream->Position();
#endif
current_section_stream_ = ASSERT_NOTNULL(clustered_stream);
if (!EnterSection(ProgramSection::Data, vm, ImageWriter::kRODataAlignment)) {
return;
}
#if defined(DART_PRECOMPILER)
if (profile_writer_ != nullptr) {
// Attribute any padding needed to the artificial root.
const intptr_t padding = clustered_stream->Position() - start_position;
profile_writer_->AttributeBytesTo(
V8SnapshotProfileWriter::kArtificialRootId, padding);
}
#endif
ImageWriter::WriteROData(clustered_stream, vm);
ExitSection(ProgramSection::Data, vm, clustered_stream->bytes_written());
}
@ -1520,7 +1619,6 @@ bool BlobImageWriter::EnterSection(ProgramSection section,
ASSERT(current_relocations_ == nullptr);
ASSERT(current_symbols_ == nullptr);
#endif
// For now, we set current_section_stream_ in ::WriteData.
ASSERT(section == ProgramSection::Data || current_section_stream_ == nullptr);
ASSERT(current_section_symbol_ == nullptr);
switch (section) {
@ -1535,6 +1633,8 @@ bool BlobImageWriter::EnterSection(ProgramSection section,
#endif
break;
case ProgramSection::Data:
// The stream to use is passed into WriteROData and set there.
ASSERT(current_section_stream_ != nullptr);
#if defined(DART_PRECOMPILER)
current_relocations_ =
new (zone_) ZoneGrowableArray<Elf::Relocation>(zone_, 0);
@ -1614,6 +1714,13 @@ void BlobImageWriter::AddCodeSymbol(const Code& code,
debug_elf_->dwarf()->AddCode(code, symbol);
}
}
void BlobImageWriter::AddDataSymbol(const char* symbol,
intptr_t offset,
size_t size) {
if (!FLAG_add_readonly_data_symbols) return;
current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size});
}
#endif // defined(DART_PRECOMPILER)
#endif // !defined(DART_PRECOMPILED_RUNTIME)

View file

@ -22,6 +22,10 @@
#include "vm/type_testing_stubs.h"
#include "vm/v8_snapshot_writer.h"
#if defined(DEBUG)
#define SNAPSHOT_BACKTRACE
#endif
namespace dart {
// Forward declarations.
@ -270,7 +274,11 @@ class ImageWriter : public ValueObject {
offset_space_ == IdSpace::kIsolateText;
}
int32_t GetTextOffsetFor(InstructionsPtr instructions, CodePtr code);
#if defined(SNAPSHOT_BACKTRACE)
uint32_t GetDataOffsetFor(ObjectPtr raw_object, ObjectPtr raw_parent);
#else
uint32_t GetDataOffsetFor(ObjectPtr raw_object);
#endif
uint32_t AddBytesToData(uint8_t* bytes, intptr_t length);
@ -356,10 +364,27 @@ class ImageWriter : public ValueObject {
};
struct ObjectData {
explicit ObjectData(ObjectPtr raw_obj)
: raw_obj(raw_obj), is_object(true) {}
#if defined(SNAPSHOT_BACKTRACE)
explicit ObjectData(ObjectPtr raw_obj, ObjectPtr raw_parent)
: raw_obj(raw_obj),
raw_parent(raw_parent),
flags(IsObjectField::encode(true) |
IsOriginalObjectField::encode(true)) {}
ObjectData(uint8_t* buf, intptr_t length)
: bytes({buf, length}), is_object(false) {}
: bytes({buf, length}),
raw_parent(Object::null()),
flags(IsObjectField::encode(false) |
IsOriginalObjectField::encode(false)) {}
#else
explicit ObjectData(ObjectPtr raw_obj)
: raw_obj(raw_obj),
flags(IsObjectField::encode(true) |
IsOriginalObjectField::encode(true)) {}
ObjectData(uint8_t* buf, intptr_t length)
: bytes({buf, length}),
flags(IsObjectField::encode(false) |
IsOriginalObjectField::encode(false)) {}
#endif
union {
struct {
@ -369,7 +394,28 @@ class ImageWriter : public ValueObject {
ObjectPtr raw_obj;
const Object* obj;
};
bool is_object;
#if defined(SNAPSHOT_BACKTRACE)
union {
ObjectPtr raw_parent;
const Object* parent;
};
#endif
uint8_t flags;
bool is_object() const {
return IsObjectField::decode(flags);
}
bool is_original_object() const {
return IsOriginalObjectField::decode(flags);
}
void set_is_object(bool value) {
flags = IsObjectField::update(value, flags);
}
using IsObjectField = BitField<uint8_t, bool, 0, 1>;
using IsOriginalObjectField =
BitField<uint8_t, bool, IsObjectField::kNextBit, 1>;
};
// Methods abstracting out the particulars of the underlying concrete writer.
@ -415,6 +461,10 @@ class ImageWriter : public ValueObject {
virtual void AddCodeSymbol(const Code& code,
const char* symbol,
intptr_t section_offset) = 0;
// Creates a static symbol for a read-only data object when appropriate.
virtual void AddDataSymbol(const char* symbol,
intptr_t section_offset,
size_t size) = 0;
// Overloaded convenience versions of the above virtual methods.
@ -442,6 +492,47 @@ class ImageWriter : public ValueObject {
GrowableArray<ObjectData> objects_;
GrowableArray<InstructionsData> instructions_;
#if defined(DART_PRECOMPILER)
class SnapshotTextObjectNamer : ValueObject {
public:
explicit SnapshotTextObjectNamer(Zone* zone)
: zone_(ASSERT_NOTNULL(zone)),
owner_(Object::Handle(zone)),
string_(String::Handle(zone)),
insns_(Instructions::Handle(zone)),
store_(IsolateGroup::Current()->object_store()) {}
const char* StubNameForType(const AbstractType& type) const;
// Returns a unique assembly-safe name for text data to use in symbols.
// Assumes that code in the InstructionsData has been allocated a handle.
const char* SnapshotNameFor(const InstructionsData& data);
// Returns a unique assembly-safe name for read-only data to use in symbols.
// Assumes that the ObjectData has already been converted to object handles.
const char* SnapshotNameFor(const ObjectData& data);
private:
// Returns a unique assembly-safe name for the given code or read-only
// data object for use in symbols.
const char* SnapshotNameFor(const Object& object);
// Adds a non-unique assembly-safe name for the given object to the given
// buffer.
void AddNonUniqueNameFor(BaseTextBuffer* buffer, const Object& object);
Zone* const zone_;
Object& owner_;
String& string_;
Instructions& insns_;
ObjectStore* const store_;
TypeTestingStubNamer namer_;
intptr_t nonce_ = 0;
DISALLOW_COPY_AND_ASSIGN(SnapshotTextObjectNamer);
};
SnapshotTextObjectNamer namer_;
#endif
IdSpace offset_space_ = IdSpace::kSnapshot;
V8SnapshotProfileWriter* profile_writer_ = nullptr;
const char* const image_type_;
@ -449,12 +540,8 @@ class ImageWriter : public ValueObject {
const char* const instructions_type_;
const char* const trampoline_type_;
// Used to make sure Code symbols are unique across text sections.
intptr_t unique_symbol_counter_ = 0;
template <class T>
friend class TraceImageObjectScope;
friend class SnapshotTextObjectNamer; // For InstructionsData.
private:
static intptr_t SizeInSnapshotForBytes(intptr_t length);
@ -503,32 +590,6 @@ class TraceImageObjectScope : ValueObject {
DISALLOW_COPY_AND_ASSIGN(TraceImageObjectScope);
};
class SnapshotTextObjectNamer : ValueObject {
public:
explicit SnapshotTextObjectNamer(Zone* zone)
: zone_(ASSERT_NOTNULL(zone)),
owner_(Object::Handle(zone)),
string_(String::Handle(zone)),
insns_(Instructions::Handle(zone)),
store_(IsolateGroup::Current()->object_store()) {}
const char* StubNameForType(const AbstractType& type) const;
const char* SnapshotNameFor(intptr_t code_index, const Code& code);
const char* SnapshotNameFor(intptr_t index,
const ImageWriter::InstructionsData& data);
private:
Zone* const zone_;
Object& owner_;
String& string_;
Instructions& insns_;
ObjectStore* const store_;
TypeTestingStubNamer namer_;
DISALLOW_COPY_AND_ASSIGN(SnapshotTextObjectNamer);
};
class AssemblyImageWriter : public ImageWriter {
public:
AssemblyImageWriter(Thread* thread,
@ -563,6 +624,7 @@ class AssemblyImageWriter : public ImageWriter {
virtual void AddCodeSymbol(const Code& code,
const char* symbol,
intptr_t offset);
virtual void AddDataSymbol(const char* symbol, intptr_t offset, size_t size);
BaseWriteStream* const assembly_stream_;
Dwarf* const assembly_dwarf_;
@ -571,8 +633,8 @@ class AssemblyImageWriter : public ImageWriter {
// Used in Relocation to output "(.)" for relocations involving the current
// section position and creating local symbols in AddCodeSymbol.
const char* current_section_symbol_ = nullptr;
// Used for creating local symbols for code objects in the debugging info,
// if separately written.
// Used for creating local symbols for code and data objects in the
// debugging info, if separately written.
ZoneGrowableArray<Elf::SymbolData>* current_symbols_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter);
@ -616,6 +678,7 @@ class BlobImageWriter : public ImageWriter {
virtual void AddCodeSymbol(const Code& code,
const char* symbol,
intptr_t offset);
virtual void AddDataSymbol(const char* symbol, intptr_t offset, size_t size);
// Set on section entrance to a new array containing the relocations for the
// current section.

View file

@ -14,6 +14,7 @@
#include "vm/stub_code.h"
#include "vm/timeline.h"
#include "vm/type_testing_stubs.h"
#include "vm/zone_text_buffer.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/backend/flow_graph_compiler.h"
@ -32,57 +33,60 @@ TypeTestingStubNamer::TypeTestingStubNamer()
const char* TypeTestingStubNamer::StubNameForType(
const AbstractType& type) const {
Zone* Z = Thread::Current()->zone();
return OS::SCreate(Z, "TypeTestingStub_%s", StringifyType(type));
ZoneTextBuffer buffer(Thread::Current()->zone());
WriteStubNameForTypeTo(&buffer, type);
return buffer.buffer();
}
const char* TypeTestingStubNamer::StringifyType(
void TypeTestingStubNamer::WriteStubNameForTypeTo(
BaseTextBuffer* buffer,
const AbstractType& type) const {
buffer->AddString("TypeTestingStub_");
StringifyTypeTo(buffer, type);
}
void TypeTestingStubNamer::StringifyTypeTo(BaseTextBuffer* buffer,
const AbstractType& type) const {
NoSafepointScope no_safepoint;
Zone* Z = Thread::Current()->zone();
if (type.IsType()) {
const intptr_t cid = Type::Cast(type).type_class_id();
ClassTable* class_table = IsolateGroup::Current()->class_table();
klass_ = class_table->At(cid);
ASSERT(!klass_.IsNull());
const char* curl = "";
lib_ = klass_.library();
if (!lib_.IsNull()) {
string_ = lib_.url();
curl = OS::SCreate(Z, "%s_", string_.ToCString());
buffer->AddString(string_.ToCString());
} else {
static std::atomic<intptr_t> counter = 0;
curl = OS::SCreate(Z, "nolib%" Pd "_", counter++);
buffer->Printf("nolib%" Pd "_", nonce_++);
}
const char* concatenated = AssemblerSafeName(
OS::SCreate(Z, "%s_%s", curl, klass_.ScrubbedNameCString()));
buffer->AddString("_");
buffer->AddString(klass_.ScrubbedNameCString());
const intptr_t type_parameters = klass_.NumTypeParameters();
auto& type_arguments = TypeArguments::Handle();
if (type.arguments() != TypeArguments::null() && type_parameters > 0) {
auto& type_arguments = TypeArguments::Handle(type.arguments());
if (!type_arguments.IsNull() && type_parameters > 0) {
type_arguments = type.arguments();
ASSERT(type_arguments.Length() >= type_parameters);
const intptr_t length = type_arguments.Length();
for (intptr_t i = 0; i < type_parameters; ++i) {
type_ = type_arguments.TypeAt(length - type_parameters + i);
concatenated =
OS::SCreate(Z, "%s__%s", concatenated, StringifyType(type_));
buffer->AddString("__");
StringifyTypeTo(buffer, type_);
}
}
return concatenated;
} else if (type.IsTypeParameter()) {
return AssemblerSafeName(
OS::SCreate(Z, "%s", TypeParameter::Cast(type).CanonicalNameCString()));
buffer->AddString(TypeParameter::Cast(type).CanonicalNameCString());
} else {
return AssemblerSafeName(OS::SCreate(Z, "%s", type.ToCString()));
buffer->AddString(type.ToCString());
}
MakeNameAssemblerSafe(buffer);
}
const char* TypeTestingStubNamer::AssemblerSafeName(char* cname) {
char* cursor = cname;
void TypeTestingStubNamer::MakeNameAssemblerSafe(BaseTextBuffer* buffer) {
char* cursor = buffer->buffer();
while (*cursor != '\0') {
char c = *cursor;
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
@ -91,7 +95,6 @@ const char* TypeTestingStubNamer::AssemblerSafeName(char* cname) {
}
cursor++;
}
return cname;
}
CodePtr TypeTestingStubGenerator::DefaultCodeForType(

View file

@ -24,15 +24,19 @@ class TypeTestingStubNamer {
//
// (only during dart_boostrap).
const char* StubNameForType(const AbstractType& type) const;
void WriteStubNameForTypeTo(BaseTextBuffer* buffer,
const AbstractType& type) const;
private:
const char* StringifyType(const AbstractType& type) const;
static const char* AssemblerSafeName(char* cname);
void StringifyTypeTo(BaseTextBuffer* buffer, const AbstractType& type) const;
// Converts the contents of the buffer to an assembly-safe name.
static void MakeNameAssemblerSafe(BaseTextBuffer* buffer);
Library& lib_;
Class& klass_;
AbstractType& type_;
String& string_;
mutable intptr_t nonce_ = 0;
};
class TypeTestingStubGenerator {