Commit ad5b005e authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[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: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarDan Elphick <delphick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67423}
parent 3a50c708
......@@ -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,24 @@ 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>;
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 +46,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 +67,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],
......
// 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();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment