diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md index 39f161231dd..a1b47ce3126 100644 --- a/pkg/native_stack_traces/CHANGELOG.md +++ b/pkg/native_stack_traces/CHANGELOG.md @@ -1,7 +1,3 @@ -## 0.5.1 - -- Exported more ELF utilities for use in Dart tests. - ## 0.5.0 - Require Dart >= 2.17 (enhanced enum support) diff --git a/pkg/native_stack_traces/lib/elf.dart b/pkg/native_stack_traces/lib/elf.dart index 7c6e4bbada0..31272b134f8 100644 --- a/pkg/native_stack_traces/lib/elf.dart +++ b/pkg/native_stack_traces/lib/elf.dart @@ -8,12 +8,4 @@ export 'src/constants.dart' isolateSymbolName, vmDataSymbolName, vmSymbolName; -export 'src/elf.dart' - show - DynamicTable, - DynamicTableTag, - Elf, - Section, - Symbol, - SymbolBinding, - SymbolType; +export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol; diff --git a/pkg/native_stack_traces/lib/src/elf.dart b/pkg/native_stack_traces/lib/src/elf.dart index 6b8bbbe7254..d8328ebf7b2 100644 --- a/pkg/native_stack_traces/lib/src/elf.dart +++ b/pkg/native_stack_traces/lib/src/elf.dart @@ -696,19 +696,16 @@ 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 { @@ -1009,17 +1006,6 @@ 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 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 @@ -1042,17 +1028,6 @@ 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 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 diff --git a/runtime/tests/vm/dart/readonly_data_symbols_test.dart b/runtime/tests/vm/dart/readonly_data_symbols_test.dart deleted file mode 100644 index 3ccad9b0513..00000000000 --- a/runtime/tests/vm/dart/readonly_data_symbols_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// 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 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, [ - '--aot', - '--platform=$platformDill', - '-o', - scriptDill, - script, - ]); - - final scriptSnapshot = path.join(tempDir, 'dwarf.so'); - final scriptDebuggingInfo = path.join(tempDir, 'debug_info.so'); - await run(genSnapshot, [ - '--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'); - } - } -} diff --git a/runtime/tests/vm/dart_2/readonly_data_symbols_test.dart b/runtime/tests/vm/dart_2/readonly_data_symbols_test.dart deleted file mode 100644 index c6468409cc3..00000000000 --- a/runtime/tests/vm/dart_2/readonly_data_symbols_test.dart +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// @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 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, [ - '--aot', - '--platform=$platformDill', - '-o', - scriptDill, - script, - ]); - - final scriptSnapshot = path.join(tempDir, 'dwarf.so'); - final scriptDebuggingInfo = path.join(tempDir, 'debug_info.so'); - await run(genSnapshot, [ - '--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'); - } - } -} diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc index 14cc87c064b..ea36ec5b84f 100644 --- a/runtime/vm/app_snapshot.cc +++ b/runtime/vm/app_snapshot.cc @@ -7288,11 +7288,7 @@ 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 { @@ -7397,16 +7393,7 @@ void Serializer::UnexpectedObject(ObjectPtr raw_object, const char* message) { } #if defined(SNAPSHOT_BACKTRACE) -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 { +ObjectPtr Serializer::ParentOf(const Object& object) { for (intptr_t i = 0; i < parent_pairs_.length(); i += 2) { if (parent_pairs_[i]->ptr() == object.ptr()) { return parent_pairs_[i + 1]->ptr(); diff --git a/runtime/vm/app_snapshot.h b/runtime/vm/app_snapshot.h index 3445cb49633..922c8acf94a 100644 --- a/runtime/vm/app_snapshot.h +++ b/runtime/vm/app_snapshot.h @@ -19,6 +19,10 @@ #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 @@ -240,8 +244,7 @@ class Serializer : public ThreadStackResource { void UnexpectedObject(ObjectPtr object, const char* message); #if defined(SNAPSHOT_BACKTRACE) - ObjectPtr ParentOf(ObjectPtr object) const; - ObjectPtr ParentOf(const Object& object) const; + ObjectPtr ParentOf(const Object& object); #endif SerializationCluster* NewClusterForClass(intptr_t cid, bool is_canonical); diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc index 508fb3e4917..811593104d9 100644 --- a/runtime/vm/image_snapshot.cc +++ b/runtime/vm/image_snapshot.cc @@ -179,9 +179,6 @@ 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")), @@ -289,20 +286,11 @@ 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; } @@ -467,11 +455,8 @@ 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 } } @@ -479,14 +464,11 @@ 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)); -#if defined(SNAPSHOT_BACKTRACE) - data.parent = &Object::null_object(); -#endif - data.set_is_object(true); + data.is_object = true; String::Cast(*data.obj).Hash(); free(bytes.buf); } @@ -507,8 +489,13 @@ void ImageWriter::Write(NonStreamingWriteStream* clustered_stream, bool vm) { } void ImageWriter::WriteROData(NonStreamingWriteStream* stream, bool vm) { - ASSERT(Utils::IsAligned(stream->Position(), kRODataAlignment)); +#if defined(DART_PRECOMPILER) + const intptr_t start_position = stream->Position(); +#endif + stream->Align(ImageWriter::kRODataAlignment); + // Heap page starts here. + intptr_t section_start = stream->Position(); stream->WriteWord(next_data_offset_); // Data length. @@ -518,20 +505,20 @@ void ImageWriter::WriteROData(NonStreamingWriteStream* stream, bool vm) { ASSERT_EQUAL(stream->Position() - section_start, Image::kHeaderSize); #if defined(DART_PRECOMPILER) if (profile_writer_ != nullptr) { - // Attribute the Image header to the artificial root. + const intptr_t end_position = stream->Position(); profile_writer_->AttributeBytesTo( - V8SnapshotProfileWriter::kArtificialRootId, Image::kHeaderSize); + V8SnapshotProfileWriter::kArtificialRootId, + end_position - start_position); } #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(); @@ -581,9 +568,6 @@ 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 } } @@ -788,6 +772,7 @@ void ImageWriter::WriteText(bool vm) { #if defined(DART_PRECOMPILER) PcDescriptors& descriptors = PcDescriptors::Handle(zone_); + SnapshotTextObjectNamer namer(zone_); #endif ASSERT(offset_space_ != IdSpace::kSnapshot); @@ -797,7 +782,10 @@ void ImageWriter::WriteText(bool vm) { ASSERT_EQUAL(data.text_offset_, text_offset); #if defined(DART_PRECOMPILER) - const char* object_name = namer_.SnapshotNameFor(data); + // 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); if (profile_writer_ != nullptr) { const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset); @@ -1118,7 +1106,7 @@ void AssemblyImageWriter::Finalize() { } } -static void AddAssemblerIdentifier(BaseTextBuffer* printer, const char* label) { +static void AddAssemblerIdentifier(ZoneTextBuffer* 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 @@ -1171,89 +1159,47 @@ static void AddAssemblerIdentifier(BaseTextBuffer* printer, const char* label) { } } -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"); - } +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); } else { UNREACHABLE(); } + + printer.Printf("_%" Pd, code_index); + return printer.buffer(); } -const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor( - const InstructionsData& data) { - ZoneTextBuffer printer(zone_); +const char* SnapshotTextObjectNamer::SnapshotNameFor( + intptr_t index, + const ImageWriter::InstructionsData& data) { if (data.trampoline_bytes != nullptr) { - printer.AddString("Trampoline"); - } else { - AddNonUniqueNameFor(&printer, *data.code_); + return OS::SCreate(zone_, "Trampoline_%" Pd "", index); } - printer.Printf("_%" Pd, nonce_++); - return printer.buffer(); -} - -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"); - } - printer.Printf("_%" Pd, nonce_++); - return printer.buffer(); + return SnapshotNameFor(index, *data.code_); } void AssemblyImageWriter::WriteBss(bool vm) { @@ -1269,35 +1215,12 @@ 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; } - // 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); + WriteBytes(clustered_stream->buffer(), clustered_stream->bytes_written()); + ExitSection(ProgramSection::Data, vm, clustered_stream->bytes_written()); } bool AssemblyImageWriter::EnterSection(ProgramSection section, @@ -1317,14 +1240,10 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section, global_symbol = true; break; case ProgramSection::Data: - // 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(zone_, 0); + if (debug_elf_ != nullptr) { + current_symbols_ = + new (zone_) ZoneGrowableArray(zone_, 0); + } #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ defined(DART_TARGET_OS_FUCHSIA) assembly_stream_->WriteString(".section .rodata\n"); @@ -1462,12 +1381,6 @@ 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 @@ -1591,22 +1504,11 @@ void BlobImageWriter::WriteBss(bool vm) { void BlobImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream, bool vm) { -#if defined(DART_PRECOMPILER) - const intptr_t start_position = clustered_stream->Position(); -#endif - current_section_stream_ = ASSERT_NOTNULL(clustered_stream); + ImageWriter::WriteROData(clustered_stream, vm); + current_section_stream_ = 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()); } @@ -1618,6 +1520,7 @@ 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) { @@ -1632,8 +1535,6 @@ 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(zone_, 0); @@ -1713,12 +1614,6 @@ 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) diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h index c1ce1513ec1..730b861e196 100644 --- a/runtime/vm/image_snapshot.h +++ b/runtime/vm/image_snapshot.h @@ -22,10 +22,6 @@ #include "vm/type_testing_stubs.h" #include "vm/v8_snapshot_writer.h" -#if defined(DEBUG) -#define SNAPSHOT_BACKTRACE -#endif - namespace dart { // Forward declarations. @@ -274,11 +270,7 @@ 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); @@ -364,27 +356,10 @@ class ImageWriter : public ValueObject { }; struct ObjectData { -#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}), - 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)) {} + : raw_obj(raw_obj), is_object(true) {} ObjectData(uint8_t* buf, intptr_t length) - : bytes({buf, length}), - flags(IsObjectField::encode(false) | - IsOriginalObjectField::encode(false)) {} -#endif + : bytes({buf, length}), is_object(false) {} union { struct { @@ -394,26 +369,7 @@ class ImageWriter : public ValueObject { ObjectPtr raw_obj; const Object* obj; }; -#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; - using IsOriginalObjectField = - BitField; + bool is_object; }; // Methods abstracting out the particulars of the underlying concrete writer. @@ -459,10 +415,6 @@ 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. @@ -490,47 +442,6 @@ class ImageWriter : public ValueObject { GrowableArray objects_; GrowableArray 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_; @@ -538,8 +449,12 @@ 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 friend class TraceImageObjectScope; + friend class SnapshotTextObjectNamer; // For InstructionsData. private: static intptr_t SizeInSnapshotForBytes(intptr_t length); @@ -588,6 +503,32 @@ 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, @@ -622,7 +563,6 @@ 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_; @@ -631,8 +571,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 and data objects in the - // debugging info, if separately written. + // Used for creating local symbols for code objects in the debugging info, + // if separately written. ZoneGrowableArray* current_symbols_ = nullptr; DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter); @@ -676,7 +616,6 @@ 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. diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc index a62a5e44aac..41ac9d1f1d9 100644 --- a/runtime/vm/type_testing_stubs.cc +++ b/runtime/vm/type_testing_stubs.cc @@ -14,7 +14,6 @@ #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" @@ -33,60 +32,57 @@ TypeTestingStubNamer::TypeTestingStubNamer() const char* TypeTestingStubNamer::StubNameForType( const AbstractType& type) const { - ZoneTextBuffer buffer(Thread::Current()->zone()); - WriteStubNameForTypeTo(&buffer, type); - return buffer.buffer(); + Zone* Z = Thread::Current()->zone(); + return OS::SCreate(Z, "TypeTestingStub_%s", StringifyType(type)); } -void TypeTestingStubNamer::WriteStubNameForTypeTo( - BaseTextBuffer* buffer, +const char* TypeTestingStubNamer::StringifyType( 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(); - buffer->AddString(string_.ToCString()); + curl = OS::SCreate(Z, "%s_", string_.ToCString()); } else { - buffer->Printf("nolib%" Pd "_", nonce_++); + static std::atomic counter = 0; + curl = OS::SCreate(Z, "nolib%" Pd "_", counter++); } - buffer->AddString("_"); - buffer->AddString(klass_.ScrubbedNameCString()); + const char* concatenated = AssemblerSafeName( + OS::SCreate(Z, "%s_%s", curl, klass_.ScrubbedNameCString())); const intptr_t type_parameters = klass_.NumTypeParameters(); - auto& type_arguments = TypeArguments::Handle(type.arguments()); - if (!type_arguments.IsNull() && type_parameters > 0) { + auto& type_arguments = TypeArguments::Handle(); + if (type.arguments() != TypeArguments::null() && 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); - buffer->AddString("__"); - StringifyTypeTo(buffer, type_); + concatenated = + OS::SCreate(Z, "%s__%s", concatenated, StringifyType(type_)); } } + + return concatenated; } else if (type.IsTypeParameter()) { - buffer->AddString(TypeParameter::Cast(type).CanonicalNameCString()); + return AssemblerSafeName( + OS::SCreate(Z, "%s", TypeParameter::Cast(type).CanonicalNameCString())); } else { - buffer->AddString(type.ToCString()); + return AssemblerSafeName(OS::SCreate(Z, "%s", type.ToCString())); } - MakeNameAssemblerSafe(buffer); } -void TypeTestingStubNamer::MakeNameAssemblerSafe(BaseTextBuffer* buffer) { - char* cursor = buffer->buffer(); +const char* TypeTestingStubNamer::AssemblerSafeName(char* cname) { + char* cursor = cname; while (*cursor != '\0') { char c = *cursor; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || @@ -95,6 +91,7 @@ void TypeTestingStubNamer::MakeNameAssemblerSafe(BaseTextBuffer* buffer) { } cursor++; } + return cname; } CodePtr TypeTestingStubGenerator::DefaultCodeForType( diff --git a/runtime/vm/type_testing_stubs.h b/runtime/vm/type_testing_stubs.h index b8232152fa2..7807bc1b014 100644 --- a/runtime/vm/type_testing_stubs.h +++ b/runtime/vm/type_testing_stubs.h @@ -24,19 +24,15 @@ class TypeTestingStubNamer { // // (only during dart_boostrap). const char* StubNameForType(const AbstractType& type) const; - void WriteStubNameForTypeTo(BaseTextBuffer* buffer, - const AbstractType& type) const; private: - void StringifyTypeTo(BaseTextBuffer* buffer, const AbstractType& type) const; - // Converts the contents of the buffer to an assembly-safe name. - static void MakeNameAssemblerSafe(BaseTextBuffer* buffer); + const char* StringifyType(const AbstractType& type) const; + static const char* AssemblerSafeName(char* cname); Library& lib_; Class& klass_; AbstractType& type_; String& string_; - mutable intptr_t nonce_ = 0; }; class TypeTestingStubGenerator {