Reland "[snapshot] Expose the serializer through %SerializeDeserializeNow"
This is a reland of ad5b005e38
Original change's description:
> [snapshot] Expose the serializer through %SerializeDeserializeNow
>
> ... in order to exercise the snapshot/ component from mjsunit tests
> and fuzzers.
>
> * Since the serializer and deserializer can now be called at any time
> instead of only in a tightly controlled environment, several
> assumptions (such as an empty execution stack, no microtasks, no
> handles) no longer hold and had to be made configurable through
> SerializerFlags.
>
> * Root iteration now skips more root categories which were previously
> guaranteed to be empty (e.g. the stack, microtask queue, handles).
>
> * The %SerializeDeserializeNow runtime function triggers
> serialization, deserialization, and heap verification on the current
> isolate and native context.
>
> Support is not yet complete and will be extended in future work. Once
> all mjsunit tests successfully run, we can add a new test mode to
> stress serialization.
>
> Bug: v8:10416
> Change-Id: Ie7ff441a761257dd7f256d0a33e73227850074ac
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2159495
> Commit-Queue: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Dan Elphick <delphick@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#67423}
Tbr: delphick@chromium.org,ulan@chromium.org
Bug: v8:10416
Change-Id: Ibed04c0f0b72fabcf811d8b18a1479391a11568b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2170090
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Dan Elphick <delphick@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67426}
This commit is contained in:
parent
22f80fc176
commit
bce121c7c5
103
src/heap/heap.cc
103
src/heap/heap.cc
@ -4456,11 +4456,6 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
|
||||
|
||||
isolate_->bootstrapper()->Iterate(v);
|
||||
v->Synchronize(VisitorSynchronization::kBootstrapper);
|
||||
if (mode != VISIT_ONLY_STRONG_IGNORE_STACK) {
|
||||
isolate_->Iterate(v);
|
||||
isolate_->global_handles()->IterateStrongStackRoots(v);
|
||||
v->Synchronize(VisitorSynchronization::kTop);
|
||||
}
|
||||
Relocatable::Iterate(isolate_, v);
|
||||
v->Synchronize(VisitorSynchronization::kRelocatable);
|
||||
isolate_->debug()->Iterate(v);
|
||||
@ -4469,22 +4464,6 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
|
||||
isolate_->compilation_cache()->Iterate(v);
|
||||
v->Synchronize(VisitorSynchronization::kCompilationCache);
|
||||
|
||||
// Iterate over local handles in handle scopes.
|
||||
FixStaleLeftTrimmedHandlesVisitor left_trim_visitor(this);
|
||||
isolate_->handle_scope_implementer()->Iterate(&left_trim_visitor);
|
||||
isolate_->handle_scope_implementer()->Iterate(v);
|
||||
|
||||
if (FLAG_local_heaps) {
|
||||
safepoint_->Iterate(&left_trim_visitor);
|
||||
safepoint_->Iterate(v);
|
||||
isolate_->persistent_handles_list()->Iterate(&left_trim_visitor);
|
||||
isolate_->persistent_handles_list()->Iterate(v);
|
||||
}
|
||||
|
||||
isolate_->IterateDeferredHandles(&left_trim_visitor);
|
||||
isolate_->IterateDeferredHandles(v);
|
||||
v->Synchronize(VisitorSynchronization::kHandleScope);
|
||||
|
||||
// Iterate over the builtin code objects in the heap. Note that it is not
|
||||
// necessary to iterate over code objects on scavenge collections.
|
||||
if (!isMinorGC) {
|
||||
@ -4516,17 +4495,6 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
|
||||
}
|
||||
v->Synchronize(VisitorSynchronization::kGlobalHandles);
|
||||
|
||||
// Iterate over eternal handles. Eternal handles are not iterated by the
|
||||
// serializer. Values referenced by eternal handles need to be added manually.
|
||||
if (mode != VISIT_FOR_SERIALIZATION) {
|
||||
if (isMinorGC) {
|
||||
isolate_->eternal_handles()->IterateYoungRoots(v);
|
||||
} else {
|
||||
isolate_->eternal_handles()->IterateAllRoots(v);
|
||||
}
|
||||
}
|
||||
v->Synchronize(VisitorSynchronization::kEternalHandles);
|
||||
|
||||
// Iterate over pointers being held by inactive threads.
|
||||
isolate_->thread_manager()->Iterate(v);
|
||||
v->Synchronize(VisitorSynchronization::kThreadManager);
|
||||
@ -4537,18 +4505,67 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
|
||||
}
|
||||
v->Synchronize(VisitorSynchronization::kStrongRoots);
|
||||
|
||||
// Iterate over pending Microtasks stored in MicrotaskQueues.
|
||||
MicrotaskQueue* default_microtask_queue = isolate_->default_microtask_queue();
|
||||
if (default_microtask_queue) {
|
||||
MicrotaskQueue* microtask_queue = default_microtask_queue;
|
||||
do {
|
||||
microtask_queue->IterateMicrotasks(v);
|
||||
microtask_queue = microtask_queue->next();
|
||||
} while (microtask_queue != default_microtask_queue);
|
||||
}
|
||||
|
||||
// Iterate over the startup object cache unless serializing or deserializing.
|
||||
// Visitors in this block only run when not serializing. These include:
|
||||
//
|
||||
// - Thread-local and stack.
|
||||
// - Handles.
|
||||
// - Microtasks.
|
||||
// - The startup object cache.
|
||||
//
|
||||
// When creating real startup snapshot, these areas are expected to be empty.
|
||||
// It is also possible to create a snapshot of a *running* isolate for testing
|
||||
// purposes. In this case, these areas are likely not empty and will simply be
|
||||
// skipped.
|
||||
//
|
||||
// The general guideline for adding visitors to this section vs. adding them
|
||||
// above is that non-transient heap state is always visited, transient heap
|
||||
// state is visited only when not serializing.
|
||||
if (mode != VISIT_FOR_SERIALIZATION) {
|
||||
if (mode != VISIT_ONLY_STRONG_IGNORE_STACK) {
|
||||
isolate_->Iterate(v);
|
||||
isolate_->global_handles()->IterateStrongStackRoots(v);
|
||||
v->Synchronize(VisitorSynchronization::kTop);
|
||||
}
|
||||
|
||||
// Iterate over local handles in handle scopes.
|
||||
FixStaleLeftTrimmedHandlesVisitor left_trim_visitor(this);
|
||||
isolate_->handle_scope_implementer()->Iterate(&left_trim_visitor);
|
||||
isolate_->handle_scope_implementer()->Iterate(v);
|
||||
|
||||
if (FLAG_local_heaps) {
|
||||
safepoint_->Iterate(&left_trim_visitor);
|
||||
safepoint_->Iterate(v);
|
||||
isolate_->persistent_handles_list()->Iterate(&left_trim_visitor);
|
||||
isolate_->persistent_handles_list()->Iterate(v);
|
||||
}
|
||||
|
||||
isolate_->IterateDeferredHandles(&left_trim_visitor);
|
||||
isolate_->IterateDeferredHandles(v);
|
||||
v->Synchronize(VisitorSynchronization::kHandleScope);
|
||||
|
||||
// Iterate over eternal handles. Eternal handles are not iterated by the
|
||||
// serializer. Values referenced by eternal handles need to be added
|
||||
// manually.
|
||||
if (isMinorGC) {
|
||||
isolate_->eternal_handles()->IterateYoungRoots(v);
|
||||
} else {
|
||||
isolate_->eternal_handles()->IterateAllRoots(v);
|
||||
}
|
||||
v->Synchronize(VisitorSynchronization::kEternalHandles);
|
||||
|
||||
// Iterate over pending Microtasks stored in MicrotaskQueues.
|
||||
MicrotaskQueue* default_microtask_queue =
|
||||
isolate_->default_microtask_queue();
|
||||
if (default_microtask_queue) {
|
||||
MicrotaskQueue* microtask_queue = default_microtask_queue;
|
||||
do {
|
||||
microtask_queue->IterateMicrotasks(v);
|
||||
microtask_queue = microtask_queue->next();
|
||||
} while (microtask_queue != default_microtask_queue);
|
||||
}
|
||||
|
||||
// Iterate over the startup object cache unless serializing or
|
||||
// deserializing.
|
||||
SerializerDeserializer::Iterate(isolate_, v);
|
||||
v->Synchronize(VisitorSynchronization::kStartupObjectCache);
|
||||
}
|
||||
|
@ -55,6 +55,20 @@ class Bootstrapper final {
|
||||
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer,
|
||||
v8::MicrotaskQueue* microtask_queue);
|
||||
|
||||
// Used for testing context deserialization. No code runs in the generated
|
||||
// context. It only needs to pass heap verification.
|
||||
Handle<Context> CreateEnvironmentForTesting() {
|
||||
MaybeHandle<JSGlobalProxy> no_global_proxy;
|
||||
v8::Local<v8::ObjectTemplate> no_global_object_template;
|
||||
ExtensionConfiguration no_extensions;
|
||||
static constexpr int kDefaultContextIndex = 0;
|
||||
v8::DeserializeEmbedderFieldsCallback no_callback;
|
||||
v8::MicrotaskQueue* no_microtask_queue = nullptr;
|
||||
return CreateEnvironment(no_global_proxy, no_global_object_template,
|
||||
&no_extensions, kDefaultContextIndex, no_callback,
|
||||
no_microtask_queue);
|
||||
}
|
||||
|
||||
Handle<JSGlobalProxy> NewRemoteContext(
|
||||
MaybeHandle<JSGlobalProxy> maybe_global_proxy,
|
||||
v8::Local<v8::ObjectTemplate> global_object_template);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "src/objects/js-array-inl.h"
|
||||
#include "src/objects/js-regexp-inl.h"
|
||||
#include "src/objects/smi.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/utils/ostreams.h"
|
||||
#include "src/wasm/memory-tracing.h"
|
||||
@ -1187,6 +1188,22 @@ RUNTIME_FUNCTION(Runtime_StringIteratorProtector) {
|
||||
Protectors::IsStringIteratorLookupChainIntact(isolate));
|
||||
}
|
||||
|
||||
// For use by tests and fuzzers. It
|
||||
//
|
||||
// 1. serializes a snapshot of the current isolate,
|
||||
// 2. deserializes the snapshot,
|
||||
// 3. and runs VerifyHeap on the resulting isolate.
|
||||
//
|
||||
// The current isolate should not be modified by this call and can keep running
|
||||
// once it completes.
|
||||
RUNTIME_FUNCTION(Runtime_SerializeDeserializeNow) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
Snapshot::SerializeDeserializeAndVerifyForTesting(isolate,
|
||||
isolate->native_context());
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
|
||||
// Take a compiled wasm module and serialize it into an array buffer, which is
|
||||
// then returned.
|
||||
RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
|
||||
|
@ -522,6 +522,7 @@ namespace internal {
|
||||
F(RedirectToWasmInterpreter, 2, 1) \
|
||||
F(RunningInSimulator, 0, 1) \
|
||||
F(RuntimeEvaluateREPL, 1, 1) \
|
||||
F(SerializeDeserializeNow, 0, 1) \
|
||||
F(SerializeWasmModule, 1, 1) \
|
||||
F(SetAllocationTimeout, -1 /* 2 || 3 */, 1) \
|
||||
F(SetForceSlowPath, 1, 1) \
|
||||
|
@ -32,7 +32,8 @@ ScriptData::ScriptData(const byte* data, int length)
|
||||
}
|
||||
|
||||
CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash)
|
||||
: Serializer(isolate), source_hash_(source_hash) {
|
||||
: Serializer(isolate, Snapshot::kDefaultSerializerFlags),
|
||||
source_hash_(source_hash) {
|
||||
allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,10 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
ContextSerializer::ContextSerializer(
|
||||
Isolate* isolate, StartupSerializer* startup_serializer,
|
||||
Isolate* isolate, Snapshot::SerializerFlags flags,
|
||||
StartupSerializer* startup_serializer,
|
||||
v8::SerializeEmbedderFieldsCallback callback)
|
||||
: Serializer(isolate),
|
||||
: Serializer(isolate, flags),
|
||||
startup_serializer_(startup_serializer),
|
||||
serialize_embedder_fields_(callback),
|
||||
can_be_rehashed_(true) {
|
||||
@ -46,12 +47,14 @@ void ContextSerializer::Serialize(Context* o, bool include_global_proxy) {
|
||||
// Reset math random cache to get fresh random numbers.
|
||||
MathRandom::ResetContext(context_);
|
||||
|
||||
#ifdef DEBUG
|
||||
MicrotaskQueue* microtask_queue = context_.native_context().microtask_queue();
|
||||
DCHECK_EQ(0, microtask_queue->size());
|
||||
DCHECK(!microtask_queue->HasMicrotasksSuppressions());
|
||||
DCHECK_EQ(0, microtask_queue->GetMicrotasksScopeDepth());
|
||||
DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
|
||||
#ifdef DEBUG
|
||||
if (!allow_microtasks_for_testing()) {
|
||||
DCHECK_EQ(0, microtask_queue->size());
|
||||
DCHECK(!microtask_queue->HasMicrotasksSuppressions());
|
||||
DCHECK_EQ(0, microtask_queue->GetMicrotasksScopeDepth());
|
||||
DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
|
||||
}
|
||||
#endif
|
||||
context_.native_context().set_microtask_queue(nullptr);
|
||||
|
||||
@ -66,6 +69,9 @@ void ContextSerializer::Serialize(Context* o, bool include_global_proxy) {
|
||||
}
|
||||
|
||||
Pad();
|
||||
|
||||
// Restore the microtask queue.
|
||||
context_.native_context().set_microtask_queue(microtask_queue);
|
||||
}
|
||||
|
||||
void ContextSerializer::SerializeObject(HeapObject obj) {
|
||||
|
@ -16,7 +16,8 @@ class StartupSerializer;
|
||||
|
||||
class V8_EXPORT_PRIVATE ContextSerializer : public Serializer {
|
||||
public:
|
||||
ContextSerializer(Isolate* isolate, StartupSerializer* startup_serializer,
|
||||
ContextSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
|
||||
StartupSerializer* startup_serializer,
|
||||
v8::SerializeEmbedderFieldsCallback callback);
|
||||
|
||||
~ContextSerializer() override;
|
||||
|
@ -16,8 +16,9 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
ReadOnlySerializer::ReadOnlySerializer(Isolate* isolate)
|
||||
: RootsSerializer(isolate, RootIndex::kFirstReadOnlyRoot) {
|
||||
ReadOnlySerializer::ReadOnlySerializer(Isolate* isolate,
|
||||
Snapshot::SerializerFlags flags)
|
||||
: RootsSerializer(isolate, flags, RootIndex::kFirstReadOnlyRoot) {
|
||||
STATIC_ASSERT(RootIndex::kFirstReadOnlyRoot == RootIndex::kFirstRoot);
|
||||
allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
|
||||
}
|
||||
@ -50,7 +51,8 @@ void ReadOnlySerializer::SerializeReadOnlyRoots() {
|
||||
// No active threads.
|
||||
CHECK_NULL(isolate()->thread_manager()->FirstThreadStateInUse());
|
||||
// No active or weak handles.
|
||||
CHECK(isolate()->handle_scope_implementer()->blocks()->empty());
|
||||
CHECK_IMPLIES(!allow_open_handles_for_testing(),
|
||||
isolate()->handle_scope_implementer()->blocks()->empty());
|
||||
|
||||
ReadOnlyRoots(isolate()).Iterate(this);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class SnapshotByteSink;
|
||||
|
||||
class V8_EXPORT_PRIVATE ReadOnlySerializer : public RootsSerializer {
|
||||
public:
|
||||
explicit ReadOnlySerializer(Isolate* isolate);
|
||||
ReadOnlySerializer(Isolate* isolate, Snapshot::SerializerFlags flags);
|
||||
~ReadOnlySerializer() override;
|
||||
|
||||
void SerializeReadOnlyRoots();
|
||||
|
@ -13,8 +13,9 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RootsSerializer::RootsSerializer(Isolate* isolate,
|
||||
Snapshot::SerializerFlags flags,
|
||||
RootIndex first_root_to_be_serialized)
|
||||
: Serializer(isolate),
|
||||
: Serializer(isolate, flags),
|
||||
first_root_to_be_serialized_(first_root_to_be_serialized),
|
||||
can_be_rehashed_(true) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(first_root_to_be_serialized);
|
||||
|
@ -24,7 +24,8 @@ class RootsSerializer : public Serializer {
|
||||
public:
|
||||
// The serializer expects that all roots before |first_root_to_be_serialized|
|
||||
// are already serialized.
|
||||
RootsSerializer(Isolate* isolate, RootIndex first_root_to_be_serialized);
|
||||
RootsSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
|
||||
RootIndex first_root_to_be_serialized);
|
||||
|
||||
bool can_be_rehashed() const { return can_be_rehashed_; }
|
||||
bool root_has_been_serialized(RootIndex root_index) const {
|
||||
|
@ -14,15 +14,15 @@
|
||||
#include "src/objects/map.h"
|
||||
#include "src/objects/slots-inl.h"
|
||||
#include "src/objects/smi.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
Serializer::Serializer(Isolate* isolate)
|
||||
Serializer::Serializer(Isolate* isolate, Snapshot::SerializerFlags flags)
|
||||
: isolate_(isolate),
|
||||
external_reference_encoder_(isolate),
|
||||
root_index_map_(isolate),
|
||||
flags_(flags),
|
||||
allocator_(this) {
|
||||
#ifdef OBJECT_PRINT
|
||||
if (FLAG_serialization_statistics) {
|
||||
@ -718,32 +718,53 @@ void Serializer::ObjectSerializer::VisitEmbeddedPointer(Code host,
|
||||
bytes_processed_so_far_ += rinfo->target_address_size();
|
||||
}
|
||||
|
||||
void Serializer::ObjectSerializer::VisitExternalReference(Foreign host,
|
||||
Address* p) {
|
||||
auto encoded_reference =
|
||||
serializer_->EncodeExternalReference(host.foreign_address());
|
||||
if (encoded_reference.is_from_api()) {
|
||||
void Serializer::ObjectSerializer::OutputExternalReference(Address target,
|
||||
int target_size) {
|
||||
DCHECK_LE(target_size, sizeof(target)); // Must fit in Address.
|
||||
ExternalReferenceEncoder::Value encoded_reference;
|
||||
bool encoded_successfully;
|
||||
|
||||
if (serializer_->allow_unknown_external_references_for_testing()) {
|
||||
encoded_successfully =
|
||||
serializer_->TryEncodeExternalReference(target).To(&encoded_reference);
|
||||
} else {
|
||||
encoded_reference = serializer_->EncodeExternalReference(target);
|
||||
encoded_successfully = true;
|
||||
}
|
||||
|
||||
if (!encoded_successfully) {
|
||||
// In this case the serialized snapshot will not be used in a different
|
||||
// Isolate and thus the target address will not change between
|
||||
// serialization and deserialization. We can serialize seen external
|
||||
// references verbatim.
|
||||
CHECK(serializer_->allow_unknown_external_references_for_testing());
|
||||
CHECK(IsAligned(target_size, kObjectAlignment));
|
||||
CHECK_LE(target_size, kNumberOfFixedRawData * kTaggedSize);
|
||||
int size_in_tagged = target_size >> kTaggedSizeLog2;
|
||||
sink_->PutSection(kFixedRawDataStart + size_in_tagged, "FixedRawData");
|
||||
sink_->PutRaw(reinterpret_cast<byte*>(&target), target_size, "Bytes");
|
||||
} else if (encoded_reference.is_from_api()) {
|
||||
sink_->Put(kApiReference, "ApiRef");
|
||||
sink_->PutInt(encoded_reference.index(), "reference index");
|
||||
} else {
|
||||
sink_->Put(kExternalReference, "ExternalRef");
|
||||
sink_->PutInt(encoded_reference.index(), "reference index");
|
||||
}
|
||||
sink_->PutInt(encoded_reference.index(), "reference index");
|
||||
bytes_processed_so_far_ += kSystemPointerSize;
|
||||
bytes_processed_so_far_ += target_size;
|
||||
}
|
||||
|
||||
void Serializer::ObjectSerializer::VisitExternalReference(Foreign host,
|
||||
Address* p) {
|
||||
OutputExternalReference(host.foreign_address(), kSystemPointerSize);
|
||||
}
|
||||
|
||||
void Serializer::ObjectSerializer::VisitExternalReference(Code host,
|
||||
RelocInfo* rinfo) {
|
||||
Address target = rinfo->target_external_reference();
|
||||
auto encoded_reference = serializer_->EncodeExternalReference(target);
|
||||
if (encoded_reference.is_from_api()) {
|
||||
DCHECK(!rinfo->IsCodedSpecially());
|
||||
sink_->Put(kApiReference, "ApiRef");
|
||||
} else {
|
||||
sink_->Put(kExternalReference, "ExternalRef");
|
||||
}
|
||||
DCHECK_NE(target, kNullAddress); // Code does not reference null.
|
||||
sink_->PutInt(encoded_reference.index(), "reference index");
|
||||
bytes_processed_so_far_ += rinfo->target_address_size();
|
||||
DCHECK_IMPLIES(serializer_->EncodeExternalReference(target).is_from_api(),
|
||||
!rinfo->IsCodedSpecially());
|
||||
OutputExternalReference(target, rinfo->target_address_size());
|
||||
}
|
||||
|
||||
void Serializer::ObjectSerializer::VisitInternalReference(Code host,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "src/snapshot/serializer-allocator.h"
|
||||
#include "src/snapshot/serializer-deserializer.h"
|
||||
#include "src/snapshot/snapshot-source-sink.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -158,7 +159,7 @@ class ObjectCacheIndexMap {
|
||||
|
||||
class Serializer : public SerializerDeserializer {
|
||||
public:
|
||||
explicit Serializer(Isolate* isolate);
|
||||
Serializer(Isolate* isolate, Snapshot::SerializerFlags flags);
|
||||
|
||||
std::vector<SerializedData::Reservation> EncodeReservations() const {
|
||||
return allocator_.EncodeReservations();
|
||||
@ -224,6 +225,10 @@ class Serializer : public SerializerDeserializer {
|
||||
ExternalReferenceEncoder::Value EncodeExternalReference(Address addr) {
|
||||
return external_reference_encoder_.Encode(addr);
|
||||
}
|
||||
Maybe<ExternalReferenceEncoder::Value> TryEncodeExternalReference(
|
||||
Address addr) {
|
||||
return external_reference_encoder_.TryEncode(addr);
|
||||
}
|
||||
|
||||
// GetInt reads 4 bytes at once, requiring padding at the end.
|
||||
// Use padding_offset to specify the space you want to use after padding.
|
||||
@ -260,6 +265,16 @@ class Serializer : public SerializerDeserializer {
|
||||
|
||||
SnapshotByteSink sink_; // Used directly by subclasses.
|
||||
|
||||
bool allow_unknown_external_references_for_testing() const {
|
||||
return (flags_ & Snapshot::kAllowUnknownExternalReferencesForTesting) != 0;
|
||||
}
|
||||
bool allow_open_handles_for_testing() const {
|
||||
return (flags_ & Snapshot::kAllowOpenHandlesForTesting) != 0;
|
||||
}
|
||||
bool allow_microtasks_for_testing() const {
|
||||
return (flags_ & Snapshot::kAllowMicrotasksForTesting) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
SerializerReferenceMap reference_map_;
|
||||
@ -269,6 +284,7 @@ class Serializer : public SerializerDeserializer {
|
||||
std::vector<byte> code_buffer_;
|
||||
std::vector<HeapObject> deferred_objects_; // To handle stack overflow.
|
||||
int recursion_depth_ = 0;
|
||||
const Snapshot::SerializerFlags flags_;
|
||||
SerializerAllocator allocator_;
|
||||
|
||||
#ifdef OBJECT_PRINT
|
||||
@ -327,6 +343,7 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
|
||||
// This function outputs or skips the raw data between the last pointer and
|
||||
// up to the current position.
|
||||
void SerializeContent(Map map, int size);
|
||||
void OutputExternalReference(Address target, int target_size);
|
||||
void OutputRawData(Address up_to);
|
||||
void OutputCode(int size);
|
||||
uint32_t SerializeBackingStore(void* backing_store, int32_t byte_length);
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "src/snapshot/snapshot.h"
|
||||
|
||||
#include "src/base/platform/platform.h"
|
||||
#include "src/execution/isolate-inl.h"
|
||||
#include "src/init/bootstrapper.h"
|
||||
#include "src/logging/counters.h"
|
||||
#include "src/snapshot/context-deserializer.h"
|
||||
#include "src/snapshot/context-serializer.h"
|
||||
@ -186,6 +188,52 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
void Snapshot::SerializeDeserializeAndVerifyForTesting(
|
||||
Isolate* isolate, Handle<Context> default_context) {
|
||||
StartupData serialized_data;
|
||||
std::unique_ptr<const char[]> auto_delete_serialized_data;
|
||||
|
||||
isolate->heap()->CollectAllAvailableGarbage(
|
||||
i::GarbageCollectionReason::kSnapshotCreator);
|
||||
|
||||
// Test serialization.
|
||||
{
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
Snapshot::SerializerFlags flags(
|
||||
Snapshot::kAllowUnknownExternalReferencesForTesting |
|
||||
Snapshot::kAllowOpenHandlesForTesting |
|
||||
Snapshot::kAllowMicrotasksForTesting);
|
||||
serialized_data =
|
||||
Snapshot::Create(isolate, *default_context, &no_gc, flags);
|
||||
auto_delete_serialized_data.reset(serialized_data.data);
|
||||
}
|
||||
|
||||
// Test deserialization.
|
||||
Isolate* new_isolate = Isolate::New();
|
||||
{
|
||||
// Set serializer_enabled() to not install extensions and experimental
|
||||
// natives on the new isolate.
|
||||
// TODO(v8:10416): This should be a separate setting on the isolate.
|
||||
new_isolate->enable_serializer();
|
||||
new_isolate->Enter();
|
||||
new_isolate->set_snapshot_blob(&serialized_data);
|
||||
CHECK(Snapshot::Initialize(new_isolate));
|
||||
|
||||
HandleScope scope(new_isolate);
|
||||
Handle<Context> new_native_context =
|
||||
new_isolate->bootstrapper()->CreateEnvironmentForTesting();
|
||||
CHECK(new_native_context->IsNativeContext());
|
||||
|
||||
#ifdef VERIFY_HEAP
|
||||
new_isolate->heap()->Verify();
|
||||
#endif // VERIFY_HEAP
|
||||
}
|
||||
new_isolate->Exit();
|
||||
Isolate::Delete(new_isolate);
|
||||
}
|
||||
|
||||
void ProfileDeserialization(
|
||||
const SnapshotData* read_only_snapshot,
|
||||
const SnapshotData* startup_snapshot,
|
||||
@ -210,19 +258,22 @@ void ProfileDeserialization(
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
constexpr Snapshot::SerializerFlags Snapshot::kDefaultSerializerFlags;
|
||||
|
||||
// static
|
||||
v8::StartupData Snapshot::Create(
|
||||
Isolate* isolate, std::vector<Context>* contexts,
|
||||
const std::vector<SerializeInternalFieldsCallback>&
|
||||
embedder_fields_serializers,
|
||||
const DisallowHeapAllocation* no_gc) {
|
||||
const DisallowHeapAllocation* no_gc, SerializerFlags flags) {
|
||||
DCHECK_EQ(contexts->size(), embedder_fields_serializers.size());
|
||||
DCHECK_GT(contexts->size(), 0);
|
||||
|
||||
ReadOnlySerializer read_only_serializer(isolate);
|
||||
ReadOnlySerializer read_only_serializer(isolate, flags);
|
||||
read_only_serializer.SerializeReadOnlyRoots();
|
||||
|
||||
StartupSerializer startup_serializer(isolate, &read_only_serializer);
|
||||
StartupSerializer startup_serializer(isolate, flags, &read_only_serializer);
|
||||
startup_serializer.SerializeStrongReferences();
|
||||
|
||||
// Serialize each context with a new serializer.
|
||||
@ -236,7 +287,7 @@ v8::StartupData Snapshot::Create(
|
||||
for (int i = 0; i < num_contexts; i++) {
|
||||
const bool is_default_context = (i == 0);
|
||||
const bool include_global_proxy = !is_default_context;
|
||||
ContextSerializer context_serializer(isolate, &startup_serializer,
|
||||
ContextSerializer context_serializer(isolate, flags, &startup_serializer,
|
||||
embedder_fields_serializers[i]);
|
||||
context_serializer.Serialize(&contexts->at(i), include_global_proxy);
|
||||
can_be_rehashed = can_be_rehashed && context_serializer.can_be_rehashed();
|
||||
@ -265,10 +316,11 @@ v8::StartupData Snapshot::Create(
|
||||
|
||||
// static
|
||||
v8::StartupData Snapshot::Create(Isolate* isolate, Context default_context,
|
||||
const DisallowHeapAllocation* no_gc) {
|
||||
const DisallowHeapAllocation* no_gc,
|
||||
SerializerFlags flags) {
|
||||
std::vector<Context> contexts{default_context};
|
||||
std::vector<SerializeInternalFieldsCallback> callbacks{{}};
|
||||
return Snapshot::Create(isolate, &contexts, callbacks, no_gc);
|
||||
return Snapshot::Create(isolate, &contexts, callbacks, no_gc, flags);
|
||||
}
|
||||
|
||||
v8::StartupData SnapshotImpl::CreateSnapshotBlob(
|
||||
|
@ -21,6 +21,25 @@ class Snapshot : public AllStatic {
|
||||
public:
|
||||
// ---------------- Serialization -------------------------------------------
|
||||
|
||||
enum SerializerFlag {
|
||||
// If set, serializes unknown external references as verbatim data. This
|
||||
// usually leads to invalid state if the snapshot is deserialized in a
|
||||
// different isolate or a different process.
|
||||
// If unset, all external references must be known to the encoder.
|
||||
kAllowUnknownExternalReferencesForTesting = 1 << 0,
|
||||
// If set, serialization can succeed even with open handles. The
|
||||
// contents of open handle scopes are *not* serialized.
|
||||
// If unset, no open handles are allowed to ensure the snapshot
|
||||
// contains no unexpected objects.
|
||||
kAllowOpenHandlesForTesting = 1 << 1,
|
||||
// As above, if set we allow but do *not* serialize existing microtasks.
|
||||
// If unset, the microtask queue must be empty.
|
||||
kAllowMicrotasksForTesting = 1 << 2,
|
||||
};
|
||||
using SerializerFlags = base::Flags<SerializerFlag>;
|
||||
V8_EXPORT_PRIVATE static constexpr SerializerFlags kDefaultSerializerFlags =
|
||||
{};
|
||||
|
||||
// Serializes the given isolate and contexts. Each context may have an
|
||||
// associated callback to serialize internal fields. The default context must
|
||||
// be passed at index 0.
|
||||
@ -28,11 +47,14 @@ class Snapshot : public AllStatic {
|
||||
Isolate* isolate, std::vector<Context>* contexts,
|
||||
const std::vector<SerializeInternalFieldsCallback>&
|
||||
embedder_fields_serializers,
|
||||
const DisallowHeapAllocation* no_gc);
|
||||
const DisallowHeapAllocation* no_gc,
|
||||
SerializerFlags flags = kDefaultSerializerFlags);
|
||||
|
||||
// Convenience helper for the above when only serializing a single context.
|
||||
static v8::StartupData Create(Isolate* isolate, Context default_context,
|
||||
const DisallowHeapAllocation* no_gc);
|
||||
static v8::StartupData Create(
|
||||
Isolate* isolate, Context default_context,
|
||||
const DisallowHeapAllocation* no_gc,
|
||||
SerializerFlags flags = kDefaultSerializerFlags);
|
||||
|
||||
// ---------------- Deserialization -----------------------------------------
|
||||
|
||||
@ -46,6 +68,15 @@ class Snapshot : public AllStatic {
|
||||
size_t context_index,
|
||||
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
|
||||
|
||||
// ---------------- Testing -------------------------------------------------
|
||||
|
||||
// This function is used to stress the snapshot component. It serializes the
|
||||
// current isolate and context into a snapshot, deserializes the snapshot into
|
||||
// a new isolate and context, and finally runs VerifyHeap on the fresh
|
||||
// isolate.
|
||||
static void SerializeDeserializeAndVerifyForTesting(
|
||||
Isolate* isolate, Handle<Context> default_context);
|
||||
|
||||
// ---------------- Helper methods ------------------------------------------
|
||||
|
||||
static bool HasContextSnapshot(Isolate* isolate, size_t index);
|
||||
|
@ -20,8 +20,9 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
StartupSerializer::StartupSerializer(Isolate* isolate,
|
||||
Snapshot::SerializerFlags flags,
|
||||
ReadOnlySerializer* read_only_serializer)
|
||||
: RootsSerializer(isolate, RootIndex::kFirstStrongRoot),
|
||||
: RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot),
|
||||
read_only_serializer_(read_only_serializer) {
|
||||
allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
|
||||
InitializeCodeAddressMap();
|
||||
@ -141,7 +142,8 @@ void StartupSerializer::SerializeStrongReferences() {
|
||||
// No active threads.
|
||||
CHECK_NULL(isolate->thread_manager()->FirstThreadStateInUse());
|
||||
// No active or weak handles.
|
||||
CHECK(isolate->handle_scope_implementer()->blocks()->empty());
|
||||
CHECK_IMPLIES(!allow_open_handles_for_testing(),
|
||||
isolate->handle_scope_implementer()->blocks()->empty());
|
||||
|
||||
// Visit smi roots and immortal immovables first to make sure they end up in
|
||||
// the first page.
|
||||
|
@ -18,7 +18,8 @@ class ReadOnlySerializer;
|
||||
|
||||
class V8_EXPORT_PRIVATE StartupSerializer : public RootsSerializer {
|
||||
public:
|
||||
StartupSerializer(Isolate* isolate, ReadOnlySerializer* read_only_serializer);
|
||||
StartupSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
|
||||
ReadOnlySerializer* read_only_serializer);
|
||||
~StartupSerializer() override;
|
||||
|
||||
// Serialize the current state of the heap. The order is:
|
||||
|
@ -172,10 +172,12 @@ static StartupBlobs Serialize(v8::Isolate* isolate) {
|
||||
internal_isolate->heap()->CollectAllAvailableGarbage(
|
||||
i::GarbageCollectionReason::kTesting);
|
||||
|
||||
ReadOnlySerializer read_only_serializer(internal_isolate);
|
||||
ReadOnlySerializer read_only_serializer(internal_isolate,
|
||||
Snapshot::kDefaultSerializerFlags);
|
||||
read_only_serializer.SerializeReadOnlyRoots();
|
||||
|
||||
StartupSerializer ser(internal_isolate, &read_only_serializer);
|
||||
StartupSerializer ser(internal_isolate, Snapshot::kDefaultSerializerFlags,
|
||||
&read_only_serializer);
|
||||
ser.SerializeStrongReferences();
|
||||
|
||||
ser.SerializeWeakReferencesAndDeferred();
|
||||
@ -385,16 +387,19 @@ static void SerializeContext(Vector<const byte>* startup_blob_out,
|
||||
env.Reset();
|
||||
|
||||
SnapshotByteSink read_only_sink;
|
||||
ReadOnlySerializer read_only_serializer(isolate);
|
||||
ReadOnlySerializer read_only_serializer(isolate,
|
||||
Snapshot::kDefaultSerializerFlags);
|
||||
read_only_serializer.SerializeReadOnlyRoots();
|
||||
|
||||
SnapshotByteSink startup_sink;
|
||||
StartupSerializer startup_serializer(isolate, &read_only_serializer);
|
||||
StartupSerializer startup_serializer(
|
||||
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
|
||||
startup_serializer.SerializeStrongReferences();
|
||||
|
||||
SnapshotByteSink context_sink;
|
||||
ContextSerializer context_serializer(isolate, &startup_serializer,
|
||||
v8::SerializeInternalFieldsCallback());
|
||||
ContextSerializer context_serializer(
|
||||
isolate, Snapshot::kDefaultSerializerFlags, &startup_serializer,
|
||||
v8::SerializeInternalFieldsCallback());
|
||||
context_serializer.Serialize(&raw_context, false);
|
||||
|
||||
startup_serializer.SerializeWeakReferencesAndDeferred();
|
||||
@ -532,16 +537,19 @@ static void SerializeCustomContext(Vector<const byte>* startup_blob_out,
|
||||
env.Reset();
|
||||
|
||||
SnapshotByteSink read_only_sink;
|
||||
ReadOnlySerializer read_only_serializer(isolate);
|
||||
ReadOnlySerializer read_only_serializer(isolate,
|
||||
Snapshot::kDefaultSerializerFlags);
|
||||
read_only_serializer.SerializeReadOnlyRoots();
|
||||
|
||||
SnapshotByteSink startup_sink;
|
||||
StartupSerializer startup_serializer(isolate, &read_only_serializer);
|
||||
StartupSerializer startup_serializer(
|
||||
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
|
||||
startup_serializer.SerializeStrongReferences();
|
||||
|
||||
SnapshotByteSink context_sink;
|
||||
ContextSerializer context_serializer(isolate, &startup_serializer,
|
||||
v8::SerializeInternalFieldsCallback());
|
||||
ContextSerializer context_serializer(
|
||||
isolate, Snapshot::kDefaultSerializerFlags, &startup_serializer,
|
||||
v8::SerializeInternalFieldsCallback());
|
||||
context_serializer.Serialize(&raw_context, false);
|
||||
|
||||
startup_serializer.SerializeWeakReferencesAndDeferred();
|
||||
|
@ -65,6 +65,10 @@
|
||||
# BUG(v8:10197)
|
||||
'regress/regress-748069': [SKIP],
|
||||
|
||||
# https://crbug.com/1043058
|
||||
# Enable once serializing a running isolate is fully implemented.
|
||||
'serialize-deserialize-now': [SKIP],
|
||||
|
||||
##############################################################################
|
||||
# Tests where variants make no sense.
|
||||
'd8/enable-tracing': [PASS, NO_VARIANTS],
|
||||
|
17
test/mjsunit/serialize-deserialize-now.js
Normal file
17
test/mjsunit/serialize-deserialize-now.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
%SerializeDeserializeNow();
|
||||
|
||||
const xs = [0, 1, 2];
|
||||
var o = { a: 0, b: 1, c: 2 };
|
||||
|
||||
%SerializeDeserializeNow();
|
||||
|
||||
const p = new Promise((resolve, reject) => { resolve("Promise"); });
|
||||
p.then((msg) => console.log(msg));
|
||||
|
||||
%SerializeDeserializeNow();
|
Loading…
Reference in New Issue
Block a user