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 #include <iomanip>
9 
10 #include "include/libplatform/libplatform.h"
11 #include "src/base/platform/platform.h"
12 #include "src/codegen/assembler-arch.h"
13 #include "src/codegen/source-position-table.h"
14 #include "src/flags/flags.h"
15 #include "src/sanitizer/msan.h"
16 #include "src/snapshot/embedded/embedded-file-writer.h"
17 #include "src/snapshot/partial-serializer.h"
18 #include "src/snapshot/snapshot.h"
19 #include "src/snapshot/startup-serializer.h"
20 
21 namespace {
22 
23 class SnapshotFileWriter {
24  public:
SetSnapshotFile(const char * snapshot_cpp_file)25   void SetSnapshotFile(const char* snapshot_cpp_file) {
26     snapshot_cpp_path_ = snapshot_cpp_file;
27   }
28 
SetStartupBlobFile(const char * snapshot_blob_file)29   void SetStartupBlobFile(const char* snapshot_blob_file) {
30     snapshot_blob_path_ = snapshot_blob_file;
31   }
32 
WriteSnapshot(v8::StartupData blob) const33   void WriteSnapshot(v8::StartupData blob) const {
34     // TODO(crbug/633159): if we crash before the files have been fully created,
35     // we end up with a corrupted snapshot file. The build step would succeed,
36     // but the build target is unusable. Ideally we would write out temporary
37     // files and only move them to the final destination as last step.
38     i::Vector<const i::byte> blob_vector(
39         reinterpret_cast<const i::byte*>(blob.data), blob.raw_size);
40     MaybeWriteSnapshotFile(blob_vector);
41     MaybeWriteStartupBlob(blob_vector);
42   }
43 
44  private:
MaybeWriteStartupBlob(const i::Vector<const i::byte> & blob) const45   void MaybeWriteStartupBlob(const i::Vector<const i::byte>& blob) const {
46     if (!snapshot_blob_path_) return;
47 
48     FILE* fp = GetFileDescriptorOrDie(snapshot_blob_path_);
49     size_t written = fwrite(blob.begin(), 1, blob.length(), fp);
50     fclose(fp);
51     if (written != static_cast<size_t>(blob.length())) {
52       i::PrintF("Writing snapshot file failed.. Aborting.\n");
53       remove(snapshot_blob_path_);
54       exit(1);
55     }
56   }
57 
MaybeWriteSnapshotFile(const i::Vector<const i::byte> & blob) const58   void MaybeWriteSnapshotFile(const i::Vector<const i::byte>& blob) const {
59     if (!snapshot_cpp_path_) return;
60 
61     FILE* fp = GetFileDescriptorOrDie(snapshot_cpp_path_);
62 
63     WriteSnapshotFilePrefix(fp);
64     WriteSnapshotFileData(fp, blob);
65     WriteSnapshotFileSuffix(fp);
66 
67     fclose(fp);
68   }
69 
WriteSnapshotFilePrefix(FILE * fp)70   static void WriteSnapshotFilePrefix(FILE* fp) {
71     fprintf(fp, "// Autogenerated snapshot file. Do not edit.\n\n");
72     fprintf(fp, "#include \"src/init/v8.h\"\n");
73     fprintf(fp, "#include \"src/base/platform/platform.h\"\n\n");
74     fprintf(fp, "#include \"src/snapshot/snapshot.h\"\n\n");
75     fprintf(fp, "namespace v8 {\n");
76     fprintf(fp, "namespace internal {\n\n");
77   }
78 
WriteSnapshotFileSuffix(FILE * fp)79   static void WriteSnapshotFileSuffix(FILE* fp) {
80     fprintf(fp, "const v8::StartupData* Snapshot::DefaultSnapshotBlob() {\n");
81     fprintf(fp, "  return &blob;\n");
82     fprintf(fp, "}\n\n");
83     fprintf(fp, "}  // namespace internal\n");
84     fprintf(fp, "}  // namespace v8\n");
85   }
86 
WriteSnapshotFileData(FILE * fp,const i::Vector<const i::byte> & blob)87   static void WriteSnapshotFileData(FILE* fp,
88                                     const i::Vector<const i::byte>& blob) {
89     fprintf(fp,
90             "alignas(kPointerAlignment) static const byte blob_data[] = {\n");
91     WriteBinaryContentsAsCArray(fp, blob);
92     fprintf(fp, "};\n");
93     fprintf(fp, "static const int blob_size = %d;\n", blob.length());
94     fprintf(fp, "static const v8::StartupData blob =\n");
95     fprintf(fp, "{ (const char*) blob_data, blob_size };\n");
96   }
97 
WriteBinaryContentsAsCArray(FILE * fp,const i::Vector<const i::byte> & blob)98   static void WriteBinaryContentsAsCArray(
99       FILE* fp, const i::Vector<const i::byte>& blob) {
100     for (int i = 0; i < blob.length(); i++) {
101       if ((i & 0x1F) == 0x1F) fprintf(fp, "\n");
102       if (i > 0) fprintf(fp, ",");
103       fprintf(fp, "%u", static_cast<unsigned char>(blob.at(i)));
104     }
105     fprintf(fp, "\n");
106   }
107 
GetFileDescriptorOrDie(const char * filename)108   static FILE* GetFileDescriptorOrDie(const char* filename) {
109     FILE* fp = v8::base::OS::FOpen(filename, "wb");
110     if (fp == nullptr) {
111       i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
112       exit(1);
113     }
114     return fp;
115   }
116 
117   const char* snapshot_cpp_path_ = nullptr;
118   const char* snapshot_blob_path_ = nullptr;
119 };
120 
GetExtraCode(char * filename,const char * description)121 char* GetExtraCode(char* filename, const char* description) {
122   if (filename == nullptr || strlen(filename) == 0) return nullptr;
123   ::printf("Loading script for %s: %s\n", description, filename);
124   FILE* file = v8::base::OS::FOpen(filename, "rb");
125   if (file == nullptr) {
126     fprintf(stderr, "Failed to open '%s': errno %d\n", filename, errno);
127     exit(1);
128   }
129   fseek(file, 0, SEEK_END);
130   size_t size = ftell(file);
131   rewind(file);
132   char* chars = new char[size + 1];
133   chars[size] = '\0';
134   for (size_t i = 0; i < size;) {
135     size_t read = fread(&chars[i], 1, size - i, file);
136     if (ferror(file)) {
137       fprintf(stderr, "Failed to read '%s': errno %d\n", filename, errno);
138       exit(1);
139     }
140     i += read;
141   }
142   fclose(file);
143   return chars;
144 }
145 
CreateSnapshotDataBlob(v8::Isolate * isolate,const char * embedded_source)146 v8::StartupData CreateSnapshotDataBlob(v8::Isolate* isolate,
147                                        const char* embedded_source) {
148   v8::base::ElapsedTimer timer;
149   timer.Start();
150 
151   v8::StartupData result = i::CreateSnapshotDataBlobInternal(
152       v8::SnapshotCreator::FunctionCodeHandling::kClear, embedded_source,
153       isolate);
154 
155   if (i::FLAG_profile_deserialization) {
156     i::PrintF("Creating snapshot took %0.3f ms\n",
157               timer.Elapsed().InMillisecondsF());
158   }
159 
160   timer.Stop();
161   return result;
162 }
163 
WarmUpSnapshotDataBlob(v8::StartupData cold_snapshot_blob,const char * warmup_source)164 v8::StartupData WarmUpSnapshotDataBlob(v8::StartupData cold_snapshot_blob,
165                                        const char* warmup_source) {
166   v8::base::ElapsedTimer timer;
167   timer.Start();
168 
169   v8::StartupData result =
170       i::WarmUpSnapshotDataBlobInternal(cold_snapshot_blob, warmup_source);
171 
172   if (i::FLAG_profile_deserialization) {
173     i::PrintF("Warming up snapshot took %0.3f ms\n",
174               timer.Elapsed().InMillisecondsF());
175   }
176 
177   timer.Stop();
178   return result;
179 }
180 
WriteEmbeddedFile(i::EmbeddedFileWriter * writer)181 void WriteEmbeddedFile(i::EmbeddedFileWriter* writer) {
182   i::EmbeddedData embedded_blob = i::EmbeddedData::FromBlob();
183   writer->WriteEmbedded(&embedded_blob);
184 }
185 
186 using CounterMap = std::map<std::string, int>;
187 CounterMap* counter_map_ = nullptr;
188 
MaybeSetCounterFunction(v8::Isolate * isolate)189 void MaybeSetCounterFunction(v8::Isolate* isolate) {
190   // If --native-code-counters is on then we enable all counters to make
191   // sure we generate code to increment them from the snapshot.
192   //
193   // Note: For the sake of the mksnapshot, the counter function must only
194   // return distinct addresses for each counter s.t. the serializer can properly
195   // distinguish between them. In theory it should be okay to just return an
196   // incremented int value each time this function is called, but we play it
197   // safe and return a real distinct memory location tied to every counter name.
198   if (i::FLAG_native_code_counters) {
199     counter_map_ = new CounterMap();
200     isolate->SetCounterFunction([](const char* name) -> int* {
201       auto map_entry = counter_map_->find(name);
202       if (map_entry == counter_map_->end()) {
203         counter_map_->emplace(name, 0);
204       }
205       return &counter_map_->at(name);
206     });
207   }
208 }
209 
210 }  // namespace
211 
main(int argc,char ** argv)212 int main(int argc, char** argv) {
213   v8::base::EnsureConsoleOutput();
214 
215   // Make mksnapshot runs predictable to create reproducible snapshots.
216   i::FLAG_predictable = true;
217 
218   // Print the usage if an error occurs when parsing the command line
219   // flags or if the help flag is set.
220   int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
221   if (result > 0 || (argc > 3) || i::FLAG_help) {
222     ::printf("Usage: %s --startup_src=... --startup_blob=... [extras]\n",
223              argv[0]);
224     i::FlagList::PrintHelp();
225     return !i::FLAG_help;
226   }
227 
228   i::CpuFeatures::Probe(true);
229   v8::V8::InitializeICUDefaultLocation(argv[0]);
230   std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
231   v8::V8::InitializePlatform(platform.get());
232   v8::V8::Initialize();
233 
234   {
235     SnapshotFileWriter snapshot_writer;
236     snapshot_writer.SetSnapshotFile(i::FLAG_startup_src);
237     snapshot_writer.SetStartupBlobFile(i::FLAG_startup_blob);
238 
239     i::EmbeddedFileWriter embedded_writer;
240     embedded_writer.SetEmbeddedFile(i::FLAG_embedded_src);
241     embedded_writer.SetEmbeddedVariant(i::FLAG_embedded_variant);
242     embedded_writer.SetTargetArch(i::FLAG_target_arch);
243     embedded_writer.SetTargetOs(i::FLAG_target_os);
244 
245     std::unique_ptr<char> embed_script(
246         GetExtraCode(argc >= 2 ? argv[1] : nullptr, "embedding"));
247     std::unique_ptr<char> warmup_script(
248         GetExtraCode(argc >= 3 ? argv[2] : nullptr, "warm up"));
249 
250     i::DisableEmbeddedBlobRefcounting();
251     v8::StartupData blob;
252     {
253       v8::Isolate* isolate = v8::Isolate::Allocate();
254 
255       MaybeSetCounterFunction(isolate);
256 
257       // Set code range such that relative jumps for builtins to
258       // builtin calls in the snapshot are possible.
259       i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
260       size_t code_range_size_mb =
261           i::kMaximalCodeRangeSize == 0
262               ? i::kMaxPCRelativeCodeRangeInMB
263               : std::min(i::kMaximalCodeRangeSize / i::MB,
264                          i::kMaxPCRelativeCodeRangeInMB);
265       v8::ResourceConstraints constraints;
266       constraints.set_code_range_size_in_bytes(code_range_size_mb * i::MB);
267       i_isolate->heap()->ConfigureHeap(constraints);
268       // The isolate contains data from builtin compilation that needs
269       // to be written out if builtins are embedded.
270       i_isolate->RegisterEmbeddedFileWriter(&embedded_writer);
271 
272       blob = CreateSnapshotDataBlob(isolate, embed_script.get());
273 
274       // At this point, the Isolate has been torn down but the embedded blob
275       // is still alive (we called DisableEmbeddedBlobRefcounting above).
276       // That's fine as far as the embedded file writer is concerned.
277       WriteEmbeddedFile(&embedded_writer);
278     }
279 
280     if (warmup_script) {
281       v8::StartupData cold = blob;
282       blob = WarmUpSnapshotDataBlob(cold, warmup_script.get());
283       delete[] cold.data;
284     }
285 
286     delete counter_map_;
287 
288     CHECK(blob.data);
289     snapshot_writer.WriteSnapshot(blob);
290     delete[] blob.data;
291   }
292   i::FreeCurrentEmbeddedBlob();
293 
294   v8::V8::Dispose();
295   v8::V8::ShutdownPlatform();
296   return 0;
297 }
298