• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/natives.h"
15 #include "src/snapshot/partial-serializer.h"
16 #include "src/snapshot/snapshot.h"
17 #include "src/snapshot/startup-serializer.h"
18 
19 namespace {
20 class SnapshotWriter {
21  public:
SnapshotWriter()22   SnapshotWriter()
23       : snapshot_cpp_path_(nullptr), snapshot_blob_path_(nullptr) {}
24 
SetEmbeddedFile(const char * embedded_cpp_file)25   void SetEmbeddedFile(const char* embedded_cpp_file) {
26     embedded_cpp_path_ = embedded_cpp_file;
27   }
28 
SetEmbeddedVariant(const char * embedded_variant)29   void SetEmbeddedVariant(const char* embedded_variant) {
30     embedded_variant_ = embedded_variant;
31   }
32 
SetSnapshotFile(const char * snapshot_cpp_file)33   void SetSnapshotFile(const char* snapshot_cpp_file) {
34     snapshot_cpp_path_ = snapshot_cpp_file;
35   }
36 
SetStartupBlobFile(const char * snapshot_blob_file)37   void SetStartupBlobFile(const char* snapshot_blob_file) {
38     snapshot_blob_path_ = snapshot_blob_file;
39   }
40 
WriteSnapshot(v8::StartupData blob) const41   void WriteSnapshot(v8::StartupData blob) const {
42     // TODO(crbug/633159): if we crash before the files have been fully created,
43     // we end up with a corrupted snapshot file. The build step would succeed,
44     // but the build target is unusable. Ideally we would write out temporary
45     // files and only move them to the final destination as last step.
46     i::Vector<const i::byte> blob_vector(
47         reinterpret_cast<const i::byte*>(blob.data), blob.raw_size);
48     MaybeWriteSnapshotFile(blob_vector);
49     MaybeWriteStartupBlob(blob_vector);
50   }
51 
WriteEmbedded(const i::EmbeddedData * blob) const52   void WriteEmbedded(const i::EmbeddedData* blob) const {
53     MaybeWriteEmbeddedFile(blob);
54   }
55 
56  private:
MaybeWriteStartupBlob(const i::Vector<const i::byte> & blob) const57   void MaybeWriteStartupBlob(const i::Vector<const i::byte>& blob) const {
58     if (!snapshot_blob_path_) return;
59 
60     FILE* fp = GetFileDescriptorOrDie(snapshot_blob_path_);
61     size_t written = fwrite(blob.begin(), 1, blob.length(), fp);
62     fclose(fp);
63     if (written != static_cast<size_t>(blob.length())) {
64       i::PrintF("Writing snapshot file failed.. Aborting.\n");
65       remove(snapshot_blob_path_);
66       exit(1);
67     }
68   }
69 
MaybeWriteSnapshotFile(const i::Vector<const i::byte> & blob) const70   void MaybeWriteSnapshotFile(const i::Vector<const i::byte>& blob) const {
71     if (!snapshot_cpp_path_) return;
72 
73     FILE* fp = GetFileDescriptorOrDie(snapshot_cpp_path_);
74 
75     WriteSnapshotFilePrefix(fp);
76     WriteSnapshotFileData(fp, blob);
77     WriteSnapshotFileSuffix(fp);
78 
79     fclose(fp);
80   }
81 
WriteSnapshotFilePrefix(FILE * fp)82   static void WriteSnapshotFilePrefix(FILE* fp) {
83     fprintf(fp, "// Autogenerated snapshot file. Do not edit.\n\n");
84     fprintf(fp, "#include \"src/v8.h\"\n");
85     fprintf(fp, "#include \"src/base/platform/platform.h\"\n\n");
86     fprintf(fp, "#include \"src/snapshot/snapshot.h\"\n\n");
87     fprintf(fp, "namespace v8 {\n");
88     fprintf(fp, "namespace internal {\n\n");
89   }
90 
WriteSnapshotFileSuffix(FILE * fp)91   static void WriteSnapshotFileSuffix(FILE* fp) {
92     fprintf(fp, "const v8::StartupData* Snapshot::DefaultSnapshotBlob() {\n");
93     fprintf(fp, "  return &blob;\n");
94     fprintf(fp, "}\n\n");
95     fprintf(fp, "}  // namespace internal\n");
96     fprintf(fp, "}  // namespace v8\n");
97   }
98 
WriteSnapshotFileData(FILE * fp,const i::Vector<const i::byte> & blob)99   static void WriteSnapshotFileData(FILE* fp,
100                                     const i::Vector<const i::byte>& blob) {
101     fprintf(fp, "static const byte blob_data[] = {\n");
102     WriteBinaryContentsAsCArray(fp, blob);
103     fprintf(fp, "};\n");
104     fprintf(fp, "static const int blob_size = %d;\n", blob.length());
105     fprintf(fp, "static const v8::StartupData blob =\n");
106     fprintf(fp, "{ (const char*) blob_data, blob_size };\n");
107   }
108 
WriteBinaryContentsAsCArray(FILE * fp,const i::Vector<const i::byte> & blob)109   static void WriteBinaryContentsAsCArray(
110       FILE* fp, const i::Vector<const i::byte>& blob) {
111     for (int i = 0; i < blob.length(); i++) {
112       if ((i & 0x1F) == 0x1F) fprintf(fp, "\n");
113       if (i > 0) fprintf(fp, ",");
114       fprintf(fp, "%u", static_cast<unsigned char>(blob.at(i)));
115     }
116     fprintf(fp, "\n");
117   }
118 
MaybeWriteEmbeddedFile(const i::EmbeddedData * blob) const119   void MaybeWriteEmbeddedFile(const i::EmbeddedData* blob) const {
120     if (embedded_cpp_path_ == nullptr) return;
121 
122     FILE* fp = GetFileDescriptorOrDie(embedded_cpp_path_);
123 
124     WriteEmbeddedFilePrefix(fp);
125     WriteEmbeddedFileData(fp, blob, embedded_variant_);
126     WriteEmbeddedFileSuffix(fp, embedded_variant_);
127 
128     fclose(fp);
129   }
130 
WriteEmbeddedFilePrefix(FILE * fp)131   static void WriteEmbeddedFilePrefix(FILE* fp) {
132     fprintf(fp, "// Autogenerated file. Do not edit.\n\n");
133     fprintf(fp, "#include <cstdint>\n\n");
134     fprintf(fp, "#include \"src/snapshot/macros.h\"\n\n");
135     fprintf(fp, "namespace v8 {\n");
136     fprintf(fp, "namespace internal {\n\n");
137     fprintf(fp, "namespace {\n\n");
138   }
139 
WriteEmbeddedFileSuffix(FILE * fp,const char * embedded_variant)140   static void WriteEmbeddedFileSuffix(FILE* fp, const char* embedded_variant) {
141     fprintf(fp, "}  // namespace\n\n");
142     fprintf(fp,
143             "const uint8_t* %sEmbeddedBlob() { return "
144             "v8_%s_embedded_blob_; }\n",
145             embedded_variant, embedded_variant);
146     fprintf(fp,
147             "uint32_t %sEmbeddedBlobSize() { return "
148             "v8_embedded_blob_size_; }\n\n",
149             embedded_variant);
150     fprintf(fp, "}  // namespace internal\n");
151     fprintf(fp, "}  // namespace v8\n");
152   }
153 
WriteEmbeddedFileData(FILE * fp,const i::EmbeddedData * blob,const char * embedded_variant)154   static void WriteEmbeddedFileData(FILE* fp, const i::EmbeddedData* blob,
155                                     const char* embedded_variant) {
156     fprintf(fp, "V8_EMBEDDED_TEXT_HEADER(v8_%s_embedded_blob_)\n",
157             embedded_variant);
158 #ifdef V8_OS_MACOSX
159     // Note: On some platforms (observed on mac64), inserting labels into the
160     // .byte stream causes the compiler to reorder symbols, invalidating stored
161     // offsets.
162     // We either need to avoid doing so, or stop relying on our own offset table
163     // and directly reference symbols instead. But there is another complication
164     // there since the chrome build process on mac verifies the order of symbols
165     // present in the binary.
166     // For now, the straight-forward solution seems to be to just emit a pure
167     // .byte stream on OSX.
168     WriteBinaryContentsAsByteDirective(fp, blob->data(), blob->size());
169 #else
170     WriteBinaryContentsAsByteDirective(fp, blob->data(),
171                                        i::EmbeddedData::RawDataOffset());
172     WriteBuiltins(fp, blob, embedded_variant);
173 #endif
174     fprintf(fp, "extern \"C\" const uint8_t v8_%s_embedded_blob_[];\n",
175             embedded_variant);
176     fprintf(fp, "static const uint32_t v8_embedded_blob_size_ = %d;\n\n",
177             blob->size());
178   }
179 
WriteBuiltins(FILE * fp,const i::EmbeddedData * blob,const char * embedded_variant)180   static void WriteBuiltins(FILE* fp, const i::EmbeddedData* blob,
181                             const char* embedded_variant) {
182     const bool is_default_variant =
183         std::strcmp(embedded_variant, "Default") == 0;
184     for (int i = 0; i < i::Builtins::builtin_count; i++) {
185       if (!blob->ContainsBuiltin(i)) continue;
186 
187       // Labels created here will show up in backtraces. We check in
188       // Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e.
189       // that labels do not insert bytes into the middle of the blob byte
190       // stream.
191       if (is_default_variant) {
192         // Create nicer symbol names for the default mode.
193         fprintf(fp, "__asm__(V8_ASM_LABEL(\"Builtins_%s\"));\n",
194                 i::Builtins::name(i));
195       } else {
196         fprintf(fp, "__asm__(V8_ASM_LABEL(\"%s_Builtins_%s\"));\n",
197                 embedded_variant, i::Builtins::name(i));
198       }
199 
200       WriteBinaryContentsAsByteDirective(
201           fp,
202           reinterpret_cast<const uint8_t*>(blob->InstructionStartOfBuiltin(i)),
203           blob->PaddedInstructionSizeOfBuiltin(i));
204     }
205     fprintf(fp, "\n");
206   }
207 
WriteBinaryContentsAsByteDirective(FILE * fp,const uint8_t * data,uint32_t size)208   static void WriteBinaryContentsAsByteDirective(FILE* fp, const uint8_t* data,
209                                                  uint32_t size) {
210     static const int kTextWidth = 80;
211     int current_line_length = 0;
212     int printed_chars;
213 
214     fprintf(fp, "__asm__(\n");
215     for (uint32_t i = 0; i < size; i++) {
216       if (current_line_length == 0) {
217         printed_chars = fprintf(fp, "%s", "  \".byte ");
218         DCHECK_LT(0, printed_chars);
219         current_line_length += printed_chars;
220       } else {
221         printed_chars = fprintf(fp, ",");
222         DCHECK_EQ(1, printed_chars);
223         current_line_length += printed_chars;
224       }
225 
226       printed_chars = fprintf(fp, "0x%02x", data[i]);
227       DCHECK_LT(0, printed_chars);
228       current_line_length += printed_chars;
229 
230       if (current_line_length + strlen(",0xFF\\n\"") > kTextWidth) {
231         fprintf(fp, "\\n\"\n");
232         current_line_length = 0;
233       }
234     }
235 
236     if (current_line_length != 0) fprintf(fp, "\\n\"\n");
237     fprintf(fp, ");\n");
238   }
239 
GetFileDescriptorOrDie(const char * filename)240   static FILE* GetFileDescriptorOrDie(const char* filename) {
241     FILE* fp = v8::base::OS::FOpen(filename, "wb");
242     if (fp == nullptr) {
243       i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
244       exit(1);
245     }
246     return fp;
247   }
248 
249   const char* embedded_cpp_path_ = nullptr;
250   const char* embedded_variant_ = "Default";
251   const char* snapshot_cpp_path_;
252   const char* snapshot_blob_path_;
253 };
254 
GetExtraCode(char * filename,const char * description)255 char* GetExtraCode(char* filename, const char* description) {
256   if (filename == nullptr || strlen(filename) == 0) return nullptr;
257   ::printf("Loading script for %s: %s\n", description, filename);
258   FILE* file = v8::base::OS::FOpen(filename, "rb");
259   if (file == nullptr) {
260     fprintf(stderr, "Failed to open '%s': errno %d\n", filename, errno);
261     exit(1);
262   }
263   fseek(file, 0, SEEK_END);
264   size_t size = ftell(file);
265   rewind(file);
266   char* chars = new char[size + 1];
267   chars[size] = '\0';
268   for (size_t i = 0; i < size;) {
269     size_t read = fread(&chars[i], 1, size - i, file);
270     if (ferror(file)) {
271       fprintf(stderr, "Failed to read '%s': errno %d\n", filename, errno);
272       exit(1);
273     }
274     i += read;
275   }
276   fclose(file);
277   return chars;
278 }
279 
RunExtraCode(v8::Isolate * isolate,v8::Local<v8::Context> context,const char * utf8_source,const char * name)280 bool RunExtraCode(v8::Isolate* isolate, v8::Local<v8::Context> context,
281                   const char* utf8_source, const char* name) {
282   v8::base::ElapsedTimer timer;
283   timer.Start();
284   v8::Context::Scope context_scope(context);
285   v8::TryCatch try_catch(isolate);
286   v8::Local<v8::String> source_string;
287   if (!v8::String::NewFromUtf8(isolate, utf8_source, v8::NewStringType::kNormal)
288            .ToLocal(&source_string)) {
289     return false;
290   }
291   v8::Local<v8::String> resource_name =
292       v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kNormal)
293           .ToLocalChecked();
294   v8::ScriptOrigin origin(resource_name);
295   v8::ScriptCompiler::Source source(source_string, origin);
296   v8::Local<v8::Script> script;
297   if (!v8::ScriptCompiler::Compile(context, &source).ToLocal(&script))
298     return false;
299   if (script->Run(context).IsEmpty()) return false;
300   if (i::FLAG_profile_deserialization) {
301     i::PrintF("Executing custom snapshot script %s took %0.3f ms\n", name,
302               timer.Elapsed().InMillisecondsF());
303   }
304   timer.Stop();
305   CHECK(!try_catch.HasCaught());
306   return true;
307 }
308 
CreateSnapshotDataBlob(v8::SnapshotCreator * snapshot_creator,const char * script_source=NULL)309 v8::StartupData CreateSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator,
310                                        const char* script_source = NULL) {
311   // Create a new isolate and a new context from scratch, optionally run
312   // a script to embed, and serialize to create a snapshot blob.
313   v8::StartupData result = {nullptr, 0};
314   v8::base::ElapsedTimer timer;
315   timer.Start();
316   {
317     v8::Isolate* isolate = snapshot_creator->GetIsolate();
318     {
319       v8::HandleScope scope(isolate);
320       v8::Local<v8::Context> context = v8::Context::New(isolate);
321       if (script_source != nullptr &&
322           !RunExtraCode(isolate, context, script_source, "<embedded>")) {
323         return result;
324       }
325       snapshot_creator->SetDefaultContext(context);
326     }
327     result = snapshot_creator->CreateBlob(
328         v8::SnapshotCreator::FunctionCodeHandling::kClear);
329   }
330 
331   if (i::FLAG_profile_deserialization) {
332     i::PrintF("Creating snapshot took %0.3f ms\n",
333               timer.Elapsed().InMillisecondsF());
334   }
335   timer.Stop();
336   return result;
337 }
338 
WarmUpSnapshotDataBlob(v8::SnapshotCreator * snapshot_creator,const char * warmup_source)339 v8::StartupData WarmUpSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator,
340                                        const char* warmup_source) {
341   CHECK_NOT_NULL(warmup_source);
342   // Use following steps to create a warmed up snapshot blob from a cold one:
343   //  - Create a new isolate from the cold snapshot.
344   //  - Create a new context to run the warmup script. This will trigger
345   //    compilation of executed functions.
346   //  - Create a new context. This context will be unpolluted.
347   //  - Serialize the isolate and the second context into a new snapshot blob.
348   v8::StartupData result = {nullptr, 0};
349   v8::base::ElapsedTimer timer;
350   timer.Start();
351   {
352     v8::Isolate* isolate = snapshot_creator->GetIsolate();
353     {
354       v8::HandleScope scope(isolate);
355       v8::Local<v8::Context> context = v8::Context::New(isolate);
356       if (!RunExtraCode(isolate, context, warmup_source, "<warm-up>")) {
357         return result;
358       }
359     }
360     {
361       v8::HandleScope handle_scope(isolate);
362       isolate->ContextDisposedNotification(false);
363       v8::Local<v8::Context> context = v8::Context::New(isolate);
364       snapshot_creator->SetDefaultContext(context);
365     }
366     result = snapshot_creator->CreateBlob(
367         v8::SnapshotCreator::FunctionCodeHandling::kKeep);
368   }
369 
370   if (i::FLAG_profile_deserialization) {
371     i::PrintF("Warming up snapshot took %0.3f ms\n",
372               timer.Elapsed().InMillisecondsF());
373   }
374   timer.Stop();
375   return result;
376 }
377 
WriteEmbeddedFile(v8::SnapshotCreator * creator,SnapshotWriter * writer)378 void WriteEmbeddedFile(v8::SnapshotCreator* creator, SnapshotWriter* writer) {
379   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(creator->GetIsolate());
380   isolate->PrepareEmbeddedBlobForSerialization();
381   i::EmbeddedData embedded_blob = i::EmbeddedData::FromBlob();
382   writer->WriteEmbedded(&embedded_blob);
383 }
384 }  // namespace
385 
main(int argc,char ** argv)386 int main(int argc, char** argv) {
387   v8::base::EnsureConsoleOutput();
388 
389   // Make mksnapshot runs predictable to create reproducible snapshots.
390   i::FLAG_predictable = true;
391 
392   // Print the usage if an error occurs when parsing the command line
393   // flags or if the help flag is set.
394   int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
395   if (result > 0 || (argc > 3) || i::FLAG_help) {
396     ::printf("Usage: %s --startup_src=... --startup_blob=... [extras]\n",
397              argv[0]);
398     i::FlagList::PrintHelp();
399     return !i::FLAG_help;
400   }
401 
402   i::CpuFeatures::Probe(true);
403   v8::V8::InitializeICUDefaultLocation(argv[0]);
404   std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
405   v8::V8::InitializePlatform(platform.get());
406   v8::V8::Initialize();
407 
408   {
409     SnapshotWriter writer;
410     if (i::FLAG_startup_src) writer.SetSnapshotFile(i::FLAG_startup_src);
411     if (i::FLAG_startup_blob) writer.SetStartupBlobFile(i::FLAG_startup_blob);
412     if (i::FLAG_embedded_builtins) {
413       if (i::FLAG_embedded_src) writer.SetEmbeddedFile(i::FLAG_embedded_src);
414       if (i::FLAG_embedded_variant)
415         writer.SetEmbeddedVariant(i::FLAG_embedded_variant);
416     }
417 
418     std::unique_ptr<char> embed_script(
419         GetExtraCode(argc >= 2 ? argv[1] : nullptr, "embedding"));
420     std::unique_ptr<char> warmup_script(
421         GetExtraCode(argc >= 3 ? argv[2] : nullptr, "warm up"));
422 
423     v8::StartupData blob;
424     {
425       v8::Isolate* isolate = v8::Isolate::Allocate();
426       if (i::FLAG_embedded_builtins) {
427         // Set code range such that relative jumps for builtins to
428         // builtin calls in the snapshot are possible.
429         i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
430         size_t code_range_size =
431             i::kMaximalCodeRangeSize == 0
432                 ? i::kMaxPCRelativeCodeRangeInMB
433                 : std::min(i::kMaximalCodeRangeSize / i::MB,
434                            i::kMaxPCRelativeCodeRangeInMB);
435         i_isolate->heap()->ConfigureHeap(0, 0, code_range_size);
436       }
437       v8::SnapshotCreator snapshot_creator(isolate);
438       if (i::FLAG_embedded_builtins) {
439         // This process is a bit tricky since we might go on to make a second
440         // snapshot if a warmup script is passed. In that case, create the first
441         // snapshot without off-heap trampolines and only move code off-heap for
442         // the warmed-up snapshot.
443         if (!warmup_script) WriteEmbeddedFile(&snapshot_creator, &writer);
444       }
445       blob = CreateSnapshotDataBlob(&snapshot_creator, embed_script.get());
446     }
447 
448     if (warmup_script) {
449       CHECK(blob.raw_size > 0 && blob.data != nullptr);
450       v8::StartupData cold = blob;
451       v8::SnapshotCreator snapshot_creator(nullptr, &cold);
452       if (i::FLAG_embedded_builtins) {
453         WriteEmbeddedFile(&snapshot_creator, &writer);
454       }
455       blob = WarmUpSnapshotDataBlob(&snapshot_creator, warmup_script.get());
456       delete[] cold.data;
457     }
458 
459     CHECK(blob.data);
460     writer.WriteSnapshot(blob);
461     delete[] blob.data;
462   }
463 
464   v8::V8::Dispose();
465   v8::V8::ShutdownPlatform();
466   return 0;
467 }
468