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 33namespace v8 { 34namespace internal { 35 36namespace { 37 38class 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 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 } 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 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 110 static uint32_t StartupSnapshotOffset(int num_contexts) { 111 return POINTER_SIZE_ALIGN(kFirstContextOffsetOffset + 112 num_contexts * kInt32Size); 113 } 114 115 static uint32_t ContextSnapshotOffsetOffset(int index) { 116 return kFirstContextOffsetOffset + index * kInt32Size; 117 } 118}; 119 120} // namespace 121 122SnapshotData 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 134bool Snapshot::SnapshotIsValid(const v8::StartupData* snapshot_blob) { 135 return SnapshotImpl::ExtractNumContexts(snapshot_blob) > 0; 136} 137#endif // DEBUG 138 139bool 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 149bool 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 161bool 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 195MaybeHandle<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 227void 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 305void 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 359constexpr Snapshot::SerializerFlags Snapshot::kDefaultSerializerFlags; 360 361// static 362v8::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 449v8::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 459v8::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 593uint32_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 599bool 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 612uint32_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 622bool 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 631namespace { 632base::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 644base::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 653base::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 661base::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 669base::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 689void 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 707namespace { 708 709bool 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 731v8::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 751v8::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