Skip to content

Commit ad5b005

Browse files
schuayCommit Bot
authored andcommitted
[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}
1 parent 3a50c70 commit ad5b005

20 files changed

Lines changed: 312 additions & 99 deletions

src/heap/heap.cc

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4456,11 +4456,6 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
44564456

44574457
isolate_->bootstrapper()->Iterate(v);
44584458
v->Synchronize(VisitorSynchronization::kBootstrapper);
4459-
if (mode != VISIT_ONLY_STRONG_IGNORE_STACK) {
4460-
isolate_->Iterate(v);
4461-
isolate_->global_handles()->IterateStrongStackRoots(v);
4462-
v->Synchronize(VisitorSynchronization::kTop);
4463-
}
44644459
Relocatable::Iterate(isolate_, v);
44654460
v->Synchronize(VisitorSynchronization::kRelocatable);
44664461
isolate_->debug()->Iterate(v);
@@ -4469,22 +4464,6 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
44694464
isolate_->compilation_cache()->Iterate(v);
44704465
v->Synchronize(VisitorSynchronization::kCompilationCache);
44714466

4472-
// Iterate over local handles in handle scopes.
4473-
FixStaleLeftTrimmedHandlesVisitor left_trim_visitor(this);
4474-
isolate_->handle_scope_implementer()->Iterate(&left_trim_visitor);
4475-
isolate_->handle_scope_implementer()->Iterate(v);
4476-
4477-
if (FLAG_local_heaps) {
4478-
safepoint_->Iterate(&left_trim_visitor);
4479-
safepoint_->Iterate(v);
4480-
isolate_->persistent_handles_list()->Iterate(&left_trim_visitor);
4481-
isolate_->persistent_handles_list()->Iterate(v);
4482-
}
4483-
4484-
isolate_->IterateDeferredHandles(&left_trim_visitor);
4485-
isolate_->IterateDeferredHandles(v);
4486-
v->Synchronize(VisitorSynchronization::kHandleScope);
4487-
44884467
// Iterate over the builtin code objects in the heap. Note that it is not
44894468
// necessary to iterate over code objects on scavenge collections.
44904469
if (!isMinorGC) {
@@ -4516,17 +4495,6 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
45164495
}
45174496
v->Synchronize(VisitorSynchronization::kGlobalHandles);
45184497

4519-
// Iterate over eternal handles. Eternal handles are not iterated by the
4520-
// serializer. Values referenced by eternal handles need to be added manually.
4521-
if (mode != VISIT_FOR_SERIALIZATION) {
4522-
if (isMinorGC) {
4523-
isolate_->eternal_handles()->IterateYoungRoots(v);
4524-
} else {
4525-
isolate_->eternal_handles()->IterateAllRoots(v);
4526-
}
4527-
}
4528-
v->Synchronize(VisitorSynchronization::kEternalHandles);
4529-
45304498
// Iterate over pointers being held by inactive threads.
45314499
isolate_->thread_manager()->Iterate(v);
45324500
v->Synchronize(VisitorSynchronization::kThreadManager);
@@ -4537,18 +4505,67 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
45374505
}
45384506
v->Synchronize(VisitorSynchronization::kStrongRoots);
45394507

4540-
// Iterate over pending Microtasks stored in MicrotaskQueues.
4541-
MicrotaskQueue* default_microtask_queue = isolate_->default_microtask_queue();
4542-
if (default_microtask_queue) {
4543-
MicrotaskQueue* microtask_queue = default_microtask_queue;
4544-
do {
4545-
microtask_queue->IterateMicrotasks(v);
4546-
microtask_queue = microtask_queue->next();
4547-
} while (microtask_queue != default_microtask_queue);
4548-
}
4549-
4550-
// Iterate over the startup object cache unless serializing or deserializing.
4508+
// Visitors in this block only run when not serializing. These include:
4509+
//
4510+
// - Thread-local and stack.
4511+
// - Handles.
4512+
// - Microtasks.
4513+
// - The startup object cache.
4514+
//
4515+
// When creating real startup snapshot, these areas are expected to be empty.
4516+
// It is also possible to create a snapshot of a *running* isolate for testing
4517+
// purposes. In this case, these areas are likely not empty and will simply be
4518+
// skipped.
4519+
//
4520+
// The general guideline for adding visitors to this section vs. adding them
4521+
// above is that non-transient heap state is always visited, transient heap
4522+
// state is visited only when not serializing.
45514523
if (mode != VISIT_FOR_SERIALIZATION) {
4524+
if (mode != VISIT_ONLY_STRONG_IGNORE_STACK) {
4525+
isolate_->Iterate(v);
4526+
isolate_->global_handles()->IterateStrongStackRoots(v);
4527+
v->Synchronize(VisitorSynchronization::kTop);
4528+
}
4529+
4530+
// Iterate over local handles in handle scopes.
4531+
FixStaleLeftTrimmedHandlesVisitor left_trim_visitor(this);
4532+
isolate_->handle_scope_implementer()->Iterate(&left_trim_visitor);
4533+
isolate_->handle_scope_implementer()->Iterate(v);
4534+
4535+
if (FLAG_local_heaps) {
4536+
safepoint_->Iterate(&left_trim_visitor);
4537+
safepoint_->Iterate(v);
4538+
isolate_->persistent_handles_list()->Iterate(&left_trim_visitor);
4539+
isolate_->persistent_handles_list()->Iterate(v);
4540+
}
4541+
4542+
isolate_->IterateDeferredHandles(&left_trim_visitor);
4543+
isolate_->IterateDeferredHandles(v);
4544+
v->Synchronize(VisitorSynchronization::kHandleScope);
4545+
4546+
// Iterate over eternal handles. Eternal handles are not iterated by the
4547+
// serializer. Values referenced by eternal handles need to be added
4548+
// manually.
4549+
if (isMinorGC) {
4550+
isolate_->eternal_handles()->IterateYoungRoots(v);
4551+
} else {
4552+
isolate_->eternal_handles()->IterateAllRoots(v);
4553+
}
4554+
v->Synchronize(VisitorSynchronization::kEternalHandles);
4555+
4556+
// Iterate over pending Microtasks stored in MicrotaskQueues.
4557+
MicrotaskQueue* default_microtask_queue =
4558+
isolate_->default_microtask_queue();
4559+
if (default_microtask_queue) {
4560+
MicrotaskQueue* microtask_queue = default_microtask_queue;
4561+
do {
4562+
microtask_queue->IterateMicrotasks(v);
4563+
microtask_queue = microtask_queue->next();
4564+
} while (microtask_queue != default_microtask_queue);
4565+
}
4566+
4567+
// Iterate over the startup object cache unless serializing or
4568+
// deserializing.
45524569
SerializerDeserializer::Iterate(isolate_, v);
45534570
v->Synchronize(VisitorSynchronization::kStartupObjectCache);
45544571
}

src/init/bootstrapper.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ class Bootstrapper final {
5555
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer,
5656
v8::MicrotaskQueue* microtask_queue);
5757

58+
// Used for testing context deserialization. No code runs in the generated
59+
// context. It only needs to pass heap verification.
60+
Handle<Context> CreateEnvironmentForTesting() {
61+
MaybeHandle<JSGlobalProxy> no_global_proxy;
62+
v8::Local<v8::ObjectTemplate> no_global_object_template;
63+
ExtensionConfiguration no_extensions;
64+
static constexpr int kDefaultContextIndex = 0;
65+
v8::DeserializeEmbedderFieldsCallback no_callback;
66+
v8::MicrotaskQueue* no_microtask_queue = nullptr;
67+
return CreateEnvironment(no_global_proxy, no_global_object_template,
68+
&no_extensions, kDefaultContextIndex, no_callback,
69+
no_microtask_queue);
70+
}
71+
5872
Handle<JSGlobalProxy> NewRemoteContext(
5973
MaybeHandle<JSGlobalProxy> maybe_global_proxy,
6074
v8::Local<v8::ObjectTemplate> global_object_template);

src/runtime/runtime-test.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "src/objects/js-array-inl.h"
2929
#include "src/objects/js-regexp-inl.h"
3030
#include "src/objects/smi.h"
31+
#include "src/snapshot/snapshot.h"
3132
#include "src/trap-handler/trap-handler.h"
3233
#include "src/utils/ostreams.h"
3334
#include "src/wasm/memory-tracing.h"
@@ -1187,6 +1188,22 @@ RUNTIME_FUNCTION(Runtime_StringIteratorProtector) {
11871188
Protectors::IsStringIteratorLookupChainIntact(isolate));
11881189
}
11891190

1191+
// For use by tests and fuzzers. It
1192+
//
1193+
// 1. serializes a snapshot of the current isolate,
1194+
// 2. deserializes the snapshot,
1195+
// 3. and runs VerifyHeap on the resulting isolate.
1196+
//
1197+
// The current isolate should not be modified by this call and can keep running
1198+
// once it completes.
1199+
RUNTIME_FUNCTION(Runtime_SerializeDeserializeNow) {
1200+
HandleScope scope(isolate);
1201+
DCHECK_EQ(0, args.length());
1202+
Snapshot::SerializeDeserializeAndVerifyForTesting(isolate,
1203+
isolate->native_context());
1204+
return ReadOnlyRoots(isolate).undefined_value();
1205+
}
1206+
11901207
// Take a compiled wasm module and serialize it into an array buffer, which is
11911208
// then returned.
11921209
RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {

src/runtime/runtime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ namespace internal {
522522
F(RedirectToWasmInterpreter, 2, 1) \
523523
F(RunningInSimulator, 0, 1) \
524524
F(RuntimeEvaluateREPL, 1, 1) \
525+
F(SerializeDeserializeNow, 0, 1) \
525526
F(SerializeWasmModule, 1, 1) \
526527
F(SetAllocationTimeout, -1 /* 2 || 3 */, 1) \
527528
F(SetForceSlowPath, 1, 1) \

src/snapshot/code-serializer.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ ScriptData::ScriptData(const byte* data, int length)
3232
}
3333

3434
CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash)
35-
: Serializer(isolate), source_hash_(source_hash) {
35+
: Serializer(isolate, Snapshot::kDefaultSerializerFlags),
36+
source_hash_(source_hash) {
3637
allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
3738
}
3839

src/snapshot/context-serializer.cc

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ namespace v8 {
1616
namespace internal {
1717

1818
ContextSerializer::ContextSerializer(
19-
Isolate* isolate, StartupSerializer* startup_serializer,
19+
Isolate* isolate, Snapshot::SerializerFlags flags,
20+
StartupSerializer* startup_serializer,
2021
v8::SerializeEmbedderFieldsCallback callback)
21-
: Serializer(isolate),
22+
: Serializer(isolate, flags),
2223
startup_serializer_(startup_serializer),
2324
serialize_embedder_fields_(callback),
2425
can_be_rehashed_(true) {
@@ -46,12 +47,14 @@ void ContextSerializer::Serialize(Context* o, bool include_global_proxy) {
4647
// Reset math random cache to get fresh random numbers.
4748
MathRandom::ResetContext(context_);
4849

49-
#ifdef DEBUG
5050
MicrotaskQueue* microtask_queue = context_.native_context().microtask_queue();
51-
DCHECK_EQ(0, microtask_queue->size());
52-
DCHECK(!microtask_queue->HasMicrotasksSuppressions());
53-
DCHECK_EQ(0, microtask_queue->GetMicrotasksScopeDepth());
54-
DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
51+
#ifdef DEBUG
52+
if (!allow_microtasks_for_testing()) {
53+
DCHECK_EQ(0, microtask_queue->size());
54+
DCHECK(!microtask_queue->HasMicrotasksSuppressions());
55+
DCHECK_EQ(0, microtask_queue->GetMicrotasksScopeDepth());
56+
DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
57+
}
5558
#endif
5659
context_.native_context().set_microtask_queue(nullptr);
5760

@@ -66,6 +69,9 @@ void ContextSerializer::Serialize(Context* o, bool include_global_proxy) {
6669
}
6770

6871
Pad();
72+
73+
// Restore the microtask queue.
74+
context_.native_context().set_microtask_queue(microtask_queue);
6975
}
7076

7177
void ContextSerializer::SerializeObject(HeapObject obj) {

src/snapshot/context-serializer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ class StartupSerializer;
1616

1717
class V8_EXPORT_PRIVATE ContextSerializer : public Serializer {
1818
public:
19-
ContextSerializer(Isolate* isolate, StartupSerializer* startup_serializer,
19+
ContextSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
20+
StartupSerializer* startup_serializer,
2021
v8::SerializeEmbedderFieldsCallback callback);
2122

2223
~ContextSerializer() override;

src/snapshot/read-only-serializer.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
namespace v8 {
1717
namespace internal {
1818

19-
ReadOnlySerializer::ReadOnlySerializer(Isolate* isolate)
20-
: RootsSerializer(isolate, RootIndex::kFirstReadOnlyRoot) {
19+
ReadOnlySerializer::ReadOnlySerializer(Isolate* isolate,
20+
Snapshot::SerializerFlags flags)
21+
: RootsSerializer(isolate, flags, RootIndex::kFirstReadOnlyRoot) {
2122
STATIC_ASSERT(RootIndex::kFirstReadOnlyRoot == RootIndex::kFirstRoot);
2223
allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
2324
}
@@ -50,7 +51,8 @@ void ReadOnlySerializer::SerializeReadOnlyRoots() {
5051
// No active threads.
5152
CHECK_NULL(isolate()->thread_manager()->FirstThreadStateInUse());
5253
// No active or weak handles.
53-
CHECK(isolate()->handle_scope_implementer()->blocks()->empty());
54+
CHECK_IMPLIES(!allow_open_handles_for_testing(),
55+
isolate()->handle_scope_implementer()->blocks()->empty());
5456

5557
ReadOnlyRoots(isolate()).Iterate(this);
5658
}

src/snapshot/read-only-serializer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class SnapshotByteSink;
1717

1818
class V8_EXPORT_PRIVATE ReadOnlySerializer : public RootsSerializer {
1919
public:
20-
explicit ReadOnlySerializer(Isolate* isolate);
20+
ReadOnlySerializer(Isolate* isolate, Snapshot::SerializerFlags flags);
2121
~ReadOnlySerializer() override;
2222

2323
void SerializeReadOnlyRoots();

src/snapshot/roots-serializer.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ namespace v8 {
1313
namespace internal {
1414

1515
RootsSerializer::RootsSerializer(Isolate* isolate,
16+
Snapshot::SerializerFlags flags,
1617
RootIndex first_root_to_be_serialized)
17-
: Serializer(isolate),
18+
: Serializer(isolate, flags),
1819
first_root_to_be_serialized_(first_root_to_be_serialized),
1920
can_be_rehashed_(true) {
2021
for (size_t i = 0; i < static_cast<size_t>(first_root_to_be_serialized);

0 commit comments

Comments
 (0)