V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
mksnapshot.cc
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 #include <errno.h>
6 #include <signal.h>
7 #include <stdio.h>
8 
9 #include "include/libplatform/libplatform.h"
10 #include "src/assembler-arch.h"
11 #include "src/base/platform/platform.h"
12 #include "src/flags.h"
13 #include "src/msan.h"
14 #include "src/snapshot/embedded-file-writer.h"
15 #include "src/snapshot/natives.h"
16 #include "src/snapshot/partial-serializer.h"
17 #include "src/snapshot/snapshot.h"
18 #include "src/snapshot/startup-serializer.h"
19 
20 namespace {
21 
22 class SnapshotFileWriter {
23  public:
24  void SetSnapshotFile(const char* snapshot_cpp_file) {
25  snapshot_cpp_path_ = snapshot_cpp_file;
26  }
27 
28  void SetStartupBlobFile(const char* snapshot_blob_file) {
29  snapshot_blob_path_ = snapshot_blob_file;
30  }
31 
32  void WriteSnapshot(v8::StartupData blob) const {
33  // TODO(crbug/633159): if we crash before the files have been fully created,
34  // we end up with a corrupted snapshot file. The build step would succeed,
35  // but the build target is unusable. Ideally we would write out temporary
36  // files and only move them to the final destination as last step.
37  i::Vector<const i::byte> blob_vector(
38  reinterpret_cast<const i::byte*>(blob.data), blob.raw_size);
39  MaybeWriteSnapshotFile(blob_vector);
40  MaybeWriteStartupBlob(blob_vector);
41  }
42 
43  private:
44  void MaybeWriteStartupBlob(const i::Vector<const i::byte>& blob) const {
45  if (!snapshot_blob_path_) return;
46 
47  FILE* fp = GetFileDescriptorOrDie(snapshot_blob_path_);
48  size_t written = fwrite(blob.begin(), 1, blob.length(), fp);
49  fclose(fp);
50  if (written != static_cast<size_t>(blob.length())) {
51  i::PrintF("Writing snapshot file failed.. Aborting.\n");
52  remove(snapshot_blob_path_);
53  exit(1);
54  }
55  }
56 
57  void MaybeWriteSnapshotFile(const i::Vector<const i::byte>& blob) const {
58  if (!snapshot_cpp_path_) return;
59 
60  FILE* fp = GetFileDescriptorOrDie(snapshot_cpp_path_);
61 
62  WriteSnapshotFilePrefix(fp);
63  WriteSnapshotFileData(fp, blob);
64  WriteSnapshotFileSuffix(fp);
65 
66  fclose(fp);
67  }
68 
69  static void WriteSnapshotFilePrefix(FILE* fp) {
70  fprintf(fp, "// Autogenerated snapshot file. Do not edit.\n\n");
71  fprintf(fp, "#include \"src/v8.h\"\n");
72  fprintf(fp, "#include \"src/base/platform/platform.h\"\n\n");
73  fprintf(fp, "#include \"src/snapshot/snapshot.h\"\n\n");
74  fprintf(fp, "namespace v8 {\n");
75  fprintf(fp, "namespace internal {\n\n");
76  }
77 
78  static void WriteSnapshotFileSuffix(FILE* fp) {
79  fprintf(fp, "const v8::StartupData* Snapshot::DefaultSnapshotBlob() {\n");
80  fprintf(fp, " return &blob;\n");
81  fprintf(fp, "}\n\n");
82  fprintf(fp, "} // namespace internal\n");
83  fprintf(fp, "} // namespace v8\n");
84  }
85 
86  static void WriteSnapshotFileData(FILE* fp,
87  const i::Vector<const i::byte>& blob) {
88  fprintf(fp, "static const byte blob_data[] = {\n");
89  WriteBinaryContentsAsCArray(fp, blob);
90  fprintf(fp, "};\n");
91  fprintf(fp, "static const int blob_size = %d;\n", blob.length());
92  fprintf(fp, "static const v8::StartupData blob =\n");
93  fprintf(fp, "{ (const char*) blob_data, blob_size };\n");
94  }
95 
96  static void WriteBinaryContentsAsCArray(
97  FILE* fp, const i::Vector<const i::byte>& blob) {
98  for (int i = 0; i < blob.length(); i++) {
99  if ((i & 0x1F) == 0x1F) fprintf(fp, "\n");
100  if (i > 0) fprintf(fp, ",");
101  fprintf(fp, "%u", static_cast<unsigned char>(blob.at(i)));
102  }
103  fprintf(fp, "\n");
104  }
105 
106  static FILE* GetFileDescriptorOrDie(const char* filename) {
107  FILE* fp = v8::base::OS::FOpen(filename, "wb");
108  if (fp == nullptr) {
109  i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
110  exit(1);
111  }
112  return fp;
113  }
114 
115  const char* snapshot_cpp_path_ = nullptr;
116  const char* snapshot_blob_path_ = nullptr;
117 };
118 
119 char* GetExtraCode(char* filename, const char* description) {
120  if (filename == nullptr || strlen(filename) == 0) return nullptr;
121  ::printf("Loading script for %s: %s\n", description, filename);
122  FILE* file = v8::base::OS::FOpen(filename, "rb");
123  if (file == nullptr) {
124  fprintf(stderr, "Failed to open '%s': errno %d\n", filename, errno);
125  exit(1);
126  }
127  fseek(file, 0, SEEK_END);
128  size_t size = ftell(file);
129  rewind(file);
130  char* chars = new char[size + 1];
131  chars[size] = '\0';
132  for (size_t i = 0; i < size;) {
133  size_t read = fread(&chars[i], 1, size - i, file);
134  if (ferror(file)) {
135  fprintf(stderr, "Failed to read '%s': errno %d\n", filename, errno);
136  exit(1);
137  }
138  i += read;
139  }
140  fclose(file);
141  return chars;
142 }
143 
144 bool RunExtraCode(v8::Isolate* isolate, v8::Local<v8::Context> context,
145  const char* utf8_source, const char* name) {
147  timer.Start();
148  v8::Context::Scope context_scope(context);
149  v8::TryCatch try_catch(isolate);
150  v8::Local<v8::String> source_string;
151  if (!v8::String::NewFromUtf8(isolate, utf8_source, v8::NewStringType::kNormal)
152  .ToLocal(&source_string)) {
153  return false;
154  }
155  v8::Local<v8::String> resource_name =
157  .ToLocalChecked();
158  v8::ScriptOrigin origin(resource_name);
159  v8::ScriptCompiler::Source source(source_string, origin);
160  v8::Local<v8::Script> script;
161  if (!v8::ScriptCompiler::Compile(context, &source).ToLocal(&script))
162  return false;
163  if (script->Run(context).IsEmpty()) return false;
164  if (i::FLAG_profile_deserialization) {
165  i::PrintF("Executing custom snapshot script %s took %0.3f ms\n", name,
166  timer.Elapsed().InMillisecondsF());
167  }
168  timer.Stop();
169  CHECK(!try_catch.HasCaught());
170  return true;
171 }
172 
173 v8::StartupData CreateSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator,
174  const char* script_source = nullptr) {
175  // Create a new isolate and a new context from scratch, optionally run
176  // a script to embed, and serialize to create a snapshot blob.
177  v8::StartupData result = {nullptr, 0};
179  timer.Start();
180  {
181  v8::Isolate* isolate = snapshot_creator->GetIsolate();
182  {
183  v8::HandleScope scope(isolate);
184  v8::Local<v8::Context> context = v8::Context::New(isolate);
185  if (script_source != nullptr &&
186  !RunExtraCode(isolate, context, script_source, "<embedded>")) {
187  return result;
188  }
189  snapshot_creator->SetDefaultContext(context);
190  }
191  result = snapshot_creator->CreateBlob(
192  v8::SnapshotCreator::FunctionCodeHandling::kClear);
193  }
194 
195  if (i::FLAG_profile_deserialization) {
196  i::PrintF("Creating snapshot took %0.3f ms\n",
197  timer.Elapsed().InMillisecondsF());
198  }
199  timer.Stop();
200  return result;
201 }
202 
203 v8::StartupData WarmUpSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator,
204  const char* warmup_source) {
205  CHECK_NOT_NULL(warmup_source);
206  // Use following steps to create a warmed up snapshot blob from a cold one:
207  // - Create a new isolate from the cold snapshot.
208  // - Create a new context to run the warmup script. This will trigger
209  // compilation of executed functions.
210  // - Create a new context. This context will be unpolluted.
211  // - Serialize the isolate and the second context into a new snapshot blob.
212  v8::StartupData result = {nullptr, 0};
214  timer.Start();
215  {
216  v8::Isolate* isolate = snapshot_creator->GetIsolate();
217  {
218  v8::HandleScope scope(isolate);
219  v8::Local<v8::Context> context = v8::Context::New(isolate);
220  if (!RunExtraCode(isolate, context, warmup_source, "<warm-up>")) {
221  return result;
222  }
223  }
224  {
225  v8::HandleScope handle_scope(isolate);
226  isolate->ContextDisposedNotification(false);
227  v8::Local<v8::Context> context = v8::Context::New(isolate);
228  snapshot_creator->SetDefaultContext(context);
229  }
230  result = snapshot_creator->CreateBlob(
231  v8::SnapshotCreator::FunctionCodeHandling::kKeep);
232  }
233 
234  if (i::FLAG_profile_deserialization) {
235  i::PrintF("Warming up snapshot took %0.3f ms\n",
236  timer.Elapsed().InMillisecondsF());
237  }
238  timer.Stop();
239  return result;
240 }
241 
242 void WriteEmbeddedFile(i::EmbeddedFileWriter* writer) {
243  i::EmbeddedData embedded_blob = i::EmbeddedData::FromBlob();
244  writer->WriteEmbedded(&embedded_blob);
245 }
246 } // namespace
247 
248 int main(int argc, char** argv) {
249  v8::base::EnsureConsoleOutput();
250 
251  // Make mksnapshot runs predictable to create reproducible snapshots.
252  i::FLAG_predictable = true;
253 
254  // Print the usage if an error occurs when parsing the command line
255  // flags or if the help flag is set.
256  int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
257  if (result > 0 || (argc > 3) || i::FLAG_help) {
258  ::printf("Usage: %s --startup_src=... --startup_blob=... [extras]\n",
259  argv[0]);
260  i::FlagList::PrintHelp();
261  return !i::FLAG_help;
262  }
263 
264  i::CpuFeatures::Probe(true);
265  v8::V8::InitializeICUDefaultLocation(argv[0]);
266  std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
267  v8::V8::InitializePlatform(platform.get());
268  v8::V8::Initialize();
269 
270  {
271  SnapshotFileWriter snapshot_writer;
272  snapshot_writer.SetSnapshotFile(i::FLAG_startup_src);
273  snapshot_writer.SetStartupBlobFile(i::FLAG_startup_blob);
274 
275  i::EmbeddedFileWriter embedded_writer;
276  embedded_writer.SetEmbeddedFile(i::FLAG_embedded_src);
277  embedded_writer.SetEmbeddedVariant(i::FLAG_embedded_variant);
278 
279  std::unique_ptr<char> embed_script(
280  GetExtraCode(argc >= 2 ? argv[1] : nullptr, "embedding"));
281  std::unique_ptr<char> warmup_script(
282  GetExtraCode(argc >= 3 ? argv[2] : nullptr, "warm up"));
283 
284  i::DisableEmbeddedBlobRefcounting();
285  v8::StartupData blob;
286  {
287  v8::Isolate* isolate = v8::Isolate::Allocate();
288  if (i::FLAG_embedded_builtins) {
289  // Set code range such that relative jumps for builtins to
290  // builtin calls in the snapshot are possible.
291  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
292  size_t code_range_size =
293  i::kMaximalCodeRangeSize == 0
294  ? i::kMaxPCRelativeCodeRangeInMB
295  : std::min(i::kMaximalCodeRangeSize / i::MB,
296  i::kMaxPCRelativeCodeRangeInMB);
297  i_isolate->heap()->ConfigureHeap(0, 0, code_range_size);
298  }
299  v8::SnapshotCreator snapshot_creator(isolate);
300  if (i::FLAG_embedded_builtins) WriteEmbeddedFile(&embedded_writer);
301  blob = CreateSnapshotDataBlob(&snapshot_creator, embed_script.get());
302  }
303 
304  if (warmup_script) {
305  CHECK(blob.raw_size > 0 && blob.data != nullptr);
306  v8::StartupData cold = blob;
307  v8::SnapshotCreator snapshot_creator(nullptr, &cold);
308  blob = WarmUpSnapshotDataBlob(&snapshot_creator, warmup_script.get());
309  delete[] cold.data;
310  }
311 
312  CHECK(blob.data);
313  snapshot_writer.WriteSnapshot(blob);
314  delete[] blob.data;
315  }
316  i::FreeCurrentEmbeddedBlob();
317 
318  v8::V8::Dispose();
319  v8::V8::ShutdownPlatform();
320  return 0;
321 }
static V8_WARN_UNUSED_RESULT MaybeLocal< Script > Compile(Local< Context > context, Source *source, CompileOptions options=kNoCompileOptions, NoCacheReason no_cache_reason=kNoCacheNoReason)
Definition: api.cc:2378
V8_WARN_UNUSED_RESULT MaybeLocal< Value > Run(Local< Context > context)
Definition: api.cc:2108
static V8_WARN_UNUSED_RESULT MaybeLocal< String > NewFromUtf8(Isolate *isolate, const char *data, v8::NewStringType type, int length=-1)
Definition: api.cc:6511