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

Reland "[snapshot] Expose the serializer through %SerializeDeserializeNow"

This is a reland of ad5b005e

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/+/2170090Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarDan Elphick <delphick@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67426}
parent 22f80fc1
......@@ -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],
......
// 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