blob: 6727ca0c05c875dfa9dde908669b9c23c0695344 [file] [log] [blame]
// 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_