mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:19:47 +00:00
2e939f215a
Instead, we just always add a BSS section and we add the ROData sections for ELF snapshots in BlobImageWriter::WriteText() (similar to how AssemblyImageWriter::WriteText() handles it for assembly). Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-mac-release-simarm64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-release-simarm_x64-try Change-Id: I87ce64037821ce10f2344964e7f379376c4538df Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150937 Reviewed-by: Martin Kustermann <kustermann@google.com>
453 lines
14 KiB
C++
453 lines
14 KiB
C++
// Copyright (c) 2017, 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.
|
|
|
|
#ifndef RUNTIME_VM_IMAGE_SNAPSHOT_H_
|
|
#define RUNTIME_VM_IMAGE_SNAPSHOT_H_
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "platform/assert.h"
|
|
#include "platform/utils.h"
|
|
#include "vm/allocation.h"
|
|
#include "vm/compiler/runtime_api.h"
|
|
#include "vm/datastream.h"
|
|
#include "vm/globals.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/object.h"
|
|
#include "vm/reusable_handles.h"
|
|
#include "vm/type_testing_stubs.h"
|
|
#include "vm/v8_snapshot_writer.h"
|
|
|
|
namespace dart {
|
|
|
|
// Forward declarations.
|
|
class Code;
|
|
class Dwarf;
|
|
class Elf;
|
|
class Instructions;
|
|
class Object;
|
|
|
|
class Image : ValueObject {
|
|
public:
|
|
explicit Image(const void* raw_memory) : raw_memory_(raw_memory) {
|
|
ASSERT(Utils::IsAligned(raw_memory, kMaxObjectAlignment));
|
|
}
|
|
|
|
void* object_start() const {
|
|
return reinterpret_cast<void*>(reinterpret_cast<uword>(raw_memory_) +
|
|
kHeaderSize);
|
|
}
|
|
|
|
uword object_size() const {
|
|
uword snapshot_size = *reinterpret_cast<const uword*>(raw_memory_);
|
|
return snapshot_size - kHeaderSize;
|
|
}
|
|
|
|
bool contains(uword address) const {
|
|
uword start = reinterpret_cast<uword>(object_start());
|
|
return address >= start && (address - start < object_size());
|
|
}
|
|
|
|
// Returns the offset of the BSS section from this image. Only has meaning for
|
|
// instructions images.
|
|
word bss_offset() const {
|
|
auto const raw_value = *(reinterpret_cast<const word*>(raw_memory_) + 1);
|
|
return Utils::RoundDown(raw_value, kBssAlignment);
|
|
}
|
|
|
|
// Returns true if the image was compiled directly to ELF. Only has meaning
|
|
// for instructions images.
|
|
bool compiled_to_elf() const {
|
|
auto const raw_value = *(reinterpret_cast<const word*>(raw_memory_) + 1);
|
|
return (raw_value & 0x1) == 0x1;
|
|
}
|
|
|
|
private:
|
|
static constexpr intptr_t kHeaderFields = 2;
|
|
static constexpr intptr_t kHeaderSize = kMaxObjectAlignment;
|
|
// Explicitly double-checking kHeaderSize is never changed. Increasing the
|
|
// Image header size would mean objects would not start at a place expected
|
|
// by parts of the VM (like the GC) that use Image pages as HeapPages.
|
|
static_assert(kHeaderSize == kMaxObjectAlignment,
|
|
"Image page cannot be used as HeapPage");
|
|
|
|
// Determines how many bits we have for encoding any extra information in
|
|
// the BSS offset.
|
|
static constexpr intptr_t kBssAlignment = compiler::target::kWordSize;
|
|
|
|
const void* raw_memory_; // The symbol kInstructionsSnapshot.
|
|
|
|
// For access to private constants.
|
|
friend class AssemblyImageWriter;
|
|
friend class BlobImageWriter;
|
|
friend class ImageWriter;
|
|
friend class Elf;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Image);
|
|
};
|
|
|
|
class ImageReader : public ZoneAllocated {
|
|
public:
|
|
ImageReader(const uint8_t* data_image, const uint8_t* instructions_image);
|
|
|
|
ApiErrorPtr VerifyAlignment() const;
|
|
|
|
ONLY_IN_PRECOMPILED(uword GetBareInstructionsAt(uint32_t offset) const);
|
|
ONLY_IN_PRECOMPILED(uword GetBareInstructionsEnd() const);
|
|
InstructionsPtr GetInstructionsAt(uint32_t offset) const;
|
|
ObjectPtr GetObjectAt(uint32_t offset) const;
|
|
|
|
private:
|
|
const uint8_t* data_image_;
|
|
const uint8_t* instructions_image_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ImageReader);
|
|
};
|
|
|
|
struct ObjectOffsetPair {
|
|
public:
|
|
ObjectOffsetPair() : ObjectOffsetPair(NULL, 0) {}
|
|
ObjectOffsetPair(ObjectPtr obj, int32_t off) : object(obj), offset(off) {}
|
|
|
|
ObjectPtr object;
|
|
int32_t offset;
|
|
};
|
|
|
|
class ObjectOffsetTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef ObjectPtr Key;
|
|
typedef int32_t Value;
|
|
typedef ObjectOffsetPair Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv.object; }
|
|
static Value ValueOf(Pair kv) { return kv.offset; }
|
|
static intptr_t Hashcode(Key key);
|
|
static inline bool IsKeyEqual(Pair pair, Key key);
|
|
};
|
|
|
|
typedef DirectChainedHashMap<ObjectOffsetTrait> ObjectOffsetMap;
|
|
|
|
// A command which instructs the image writer to emit something into the ".text"
|
|
// segment.
|
|
//
|
|
// For now this supports
|
|
//
|
|
// * emitting the instructions of a [Code] object
|
|
// * emitting a trampoline of a certain size
|
|
//
|
|
struct ImageWriterCommand {
|
|
enum Opcode {
|
|
InsertInstructionOfCode,
|
|
InsertBytesOfTrampoline,
|
|
};
|
|
|
|
ImageWriterCommand(intptr_t expected_offset, CodePtr code)
|
|
: expected_offset(expected_offset),
|
|
op(ImageWriterCommand::InsertInstructionOfCode),
|
|
insert_instruction_of_code({code}) {}
|
|
|
|
ImageWriterCommand(intptr_t expected_offset,
|
|
uint8_t* trampoline_bytes,
|
|
intptr_t trampoine_length)
|
|
: expected_offset(expected_offset),
|
|
op(ImageWriterCommand::InsertBytesOfTrampoline),
|
|
insert_trampoline_bytes({trampoline_bytes, trampoine_length}) {}
|
|
|
|
// The offset (relative to the very first [ImageWriterCommand]) we expect
|
|
// this [ImageWriterCommand] to have.
|
|
intptr_t expected_offset;
|
|
|
|
Opcode op;
|
|
union {
|
|
struct {
|
|
CodePtr code;
|
|
} insert_instruction_of_code;
|
|
struct {
|
|
uint8_t* buffer;
|
|
intptr_t buffer_length;
|
|
} insert_trampoline_bytes;
|
|
};
|
|
};
|
|
|
|
class ImageWriter : public ValueObject {
|
|
public:
|
|
explicit ImageWriter(Thread* thread);
|
|
virtual ~ImageWriter() {}
|
|
|
|
void ResetOffsets() {
|
|
next_data_offset_ = Image::kHeaderSize;
|
|
next_text_offset_ = Image::kHeaderSize;
|
|
if (FLAG_use_bare_instructions && FLAG_precompiled_mode) {
|
|
next_text_offset_ += compiler::target::InstructionsSection::HeaderSize();
|
|
}
|
|
objects_.Clear();
|
|
instructions_.Clear();
|
|
}
|
|
|
|
// Will start preparing the ".text" segment by interpreting the provided
|
|
// [ImageWriterCommand]s.
|
|
void PrepareForSerialization(GrowableArray<ImageWriterCommand>* commands);
|
|
|
|
bool IsROSpace() const {
|
|
return offset_space_ == V8SnapshotProfileWriter::kVmData ||
|
|
offset_space_ == V8SnapshotProfileWriter::kVmText ||
|
|
offset_space_ == V8SnapshotProfileWriter::kIsolateData ||
|
|
offset_space_ == V8SnapshotProfileWriter::kIsolateText;
|
|
}
|
|
int32_t GetTextOffsetFor(InstructionsPtr instructions, CodePtr code);
|
|
uint32_t GetDataOffsetFor(ObjectPtr raw_object);
|
|
|
|
void Write(WriteStream* clustered_stream, bool vm);
|
|
intptr_t data_size() const { return next_data_offset_; }
|
|
intptr_t text_size() const { return next_text_offset_; }
|
|
intptr_t GetTextObjectCount() const;
|
|
void GetTrampolineInfo(intptr_t* count, intptr_t* size) const;
|
|
|
|
void DumpStatistics();
|
|
|
|
void SetProfileWriter(V8SnapshotProfileWriter* profile_writer) {
|
|
profile_writer_ = profile_writer;
|
|
}
|
|
|
|
void ClearProfileWriter() { profile_writer_ = nullptr; }
|
|
|
|
void TraceInstructions(const Instructions& instructions);
|
|
|
|
static intptr_t SizeInSnapshot(ObjectPtr object);
|
|
static const intptr_t kBareInstructionsAlignment = 4;
|
|
|
|
static_assert(
|
|
(kObjectAlignmentLog2 -
|
|
compiler::target::ObjectAlignment::kObjectAlignmentLog2) >= 0,
|
|
"Target object alignment is larger than the host object alignment");
|
|
|
|
// Converts the target object size (in bytes) to an appropriate argument for
|
|
// ObjectLayout::SizeTag methods on the host machine.
|
|
//
|
|
// ObjectLayout::SizeTag expects a size divisible by kObjectAlignment and
|
|
// checks this in debug mode, but the size on the target machine may not be
|
|
// divisible by the host machine's object alignment if they differ.
|
|
//
|
|
// If target_size = n, we convert it to n * m, where m is the host alignment
|
|
// divided by the target alignment. This means AdjustObjectSizeForTarget(n)
|
|
// encodes on the host machine to the same bits that decode to n on the target
|
|
// machine. That is:
|
|
// n * (host align / target align) / host align => n / target align
|
|
static constexpr intptr_t AdjustObjectSizeForTarget(intptr_t target_size) {
|
|
return target_size
|
|
<< (kObjectAlignmentLog2 -
|
|
compiler::target::ObjectAlignment::kObjectAlignmentLog2);
|
|
}
|
|
|
|
static UNLESS_DEBUG(constexpr) compiler::target::uword
|
|
UpdateObjectSizeForTarget(intptr_t size, uword marked_tags) {
|
|
return ObjectLayout::SizeTag::update(AdjustObjectSizeForTarget(size),
|
|
marked_tags);
|
|
}
|
|
|
|
// Returns nullptr if there is no profile writer.
|
|
const char* ObjectTypeForProfile(const Object& object) const;
|
|
static const char* TagObjectTypeAsReadOnly(Zone* zone, const char* type);
|
|
|
|
protected:
|
|
void WriteROData(WriteStream* stream);
|
|
virtual void WriteText(WriteStream* clustered_stream, bool vm) = 0;
|
|
|
|
void DumpInstructionStats();
|
|
void DumpInstructionsSizes();
|
|
|
|
struct InstructionsData {
|
|
InstructionsData(InstructionsPtr insns, CodePtr code, intptr_t text_offset)
|
|
: raw_insns_(insns),
|
|
raw_code_(code),
|
|
text_offset_(text_offset),
|
|
trampoline_bytes(nullptr),
|
|
trampoline_length(0) {}
|
|
|
|
InstructionsData(uint8_t* trampoline_bytes,
|
|
intptr_t trampoline_length,
|
|
intptr_t text_offset)
|
|
: raw_insns_(nullptr),
|
|
raw_code_(nullptr),
|
|
text_offset_(text_offset),
|
|
trampoline_bytes(trampoline_bytes),
|
|
trampoline_length(trampoline_length) {}
|
|
|
|
union {
|
|
InstructionsPtr raw_insns_;
|
|
const Instructions* insns_;
|
|
};
|
|
union {
|
|
CodePtr raw_code_;
|
|
const Code* code_;
|
|
};
|
|
intptr_t text_offset_;
|
|
|
|
uint8_t* trampoline_bytes;
|
|
intptr_t trampoline_length;
|
|
};
|
|
|
|
struct ObjectData {
|
|
explicit ObjectData(ObjectPtr raw_obj) : raw_obj_(raw_obj) {}
|
|
|
|
union {
|
|
ObjectPtr raw_obj_;
|
|
const Object* obj_;
|
|
};
|
|
};
|
|
|
|
Heap* heap_; // Used for mapping RawInstructiosn to object ids.
|
|
intptr_t next_data_offset_;
|
|
intptr_t next_text_offset_;
|
|
GrowableArray<ObjectData> objects_;
|
|
GrowableArray<InstructionsData> instructions_;
|
|
|
|
V8SnapshotProfileWriter::IdSpace offset_space_ =
|
|
V8SnapshotProfileWriter::kSnapshot;
|
|
V8SnapshotProfileWriter* profile_writer_ = nullptr;
|
|
const char* const instructions_section_type_;
|
|
const char* const instructions_type_;
|
|
const char* const trampoline_type_;
|
|
|
|
template <class T>
|
|
friend class TraceImageObjectScope;
|
|
friend class SnapshotTextObjectNamer; // For InstructionsData.
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
|
|
};
|
|
|
|
#define AutoTraceImage(object, section_offset, stream) \
|
|
auto AutoTraceImagObjectScopeVar##__COUNTER__ = \
|
|
TraceImageObjectScope<std::remove_pointer<decltype(stream)>::type>( \
|
|
this, section_offset, stream, object);
|
|
|
|
template <typename T>
|
|
class TraceImageObjectScope {
|
|
public:
|
|
TraceImageObjectScope(ImageWriter* writer,
|
|
intptr_t section_offset,
|
|
const T* stream,
|
|
const Object& object)
|
|
: writer_(ASSERT_NOTNULL(writer)),
|
|
stream_(ASSERT_NOTNULL(stream)),
|
|
section_offset_(section_offset),
|
|
start_offset_(stream_->Position() - section_offset),
|
|
object_(object) {}
|
|
|
|
~TraceImageObjectScope() {
|
|
if (writer_->profile_writer_ == nullptr) return;
|
|
ASSERT(writer_->IsROSpace());
|
|
writer_->profile_writer_->SetObjectTypeAndName(
|
|
{writer_->offset_space_, start_offset_},
|
|
writer_->ObjectTypeForProfile(object_), nullptr);
|
|
writer_->profile_writer_->AttributeBytesTo(
|
|
{writer_->offset_space_, start_offset_},
|
|
stream_->Position() - section_offset_ - start_offset_);
|
|
}
|
|
|
|
private:
|
|
ImageWriter* const writer_;
|
|
const T* const stream_;
|
|
const intptr_t section_offset_;
|
|
const intptr_t start_offset_;
|
|
const Object& object_;
|
|
};
|
|
|
|
class SnapshotTextObjectNamer {
|
|
public:
|
|
explicit SnapshotTextObjectNamer(Zone* zone)
|
|
: zone_(zone),
|
|
owner_(Object::Handle(zone)),
|
|
string_(String::Handle(zone)),
|
|
insns_(Instructions::Handle(zone)),
|
|
store_(Isolate::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_;
|
|
};
|
|
|
|
class AssemblyImageWriter : public ImageWriter {
|
|
public:
|
|
AssemblyImageWriter(Thread* thread,
|
|
Dart_StreamingWriteCallback callback,
|
|
void* callback_data,
|
|
bool strip = false,
|
|
Elf* debug_elf = nullptr);
|
|
void Finalize();
|
|
|
|
virtual void WriteText(WriteStream* clustered_stream, bool vm);
|
|
|
|
private:
|
|
void FrameUnwindPrologue();
|
|
void FrameUnwindEpilogue();
|
|
intptr_t WriteByteSequence(uword start, uword end);
|
|
intptr_t Align(intptr_t alignment, uword position = 0);
|
|
|
|
#if defined(TARGET_ARCH_IS_64_BIT)
|
|
const char* kLiteralPrefix = ".quad";
|
|
#else
|
|
const char* kLiteralPrefix = ".long";
|
|
#endif
|
|
|
|
intptr_t WriteWordLiteralText(compiler::target::uword value) {
|
|
// Padding is helpful for comparing the .S with --disassemble.
|
|
#if defined(TARGET_ARCH_IS_64_BIT)
|
|
assembly_stream_.Print(".quad 0x%0.16" Px "\n", value);
|
|
#else
|
|
assembly_stream_.Print(".long 0x%0.8" Px "\n", value);
|
|
#endif
|
|
return compiler::target::kWordSize;
|
|
}
|
|
|
|
StreamingWriteStream assembly_stream_;
|
|
Dwarf* assembly_dwarf_;
|
|
Elf* debug_elf_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter);
|
|
};
|
|
|
|
class BlobImageWriter : public ImageWriter {
|
|
public:
|
|
BlobImageWriter(Thread* thread,
|
|
uint8_t** instructions_blob_buffer,
|
|
ReAlloc alloc,
|
|
intptr_t initial_size,
|
|
Elf* debug_elf = nullptr,
|
|
Elf* elf = nullptr);
|
|
|
|
virtual void WriteText(WriteStream* clustered_stream, bool vm);
|
|
|
|
intptr_t InstructionsBlobSize() const {
|
|
return instructions_blob_stream_.bytes_written();
|
|
}
|
|
|
|
private:
|
|
intptr_t WriteByteSequence(uword start, uword end);
|
|
|
|
WriteStream instructions_blob_stream_;
|
|
Elf* const elf_;
|
|
Elf* const debug_elf_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(BlobImageWriter);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_IMAGE_SNAPSHOT_H_
|