1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // The common functionality when building with or without snapshots.
6
7 #include "src/snapshot/snapshot.h"
8
9 #include "src/base/platform/platform.h"
10 #include "src/common/assert-scope.h"
11 #include "src/execution/isolate-inl.h"
12 #include "src/heap/safepoint.h"
13 #include "src/init/bootstrapper.h"
14 #include "src/logging/runtime-call-stats-scope.h"
15 #include "src/objects/code-kind.h"
16 #include "src/objects/js-regexp-inl.h"
17 #include "src/snapshot/context-deserializer.h"
18 #include "src/snapshot/context-serializer.h"
19 #include "src/snapshot/read-only-deserializer.h"
20 #include "src/snapshot/read-only-serializer.h"
21 #include "src/snapshot/shared-heap-deserializer.h"
22 #include "src/snapshot/shared-heap-serializer.h"
23 #include "src/snapshot/snapshot-utils.h"
24 #include "src/snapshot/startup-deserializer.h"
25 #include "src/snapshot/startup-serializer.h"
26 #include "src/utils/memcopy.h"
27 #include "src/utils/version.h"
28
29 #ifdef V8_SNAPSHOT_COMPRESSION
30 #include "src/snapshot/snapshot-compression.h"
31 #endif
32
33 namespace v8 {
34 namespace internal {
35
36 namespace {
37
38 class SnapshotImpl : public AllStatic {
39 public:
40 static v8::StartupData CreateSnapshotBlob(
41 const SnapshotData* startup_snapshot_in,
42 const SnapshotData* read_only_snapshot_in,
43 const SnapshotData* shared_heap_snapshot_in,
44 const std::vector<SnapshotData*>& context_snapshots_in,
45 bool can_be_rehashed);
46
47 static uint32_t ExtractNumContexts(const v8::StartupData* data);
48 static uint32_t ExtractContextOffset(const v8::StartupData* data,
49 uint32_t index);
50 static base::Vector<const byte> ExtractStartupData(
51 const v8::StartupData* data);
52 static base::Vector<const byte> ExtractReadOnlyData(
53 const v8::StartupData* data);
54 static base::Vector<const byte> ExtractSharedHeapData(
55 const v8::StartupData* data);
56 static base::Vector<const byte> ExtractContextData(
57 const v8::StartupData* data, uint32_t index);
58
GetHeaderValue(const v8::StartupData * data,uint32_t offset)59 static uint32_t GetHeaderValue(const v8::StartupData* data, uint32_t offset) {
60 return base::ReadLittleEndianValue<uint32_t>(
61 reinterpret_cast<Address>(data->data) + offset);
62 }
SetHeaderValue(char * data,uint32_t offset,uint32_t value)63 static void SetHeaderValue(char* data, uint32_t offset, uint32_t value) {
64 base::WriteLittleEndianValue(reinterpret_cast<Address>(data) + offset,
65 value);
66 }
67
68 static void CheckVersion(const v8::StartupData* data);
69
70 // Snapshot blob layout:
71 // [0] number of contexts N
72 // [1] rehashability
73 // [2] checksum
74 // [3] (64 bytes) version string
75 // [4] offset to readonly
76 // [5] offset to shared heap
77 // [6] offset to context 0
78 // [7] offset to context 1
79 // ...
80 // ... offset to context N - 1
81 // ... startup snapshot data
82 // ... read-only snapshot data
83 // ... shared heap snapshot data
84 // ... context 0 snapshot data
85 // ... context 1 snapshot data
86
87 static const uint32_t kNumberOfContextsOffset = 0;
88 // TODO(yangguo): generalize rehashing, and remove this flag.
89 static const uint32_t kRehashabilityOffset =
90 kNumberOfContextsOffset + kUInt32Size;
91 static const uint32_t kChecksumOffset = kRehashabilityOffset + kUInt32Size;
92 static const uint32_t kVersionStringOffset = kChecksumOffset + kUInt32Size;
93 static const uint32_t kVersionStringLength = 64;
94 static const uint32_t kReadOnlyOffsetOffset =
95 kVersionStringOffset + kVersionStringLength;
96 static const uint32_t kSharedHeapOffsetOffset =
97 kReadOnlyOffsetOffset + kUInt32Size;
98 static const uint32_t kFirstContextOffsetOffset =
99 kSharedHeapOffsetOffset + kUInt32Size;
100
ChecksummedContent(const v8::StartupData * data)101 static base::Vector<const byte> ChecksummedContent(
102 const v8::StartupData* data) {
103 STATIC_ASSERT(kVersionStringOffset == kChecksumOffset + kUInt32Size);
104 const uint32_t kChecksumStart = kVersionStringOffset;
105 return base::Vector<const byte>(
106 reinterpret_cast<const byte*>(data->data + kChecksumStart),
107 data->raw_size - kChecksumStart);
108 }
109
StartupSnapshotOffset(int num_contexts)110 static uint32_t StartupSnapshotOffset(int num_contexts) {
111 return POINTER_SIZE_ALIGN(kFirstContextOffsetOffset +
112 num_contexts * kInt32Size);
113 }
114
ContextSnapshotOffsetOffset(int index)115 static uint32_t ContextSnapshotOffsetOffset(int index) {
116 return kFirstContextOffsetOffset + index * kInt32Size;
117 }
118 };
119
120 } // namespace
121
MaybeDecompress(Isolate * isolate,const base::Vector<const byte> & snapshot_data)122 SnapshotData MaybeDecompress(Isolate* isolate,
123 const base::Vector<const byte>& snapshot_data) {
124 #ifdef V8_SNAPSHOT_COMPRESSION
125 TRACE_EVENT0("v8", "V8.SnapshotDecompress");
126 RCS_SCOPE(isolate, RuntimeCallCounterId::kSnapshotDecompress);
127 return SnapshotCompression::Decompress(snapshot_data);
128 #else
129 return SnapshotData(snapshot_data);
130 #endif
131 }
132
133 #ifdef DEBUG
SnapshotIsValid(const v8::StartupData * snapshot_blob)134 bool Snapshot::SnapshotIsValid(const v8::StartupData* snapshot_blob) {
135 return SnapshotImpl::ExtractNumContexts(snapshot_blob) > 0;
136 }
137 #endif // DEBUG
138
HasContextSnapshot(Isolate * isolate,size_t index)139 bool Snapshot::HasContextSnapshot(Isolate* isolate, size_t index) {
140 // Do not use snapshots if the isolate is used to create snapshots.
141 const v8::StartupData* blob = isolate->snapshot_blob();
142 if (blob == nullptr) return false;
143 if (blob->data == nullptr) return false;
144 size_t num_contexts =
145 static_cast<size_t>(SnapshotImpl::ExtractNumContexts(blob));
146 return index < num_contexts;
147 }
148
VersionIsValid(const v8::StartupData * data)149 bool Snapshot::VersionIsValid(const v8::StartupData* data) {
150 char version[SnapshotImpl::kVersionStringLength];
151 memset(version, 0, SnapshotImpl::kVersionStringLength);
152 CHECK_LT(
153 SnapshotImpl::kVersionStringOffset + SnapshotImpl::kVersionStringLength,
154 static_cast<uint32_t>(data->raw_size));
155 Version::GetString(
156 base::Vector<char>(version, SnapshotImpl::kVersionStringLength));
157 return strncmp(version, data->data + SnapshotImpl::kVersionStringOffset,
158 SnapshotImpl::kVersionStringLength) == 0;
159 }
160
Initialize(Isolate * isolate)161 bool Snapshot::Initialize(Isolate* isolate) {
162 if (!isolate->snapshot_available()) return false;
163 TRACE_EVENT0("v8", "V8.DeserializeIsolate");
164 RCS_SCOPE(isolate, RuntimeCallCounterId::kDeserializeIsolate);
165 base::ElapsedTimer timer;
166 if (FLAG_profile_deserialization) timer.Start();
167
168 const v8::StartupData* blob = isolate->snapshot_blob();
169 SnapshotImpl::CheckVersion(blob);
170 if (FLAG_verify_snapshot_checksum) CHECK(VerifyChecksum(blob));
171 base::Vector<const byte> startup_data =
172 SnapshotImpl::ExtractStartupData(blob);
173 base::Vector<const byte> read_only_data =
174 SnapshotImpl::ExtractReadOnlyData(blob);
175 base::Vector<const byte> shared_heap_data =
176 SnapshotImpl::ExtractSharedHeapData(blob);
177
178 SnapshotData startup_snapshot_data(MaybeDecompress(isolate, startup_data));
179 SnapshotData read_only_snapshot_data(
180 MaybeDecompress(isolate, read_only_data));
181 SnapshotData shared_heap_snapshot_data(
182 MaybeDecompress(isolate, shared_heap_data));
183
184 bool success = isolate->InitWithSnapshot(
185 &startup_snapshot_data, &read_only_snapshot_data,
186 &shared_heap_snapshot_data, ExtractRehashability(blob));
187 if (FLAG_profile_deserialization) {
188 double ms = timer.Elapsed().InMillisecondsF();
189 int bytes = startup_data.length();
190 PrintF("[Deserializing isolate (%d bytes) took %0.3f ms]\n", bytes, ms);
191 }
192 return success;
193 }
194
NewContextFromSnapshot(Isolate * isolate,Handle<JSGlobalProxy> global_proxy,size_t context_index,v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer)195 MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
196 Isolate* isolate, Handle<JSGlobalProxy> global_proxy, size_t context_index,
197 v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {
198 if (!isolate->snapshot_available()) return Handle<Context>();
199 TRACE_EVENT0("v8", "V8.DeserializeContext");
200 RCS_SCOPE(isolate, RuntimeCallCounterId::kDeserializeContext);
201 base::ElapsedTimer timer;
202 if (FLAG_profile_deserialization) timer.Start();
203
204 const v8::StartupData* blob = isolate->snapshot_blob();
205 bool can_rehash = ExtractRehashability(blob);
206 base::Vector<const byte> context_data = SnapshotImpl::ExtractContextData(
207 blob, static_cast<uint32_t>(context_index));
208 SnapshotData snapshot_data(MaybeDecompress(isolate, context_data));
209
210 MaybeHandle<Context> maybe_result = ContextDeserializer::DeserializeContext(
211 isolate, &snapshot_data, can_rehash, global_proxy,
212 embedder_fields_deserializer);
213
214 Handle<Context> result;
215 if (!maybe_result.ToHandle(&result)) return MaybeHandle<Context>();
216
217 if (FLAG_profile_deserialization) {
218 double ms = timer.Elapsed().InMillisecondsF();
219 int bytes = context_data.length();
220 PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
221 context_index, bytes, ms);
222 }
223 return result;
224 }
225
226 // static
ClearReconstructableDataForSerialization(Isolate * isolate,bool clear_recompilable_data)227 void Snapshot::ClearReconstructableDataForSerialization(
228 Isolate* isolate, bool clear_recompilable_data) {
229 // Clear SFIs and JSRegExps.
230 PtrComprCageBase cage_base(isolate);
231
232 if (clear_recompilable_data) {
233 HandleScope scope(isolate);
234 std::vector<i::Handle<i::SharedFunctionInfo>> sfis_to_clear;
235 {
236 i::HeapObjectIterator it(isolate->heap());
237 for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
238 if (o.IsSharedFunctionInfo(cage_base)) {
239 i::SharedFunctionInfo shared = i::SharedFunctionInfo::cast(o);
240 if (shared.script(cage_base).IsScript(cage_base) &&
241 Script::cast(shared.script(cage_base)).type() ==
242 Script::TYPE_EXTENSION) {
243 continue; // Don't clear extensions, they cannot be recompiled.
244 }
245 if (shared.CanDiscardCompiled()) {
246 sfis_to_clear.emplace_back(shared, isolate);
247 }
248 } else if (o.IsJSRegExp(cage_base)) {
249 i::JSRegExp regexp = i::JSRegExp::cast(o);
250 if (regexp.HasCompiledCode()) {
251 regexp.DiscardCompiledCodeForSerialization();
252 }
253 }
254 }
255 }
256
257 // Must happen after heap iteration since SFI::DiscardCompiled may allocate.
258 for (i::Handle<i::SharedFunctionInfo> shared : sfis_to_clear) {
259 if (shared->CanDiscardCompiled()) {
260 i::SharedFunctionInfo::DiscardCompiled(isolate, shared);
261 }
262 }
263 }
264
265 // Clear JSFunctions.
266
267 i::HeapObjectIterator it(isolate->heap());
268 for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
269 if (!o.IsJSFunction(cage_base)) continue;
270
271 i::JSFunction fun = i::JSFunction::cast(o);
272 fun.CompleteInobjectSlackTrackingIfActive();
273
274 i::SharedFunctionInfo shared = fun.shared();
275 if (shared.script(cage_base).IsScript(cage_base) &&
276 Script::cast(shared.script(cage_base)).type() ==
277 Script::TYPE_EXTENSION) {
278 continue; // Don't clear extensions, they cannot be recompiled.
279 }
280
281 // Also, clear out feedback vectors and recompilable code.
282 if (fun.CanDiscardCompiled()) {
283 fun.set_code(*BUILTIN_CODE(isolate, CompileLazy));
284 }
285 if (!fun.raw_feedback_cell(cage_base).value(cage_base).IsUndefined()) {
286 fun.raw_feedback_cell(cage_base).set_value(
287 i::ReadOnlyRoots(isolate).undefined_value());
288 }
289 #ifdef DEBUG
290 if (clear_recompilable_data) {
291 #if V8_ENABLE_WEBASSEMBLY
292 DCHECK(fun.shared().HasWasmExportedFunctionData() ||
293 fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() ||
294 fun.shared().HasUncompiledDataWithoutPreparseData());
295 #else
296 DCHECK(fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() ||
297 fun.shared().HasUncompiledDataWithoutPreparseData());
298 #endif // V8_ENABLE_WEBASSEMBLY
299 }
300 #endif // DEBUG
301 }
302 }
303
304 // static
SerializeDeserializeAndVerifyForTesting(Isolate * isolate,Handle<Context> default_context)305 void Snapshot::SerializeDeserializeAndVerifyForTesting(
306 Isolate* isolate, Handle<Context> default_context) {
307 StartupData serialized_data;
308 std::unique_ptr<const char[]> auto_delete_serialized_data;
309
310 isolate->heap()->CollectAllAvailableGarbage(
311 i::GarbageCollectionReason::kSnapshotCreator);
312
313 // Test serialization.
314 {
315 GlobalSafepointScope global_safepoint(isolate);
316 DisallowGarbageCollection no_gc;
317
318 Snapshot::SerializerFlags flags(
319 Snapshot::kAllowUnknownExternalReferencesForTesting |
320 Snapshot::kAllowActiveIsolateForTesting |
321 ((isolate->shared_isolate() || ReadOnlyHeap::IsReadOnlySpaceShared())
322 ? Snapshot::kReconstructReadOnlyAndSharedObjectCachesForTesting
323 : 0));
324 serialized_data = Snapshot::Create(isolate, *default_context,
325 global_safepoint, no_gc, flags);
326 auto_delete_serialized_data.reset(serialized_data.data);
327 }
328
329 // Test deserialization.
330 Isolate* new_isolate = Isolate::New();
331 {
332 // Set serializer_enabled() to not install extensions and experimental
333 // natives on the new isolate.
334 // TODO(v8:10416): This should be a separate setting on the isolate.
335 new_isolate->enable_serializer();
336 new_isolate->Enter();
337 new_isolate->set_snapshot_blob(&serialized_data);
338 new_isolate->set_array_buffer_allocator(
339 v8::ArrayBuffer::Allocator::NewDefaultAllocator());
340 if (Isolate* shared_isolate = isolate->shared_isolate()) {
341 new_isolate->set_shared_isolate(shared_isolate);
342 }
343 CHECK(Snapshot::Initialize(new_isolate));
344
345 HandleScope scope(new_isolate);
346 Handle<Context> new_native_context =
347 new_isolate->bootstrapper()->CreateEnvironmentForTesting();
348 CHECK(new_native_context->IsNativeContext());
349
350 #ifdef VERIFY_HEAP
351 if (FLAG_verify_heap) new_isolate->heap()->Verify();
352 #endif // VERIFY_HEAP
353 }
354 new_isolate->Exit();
355 Isolate::Delete(new_isolate);
356 }
357
358 // static
359 constexpr Snapshot::SerializerFlags Snapshot::kDefaultSerializerFlags;
360
361 // static
Create(Isolate * isolate,std::vector<Context> * contexts,const std::vector<SerializeInternalFieldsCallback> & embedder_fields_serializers,const GlobalSafepointScope & global_safepoint,const DisallowGarbageCollection & no_gc,SerializerFlags flags)362 v8::StartupData Snapshot::Create(
363 Isolate* isolate, std::vector<Context>* contexts,
364 const std::vector<SerializeInternalFieldsCallback>&
365 embedder_fields_serializers,
366 const GlobalSafepointScope& global_safepoint,
367 const DisallowGarbageCollection& no_gc, SerializerFlags flags) {
368 TRACE_EVENT0("v8", "V8.SnapshotCreate");
369 DCHECK_EQ(contexts->size(), embedder_fields_serializers.size());
370 DCHECK_GT(contexts->size(), 0);
371 HandleScope scope(isolate);
372
373 // The GlobalSafepointScope ensures we are in a safepoint scope so that the
374 // string table is safe to iterate. Unlike mksnapshot, embedders may have
375 // background threads running.
376
377 ReadOnlySerializer read_only_serializer(isolate, flags);
378 read_only_serializer.SerializeReadOnlyRoots();
379
380 SharedHeapSerializer shared_heap_serializer(isolate, flags,
381 &read_only_serializer);
382
383 StartupSerializer startup_serializer(isolate, flags, &read_only_serializer,
384 &shared_heap_serializer);
385 startup_serializer.SerializeStrongReferences(no_gc);
386
387 // Serialize each context with a new serializer.
388 const int num_contexts = static_cast<int>(contexts->size());
389 std::vector<SnapshotData*> context_snapshots;
390 context_snapshots.reserve(num_contexts);
391
392 // TODO(v8:6593): generalize rehashing, and remove this flag.
393 bool can_be_rehashed = true;
394
395 std::vector<int> context_allocation_sizes;
396 for (int i = 0; i < num_contexts; i++) {
397 ContextSerializer context_serializer(isolate, flags, &startup_serializer,
398 embedder_fields_serializers[i]);
399 context_serializer.Serialize(&contexts->at(i), no_gc);
400 can_be_rehashed = can_be_rehashed && context_serializer.can_be_rehashed();
401 context_snapshots.push_back(new SnapshotData(&context_serializer));
402 if (FLAG_serialization_statistics) {
403 context_allocation_sizes.push_back(
404 context_serializer.TotalAllocationSize());
405 }
406 }
407
408 startup_serializer.SerializeWeakReferencesAndDeferred();
409 can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed();
410
411 startup_serializer.CheckNoDirtyFinalizationRegistries();
412
413 shared_heap_serializer.FinalizeSerialization();
414 can_be_rehashed = can_be_rehashed && shared_heap_serializer.can_be_rehashed();
415
416 read_only_serializer.FinalizeSerialization();
417 can_be_rehashed = can_be_rehashed && read_only_serializer.can_be_rehashed();
418
419 if (FLAG_serialization_statistics) {
420 // These prints should match the regexp in test/memory/Memory.json
421 DCHECK_NE(read_only_serializer.TotalAllocationSize(), 0);
422 DCHECK_NE(shared_heap_serializer.TotalAllocationSize(), 0);
423 DCHECK_NE(startup_serializer.TotalAllocationSize(), 0);
424 PrintF("Deserialization will allocate:\n");
425 PrintF("%10d bytes per isolate\n",
426 read_only_serializer.TotalAllocationSize() +
427 startup_serializer.TotalAllocationSize());
428 for (int i = 0; i < num_contexts; i++) {
429 DCHECK_NE(context_allocation_sizes[i], 0);
430 PrintF("%10d bytes per context #%d\n", context_allocation_sizes[i], i);
431 }
432 PrintF("\n");
433 }
434
435 SnapshotData read_only_snapshot(&read_only_serializer);
436 SnapshotData shared_heap_snapshot(&shared_heap_serializer);
437 SnapshotData startup_snapshot(&startup_serializer);
438 v8::StartupData result = SnapshotImpl::CreateSnapshotBlob(
439 &startup_snapshot, &read_only_snapshot, &shared_heap_snapshot,
440 context_snapshots, can_be_rehashed);
441
442 for (const SnapshotData* ptr : context_snapshots) delete ptr;
443
444 CHECK(Snapshot::VerifyChecksum(&result));
445 return result;
446 }
447
448 // static
Create(Isolate * isolate,Context default_context,const GlobalSafepointScope & global_safepoint,const DisallowGarbageCollection & no_gc,SerializerFlags flags)449 v8::StartupData Snapshot::Create(Isolate* isolate, Context default_context,
450 const GlobalSafepointScope& global_safepoint,
451 const DisallowGarbageCollection& no_gc,
452 SerializerFlags flags) {
453 std::vector<Context> contexts{default_context};
454 std::vector<SerializeInternalFieldsCallback> callbacks{{}};
455 return Snapshot::Create(isolate, &contexts, callbacks, global_safepoint,
456 no_gc, flags);
457 }
458
CreateSnapshotBlob(const SnapshotData * startup_snapshot_in,const SnapshotData * read_only_snapshot_in,const SnapshotData * shared_heap_snapshot_in,const std::vector<SnapshotData * > & context_snapshots_in,bool can_be_rehashed)459 v8::StartupData SnapshotImpl::CreateSnapshotBlob(
460 const SnapshotData* startup_snapshot_in,
461 const SnapshotData* read_only_snapshot_in,
462 const SnapshotData* shared_heap_snapshot_in,
463 const std::vector<SnapshotData*>& context_snapshots_in,
464 bool can_be_rehashed) {
465 TRACE_EVENT0("v8", "V8.SnapshotCompress");
466 // Have these separate from snapshot_in for compression, since we need to
467 // access the compressed data as well as the uncompressed reservations.
468 const SnapshotData* startup_snapshot;
469 const SnapshotData* read_only_snapshot;
470 const SnapshotData* shared_heap_snapshot;
471 const std::vector<SnapshotData*>* context_snapshots;
472 #ifdef V8_SNAPSHOT_COMPRESSION
473 SnapshotData startup_compressed(
474 SnapshotCompression::Compress(startup_snapshot_in));
475 SnapshotData read_only_compressed(
476 SnapshotCompression::Compress(read_only_snapshot_in));
477 SnapshotData shared_heap_compressed(
478 SnapshotCompression::Compress(shared_heap_snapshot_in));
479 startup_snapshot = &startup_compressed;
480 read_only_snapshot = &read_only_compressed;
481 shared_heap_snapshot = &shared_heap_compressed;
482 std::vector<SnapshotData> context_snapshots_compressed;
483 context_snapshots_compressed.reserve(context_snapshots_in.size());
484 std::vector<SnapshotData*> context_snapshots_compressed_ptrs;
485 for (unsigned int i = 0; i < context_snapshots_in.size(); ++i) {
486 context_snapshots_compressed.push_back(
487 SnapshotCompression::Compress(context_snapshots_in[i]));
488 context_snapshots_compressed_ptrs.push_back(
489 &context_snapshots_compressed[i]);
490 }
491 context_snapshots = &context_snapshots_compressed_ptrs;
492 #else
493 startup_snapshot = startup_snapshot_in;
494 read_only_snapshot = read_only_snapshot_in;
495 shared_heap_snapshot = shared_heap_snapshot_in;
496 context_snapshots = &context_snapshots_in;
497 #endif
498
499 uint32_t num_contexts = static_cast<uint32_t>(context_snapshots->size());
500 uint32_t startup_snapshot_offset =
501 SnapshotImpl::StartupSnapshotOffset(num_contexts);
502 uint32_t total_length = startup_snapshot_offset;
503 total_length += static_cast<uint32_t>(startup_snapshot->RawData().length());
504 total_length += static_cast<uint32_t>(read_only_snapshot->RawData().length());
505 total_length +=
506 static_cast<uint32_t>(shared_heap_snapshot->RawData().length());
507 for (const auto context_snapshot : *context_snapshots) {
508 total_length += static_cast<uint32_t>(context_snapshot->RawData().length());
509 }
510
511 char* data = new char[total_length];
512 // Zero out pre-payload data. Part of that is only used for padding.
513 memset(data, 0, SnapshotImpl::StartupSnapshotOffset(num_contexts));
514
515 SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kNumberOfContextsOffset,
516 num_contexts);
517 SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kRehashabilityOffset,
518 can_be_rehashed ? 1 : 0);
519
520 // Write version string into snapshot data.
521 memset(data + SnapshotImpl::kVersionStringOffset, 0,
522 SnapshotImpl::kVersionStringLength);
523 Version::GetString(
524 base::Vector<char>(data + SnapshotImpl::kVersionStringOffset,
525 SnapshotImpl::kVersionStringLength));
526
527 // Startup snapshot (isolate-specific data).
528 uint32_t payload_offset = startup_snapshot_offset;
529 uint32_t payload_length =
530 static_cast<uint32_t>(startup_snapshot->RawData().length());
531 CopyBytes(data + payload_offset,
532 reinterpret_cast<const char*>(startup_snapshot->RawData().begin()),
533 payload_length);
534 if (FLAG_serialization_statistics) {
535 PrintF("Snapshot blob consists of:\n%10d bytes for startup\n",
536 payload_length);
537 }
538 payload_offset += payload_length;
539
540 // Read-only.
541 SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kReadOnlyOffsetOffset,
542 payload_offset);
543 payload_length = read_only_snapshot->RawData().length();
544 CopyBytes(
545 data + payload_offset,
546 reinterpret_cast<const char*>(read_only_snapshot->RawData().begin()),
547 payload_length);
548 if (FLAG_serialization_statistics) {
549 PrintF("%10d bytes for read-only\n", payload_length);
550 }
551 payload_offset += payload_length;
552
553 // Shared heap.
554 SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kSharedHeapOffsetOffset,
555 payload_offset);
556 payload_length = shared_heap_snapshot->RawData().length();
557 CopyBytes(
558 data + payload_offset,
559 reinterpret_cast<const char*>(shared_heap_snapshot->RawData().begin()),
560 payload_length);
561 if (FLAG_serialization_statistics) {
562 PrintF("%10d bytes for shared heap\n", payload_length);
563 }
564 payload_offset += payload_length;
565
566 // Context snapshots (context-specific data).
567 for (uint32_t i = 0; i < num_contexts; i++) {
568 SnapshotImpl::SetHeaderValue(
569 data, SnapshotImpl::ContextSnapshotOffsetOffset(i), payload_offset);
570 SnapshotData* context_snapshot = (*context_snapshots)[i];
571 payload_length = context_snapshot->RawData().length();
572 CopyBytes(
573 data + payload_offset,
574 reinterpret_cast<const char*>(context_snapshot->RawData().begin()),
575 payload_length);
576 if (FLAG_serialization_statistics) {
577 PrintF("%10d bytes for context #%d\n", payload_length, i);
578 }
579 payload_offset += payload_length;
580 }
581 if (FLAG_serialization_statistics) PrintF("\n");
582
583 DCHECK_EQ(total_length, payload_offset);
584 v8::StartupData result = {data, static_cast<int>(total_length)};
585
586 SnapshotImpl::SetHeaderValue(
587 data, SnapshotImpl::kChecksumOffset,
588 Checksum(SnapshotImpl::ChecksummedContent(&result)));
589
590 return result;
591 }
592
ExtractNumContexts(const v8::StartupData * data)593 uint32_t SnapshotImpl::ExtractNumContexts(const v8::StartupData* data) {
594 CHECK_LT(kNumberOfContextsOffset, data->raw_size);
595 uint32_t num_contexts = GetHeaderValue(data, kNumberOfContextsOffset);
596 return num_contexts;
597 }
598
VerifyChecksum(const v8::StartupData * data)599 bool Snapshot::VerifyChecksum(const v8::StartupData* data) {
600 base::ElapsedTimer timer;
601 if (FLAG_profile_deserialization) timer.Start();
602 uint32_t expected =
603 SnapshotImpl::GetHeaderValue(data, SnapshotImpl::kChecksumOffset);
604 uint32_t result = Checksum(SnapshotImpl::ChecksummedContent(data));
605 if (FLAG_profile_deserialization) {
606 double ms = timer.Elapsed().InMillisecondsF();
607 PrintF("[Verifying snapshot checksum took %0.3f ms]\n", ms);
608 }
609 return result == expected;
610 }
611
ExtractContextOffset(const v8::StartupData * data,uint32_t index)612 uint32_t SnapshotImpl::ExtractContextOffset(const v8::StartupData* data,
613 uint32_t index) {
614 // Extract the offset of the context at a given index from the StartupData,
615 // and check that it is within bounds.
616 uint32_t context_offset =
617 GetHeaderValue(data, ContextSnapshotOffsetOffset(index));
618 CHECK_LT(context_offset, static_cast<uint32_t>(data->raw_size));
619 return context_offset;
620 }
621
ExtractRehashability(const v8::StartupData * data)622 bool Snapshot::ExtractRehashability(const v8::StartupData* data) {
623 CHECK_LT(SnapshotImpl::kRehashabilityOffset,
624 static_cast<uint32_t>(data->raw_size));
625 uint32_t rehashability =
626 SnapshotImpl::GetHeaderValue(data, SnapshotImpl::kRehashabilityOffset);
627 CHECK_IMPLIES(rehashability != 0, rehashability == 1);
628 return rehashability != 0;
629 }
630
631 namespace {
ExtractData(const v8::StartupData * snapshot,uint32_t start_offset,uint32_t end_offset)632 base::Vector<const byte> ExtractData(const v8::StartupData* snapshot,
633 uint32_t start_offset,
634 uint32_t end_offset) {
635 CHECK_LT(start_offset, end_offset);
636 CHECK_LT(end_offset, snapshot->raw_size);
637 uint32_t length = end_offset - start_offset;
638 const byte* data =
639 reinterpret_cast<const byte*>(snapshot->data + start_offset);
640 return base::Vector<const byte>(data, length);
641 }
642 } // namespace
643
ExtractStartupData(const v8::StartupData * data)644 base::Vector<const byte> SnapshotImpl::ExtractStartupData(
645 const v8::StartupData* data) {
646 DCHECK(Snapshot::SnapshotIsValid(data));
647
648 uint32_t num_contexts = ExtractNumContexts(data);
649 return ExtractData(data, StartupSnapshotOffset(num_contexts),
650 GetHeaderValue(data, kReadOnlyOffsetOffset));
651 }
652
ExtractReadOnlyData(const v8::StartupData * data)653 base::Vector<const byte> SnapshotImpl::ExtractReadOnlyData(
654 const v8::StartupData* data) {
655 DCHECK(Snapshot::SnapshotIsValid(data));
656
657 return ExtractData(data, GetHeaderValue(data, kReadOnlyOffsetOffset),
658 GetHeaderValue(data, kSharedHeapOffsetOffset));
659 }
660
ExtractSharedHeapData(const v8::StartupData * data)661 base::Vector<const byte> SnapshotImpl::ExtractSharedHeapData(
662 const v8::StartupData* data) {
663 DCHECK(Snapshot::SnapshotIsValid(data));
664
665 return ExtractData(data, GetHeaderValue(data, kSharedHeapOffsetOffset),
666 GetHeaderValue(data, ContextSnapshotOffsetOffset(0)));
667 }
668
ExtractContextData(const v8::StartupData * data,uint32_t index)669 base::Vector<const byte> SnapshotImpl::ExtractContextData(
670 const v8::StartupData* data, uint32_t index) {
671 uint32_t num_contexts = ExtractNumContexts(data);
672 CHECK_LT(index, num_contexts);
673
674 uint32_t context_offset = ExtractContextOffset(data, index);
675 uint32_t next_context_offset;
676 if (index == num_contexts - 1) {
677 next_context_offset = data->raw_size;
678 } else {
679 next_context_offset = ExtractContextOffset(data, index + 1);
680 CHECK_LT(next_context_offset, data->raw_size);
681 }
682
683 const byte* context_data =
684 reinterpret_cast<const byte*>(data->data + context_offset);
685 uint32_t context_length = next_context_offset - context_offset;
686 return base::Vector<const byte>(context_data, context_length);
687 }
688
CheckVersion(const v8::StartupData * data)689 void SnapshotImpl::CheckVersion(const v8::StartupData* data) {
690 if (!Snapshot::VersionIsValid(data)) {
691 char version[kVersionStringLength];
692 memset(version, 0, kVersionStringLength);
693 CHECK_LT(kVersionStringOffset + kVersionStringLength,
694 static_cast<uint32_t>(data->raw_size));
695 Version::GetString(base::Vector<char>(version, kVersionStringLength));
696 FATAL(
697 "Version mismatch between V8 binary and snapshot.\n"
698 "# V8 binary version: %.*s\n"
699 "# Snapshot version: %.*s\n"
700 "# The snapshot consists of %d bytes and contains %d context(s).",
701 kVersionStringLength, version, kVersionStringLength,
702 data->data + kVersionStringOffset, data->raw_size,
703 ExtractNumContexts(data));
704 }
705 }
706
707 namespace {
708
RunExtraCode(v8::Isolate * isolate,v8::Local<v8::Context> context,const char * utf8_source,const char * name)709 bool RunExtraCode(v8::Isolate* isolate, v8::Local<v8::Context> context,
710 const char* utf8_source, const char* name) {
711 v8::Context::Scope context_scope(context);
712 v8::TryCatch try_catch(isolate);
713 v8::Local<v8::String> source_string;
714 if (!v8::String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
715 return false;
716 }
717 v8::Local<v8::String> resource_name =
718 v8::String::NewFromUtf8(isolate, name).ToLocalChecked();
719 v8::ScriptOrigin origin(isolate, resource_name);
720 v8::ScriptCompiler::Source source(source_string, origin);
721 v8::Local<v8::Script> script;
722 if (!v8::ScriptCompiler::Compile(context, &source).ToLocal(&script))
723 return false;
724 if (script->Run(context).IsEmpty()) return false;
725 CHECK(!try_catch.HasCaught());
726 return true;
727 }
728
729 } // namespace
730
CreateSnapshotDataBlobInternal(v8::SnapshotCreator::FunctionCodeHandling function_code_handling,const char * embedded_source,v8::Isolate * isolate)731 v8::StartupData CreateSnapshotDataBlobInternal(
732 v8::SnapshotCreator::FunctionCodeHandling function_code_handling,
733 const char* embedded_source, v8::Isolate* isolate) {
734 // If no isolate is passed in, create it (and a new context) from scratch.
735 if (isolate == nullptr) isolate = v8::Isolate::Allocate();
736
737 // Optionally run a script to embed, and serialize to create a snapshot blob.
738 v8::SnapshotCreator snapshot_creator(isolate);
739 {
740 v8::HandleScope scope(isolate);
741 v8::Local<v8::Context> context = v8::Context::New(isolate);
742 if (embedded_source != nullptr &&
743 !RunExtraCode(isolate, context, embedded_source, "<embedded>")) {
744 return {};
745 }
746 snapshot_creator.SetDefaultContext(context);
747 }
748 return snapshot_creator.CreateBlob(function_code_handling);
749 }
750
WarmUpSnapshotDataBlobInternal(v8::StartupData cold_snapshot_blob,const char * warmup_source)751 v8::StartupData WarmUpSnapshotDataBlobInternal(
752 v8::StartupData cold_snapshot_blob, const char* warmup_source) {
753 CHECK(cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != nullptr);
754 CHECK_NOT_NULL(warmup_source);
755
756 // Use following steps to create a warmed up snapshot blob from a cold one:
757 // - Create a new isolate from the cold snapshot.
758 // - Create a new context to run the warmup script. This will trigger
759 // compilation of executed functions.
760 // - Create a new context. This context will be unpolluted.
761 // - Serialize the isolate and the second context into a new snapshot blob.
762 v8::SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
763 v8::Isolate* isolate = snapshot_creator.GetIsolate();
764 {
765 v8::HandleScope scope(isolate);
766 v8::Local<v8::Context> context = v8::Context::New(isolate);
767 if (!RunExtraCode(isolate, context, warmup_source, "<warm-up>")) {
768 return {};
769 }
770 }
771 {
772 v8::HandleScope handle_scope(isolate);
773 isolate->ContextDisposedNotification(false);
774 v8::Local<v8::Context> context = v8::Context::New(isolate);
775 snapshot_creator.SetDefaultContext(context);
776 }
777
778 return snapshot_creator.CreateBlob(
779 v8::SnapshotCreator::FunctionCodeHandling::kKeep);
780 }
781
782 } // namespace internal
783 } // namespace v8
784