dart-sdk/runtime/vm/clustered_snapshot.h
Regis Crelier 3d2093ad39 [VM/runtime] Use signature instead of function in type test cache for closure.
The same signature (canonical function type) may be shared between different closure functions.
Clean up asserts verifying that members are canonical when updating type test cache.

TEST=existing ones

Change-Id: Ic206409e56d34dfd02d6705af93065fd3b83b8fa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/199561
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2021-05-20 23:46:45 +00:00

812 lines
28 KiB
C++

// Copyright (c) 2016, 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_CLUSTERED_SNAPSHOT_H_
#define RUNTIME_VM_CLUSTERED_SNAPSHOT_H_
#include "platform/assert.h"
#include "vm/allocation.h"
#include "vm/bitfield.h"
#include "vm/datastream.h"
#include "vm/globals.h"
#include "vm/growable_array.h"
#include "vm/hash_map.h"
#include "vm/heap/heap.h"
#include "vm/image_snapshot.h"
#include "vm/object.h"
#include "vm/raw_object_fields.h"
#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
// serialization time for faster deserialization time and smaller snapshots.
// Objects are clustered by class to allow writing type information once per
// class instead once per object, and to allow filling the objects in a tight
// loop. The snapshot has two major sections: the first describes how to
// allocate the objects and the second describes how to initialize them.
// Deserialization starts by allocating a reference array large enough to hold
// the base objects (objects already available to both the serializer and
// deserializer) and the objects written in the snapshot. The allocation section
// is then read for each cluster, filling the reference array. Then the
// initialization/fill secton is read for each cluster, using the indices into
// the reference array to fill pointers. At this point, every object has been
// touched exactly once and in order, making this approach very cache friendly.
// Finally, each cluster is given an opportunity to perform some fix-ups that
// require the graph has been fully loaded, such as rehashing, though most
// clusters do not require fixups.
// Forward declarations.
class Serializer;
class Deserializer;
class ObjectStore;
class ImageWriter;
class ImageReader;
class LoadingUnitSerializationData : public ZoneAllocated {
public:
LoadingUnitSerializationData(intptr_t id,
LoadingUnitSerializationData* parent)
: id_(id), parent_(parent), deferred_objects_(), objects_(nullptr) {}
intptr_t id() const { return id_; }
LoadingUnitSerializationData* parent() const { return parent_; }
void AddDeferredObject(CodePtr obj) {
deferred_objects_.Add(&Code::ZoneHandle(obj));
}
GrowableArray<Code*>* deferred_objects() { return &deferred_objects_; }
ZoneGrowableArray<Object*>* objects() {
ASSERT(objects_ != nullptr);
return objects_;
}
void set_objects(ZoneGrowableArray<Object*>* objects) {
ASSERT(objects_ == nullptr);
objects_ = objects;
}
private:
intptr_t id_;
LoadingUnitSerializationData* parent_;
GrowableArray<Code*> deferred_objects_;
ZoneGrowableArray<Object*>* objects_;
};
class SerializationCluster : public ZoneAllocated {
public:
static constexpr intptr_t kSizeVaries = -1;
explicit SerializationCluster(const char* name,
intptr_t cid,
intptr_t target_instance_size = kSizeVaries,
bool is_canonical = false)
: name_(name),
cid_(cid),
target_instance_size_(target_instance_size),
is_canonical_(is_canonical) {
ASSERT(target_instance_size == kSizeVaries || target_instance_size >= 0);
}
virtual ~SerializationCluster() {}
// Add [object] to the cluster and push its outgoing references.
virtual void Trace(Serializer* serializer, ObjectPtr object) = 0;
// Write the cluster type and information needed to allocate the cluster's
// objects. For fixed sized objects, this is just the object count. For
// variable sized objects, this is the object count and length of each object.
virtual void WriteAlloc(Serializer* serializer) = 0;
// Write the byte and reference data of the cluster's objects.
virtual void WriteFill(Serializer* serializer) = 0;
void WriteAndMeasureAlloc(Serializer* serializer);
void WriteAndMeasureFill(Serializer* serializer);
const char* name() const { return name_; }
intptr_t cid() const { return cid_; }
bool is_canonical() const { return is_canonical_; }
intptr_t size() const { return size_; }
intptr_t num_objects() const { return num_objects_; }
// Returns number of bytes needed for deserialized objects in
// this cluster. Printed in --print_snapshot_sizes_verbose statistics.
//
// In order to calculate this size, clusters of fixed-size objects
// can pass instance size as [target_instance_size] constructor parameter.
// Otherwise clusters should count [target_memory_size] in
// their [WriteAlloc] methods.
intptr_t target_memory_size() const { return target_memory_size_; }
protected:
const char* const name_;
const intptr_t cid_;
const intptr_t target_instance_size_;
const bool is_canonical_;
intptr_t size_ = 0;
intptr_t num_objects_ = 0;
intptr_t target_memory_size_ = 0;
};
class DeserializationCluster : public ZoneAllocated {
public:
explicit DeserializationCluster(const char* name, bool is_canonical = false)
: name_(name),
is_canonical_(is_canonical),
start_index_(-1),
stop_index_(-1) {}
virtual ~DeserializationCluster() {}
// Allocate memory for all objects in the cluster and write their addresses
// into the ref array. Do not touch this memory.
virtual void ReadAlloc(Deserializer* deserializer) = 0;
// Initialize the cluster's objects. Do not touch the memory of other objects.
virtual void ReadFill(Deserializer* deserializer, bool primary) = 0;
// Complete any action that requires the full graph to be deserialized, such
// as rehashing.
virtual void PostLoad(Deserializer* deserializer,
const Array& refs,
bool primary) {
if (!primary && is_canonical()) {
FATAL1("%s needs canonicalization but doesn't define PostLoad", name());
}
}
const char* name() const { return name_; }
bool is_canonical() const { return is_canonical_; }
protected:
const char* const name_;
const bool is_canonical_;
// The range of the ref array that belongs to this cluster.
intptr_t start_index_;
intptr_t stop_index_;
};
class SerializationRoots {
public:
virtual ~SerializationRoots() {}
virtual void AddBaseObjects(Serializer* serializer) = 0;
virtual void PushRoots(Serializer* serializer) = 0;
virtual void WriteRoots(Serializer* serializer) = 0;
};
class DeserializationRoots {
public:
virtual ~DeserializationRoots() {}
// Returns true if these roots are the first snapshot loaded into a heap, and
// so can assume any canonical objects don't already exist. Returns false if
// some other snapshot may be loaded before these roots, and so written
// canonical objects need to run canonicalization during load.
virtual bool AddBaseObjects(Deserializer* deserializer) = 0;
virtual void ReadRoots(Deserializer* deserializer) = 0;
virtual void PostLoad(Deserializer* deserializer, const Array& refs) = 0;
};
// Reference value for objects that either are not reachable from the roots or
// should never have a reference in the snapshot (because they are dropped,
// for example). Should be the default value for Heap::GetObjectId.
static constexpr intptr_t kUnreachableReference = 0;
COMPILE_ASSERT(kUnreachableReference == WeakTable::kNoValue);
static constexpr intptr_t kFirstReference = 1;
// Reference value for traced objects that have not been allocated their final
// reference ID.
static const intptr_t kUnallocatedReference = -1;
static constexpr bool IsAllocatedReference(intptr_t ref) {
return ref > kUnreachableReference;
}
static constexpr bool IsArtificialReference(intptr_t ref) {
return ref < kUnallocatedReference;
}
static constexpr bool IsReachableReference(intptr_t ref) {
return ref == kUnallocatedReference || IsAllocatedReference(ref);
}
class CodeSerializationCluster;
class Serializer : public ThreadStackResource {
public:
Serializer(Thread* thread,
Snapshot::Kind kind,
NonStreamingWriteStream* stream,
ImageWriter* image_writer_,
bool vm_,
V8SnapshotProfileWriter* profile_writer = nullptr);
~Serializer();
void AddBaseObject(ObjectPtr base_object,
const char* type = nullptr,
const char* name = nullptr);
intptr_t AssignRef(ObjectPtr object);
intptr_t AssignArtificialRef(ObjectPtr object = nullptr);
void Push(ObjectPtr object);
void AddUntracedRef() { num_written_objects_++; }
void Trace(ObjectPtr object);
void UnexpectedObject(ObjectPtr object, const char* message);
#if defined(SNAPSHOT_BACKTRACE)
ObjectPtr ParentOf(const Object& object);
#endif
SerializationCluster* NewClusterForClass(intptr_t cid, bool is_canonical);
void ReserveHeader() {
// Make room for recording snapshot buffer size.
stream_->SetPosition(Snapshot::kHeaderSize);
}
void FillHeader(Snapshot::Kind kind) {
Snapshot* header = reinterpret_cast<Snapshot*>(stream_->buffer());
header->set_magic();
header->set_length(stream_->bytes_written());
header->set_kind(kind);
}
void WriteVersionAndFeatures(bool is_vm_snapshot);
ZoneGrowableArray<Object*>* Serialize(SerializationRoots* roots);
void PrintSnapshotSizes();
FieldTable* initial_field_table() const { return initial_field_table_; }
NonStreamingWriteStream* stream() { return stream_; }
intptr_t bytes_written() { return stream_->bytes_written(); }
intptr_t bytes_heap_allocated() { return bytes_heap_allocated_; }
class WritingObjectScope : ValueObject {
public:
WritingObjectScope(Serializer* serializer,
const char* type,
ObjectPtr object,
StringPtr name)
: WritingObjectScope(
serializer,
ReserveId(serializer,
type,
object,
String::ToCString(serializer->thread(), name)),
object) {}
WritingObjectScope(Serializer* serializer,
const char* type,
ObjectPtr object,
const char* name)
: WritingObjectScope(serializer,
ReserveId(serializer, type, object, name),
object) {}
WritingObjectScope(Serializer* serializer,
const V8SnapshotProfileWriter::ObjectId& id,
ObjectPtr object = nullptr);
WritingObjectScope(Serializer* serializer, ObjectPtr object)
: WritingObjectScope(serializer,
serializer->GetProfileId(object),
object) {}
~WritingObjectScope();
private:
static V8SnapshotProfileWriter::ObjectId ReserveId(Serializer* serializer,
const char* type,
ObjectPtr object,
const char* name);
private:
Serializer* const serializer_;
const ObjectPtr old_object_;
const V8SnapshotProfileWriter::ObjectId old_id_;
const classid_t old_cid_;
};
// Writes raw data to the stream (basic type).
// sizeof(T) must be in {1,2,4,8}.
template <typename T>
void Write(T value) {
BaseWriteStream::Raw<sizeof(T), T>::Write(stream_, value);
}
void WriteUnsigned(intptr_t value) { stream_->WriteUnsigned(value); }
void WriteUnsigned64(uint64_t value) { stream_->WriteUnsigned(value); }
void WriteWordWith32BitWrites(uword value) {
stream_->WriteWordWith32BitWrites(value);
}
void WriteBytes(const uint8_t* addr, intptr_t len) {
stream_->WriteBytes(addr, len);
}
void Align(intptr_t alignment) { stream_->Align(alignment); }
V8SnapshotProfileWriter::ObjectId GetProfileId(ObjectPtr object) const;
V8SnapshotProfileWriter::ObjectId GetProfileId(intptr_t ref) const;
void WriteRootRef(ObjectPtr object, const char* name = nullptr) {
intptr_t id = RefId(object);
WriteUnsigned(id);
if (profile_writer_ != nullptr) {
profile_writer_->AddRoot(GetProfileId(object), name);
}
}
// Record a reference from the currently written object to the given object
// and return reference id for the given object.
void AttributeReference(ObjectPtr object,
const V8SnapshotProfileWriter::Reference& reference);
void AttributeElementRef(ObjectPtr object, intptr_t index) {
AttributeReference(object,
V8SnapshotProfileWriter::Reference::Element(index));
}
void WriteElementRef(ObjectPtr object, intptr_t index) {
AttributeElementRef(object, index);
WriteUnsigned(RefId(object));
}
void AttributePropertyRef(ObjectPtr object, const char* property) {
AttributeReference(object,
V8SnapshotProfileWriter::Reference::Property(property));
}
void WritePropertyRef(ObjectPtr object, const char* property) {
AttributePropertyRef(object, property);
WriteUnsigned(RefId(object));
}
void WriteOffsetRef(ObjectPtr object, intptr_t offset) {
intptr_t id = RefId(object);
WriteUnsigned(id);
if (profile_writer_ != nullptr) {
if (auto const property = offsets_table_->FieldNameForOffset(
object_currently_writing_.cid_, offset)) {
AttributePropertyRef(object, property);
} else {
AttributeElementRef(object, offset);
}
}
}
template <typename T, typename... P>
void WriteFromTo(T obj, P&&... args) {
auto* from = obj->untag()->from();
auto* to = obj->untag()->to_snapshot(kind(), args...);
for (auto* p = from; p <= to; p++) {
WriteOffsetRef(
p->Decompress(obj->heap_base()),
reinterpret_cast<uword>(p) - reinterpret_cast<uword>(obj->untag()));
}
}
template <typename T, typename... P>
void PushFromTo(T obj, P&&... args) {
auto* from = obj->untag()->from();
auto* to = obj->untag()->to_snapshot(kind(), args...);
for (auto* p = from; p <= to; p++) {
Push(p->Decompress(obj->heap_base()));
}
}
void WriteTokenPosition(TokenPosition pos) { Write(pos.Serialize()); }
void WriteCid(intptr_t cid) {
COMPILE_ASSERT(UntaggedObject::kClassIdTagSize <= 32);
Write<int32_t>(cid);
}
// Sorts Code objects and reorders instructions before writing snapshot.
// Returns length of instructions table (in bare instructions mode).
intptr_t PrepareInstructions();
void WriteInstructions(InstructionsPtr instr,
uint32_t unchecked_offset,
CodePtr code,
bool deferred);
uint32_t GetDataOffset(ObjectPtr object) const;
void TraceDataOffset(uint32_t offset);
intptr_t GetDataSize() const;
void WriteDispatchTable(const Array& entries);
Heap* heap() const { return heap_; }
Zone* zone() const { return zone_; }
Snapshot::Kind kind() const { return kind_; }
intptr_t next_ref_index() const { return next_ref_index_; }
void DumpCombinedCodeStatistics();
V8SnapshotProfileWriter* profile_writer() const { return profile_writer_; }
// If the given [obj] was not included into the snaposhot and have not
// yet gotten an artificial node created for it create an artificial node
// in the profile representing this object.
// Returns true if [obj] has an artificial profile node associated with it.
bool CreateArtificialNodeIfNeeded(ObjectPtr obj);
bool InCurrentLoadingUnitOrRoot(ObjectPtr obj);
void RecordDeferredCode(CodePtr ptr);
GrowableArray<LoadingUnitSerializationData*>* loading_units() const {
return loading_units_;
}
void set_loading_units(GrowableArray<LoadingUnitSerializationData*>* units) {
loading_units_ = units;
}
intptr_t current_loading_unit_id() const { return current_loading_unit_id_; }
void set_current_loading_unit_id(intptr_t id) {
current_loading_unit_id_ = id;
}
// Returns the reference ID for the object. Fails for objects that have not
// been allocated a reference ID yet, so should be used only after all
// WriteAlloc calls.
intptr_t RefId(ObjectPtr object) const;
// Same as RefId, but allows artificial and unreachable references. Still
// fails for unallocated references.
intptr_t UnsafeRefId(ObjectPtr object) const;
// Whether the object is reachable.
bool IsReachable(ObjectPtr object) const {
return IsReachableReference(heap_->GetObjectId(object));
}
// Whether the object has an allocated reference.
bool HasRef(ObjectPtr object) const {
return IsAllocatedReference(heap_->GetObjectId(object));
}
// Whether the object only appears in the V8 snapshot profile.
bool HasArtificialRef(ObjectPtr object) const {
return IsArtificialReference(heap_->GetObjectId(object));
}
// Whether a node for the object already has been added to the V8 snapshot
// profile.
bool HasProfileNode(ObjectPtr object) const {
ASSERT(profile_writer_ != nullptr);
return profile_writer_->HasId(GetProfileId(object));
}
bool IsWritten(ObjectPtr object) const {
return heap_->GetObjectId(object) > num_base_objects_;
}
private:
const char* ReadOnlyObjectType(intptr_t cid);
void FlushProfile();
Heap* heap_;
Zone* zone_;
Snapshot::Kind kind_;
NonStreamingWriteStream* stream_;
ImageWriter* image_writer_;
SerializationCluster** canonical_clusters_by_cid_;
SerializationCluster** clusters_by_cid_;
CodeSerializationCluster* code_cluster_ = nullptr;
GrowableArray<ObjectPtr> stack_;
intptr_t num_cids_;
intptr_t num_tlc_cids_;
intptr_t num_base_objects_;
intptr_t num_written_objects_;
intptr_t next_ref_index_;
intptr_t previous_text_offset_;
FieldTable* initial_field_table_;
intptr_t dispatch_table_size_ = 0;
intptr_t bytes_heap_allocated_ = 0;
intptr_t instructions_table_len_ = 0;
// True if writing VM snapshot, false for Isolate snapshot.
bool vm_;
V8SnapshotProfileWriter* profile_writer_ = nullptr;
struct ProfilingObject {
ObjectPtr object_ = nullptr;
// Unless within a WritingObjectScope, any bytes written are attributed to
// the artificial root.
V8SnapshotProfileWriter::ObjectId id_ =
V8SnapshotProfileWriter::kArtificialRootId;
intptr_t last_stream_position_ = 0;
intptr_t cid_ = -1;
} object_currently_writing_;
OffsetsTable* offsets_table_ = nullptr;
#if defined(SNAPSHOT_BACKTRACE)
ObjectPtr current_parent_;
GrowableArray<Object*> parent_pairs_;
#endif
#if defined(DART_PRECOMPILER)
IntMap<intptr_t> deduped_instructions_sources_;
#endif
intptr_t current_loading_unit_id_ = 0;
GrowableArray<LoadingUnitSerializationData*>* loading_units_ = nullptr;
ZoneGrowableArray<Object*>* objects_ = new ZoneGrowableArray<Object*>();
DISALLOW_IMPLICIT_CONSTRUCTORS(Serializer);
};
#define AutoTraceObject(obj) \
Serializer::WritingObjectScope scope_##__COUNTER__(s, name(), obj, nullptr)
#define AutoTraceObjectName(obj, str) \
Serializer::WritingObjectScope scope_##__COUNTER__(s, name(), obj, str)
#define WriteFieldValue(field, value) s->WritePropertyRef(value, #field);
#define WriteFromTo(obj, ...) s->WriteFromTo(obj, ##__VA_ARGS__);
#define PushFromTo(obj, ...) s->PushFromTo(obj, ##__VA_ARGS__);
#define WriteField(obj, field) s->WritePropertyRef(obj->untag()->field, #field)
#define WriteCompressedField(obj, name) \
s->WritePropertyRef(obj->untag()->name(), #name "_")
// This class can be used to read version and features from a snapshot before
// the VM has been initialized.
class SnapshotHeaderReader {
public:
static char* InitializeGlobalVMFlagsFromSnapshot(const Snapshot* snapshot);
static bool NullSafetyFromSnapshot(const Snapshot* snapshot);
explicit SnapshotHeaderReader(const Snapshot* snapshot)
: SnapshotHeaderReader(snapshot->kind(),
snapshot->Addr(),
snapshot->length()) {}
SnapshotHeaderReader(Snapshot::Kind kind,
const uint8_t* buffer,
intptr_t size)
: kind_(kind), stream_(buffer, size) {
stream_.SetPosition(Snapshot::kHeaderSize);
}
// Verifies the version and features in the snapshot are compatible with the
// current VM. If isolate is non-null it validates isolate-specific features.
//
// Returns null on success and a malloc()ed error on failure.
// The [offset] will be the next position in the snapshot stream after the
// features.
char* VerifyVersionAndFeatures(IsolateGroup* isolate_group, intptr_t* offset);
private:
char* VerifyVersion();
char* ReadFeatures(const char** features, intptr_t* features_length);
char* VerifyFeatures(IsolateGroup* isolate_group);
char* BuildError(const char* message);
Snapshot::Kind kind_;
ReadStream stream_;
};
class Deserializer : public ThreadStackResource {
public:
Deserializer(Thread* thread,
Snapshot::Kind kind,
const uint8_t* buffer,
intptr_t size,
const uint8_t* data_buffer,
const uint8_t* instructions_buffer,
bool is_non_root_unit,
intptr_t offset = 0);
~Deserializer();
// Verifies the image alignment.
//
// Returns ApiError::null() on success and an ApiError with an an appropriate
// message otherwise.
ApiErrorPtr VerifyImageAlignment();
static void InitializeHeader(ObjectPtr raw,
intptr_t cid,
intptr_t size,
bool is_canonical = false);
// Reads raw data (for basic types).
// sizeof(T) must be in {1,2,4,8}.
template <typename T>
T Read() {
return ReadStream::Raw<sizeof(T), T>::Read(&stream_);
}
intptr_t ReadUnsigned() { return stream_.ReadUnsigned(); }
uint64_t ReadUnsigned64() { return stream_.ReadUnsigned<uint64_t>(); }
void ReadBytes(uint8_t* addr, intptr_t len) { stream_.ReadBytes(addr, len); }
uword ReadWordWith32BitReads() { return stream_.ReadWordWith32BitReads(); }
intptr_t position() const { return stream_.Position(); }
void set_position(intptr_t p) { stream_.SetPosition(p); }
const uint8_t* CurrentBufferAddress() const {
return stream_.AddressOfCurrentPosition();
}
void Advance(intptr_t value) { stream_.Advance(value); }
void Align(intptr_t alignment) { stream_.Align(alignment); }
void AddBaseObject(ObjectPtr base_object) { AssignRef(base_object); }
void AssignRef(ObjectPtr object) {
ASSERT(next_ref_index_ <= num_objects_);
refs_->untag()->data()[next_ref_index_] = object;
next_ref_index_++;
}
ObjectPtr Ref(intptr_t index) const {
ASSERT(index > 0);
ASSERT(index <= num_objects_);
return refs_->untag()->data()[index];
}
ObjectPtr ReadRef() { return Ref(ReadUnsigned()); }
template <typename T, typename... P>
void ReadFromTo(T obj, P&&... params) {
auto* from = obj->untag()->from();
auto* to_snapshot = obj->untag()->to_snapshot(kind(), params...);
auto* to = obj->untag()->to(params...);
for (auto* p = from; p <= to_snapshot; p++) {
*p = ReadRef();
}
// This is necessary because, unlike Object::Allocate, the clustered
// deserializer allocates object without null-initializing them. Instead,
// each deserialization cluster is responsible for initializing every field,
// ensuring that every field is written to exactly once.
for (auto* p = to_snapshot + 1; p <= to; p++) {
*p = Object::null();
}
}
TokenPosition ReadTokenPosition() {
return TokenPosition::Deserialize(Read<int32_t>());
}
intptr_t ReadCid() {
COMPILE_ASSERT(UntaggedObject::kClassIdTagSize <= 32);
return Read<int32_t>();
}
void ReadInstructions(CodePtr code, bool deferred, bool discarded);
void EndInstructions();
ObjectPtr GetObjectAt(uint32_t offset) const;
void Deserialize(DeserializationRoots* roots);
DeserializationCluster* ReadCluster();
void ReadDispatchTable() {
ReadDispatchTable(&stream_, /*deferred=*/false, -1, -1);
}
void ReadDispatchTable(ReadStream* stream,
bool deferred,
intptr_t deferred_code_start_index,
intptr_t deferred_code_end_index);
intptr_t next_index() const { return next_ref_index_; }
Heap* heap() const { return heap_; }
Zone* zone() const { return zone_; }
Snapshot::Kind kind() const { return kind_; }
FieldTable* initial_field_table() const { return initial_field_table_; }
bool is_non_root_unit() const { return is_non_root_unit_; }
void set_code_start_index(intptr_t value) { code_start_index_ = value; }
intptr_t code_start_index() { return code_start_index_; }
const InstructionsTable& instructions_table() const {
return instructions_table_;
}
private:
Heap* heap_;
Zone* zone_;
Snapshot::Kind kind_;
ReadStream stream_;
ImageReader* image_reader_;
intptr_t num_base_objects_;
intptr_t num_objects_;
intptr_t num_clusters_;
ArrayPtr refs_;
intptr_t next_ref_index_;
intptr_t previous_text_offset_;
intptr_t code_start_index_ = 0;
intptr_t instructions_index_ = 0;
DeserializationCluster** clusters_;
FieldTable* initial_field_table_;
const bool is_non_root_unit_;
InstructionsTable& instructions_table_;
};
#define ReadFromTo(obj, ...) d->ReadFromTo(obj, ##__VA_ARGS__);
class FullSnapshotWriter {
public:
static const intptr_t kInitialSize = 64 * KB;
FullSnapshotWriter(Snapshot::Kind kind,
NonStreamingWriteStream* vm_snapshot_data,
NonStreamingWriteStream* isolate_snapshot_data,
ImageWriter* vm_image_writer,
ImageWriter* iso_image_writer);
~FullSnapshotWriter();
Thread* thread() const { return thread_; }
Zone* zone() const { return thread_->zone(); }
IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
Heap* heap() const { return isolate_group()->heap(); }
// Writes a full snapshot of the program(VM isolate, regular isolate group).
void WriteFullSnapshot(
GrowableArray<LoadingUnitSerializationData*>* data = nullptr);
void WriteUnitSnapshot(GrowableArray<LoadingUnitSerializationData*>* units,
LoadingUnitSerializationData* unit,
uint32_t program_hash);
intptr_t VmIsolateSnapshotSize() const { return vm_isolate_snapshot_size_; }
intptr_t IsolateSnapshotSize() const { return isolate_snapshot_size_; }
private:
// Writes a snapshot of the VM Isolate.
ZoneGrowableArray<Object*>* WriteVMSnapshot();
// Writes a full snapshot of regular Dart isolate group.
void WriteProgramSnapshot(ZoneGrowableArray<Object*>* objects,
GrowableArray<LoadingUnitSerializationData*>* data);
Thread* thread_;
Snapshot::Kind kind_;
NonStreamingWriteStream* const vm_snapshot_data_;
NonStreamingWriteStream* const isolate_snapshot_data_;
intptr_t vm_isolate_snapshot_size_;
intptr_t isolate_snapshot_size_;
ImageWriter* vm_image_writer_;
ImageWriter* isolate_image_writer_;
// Stats for benchmarking.
intptr_t clustered_vm_size_ = 0;
intptr_t clustered_isolate_size_ = 0;
intptr_t mapped_data_size_ = 0;
intptr_t mapped_text_size_ = 0;
intptr_t heap_vm_size_ = 0;
intptr_t heap_isolate_size_ = 0;
V8SnapshotProfileWriter* profile_writer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(FullSnapshotWriter);
};
class FullSnapshotReader {
public:
FullSnapshotReader(const Snapshot* snapshot,
const uint8_t* instructions_buffer,
Thread* thread);
~FullSnapshotReader() {}
ApiErrorPtr ReadVMSnapshot();
ApiErrorPtr ReadProgramSnapshot();
ApiErrorPtr ReadUnitSnapshot(const LoadingUnit& unit);
private:
IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
ApiErrorPtr ConvertToApiError(char* message);
void PatchGlobalObjectPool();
void InitializeBSS();
Snapshot::Kind kind_;
Thread* thread_;
const uint8_t* buffer_;
intptr_t size_;
const uint8_t* data_image_;
const uint8_t* instructions_image_;
DISALLOW_COPY_AND_ASSIGN(FullSnapshotReader);
};
} // namespace dart
#endif // RUNTIME_VM_CLUSTERED_SNAPSHOT_H_