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
25
26bool Snapshot::HaveASnapshotToStartFrom(Isolate* isolate) {
27  // Do not use snapshots if the isolate is used to create snapshots.
28  return isolate->snapshot_blob() != NULL &&
29         isolate->snapshot_blob()->data != NULL;
30}
31
32
33uint32_t Snapshot::SizeOfFirstPage(Isolate* isolate, AllocationSpace space) {
34  DCHECK(space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE);
35  if (!isolate->snapshot_available()) {
36    return static_cast<uint32_t>(MemoryAllocator::PageAreaSize(space));
37  }
38  uint32_t size;
39  int offset = kFirstPageSizesOffset + (space - FIRST_PAGED_SPACE) * kInt32Size;
40  memcpy(&size, isolate->snapshot_blob()->data + offset, kInt32Size);
41  return size;
42}
43
44
45bool Snapshot::Initialize(Isolate* isolate) {
46  if (!isolate->snapshot_available()) return false;
47  base::ElapsedTimer timer;
48  if (FLAG_profile_deserialization) timer.Start();
49
50  const v8::StartupData* blob = isolate->snapshot_blob();
51  Vector<const byte> startup_data = ExtractStartupData(blob);
52  SnapshotData snapshot_data(startup_data);
53  Deserializer deserializer(&snapshot_data);
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
63MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
64    Isolate* isolate, Handle<JSGlobalProxy> global_proxy,
65    size_t context_index) {
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  Vector<const byte> context_data =
72      ExtractContextData(blob, static_cast<int>(context_index));
73  SnapshotData snapshot_data(context_data);
74  Deserializer deserializer(&snapshot_data);
75
76  MaybeHandle<Object> maybe_context =
77      deserializer.DeserializePartial(isolate, global_proxy);
78  Handle<Object> result;
79  if (!maybe_context.ToHandle(&result)) return MaybeHandle<Context>();
80  CHECK(result->IsContext());
81  if (FLAG_profile_deserialization) {
82    double ms = timer.Elapsed().InMillisecondsF();
83    int bytes = context_data.length();
84    PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
85           context_index, bytes, ms);
86  }
87  return Handle<Context>::cast(result);
88}
89
90void UpdateMaxRequirementPerPage(
91    uint32_t* requirements,
92    Vector<const SerializedData::Reservation> reservations) {
93  int space = 0;
94  uint32_t current_requirement = 0;
95  for (const auto& reservation : reservations) {
96    current_requirement += reservation.chunk_size();
97    if (reservation.is_last()) {
98      requirements[space] = std::max(requirements[space], current_requirement);
99      current_requirement = 0;
100      space++;
101    }
102  }
103  DCHECK_EQ(i::Serializer::kNumberOfSpaces, space);
104}
105
106void CalculateFirstPageSizes(const SnapshotData* startup_snapshot,
107                             const List<SnapshotData*>* context_snapshots,
108                             uint32_t* sizes_out) {
109  if (FLAG_profile_deserialization) {
110    int startup_total = 0;
111    PrintF("Deserialization will reserve:\n");
112    for (const auto& reservation : startup_snapshot->Reservations()) {
113      startup_total += reservation.chunk_size();
114    }
115    PrintF("%10d bytes per isolate\n", startup_total);
116    for (int i = 0; i < context_snapshots->length(); i++) {
117      int context_total = 0;
118      for (const auto& reservation : context_snapshots->at(i)->Reservations()) {
119        context_total += reservation.chunk_size();
120      }
121      PrintF("%10d bytes per context #%d\n", context_total, i);
122    }
123  }
124
125  uint32_t startup_requirements[i::Serializer::kNumberOfSpaces];
126  uint32_t context_requirements[i::Serializer::kNumberOfSpaces];
127  for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
128    startup_requirements[space] = 0;
129    context_requirements[space] = 0;
130  }
131
132  UpdateMaxRequirementPerPage(startup_requirements,
133                              startup_snapshot->Reservations());
134  for (const auto& context_snapshot : *context_snapshots) {
135    UpdateMaxRequirementPerPage(context_requirements,
136                                context_snapshot->Reservations());
137  }
138
139  for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
140    // If the space requirement for a page is less than a page size, we consider
141    // limiting the size of the first page in order to save memory on startup.
142    uint32_t required = startup_requirements[space] +
143                        2 * context_requirements[space] +
144                        Page::kObjectStartOffset;
145    // Add a small allowance to the code space for small scripts.
146    if (space == CODE_SPACE) required += 32 * KB;
147
148    if (space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE) {
149      uint32_t max_size =
150          MemoryAllocator::PageAreaSize(static_cast<AllocationSpace>(space));
151      sizes_out[space - FIRST_PAGED_SPACE] = std::min(required, max_size);
152    }
153  }
154}
155
156v8::StartupData Snapshot::CreateSnapshotBlob(
157    const SnapshotData* startup_snapshot,
158    const List<SnapshotData*>* context_snapshots) {
159  int num_contexts = context_snapshots->length();
160  int startup_snapshot_offset = StartupSnapshotOffset(num_contexts);
161  int total_length = startup_snapshot_offset;
162  total_length += startup_snapshot->RawData().length();
163  for (const auto& context_snapshot : *context_snapshots) {
164    total_length += context_snapshot->RawData().length();
165  }
166
167  uint32_t first_page_sizes[kNumPagedSpaces];
168  CalculateFirstPageSizes(startup_snapshot, context_snapshots,
169                          first_page_sizes);
170
171  char* data = new char[total_length];
172  memcpy(data + kFirstPageSizesOffset, first_page_sizes,
173         kNumPagedSpaces * kInt32Size);
174  memcpy(data + kNumberOfContextsOffset, &num_contexts, kInt32Size);
175  int payload_offset = StartupSnapshotOffset(num_contexts);
176  int payload_length = startup_snapshot->RawData().length();
177  memcpy(data + payload_offset, startup_snapshot->RawData().start(),
178         payload_length);
179  if (FLAG_profile_deserialization) {
180    PrintF("Snapshot blob consists of:\n%10d bytes for startup\n",
181           payload_length);
182  }
183  payload_offset += payload_length;
184  for (int i = 0; i < num_contexts; i++) {
185    memcpy(data + ContextSnapshotOffsetOffset(i), &payload_offset, kInt32Size);
186    SnapshotData* context_snapshot = context_snapshots->at(i);
187    payload_length = context_snapshot->RawData().length();
188    memcpy(data + payload_offset, context_snapshot->RawData().start(),
189           payload_length);
190    if (FLAG_profile_deserialization) {
191      PrintF("%10d bytes for context #%d\n", payload_length, i);
192    }
193    payload_offset += payload_length;
194  }
195
196  v8::StartupData result = {data, total_length};
197  return result;
198}
199
200int Snapshot::ExtractNumContexts(const v8::StartupData* data) {
201  CHECK_LT(kNumberOfContextsOffset, data->raw_size);
202  int num_contexts;
203  memcpy(&num_contexts, data->data + kNumberOfContextsOffset, kInt32Size);
204  return num_contexts;
205}
206
207Vector<const byte> Snapshot::ExtractStartupData(const v8::StartupData* data) {
208  int num_contexts = ExtractNumContexts(data);
209  int startup_offset = StartupSnapshotOffset(num_contexts);
210  CHECK_LT(startup_offset, data->raw_size);
211  int first_context_offset;
212  memcpy(&first_context_offset, data->data + ContextSnapshotOffsetOffset(0),
213         kInt32Size);
214  CHECK_LT(first_context_offset, data->raw_size);
215  int startup_length = first_context_offset - startup_offset;
216  const byte* startup_data =
217      reinterpret_cast<const byte*>(data->data + startup_offset);
218  return Vector<const byte>(startup_data, startup_length);
219}
220
221Vector<const byte> Snapshot::ExtractContextData(const v8::StartupData* data,
222                                                int index) {
223  int num_contexts = ExtractNumContexts(data);
224  CHECK_LT(index, num_contexts);
225
226  int context_offset;
227  memcpy(&context_offset, data->data + ContextSnapshotOffsetOffset(index),
228         kInt32Size);
229  int next_context_offset;
230  if (index == num_contexts - 1) {
231    next_context_offset = data->raw_size;
232  } else {
233    memcpy(&next_context_offset,
234           data->data + ContextSnapshotOffsetOffset(index + 1), kInt32Size);
235    CHECK_LT(next_context_offset, data->raw_size);
236  }
237
238  const byte* context_data =
239      reinterpret_cast<const byte*>(data->data + context_offset);
240  int context_length = next_context_offset - context_offset;
241  return Vector<const byte>(context_data, context_length);
242}
243
244SnapshotData::SnapshotData(const Serializer* serializer) {
245  DisallowHeapAllocation no_gc;
246  List<Reservation> reservations;
247  serializer->EncodeReservations(&reservations);
248  const List<byte>* payload = serializer->sink()->data();
249
250  // Calculate sizes.
251  int reservation_size = reservations.length() * kInt32Size;
252  int size = kHeaderSize + reservation_size + payload->length();
253
254  // Allocate backing store and create result data.
255  AllocateData(size);
256
257  // Set header values.
258  SetMagicNumber(serializer->isolate());
259  SetHeaderValue(kCheckSumOffset, Version::Hash());
260  SetHeaderValue(kNumReservationsOffset, reservations.length());
261  SetHeaderValue(kPayloadLengthOffset, payload->length());
262
263  // Copy reservation chunk sizes.
264  CopyBytes(data_ + kHeaderSize, reinterpret_cast<byte*>(reservations.begin()),
265            reservation_size);
266
267  // Copy serialized data.
268  CopyBytes(data_ + kHeaderSize + reservation_size, payload->begin(),
269            static_cast<size_t>(payload->length()));
270}
271
272bool SnapshotData::IsSane() {
273  return GetHeaderValue(kCheckSumOffset) == Version::Hash();
274}
275
276Vector<const SerializedData::Reservation> SnapshotData::Reservations() const {
277  return Vector<const Reservation>(
278      reinterpret_cast<const Reservation*>(data_ + kHeaderSize),
279      GetHeaderValue(kNumReservationsOffset));
280}
281
282Vector<const byte> SnapshotData::Payload() const {
283  int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size;
284  const byte* payload = data_ + kHeaderSize + reservations_size;
285  int length = GetHeaderValue(kPayloadLengthOffset);
286  DCHECK_EQ(data_ + size_, payload + length);
287  return Vector<const byte>(payload, length);
288}
289
290}  // namespace internal
291}  // namespace v8
292