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 #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:
     22   SnapshotWriter()
     23       : snapshot_cpp_path_(nullptr), snapshot_blob_path_(nullptr) {}
     24 
     25   void SetEmbeddedFile(const char* embedded_cpp_file) {
     26     embedded_cpp_path_ = embedded_cpp_file;
     27   }
     28 
     29   void SetEmbeddedVariant(const char* embedded_variant) {
     30     embedded_variant_ = embedded_variant;
     31   }
     32 
     33   void SetSnapshotFile(const char* snapshot_cpp_file) {
     34     snapshot_cpp_path_ = snapshot_cpp_file;
     35   }
     36 
     37   void SetStartupBlobFile(const char* snapshot_blob_file) {
     38     snapshot_blob_path_ = snapshot_blob_file;
     39   }
     40 
     41   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 
     52   void WriteEmbedded(const i::EmbeddedData* blob) const {
     53     MaybeWriteEmbeddedFile(blob);
     54   }
     55 
     56  private:
     57   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 
     70   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 
     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 
     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 
     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 
    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 
    119   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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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