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/api.h"
10#include "src/base/platform/platform.h"
11#include "src/full-codegen/full-codegen.h"
12#include "src/snapshot/deserializer.h"
13#include "src/snapshot/snapshot-source-sink.h"
14#include "src/version.h"
15
16namespace v8 {
17namespace internal {
18
19#ifdef DEBUG
20bool Snapshot::SnapshotIsValid(v8::StartupData* snapshot_blob) {
21  return Snapshot::ExtractNumContexts(snapshot_blob) > 0;
22}
23#endif  // DEBUG
24
25bool Snapshot::HasContextSnapshot(Isolate* isolate, size_t index) {
26  // Do not use snapshots if the isolate is used to create snapshots.
27  const v8::StartupData* blob = isolate->snapshot_blob();
28  if (blob == nullptr) return false;
29  if (blob->data == nullptr) return false;
30  size_t num_contexts = static_cast<size_t>(ExtractNumContexts(blob));
31  return index < num_contexts;
32}
33
34bool Snapshot::Initialize(Isolate* isolate) {
35  if (!isolate->snapshot_available()) return false;
36  base::ElapsedTimer timer;
37  if (FLAG_profile_deserialization) timer.Start();
38
39  const v8::StartupData* blob = isolate->snapshot_blob();
40  Vector<const byte> startup_data = ExtractStartupData(blob);
41  SnapshotData snapshot_data(startup_data);
42  Deserializer deserializer(&snapshot_data);
43  bool success = isolate->Init(&deserializer);
44  if (FLAG_profile_deserialization) {
45    double ms = timer.Elapsed().InMillisecondsF();
46    int bytes = startup_data.length();
47    PrintF("[Deserializing isolate (%d bytes) took %0.3f ms]\n", bytes, ms);
48  }
49  return success;
50}
51
52MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
53    Isolate* isolate, Handle<JSGlobalProxy> global_proxy,
54    size_t context_index) {
55  if (!isolate->snapshot_available()) return Handle<Context>();
56  base::ElapsedTimer timer;
57  if (FLAG_profile_deserialization) timer.Start();
58
59  const v8::StartupData* blob = isolate->snapshot_blob();
60  Vector<const byte> context_data =
61      ExtractContextData(blob, static_cast<int>(context_index));
62  SnapshotData snapshot_data(context_data);
63  Deserializer deserializer(&snapshot_data);
64
65  MaybeHandle<Object> maybe_context =
66      deserializer.DeserializePartial(isolate, global_proxy);
67  Handle<Object> result;
68  if (!maybe_context.ToHandle(&result)) return MaybeHandle<Context>();
69  CHECK(result->IsContext());
70  if (FLAG_profile_deserialization) {
71    double ms = timer.Elapsed().InMillisecondsF();
72    int bytes = context_data.length();
73    PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
74           context_index, bytes, ms);
75  }
76  return Handle<Context>::cast(result);
77}
78
79void ProfileDeserialization(const SnapshotData* startup_snapshot,
80                            const List<SnapshotData*>* context_snapshots) {
81  if (FLAG_profile_deserialization) {
82    int startup_total = 0;
83    PrintF("Deserialization will reserve:\n");
84    for (const auto& reservation : startup_snapshot->Reservations()) {
85      startup_total += reservation.chunk_size();
86    }
87    PrintF("%10d bytes per isolate\n", startup_total);
88    for (int i = 0; i < context_snapshots->length(); i++) {
89      int context_total = 0;
90      for (const auto& reservation : context_snapshots->at(i)->Reservations()) {
91        context_total += reservation.chunk_size();
92      }
93      PrintF("%10d bytes per context #%d\n", context_total, i);
94    }
95  }
96}
97
98v8::StartupData Snapshot::CreateSnapshotBlob(
99    const SnapshotData* startup_snapshot,
100    const List<SnapshotData*>* context_snapshots) {
101  int num_contexts = context_snapshots->length();
102  int startup_snapshot_offset = StartupSnapshotOffset(num_contexts);
103  int total_length = startup_snapshot_offset;
104  total_length += startup_snapshot->RawData().length();
105  for (const auto& context_snapshot : *context_snapshots) {
106    total_length += context_snapshot->RawData().length();
107  }
108
109  ProfileDeserialization(startup_snapshot, context_snapshots);
110
111  char* data = new char[total_length];
112  memcpy(data + kNumberOfContextsOffset, &num_contexts, kInt32Size);
113  int payload_offset = StartupSnapshotOffset(num_contexts);
114  int payload_length = startup_snapshot->RawData().length();
115  memcpy(data + payload_offset, startup_snapshot->RawData().start(),
116         payload_length);
117  if (FLAG_profile_deserialization) {
118    PrintF("Snapshot blob consists of:\n%10d bytes for startup\n",
119           payload_length);
120  }
121  payload_offset += payload_length;
122  for (int i = 0; i < num_contexts; i++) {
123    memcpy(data + ContextSnapshotOffsetOffset(i), &payload_offset, kInt32Size);
124    SnapshotData* context_snapshot = context_snapshots->at(i);
125    payload_length = context_snapshot->RawData().length();
126    memcpy(data + payload_offset, context_snapshot->RawData().start(),
127           payload_length);
128    if (FLAG_profile_deserialization) {
129      PrintF("%10d bytes for context #%d\n", payload_length, i);
130    }
131    payload_offset += payload_length;
132  }
133
134  v8::StartupData result = {data, total_length};
135  return result;
136}
137
138int Snapshot::ExtractNumContexts(const v8::StartupData* data) {
139  CHECK_LT(kNumberOfContextsOffset, data->raw_size);
140  int num_contexts;
141  memcpy(&num_contexts, data->data + kNumberOfContextsOffset, kInt32Size);
142  return num_contexts;
143}
144
145Vector<const byte> Snapshot::ExtractStartupData(const v8::StartupData* data) {
146  int num_contexts = ExtractNumContexts(data);
147  int startup_offset = StartupSnapshotOffset(num_contexts);
148  CHECK_LT(startup_offset, data->raw_size);
149  int first_context_offset;
150  memcpy(&first_context_offset, data->data + ContextSnapshotOffsetOffset(0),
151         kInt32Size);
152  CHECK_LT(first_context_offset, data->raw_size);
153  int startup_length = first_context_offset - startup_offset;
154  const byte* startup_data =
155      reinterpret_cast<const byte*>(data->data + startup_offset);
156  return Vector<const byte>(startup_data, startup_length);
157}
158
159Vector<const byte> Snapshot::ExtractContextData(const v8::StartupData* data,
160                                                int index) {
161  int num_contexts = ExtractNumContexts(data);
162  CHECK_LT(index, num_contexts);
163
164  int context_offset;
165  memcpy(&context_offset, data->data + ContextSnapshotOffsetOffset(index),
166         kInt32Size);
167  int next_context_offset;
168  if (index == num_contexts - 1) {
169    next_context_offset = data->raw_size;
170  } else {
171    memcpy(&next_context_offset,
172           data->data + ContextSnapshotOffsetOffset(index + 1), kInt32Size);
173    CHECK_LT(next_context_offset, data->raw_size);
174  }
175
176  const byte* context_data =
177      reinterpret_cast<const byte*>(data->data + context_offset);
178  int context_length = next_context_offset - context_offset;
179  return Vector<const byte>(context_data, context_length);
180}
181
182SnapshotData::SnapshotData(const Serializer* serializer) {
183  DisallowHeapAllocation no_gc;
184  List<Reservation> reservations;
185  serializer->EncodeReservations(&reservations);
186  const List<byte>* payload = serializer->sink()->data();
187
188  // Calculate sizes.
189  int reservation_size = reservations.length() * kInt32Size;
190  int size = kHeaderSize + reservation_size + payload->length();
191
192  // Allocate backing store and create result data.
193  AllocateData(size);
194
195  // Set header values.
196  SetMagicNumber(serializer->isolate());
197  SetHeaderValue(kCheckSumOffset, Version::Hash());
198  SetHeaderValue(kNumReservationsOffset, reservations.length());
199  SetHeaderValue(kPayloadLengthOffset, payload->length());
200
201  // Copy reservation chunk sizes.
202  CopyBytes(data_ + kHeaderSize, reinterpret_cast<byte*>(reservations.begin()),
203            reservation_size);
204
205  // Copy serialized data.
206  CopyBytes(data_ + kHeaderSize + reservation_size, payload->begin(),
207            static_cast<size_t>(payload->length()));
208}
209
210bool SnapshotData::IsSane() {
211  return GetHeaderValue(kCheckSumOffset) == Version::Hash();
212}
213
214Vector<const SerializedData::Reservation> SnapshotData::Reservations() const {
215  return Vector<const Reservation>(
216      reinterpret_cast<const Reservation*>(data_ + kHeaderSize),
217      GetHeaderValue(kNumReservationsOffset));
218}
219
220Vector<const byte> SnapshotData::Payload() const {
221  int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
222  const byte* payload = data_ + kHeaderSize + reservations_size;
223  int length = GetHeaderValue(kPayloadLengthOffset);
224  DCHECK_EQ(data_ + size_, payload + length);
225  return Vector<const byte>(payload, length);
226}
227
228}  // namespace internal
229}  // namespace v8
230