Home | History | Annotate | Download | only in snapshot
      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/assembler-inl.h"
     10 #include "src/base/platform/platform.h"
     11 #include "src/callable.h"
     12 #include "src/interface-descriptors.h"
     13 #include "src/objects-inl.h"
     14 #include "src/snapshot/builtin-deserializer.h"
     15 #include "src/snapshot/builtin-serializer.h"
     16 #include "src/snapshot/partial-deserializer.h"
     17 #include "src/snapshot/snapshot-source-sink.h"
     18 #include "src/snapshot/startup-deserializer.h"
     19 #include "src/utils.h"
     20 #include "src/version.h"
     21 
     22 namespace v8 {
     23 namespace internal {
     24 
     25 #ifdef DEBUG
     26 bool Snapshot::SnapshotIsValid(const v8::StartupData* snapshot_blob) {
     27   return Snapshot::ExtractNumContexts(snapshot_blob) > 0;
     28 }
     29 #endif  // DEBUG
     30 
     31 bool Snapshot::HasContextSnapshot(Isolate* isolate, size_t index) {
     32   // Do not use snapshots if the isolate is used to create snapshots.
     33   const v8::StartupData* blob = isolate->snapshot_blob();
     34   if (blob == nullptr) return false;
     35   if (blob->data == nullptr) return false;
     36   size_t num_contexts = static_cast<size_t>(ExtractNumContexts(blob));
     37   return index < num_contexts;
     38 }
     39 
     40 bool Snapshot::Initialize(Isolate* isolate) {
     41   if (!isolate->snapshot_available()) return false;
     42   base::ElapsedTimer timer;
     43   if (FLAG_profile_deserialization) timer.Start();
     44 
     45   const v8::StartupData* blob = isolate->snapshot_blob();
     46   CheckVersion(blob);
     47   Vector<const byte> startup_data = ExtractStartupData(blob);
     48   SnapshotData startup_snapshot_data(startup_data);
     49   Vector<const byte> builtin_data = ExtractBuiltinData(blob);
     50   BuiltinSnapshotData builtin_snapshot_data(builtin_data);
     51   StartupDeserializer deserializer(&startup_snapshot_data,
     52                                    &builtin_snapshot_data);
     53   deserializer.SetRehashability(ExtractRehashability(blob));
     54   bool success = isolate->Init(&deserializer);
     55   if (FLAG_profile_deserialization) {
     56     double ms = timer.Elapsed().InMillisecondsF();
     57     int bytes = startup_data.length();
     58     PrintF("[Deserializing isolate (%d bytes) took %0.3f ms]\n", bytes, ms);
     59   }
     60   return success;
     61 }
     62 
     63 MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
     64     Isolate* isolate, Handle<JSGlobalProxy> global_proxy, size_t context_index,
     65     v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {
     66   if (!isolate->snapshot_available()) return Handle<Context>();
     67   base::ElapsedTimer timer;
     68   if (FLAG_profile_deserialization) timer.Start();
     69 
     70   const v8::StartupData* blob = isolate->snapshot_blob();
     71   bool can_rehash = ExtractRehashability(blob);
     72   Vector<const byte> context_data =
     73       ExtractContextData(blob, static_cast<uint32_t>(context_index));
     74   SnapshotData snapshot_data(context_data);
     75 
     76   MaybeHandle<Context> maybe_result = PartialDeserializer::DeserializeContext(
     77       isolate, &snapshot_data, can_rehash, global_proxy,
     78       embedder_fields_deserializer);
     79 
     80   Handle<Context> result;
     81   if (!maybe_result.ToHandle(&result)) return MaybeHandle<Context>();
     82 
     83   if (FLAG_profile_deserialization) {
     84     double ms = timer.Elapsed().InMillisecondsF();
     85     int bytes = context_data.length();
     86     PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
     87            context_index, bytes, ms);
     88   }
     89   return result;
     90 }
     91 
     92 // static
     93 Code* Snapshot::DeserializeBuiltin(Isolate* isolate, int builtin_id) {
     94   if (FLAG_trace_lazy_deserialization) {
     95     PrintF("Lazy-deserializing builtin %s\n", Builtins::name(builtin_id));
     96   }
     97 
     98   base::ElapsedTimer timer;
     99   if (FLAG_profile_deserialization) timer.Start();
    100 
    101   const v8::StartupData* blob = isolate->snapshot_blob();
    102   Vector<const byte> builtin_data = Snapshot::ExtractBuiltinData(blob);
    103   BuiltinSnapshotData builtin_snapshot_data(builtin_data);
    104 
    105   CodeSpaceMemoryModificationScope code_allocation(isolate->heap());
    106   BuiltinDeserializer builtin_deserializer(isolate, &builtin_snapshot_data);
    107   Code* code = builtin_deserializer.DeserializeBuiltin(builtin_id);
    108   DCHECK_EQ(code, isolate->builtins()->builtin(builtin_id));
    109 
    110   if (FLAG_profile_deserialization) {
    111     double ms = timer.Elapsed().InMillisecondsF();
    112     int bytes = code->Size();
    113     PrintF("[Deserializing builtin %s (%d bytes) took %0.3f ms]\n",
    114            Builtins::name(builtin_id), bytes, ms);
    115   }
    116 
    117   if (isolate->logger()->is_listening_to_code_events() ||
    118       isolate->is_profiling()) {
    119     isolate->logger()->LogCodeObject(code);
    120   }
    121 
    122   return code;
    123 }
    124 
    125 // static
    126 void Snapshot::EnsureAllBuiltinsAreDeserialized(Isolate* isolate) {
    127   if (!FLAG_lazy_deserialization) return;
    128 
    129   if (FLAG_trace_lazy_deserialization) {
    130     PrintF("Forcing eager builtin deserialization\n");
    131   }
    132 
    133   Builtins* builtins = isolate->builtins();
    134   for (int i = 0; i < Builtins::builtin_count; i++) {
    135     if (!Builtins::IsLazy(i)) continue;
    136 
    137     DCHECK_NE(Builtins::kDeserializeLazy, i);
    138     Code* code = builtins->builtin(i);
    139     if (code->builtin_index() == Builtins::kDeserializeLazy) {
    140       code = Snapshot::DeserializeBuiltin(isolate, i);
    141     }
    142 
    143     DCHECK_EQ(i, code->builtin_index());
    144     DCHECK_EQ(code, builtins->builtin(i));
    145   }
    146 }
    147 
    148 // static
    149 Code* Snapshot::EnsureBuiltinIsDeserialized(Isolate* isolate,
    150                                             Handle<SharedFunctionInfo> shared) {
    151   DCHECK(FLAG_lazy_deserialization);
    152 
    153   int builtin_id = shared->builtin_id();
    154 
    155   // We should never lazily deserialize DeserializeLazy.
    156   DCHECK_NE(Builtins::kDeserializeLazy, builtin_id);
    157 
    158   // Look up code from builtins list.
    159   Code* code = isolate->builtins()->builtin(builtin_id);
    160 
    161   // Deserialize if builtin is not on the list.
    162   if (code->builtin_index() != builtin_id) {
    163     DCHECK_EQ(code->builtin_index(), Builtins::kDeserializeLazy);
    164     code = Snapshot::DeserializeBuiltin(isolate, builtin_id);
    165     DCHECK_EQ(builtin_id, code->builtin_index());
    166     DCHECK_EQ(code, isolate->builtins()->builtin(builtin_id));
    167   }
    168   return code;
    169 }
    170 
    171 // static
    172 Code* Snapshot::DeserializeHandler(Isolate* isolate,
    173                                    interpreter::Bytecode bytecode,
    174                                    interpreter::OperandScale operand_scale) {
    175   if (FLAG_trace_lazy_deserialization) {
    176     PrintF("Lazy-deserializing handler %s\n",
    177            interpreter::Bytecodes::ToString(bytecode, operand_scale).c_str());
    178   }
    179 
    180   base::ElapsedTimer timer;
    181   if (FLAG_profile_deserialization) timer.Start();
    182 
    183   const v8::StartupData* blob = isolate->snapshot_blob();
    184   Vector<const byte> builtin_data = Snapshot::ExtractBuiltinData(blob);
    185   BuiltinSnapshotData builtin_snapshot_data(builtin_data);
    186 
    187   CodeSpaceMemoryModificationScope code_allocation(isolate->heap());
    188   BuiltinDeserializer builtin_deserializer(isolate, &builtin_snapshot_data);
    189   Code* code = builtin_deserializer.DeserializeHandler(bytecode, operand_scale);
    190 
    191   if (FLAG_profile_deserialization) {
    192     double ms = timer.Elapsed().InMillisecondsF();
    193     int bytes = code->Size();
    194     PrintF("[Deserializing handler %s (%d bytes) took %0.3f ms]\n",
    195            interpreter::Bytecodes::ToString(bytecode, operand_scale).c_str(),
    196            bytes, ms);
    197   }
    198 
    199   if (isolate->logger()->is_listening_to_code_events() ||
    200       isolate->is_profiling()) {
    201     isolate->logger()->LogBytecodeHandler(bytecode, operand_scale, code);
    202   }
    203 
    204   return code;
    205 }
    206 
    207 void ProfileDeserialization(
    208     const SnapshotData* startup_snapshot, const SnapshotData* builtin_snapshot,
    209     const std::vector<SnapshotData*>& context_snapshots) {
    210   if (FLAG_profile_deserialization) {
    211     int startup_total = 0;
    212     PrintF("Deserialization will reserve:\n");
    213     for (const auto& reservation : startup_snapshot->Reservations()) {
    214       startup_total += reservation.chunk_size();
    215     }
    216     for (const auto& reservation : builtin_snapshot->Reservations()) {
    217       startup_total += reservation.chunk_size();
    218     }
    219     PrintF("%10d bytes per isolate\n", startup_total);
    220     for (size_t i = 0; i < context_snapshots.size(); i++) {
    221       int context_total = 0;
    222       for (const auto& reservation : context_snapshots[i]->Reservations()) {
    223         context_total += reservation.chunk_size();
    224       }
    225       PrintF("%10d bytes per context #%zu\n", context_total, i);
    226     }
    227   }
    228 }
    229 
    230 v8::StartupData Snapshot::CreateSnapshotBlob(
    231     const SnapshotData* startup_snapshot,
    232     const BuiltinSnapshotData* builtin_snapshot,
    233     const std::vector<SnapshotData*>& context_snapshots, bool can_be_rehashed) {
    234   uint32_t num_contexts = static_cast<uint32_t>(context_snapshots.size());
    235   uint32_t startup_snapshot_offset = StartupSnapshotOffset(num_contexts);
    236   uint32_t total_length = startup_snapshot_offset;
    237   total_length += static_cast<uint32_t>(startup_snapshot->RawData().length());
    238   total_length += static_cast<uint32_t>(builtin_snapshot->RawData().length());
    239   for (const auto context_snapshot : context_snapshots) {
    240     total_length += static_cast<uint32_t>(context_snapshot->RawData().length());
    241   }
    242 
    243   ProfileDeserialization(startup_snapshot, builtin_snapshot, context_snapshots);
    244 
    245   char* data = new char[total_length];
    246   SetHeaderValue(data, kNumberOfContextsOffset, num_contexts);
    247   SetHeaderValue(data, kRehashabilityOffset, can_be_rehashed ? 1 : 0);
    248 
    249   // Write version string into snapshot data.
    250   memset(data + kVersionStringOffset, 0, kVersionStringLength);
    251   Version::GetString(
    252       Vector<char>(data + kVersionStringOffset, kVersionStringLength));
    253 
    254   // Startup snapshot (isolate-specific data).
    255   uint32_t payload_offset = startup_snapshot_offset;
    256   uint32_t payload_length =
    257       static_cast<uint32_t>(startup_snapshot->RawData().length());
    258   CopyBytes(data + payload_offset,
    259             reinterpret_cast<const char*>(startup_snapshot->RawData().start()),
    260             payload_length);
    261   if (FLAG_profile_deserialization) {
    262     PrintF("Snapshot blob consists of:\n%10d bytes in %d chunks for startup\n",
    263            payload_length,
    264            static_cast<uint32_t>(startup_snapshot->Reservations().size()));
    265   }
    266   payload_offset += payload_length;
    267 
    268   // Builtins.
    269   SetHeaderValue(data, kBuiltinOffsetOffset, payload_offset);
    270   payload_length = builtin_snapshot->RawData().length();
    271   CopyBytes(data + payload_offset,
    272             reinterpret_cast<const char*>(builtin_snapshot->RawData().start()),
    273             payload_length);
    274   if (FLAG_profile_deserialization) {
    275     PrintF("%10d bytes for builtins\n", payload_length);
    276   }
    277   payload_offset += payload_length;
    278 
    279   // Partial snapshots (context-specific data).
    280   for (uint32_t i = 0; i < num_contexts; i++) {
    281     SetHeaderValue(data, ContextSnapshotOffsetOffset(i), payload_offset);
    282     SnapshotData* context_snapshot = context_snapshots[i];
    283     payload_length = context_snapshot->RawData().length();
    284     CopyBytes(
    285         data + payload_offset,
    286         reinterpret_cast<const char*>(context_snapshot->RawData().start()),
    287         payload_length);
    288     if (FLAG_profile_deserialization) {
    289       PrintF("%10d bytes in %d chunks for context #%d\n", payload_length,
    290              static_cast<uint32_t>(context_snapshot->Reservations().size()), i);
    291     }
    292     payload_offset += payload_length;
    293   }
    294 
    295   v8::StartupData result = {data, static_cast<int>(total_length)};
    296   DCHECK_EQ(total_length, payload_offset);
    297   return result;
    298 }
    299 
    300 namespace {
    301 bool BuiltinAliasesOffHeapTrampolineRegister(Isolate* isolate, Code* code) {
    302   DCHECK(Builtins::IsIsolateIndependent(code->builtin_index()));
    303   switch (Builtins::KindOf(code->builtin_index())) {
    304     case Builtins::CPP:
    305     case Builtins::TFC:
    306     case Builtins::TFH:
    307     case Builtins::TFJ:
    308     case Builtins::TFS:
    309       break;
    310 
    311     // Bytecode handlers will only ever be used by the interpreter and so there
    312     // will never be a need to use trampolines with them.
    313     case Builtins::BCH:
    314     case Builtins::API:
    315     case Builtins::ASM:
    316       // TODO(jgruber): Extend checks to remaining kinds.
    317       return false;
    318   }
    319 
    320   Callable callable = Builtins::CallableFor(
    321       isolate, static_cast<Builtins::Name>(code->builtin_index()));
    322   CallInterfaceDescriptor descriptor = callable.descriptor();
    323 
    324   if (descriptor.ContextRegister() == kOffHeapTrampolineRegister) {
    325     return true;
    326   }
    327 
    328   for (int i = 0; i < descriptor.GetRegisterParameterCount(); i++) {
    329     Register reg = descriptor.GetRegisterParameter(i);
    330     if (reg == kOffHeapTrampolineRegister) return true;
    331   }
    332 
    333   return false;
    334 }
    335 
    336 void FinalizeEmbeddedCodeTargets(Isolate* isolate, EmbeddedData* blob) {
    337   static const int kRelocMask =
    338       RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
    339       RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET);
    340 
    341   for (int i = 0; i < Builtins::builtin_count; i++) {
    342     if (!Builtins::IsIsolateIndependent(i)) continue;
    343 
    344     Code* code = isolate->builtins()->builtin(i);
    345     RelocIterator on_heap_it(code, kRelocMask);
    346     RelocIterator off_heap_it(blob, code, kRelocMask);
    347 
    348 #if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64) || \
    349     defined(V8_TARGET_ARCH_ARM)
    350     // On X64, ARM, ARM64 we emit relative builtin-to-builtin jumps for isolate
    351     // independent builtins in the snapshot. This fixes up the relative jumps
    352     // to the right offsets in the snapshot.
    353     // See also: Code::IsIsolateIndependent.
    354     while (!on_heap_it.done()) {
    355       DCHECK(!off_heap_it.done());
    356 
    357       RelocInfo* rinfo = on_heap_it.rinfo();
    358       DCHECK_EQ(rinfo->rmode(), off_heap_it.rinfo()->rmode());
    359       Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
    360       CHECK(Builtins::IsIsolateIndependentBuiltin(target));
    361 
    362       // Do not emit write-barrier for off-heap writes.
    363       off_heap_it.rinfo()->set_target_address(
    364           blob->InstructionStartOfBuiltin(target->builtin_index()),
    365           SKIP_WRITE_BARRIER);
    366 
    367       on_heap_it.next();
    368       off_heap_it.next();
    369     }
    370     DCHECK(off_heap_it.done());
    371 #else
    372     // Architectures other than x64 and arm/arm64 do not use pc-relative calls
    373     // and thus must not contain embedded code targets. Instead, we use an
    374     // indirection through the root register.
    375     CHECK(on_heap_it.done());
    376     CHECK(off_heap_it.done());
    377 #endif  // defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
    378   }
    379 }
    380 }  // namespace
    381 
    382 // static
    383 EmbeddedData EmbeddedData::FromIsolate(Isolate* isolate) {
    384   Builtins* builtins = isolate->builtins();
    385 
    386   // Store instruction stream lengths and offsets.
    387   std::vector<struct Metadata> metadata(kTableSize);
    388 
    389   bool saw_unsafe_builtin = false;
    390   uint32_t raw_data_size = 0;
    391   for (int i = 0; i < Builtins::builtin_count; i++) {
    392     Code* code = builtins->builtin(i);
    393 
    394     if (Builtins::IsIsolateIndependent(i)) {
    395       DCHECK(!Builtins::IsLazy(i));
    396 
    397       // Sanity-check that the given builtin is isolate-independent and does not
    398       // use the trampoline register in its calling convention.
    399       if (!code->IsIsolateIndependent(isolate)) {
    400         saw_unsafe_builtin = true;
    401         fprintf(stderr, "%s is not isolate-independent.\n", Builtins::name(i));
    402       }
    403       if (Builtins::IsWasmRuntimeStub(i) &&
    404           RelocInfo::RequiresRelocation(code)) {
    405         // Wasm additionally requires that its runtime stubs must be
    406         // individually PIC (i.e. we must be able to copy each stub outside the
    407         // embedded area without relocations). In particular, that means
    408         // pc-relative calls to other builtins are disallowed.
    409         saw_unsafe_builtin = true;
    410         fprintf(stderr, "%s is a wasm runtime stub but needs relocation.\n",
    411                 Builtins::name(i));
    412       }
    413       if (BuiltinAliasesOffHeapTrampolineRegister(isolate, code)) {
    414         saw_unsafe_builtin = true;
    415         fprintf(stderr, "%s aliases the off-heap trampoline register.\n",
    416                 Builtins::name(i));
    417       }
    418 
    419       uint32_t length = static_cast<uint32_t>(code->raw_instruction_size());
    420 
    421       DCHECK_EQ(0, raw_data_size % kCodeAlignment);
    422       metadata[i].instructions_offset = raw_data_size;
    423       metadata[i].instructions_length = length;
    424 
    425       // Align the start of each instruction stream.
    426       raw_data_size += PadAndAlign(length);
    427     } else {
    428       metadata[i].instructions_offset = raw_data_size;
    429     }
    430   }
    431   CHECK_WITH_MSG(
    432       !saw_unsafe_builtin,
    433       "One or more builtins marked as isolate-independent either contains "
    434       "isolate-dependent code or aliases the off-heap trampoline register. "
    435       "If in doubt, ask jgruber@");
    436 
    437   const uint32_t blob_size = RawDataOffset() + raw_data_size;
    438   uint8_t* const blob = new uint8_t[blob_size];
    439   uint8_t* const raw_data_start = blob + RawDataOffset();
    440 
    441   // Initially zap the entire blob, effectively padding the alignment area
    442   // between two builtins with int3's (on x64/ia32).
    443   ZapCode(reinterpret_cast<Address>(blob), blob_size);
    444 
    445   // Write the metadata tables.
    446   DCHECK_EQ(MetadataSize(), sizeof(metadata[0]) * metadata.size());
    447   std::memcpy(blob + MetadataOffset(), metadata.data(), MetadataSize());
    448 
    449   // Write the raw data section.
    450   for (int i = 0; i < Builtins::builtin_count; i++) {
    451     if (!Builtins::IsIsolateIndependent(i)) continue;
    452     Code* code = builtins->builtin(i);
    453     uint32_t offset = metadata[i].instructions_offset;
    454     uint8_t* dst = raw_data_start + offset;
    455     DCHECK_LE(RawDataOffset() + offset + code->raw_instruction_size(),
    456               blob_size);
    457     std::memcpy(dst, reinterpret_cast<uint8_t*>(code->raw_instruction_start()),
    458                 code->raw_instruction_size());
    459   }
    460 
    461   EmbeddedData d(blob, blob_size);
    462 
    463   // Fix up call targets that point to other embedded builtins.
    464   FinalizeEmbeddedCodeTargets(isolate, &d);
    465 
    466   // Hash the blob and store the result.
    467   STATIC_ASSERT(HashSize() == kSizetSize);
    468   const size_t hash = d.CreateHash();
    469   std::memcpy(blob + HashOffset(), &hash, HashSize());
    470 
    471   DCHECK_EQ(hash, d.CreateHash());
    472   DCHECK_EQ(hash, d.Hash());
    473 
    474   if (FLAG_serialization_statistics) d.PrintStatistics();
    475 
    476   return d;
    477 }
    478 
    479 EmbeddedData EmbeddedData::FromBlob() {
    480   const uint8_t* data = Isolate::CurrentEmbeddedBlob();
    481   uint32_t size = Isolate::CurrentEmbeddedBlobSize();
    482   DCHECK_NOT_NULL(data);
    483   DCHECK_LT(0, size);
    484   return {data, size};
    485 }
    486 
    487 Address EmbeddedData::InstructionStartOfBuiltin(int i) const {
    488   DCHECK(Builtins::IsBuiltinId(i));
    489   const struct Metadata* metadata = Metadata();
    490   const uint8_t* result = RawData() + metadata[i].instructions_offset;
    491   DCHECK_LE(result, data_ + size_);
    492   DCHECK_IMPLIES(result == data_ + size_, InstructionSizeOfBuiltin(i) == 0);
    493   return reinterpret_cast<Address>(result);
    494 }
    495 
    496 uint32_t EmbeddedData::InstructionSizeOfBuiltin(int i) const {
    497   DCHECK(Builtins::IsBuiltinId(i));
    498   const struct Metadata* metadata = Metadata();
    499   return metadata[i].instructions_length;
    500 }
    501 
    502 size_t EmbeddedData::CreateHash() const {
    503   STATIC_ASSERT(HashOffset() == 0);
    504   STATIC_ASSERT(HashSize() == kSizetSize);
    505   return base::hash_range(data_ + HashSize(), data_ + size_);
    506 }
    507 
    508 uint32_t Snapshot::ExtractNumContexts(const v8::StartupData* data) {
    509   CHECK_LT(kNumberOfContextsOffset, data->raw_size);
    510   uint32_t num_contexts = GetHeaderValue(data, kNumberOfContextsOffset);
    511   return num_contexts;
    512 }
    513 
    514 void EmbeddedData::PrintStatistics() const {
    515   DCHECK(FLAG_serialization_statistics);
    516 
    517   constexpr int kCount = Builtins::builtin_count;
    518 
    519   int embedded_count = 0;
    520   int instruction_size = 0;
    521   int sizes[kCount];
    522   for (int i = 0; i < kCount; i++) {
    523     if (!Builtins::IsIsolateIndependent(i)) continue;
    524     const int size = InstructionSizeOfBuiltin(i);
    525     instruction_size += size;
    526     sizes[embedded_count] = size;
    527     embedded_count++;
    528   }
    529 
    530   // Sort for percentiles.
    531   std::sort(&sizes[0], &sizes[embedded_count]);
    532 
    533   const int k50th = embedded_count * 0.5;
    534   const int k75th = embedded_count * 0.75;
    535   const int k90th = embedded_count * 0.90;
    536   const int k99th = embedded_count * 0.99;
    537 
    538   const int metadata_size = static_cast<int>(HashSize() + MetadataSize());
    539 
    540   PrintF("EmbeddedData:\n");
    541   PrintF("  Total size:                         %d\n",
    542          static_cast<int>(size()));
    543   PrintF("  Metadata size:                      %d\n", metadata_size);
    544   PrintF("  Instruction size:                   %d\n", instruction_size);
    545   PrintF("  Padding:                            %d\n",
    546          static_cast<int>(size() - metadata_size - instruction_size));
    547   PrintF("  Embedded builtin count:             %d\n", embedded_count);
    548   PrintF("  Instruction size (50th percentile): %d\n", sizes[k50th]);
    549   PrintF("  Instruction size (75th percentile): %d\n", sizes[k75th]);
    550   PrintF("  Instruction size (90th percentile): %d\n", sizes[k90th]);
    551   PrintF("  Instruction size (99th percentile): %d\n", sizes[k99th]);
    552   PrintF("\n");
    553 }
    554 
    555 uint32_t Snapshot::ExtractContextOffset(const v8::StartupData* data,
    556                                         uint32_t index) {
    557   // Extract the offset of the context at a given index from the StartupData,
    558   // and check that it is within bounds.
    559   uint32_t context_offset =
    560       GetHeaderValue(data, ContextSnapshotOffsetOffset(index));
    561   CHECK_LT(context_offset, static_cast<uint32_t>(data->raw_size));
    562   return context_offset;
    563 }
    564 
    565 bool Snapshot::ExtractRehashability(const v8::StartupData* data) {
    566   CHECK_LT(kRehashabilityOffset, static_cast<uint32_t>(data->raw_size));
    567   return GetHeaderValue(data, kRehashabilityOffset) != 0;
    568 }
    569 
    570 Vector<const byte> Snapshot::ExtractStartupData(const v8::StartupData* data) {
    571   uint32_t num_contexts = ExtractNumContexts(data);
    572   uint32_t startup_offset = StartupSnapshotOffset(num_contexts);
    573   CHECK_LT(startup_offset, data->raw_size);
    574   uint32_t builtin_offset = GetHeaderValue(data, kBuiltinOffsetOffset);
    575   CHECK_LT(builtin_offset, data->raw_size);
    576   CHECK_GT(builtin_offset, startup_offset);
    577   uint32_t startup_length = builtin_offset - startup_offset;
    578   const byte* startup_data =
    579       reinterpret_cast<const byte*>(data->data + startup_offset);
    580   return Vector<const byte>(startup_data, startup_length);
    581 }
    582 
    583 Vector<const byte> Snapshot::ExtractBuiltinData(const v8::StartupData* data) {
    584   DCHECK(SnapshotIsValid(data));
    585 
    586   uint32_t from_offset = GetHeaderValue(data, kBuiltinOffsetOffset);
    587   CHECK_LT(from_offset, data->raw_size);
    588 
    589   uint32_t to_offset = GetHeaderValue(data, ContextSnapshotOffsetOffset(0));
    590   CHECK_LT(to_offset, data->raw_size);
    591 
    592   CHECK_GT(to_offset, from_offset);
    593   uint32_t length = to_offset - from_offset;
    594   const byte* builtin_data =
    595       reinterpret_cast<const byte*>(data->data + from_offset);
    596   return Vector<const byte>(builtin_data, length);
    597 }
    598 
    599 Vector<const byte> Snapshot::ExtractContextData(const v8::StartupData* data,
    600                                                 uint32_t index) {
    601   uint32_t num_contexts = ExtractNumContexts(data);
    602   CHECK_LT(index, num_contexts);
    603 
    604   uint32_t context_offset = ExtractContextOffset(data, index);
    605   uint32_t next_context_offset;
    606   if (index == num_contexts - 1) {
    607     next_context_offset = data->raw_size;
    608   } else {
    609     next_context_offset = ExtractContextOffset(data, index + 1);
    610     CHECK_LT(next_context_offset, data->raw_size);
    611   }
    612 
    613   const byte* context_data =
    614       reinterpret_cast<const byte*>(data->data + context_offset);
    615   uint32_t context_length = next_context_offset - context_offset;
    616   return Vector<const byte>(context_data, context_length);
    617 }
    618 
    619 void Snapshot::CheckVersion(const v8::StartupData* data) {
    620   char version[kVersionStringLength];
    621   memset(version, 0, kVersionStringLength);
    622   CHECK_LT(kVersionStringOffset + kVersionStringLength,
    623            static_cast<uint32_t>(data->raw_size));
    624   Version::GetString(Vector<char>(version, kVersionStringLength));
    625   if (strncmp(version, data->data + kVersionStringOffset,
    626               kVersionStringLength) != 0) {
    627     FATAL(
    628         "Version mismatch between V8 binary and snapshot.\n"
    629         "#   V8 binary version: %.*s\n"
    630         "#    Snapshot version: %.*s\n"
    631         "# The snapshot consists of %d bytes and contains %d context(s).",
    632         kVersionStringLength, version, kVersionStringLength,
    633         data->data + kVersionStringOffset, data->raw_size,
    634         ExtractNumContexts(data));
    635   }
    636 }
    637 
    638 template <class AllocatorT>
    639 SnapshotData::SnapshotData(const Serializer<AllocatorT>* serializer) {
    640   DisallowHeapAllocation no_gc;
    641   std::vector<Reservation> reservations = serializer->EncodeReservations();
    642   const std::vector<byte>* payload = serializer->Payload();
    643 
    644   // Calculate sizes.
    645   uint32_t reservation_size =
    646       static_cast<uint32_t>(reservations.size()) * kUInt32Size;
    647   uint32_t size =
    648       kHeaderSize + reservation_size + static_cast<uint32_t>(payload->size());
    649 
    650   // Allocate backing store and create result data.
    651   AllocateData(size);
    652 
    653   // Set header values.
    654   SetMagicNumber(serializer->isolate());
    655   SetHeaderValue(kNumReservationsOffset, static_cast<int>(reservations.size()));
    656   SetHeaderValue(kPayloadLengthOffset, static_cast<int>(payload->size()));
    657 
    658   // Copy reservation chunk sizes.
    659   CopyBytes(data_ + kHeaderSize, reinterpret_cast<byte*>(reservations.data()),
    660             reservation_size);
    661 
    662   // Copy serialized data.
    663   CopyBytes(data_ + kHeaderSize + reservation_size, payload->data(),
    664             static_cast<size_t>(payload->size()));
    665 }
    666 
    667 // Explicit instantiation.
    668 template SnapshotData::SnapshotData(
    669     const Serializer<DefaultSerializerAllocator>* serializer);
    670 
    671 std::vector<SerializedData::Reservation> SnapshotData::Reservations() const {
    672   uint32_t size = GetHeaderValue(kNumReservationsOffset);
    673   std::vector<SerializedData::Reservation> reservations(size);
    674   memcpy(reservations.data(), data_ + kHeaderSize,
    675          size * sizeof(SerializedData::Reservation));
    676   return reservations;
    677 }
    678 
    679 Vector<const byte> SnapshotData::Payload() const {
    680   uint32_t reservations_size =
    681       GetHeaderValue(kNumReservationsOffset) * kUInt32Size;
    682   const byte* payload = data_ + kHeaderSize + reservations_size;
    683   uint32_t length = GetHeaderValue(kPayloadLengthOffset);
    684   DCHECK_EQ(data_ + size_, payload + length);
    685   return Vector<const byte>(payload, length);
    686 }
    687 
    688 BuiltinSnapshotData::BuiltinSnapshotData(const BuiltinSerializer* serializer)
    689     : SnapshotData(serializer) {}
    690 
    691 Vector<const byte> BuiltinSnapshotData::Payload() const {
    692   uint32_t reservations_size =
    693       GetHeaderValue(kNumReservationsOffset) * kUInt32Size;
    694   const byte* payload = data_ + kHeaderSize + reservations_size;
    695   const int builtin_offsets_size =
    696       BuiltinSnapshotUtils::kNumberOfCodeObjects * kUInt32Size;
    697   uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
    698   DCHECK_EQ(data_ + size_, payload + payload_length);
    699   DCHECK_GT(payload_length, builtin_offsets_size);
    700   return Vector<const byte>(payload, payload_length - builtin_offsets_size);
    701 }
    702 
    703 Vector<const uint32_t> BuiltinSnapshotData::BuiltinOffsets() const {
    704   uint32_t reservations_size =
    705       GetHeaderValue(kNumReservationsOffset) * kUInt32Size;
    706   const byte* payload = data_ + kHeaderSize + reservations_size;
    707   const int builtin_offsets_size =
    708       BuiltinSnapshotUtils::kNumberOfCodeObjects * kUInt32Size;
    709   uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
    710   DCHECK_EQ(data_ + size_, payload + payload_length);
    711   DCHECK_GT(payload_length, builtin_offsets_size);
    712   const uint32_t* data = reinterpret_cast<const uint32_t*>(
    713       payload + payload_length - builtin_offsets_size);
    714   return Vector<const uint32_t>(data,
    715                                 BuiltinSnapshotUtils::kNumberOfCodeObjects);
    716 }
    717 
    718 }  // namespace internal
    719 }  // namespace v8
    720