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/context-serializer.h"
17 #include "src/snapshot/embedded/embedded-file-writer.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 using HelpOptions = i::FlagList::HelpOptions;
221 std::string usage = "Usage: " + std::string(argv[0]) +
222 " [--startup-src=file]" + " [--startup-blob=file]" +
223 " [--embedded-src=file]" + " [--embedded-variant=label]" +
224 " [--target-arch=arch]" +
225 " [--target-os=os] [extras]\n\n";
226 int result = i::FlagList::SetFlagsFromCommandLine(
227 &argc, argv, true, HelpOptions(HelpOptions::kExit, usage.c_str()));
228 if (result > 0 || (argc > 3)) {
229 i::PrintF(stdout, "%s", usage.c_str());
230 return result;
231 }
232
233 i::CpuFeatures::Probe(true);
234 v8::V8::InitializeICUDefaultLocation(argv[0]);
235 std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
236 v8::V8::InitializePlatform(platform.get());
237 v8::V8::Initialize();
238
239 {
240 SnapshotFileWriter snapshot_writer;
241 snapshot_writer.SetSnapshotFile(i::FLAG_startup_src);
242 snapshot_writer.SetStartupBlobFile(i::FLAG_startup_blob);
243
244 i::EmbeddedFileWriter embedded_writer;
245 embedded_writer.SetEmbeddedFile(i::FLAG_embedded_src);
246 embedded_writer.SetEmbeddedVariant(i::FLAG_embedded_variant);
247 embedded_writer.SetTargetArch(i::FLAG_target_arch);
248 embedded_writer.SetTargetOs(i::FLAG_target_os);
249
250 std::unique_ptr<char> embed_script(
251 GetExtraCode(argc >= 2 ? argv[1] : nullptr, "embedding"));
252 std::unique_ptr<char> warmup_script(
253 GetExtraCode(argc >= 3 ? argv[2] : nullptr, "warm up"));
254
255 i::DisableEmbeddedBlobRefcounting();
256 v8::StartupData blob;
257 {
258 v8::Isolate* isolate = v8::Isolate::Allocate();
259
260 MaybeSetCounterFunction(isolate);
261
262 // Set code range such that relative jumps for builtins to
263 // builtin calls in the snapshot are possible.
264 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
265 size_t code_range_size_mb =
266 i::kMaximalCodeRangeSize == 0
267 ? i::kMaxPCRelativeCodeRangeInMB
268 : std::min(i::kMaximalCodeRangeSize / i::MB,
269 i::kMaxPCRelativeCodeRangeInMB);
270 v8::ResourceConstraints constraints;
271 constraints.set_code_range_size_in_bytes(code_range_size_mb * i::MB);
272 i_isolate->heap()->ConfigureHeap(constraints);
273 // The isolate contains data from builtin compilation that needs
274 // to be written out if builtins are embedded.
275 i_isolate->RegisterEmbeddedFileWriter(&embedded_writer);
276
277 blob = CreateSnapshotDataBlob(isolate, embed_script.get());
278
279 // At this point, the Isolate has been torn down but the embedded blob
280 // is still alive (we called DisableEmbeddedBlobRefcounting above).
281 // That's fine as far as the embedded file writer is concerned.
282 WriteEmbeddedFile(&embedded_writer);
283 }
284
285 if (warmup_script) {
286 v8::StartupData cold = blob;
287 blob = WarmUpSnapshotDataBlob(cold, warmup_script.get());
288 delete[] cold.data;
289 }
290
291 delete counter_map_;
292
293 CHECK(blob.data);
294 snapshot_writer.WriteSnapshot(blob);
295 delete[] blob.data;
296 }
297 i::FreeCurrentEmbeddedBlob();
298
299 v8::V8::Dispose();
300 v8::V8::ShutdownPlatform();
301 return 0;
302 }
303