1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-01 07:14:29 +00:00

[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>
This commit is contained in:
Tess Strickland 2022-07-25 11:03:29 +00:00 committed by Commit Bot
parent c8728d92e3
commit 286326f834
11 changed files with 581 additions and 129 deletions

View File

@ -1,3 +1,7 @@
## 0.5.1
- Exported more ELF utilities for use in Dart tests.
## 0.5.0
- Require Dart >= 2.17 (enhanced enum support)

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

@ -696,16 +696,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 {
@ -1006,6 +1009,17 @@ class Elf implements 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
@ -1028,6 +1042,17 @@ class Elf implements 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

@ -0,0 +1,115 @@
// 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 gen_snapshot 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>[
'--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,117 @@
// 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 gen_snapshot 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>[
'--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

@ -7285,7 +7285,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 {
@ -7390,7 +7394,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

@ -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,12 @@ void AssemblyImageWriter::AddCodeSymbol(const Code& code,
assembly_stream_->Printf("%s:\n", symbol);
}
void AssemblyImageWriter::AddDataSymbol(const char* symbol,
intptr_t offset,
size_t size) {
current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size});
}
void AssemblyImageWriter::FrameUnwindPrologue() {
// Creates DWARF's .debug_frame
// CFI = Call frame information
@ -1504,11 +1591,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 +1618,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 +1632,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 +1713,12 @@ void BlobImageWriter::AddCodeSymbol(const Code& code,
debug_elf_->dwarf()->AddCode(code, symbol);
}
}
void BlobImageWriter::AddDataSymbol(const char* symbol,
intptr_t offset,
size_t size) {
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,26 @@ 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 +459,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 +490,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 +538,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 +588,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 +622,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 +631,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 +676,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 {