1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | // Generate a snapshot file after loading all the scripts specified on the |
6 | // command line. |
7 | |
8 | #include <stdio.h> |
9 | #include <stdlib.h> |
10 | #include <string.h> |
11 | |
12 | #include <cstdarg> |
13 | #include <memory> |
14 | |
15 | #include "bin/builtin.h" |
16 | #include "bin/console.h" |
17 | #include "bin/dartutils.h" |
18 | #include "bin/error_exit.h" |
19 | #include "bin/eventhandler.h" |
20 | #include "bin/file.h" |
21 | #include "bin/loader.h" |
22 | #include "bin/options.h" |
23 | #include "bin/platform.h" |
24 | #include "bin/snapshot_utils.h" |
25 | #include "bin/thread.h" |
26 | #include "bin/utils.h" |
27 | #include "bin/vmservice_impl.h" |
28 | |
29 | #include "include/dart_api.h" |
30 | #include "include/dart_tools_api.h" |
31 | |
32 | #include "platform/globals.h" |
33 | #include "platform/growable_array.h" |
34 | #include "platform/hashmap.h" |
35 | #include "platform/syslog.h" |
36 | #include "platform/text_buffer.h" |
37 | |
38 | namespace dart { |
39 | namespace bin { |
40 | |
41 | #define CHECK_RESULT(result) \ |
42 | if (Dart_IsError(result)) { \ |
43 | intptr_t exit_code = 0; \ |
44 | Syslog::PrintErr("Error: %s\n", Dart_GetError(result)); \ |
45 | if (Dart_IsCompilationError(result)) { \ |
46 | exit_code = kCompilationErrorExitCode; \ |
47 | } else if (Dart_IsApiError(result)) { \ |
48 | exit_code = kApiErrorExitCode; \ |
49 | } else { \ |
50 | exit_code = kErrorExitCode; \ |
51 | } \ |
52 | Dart_ExitScope(); \ |
53 | Dart_ShutdownIsolate(); \ |
54 | exit(exit_code); \ |
55 | } |
56 | |
57 | // The environment provided through the command line using -D options. |
58 | static dart::SimpleHashMap* environment = nullptr; |
59 | |
60 | static bool ProcessEnvironmentOption(const char* arg, |
61 | CommandLineOptions* vm_options) { |
62 | return OptionProcessor::ProcessEnvironmentOption(arg, vm_options, |
63 | environment: &environment); |
64 | } |
65 | |
66 | // The core snapshot to use when creating isolates. Normally nullptr, but loaded |
67 | // from a file when creating AppJIT snapshots. |
68 | const uint8_t* isolate_snapshot_data = nullptr; |
69 | const uint8_t* isolate_snapshot_instructions = nullptr; |
70 | |
71 | // Global state that indicates whether a snapshot is to be created and |
72 | // if so which file to write the snapshot into. The ordering of this list must |
73 | // match kSnapshotKindNames below. |
74 | enum SnapshotKind { |
75 | kCore, |
76 | kCoreJIT, |
77 | kApp, |
78 | kAppJIT, |
79 | kAppAOTAssembly, |
80 | kAppAOTElf, |
81 | kVMAOTAssembly, |
82 | }; |
83 | static SnapshotKind snapshot_kind = kCore; |
84 | |
85 | // The ordering of this list must match the SnapshotKind enum above. |
86 | static const char* const kSnapshotKindNames[] = { |
87 | // clang-format off |
88 | "core" , |
89 | "core-jit" , |
90 | "app" , |
91 | "app-jit" , |
92 | "app-aot-assembly" , |
93 | "app-aot-elf" , |
94 | "vm-aot-assembly" , |
95 | nullptr, |
96 | // clang-format on |
97 | }; |
98 | |
99 | #define STRING_OPTIONS_LIST(V) \ |
100 | V(load_vm_snapshot_data, load_vm_snapshot_data_filename) \ |
101 | V(load_vm_snapshot_instructions, load_vm_snapshot_instructions_filename) \ |
102 | V(load_isolate_snapshot_data, load_isolate_snapshot_data_filename) \ |
103 | V(load_isolate_snapshot_instructions, \ |
104 | load_isolate_snapshot_instructions_filename) \ |
105 | V(vm_snapshot_data, vm_snapshot_data_filename) \ |
106 | V(vm_snapshot_instructions, vm_snapshot_instructions_filename) \ |
107 | V(isolate_snapshot_data, isolate_snapshot_data_filename) \ |
108 | V(isolate_snapshot_instructions, isolate_snapshot_instructions_filename) \ |
109 | V(blobs_container_filename, blobs_container_filename) \ |
110 | V(assembly, assembly_filename) \ |
111 | V(elf, elf_filename) \ |
112 | V(loading_unit_manifest, loading_unit_manifest_filename) \ |
113 | V(save_debugging_info, debugging_info_filename) \ |
114 | V(save_obfuscation_map, obfuscation_map_filename) |
115 | |
116 | #define BOOL_OPTIONS_LIST(V) \ |
117 | V(compile_all, compile_all) \ |
118 | V(help, help) \ |
119 | V(obfuscate, obfuscate) \ |
120 | V(strip, strip) \ |
121 | V(verbose, verbose) \ |
122 | V(version, version) |
123 | |
124 | #define STRING_OPTION_DEFINITION(flag, variable) \ |
125 | static const char* variable = nullptr; \ |
126 | DEFINE_STRING_OPTION(flag, variable) |
127 | STRING_OPTIONS_LIST(STRING_OPTION_DEFINITION) |
128 | #undef STRING_OPTION_DEFINITION |
129 | |
130 | #define BOOL_OPTION_DEFINITION(flag, variable) \ |
131 | static bool variable = false; \ |
132 | DEFINE_BOOL_OPTION(flag, variable) |
133 | BOOL_OPTIONS_LIST(BOOL_OPTION_DEFINITION) |
134 | #undef BOOL_OPTION_DEFINITION |
135 | |
136 | DEFINE_ENUM_OPTION(snapshot_kind, SnapshotKind, snapshot_kind); |
137 | DEFINE_CB_OPTION(ProcessEnvironmentOption); |
138 | |
139 | static bool IsSnapshottingForPrecompilation() { |
140 | return (snapshot_kind == kAppAOTAssembly) || (snapshot_kind == kAppAOTElf) || |
141 | (snapshot_kind == kVMAOTAssembly); |
142 | } |
143 | |
144 | // clang-format off |
145 | static void PrintUsage() { |
146 | Syslog::PrintErr( |
147 | format: "Usage: gen_snapshot [<vm-flags>] [<options>] <dart-kernel-file> \n" |
148 | " \n" |
149 | "Common options: \n" |
150 | "--help \n" |
151 | " Display this message (add --verbose for information about all VM options).\n" |
152 | "--version \n" |
153 | " Print the SDK version. \n" |
154 | " \n" |
155 | "To create a core snapshot: \n" |
156 | "--snapshot_kind=core \n" |
157 | "--vm_snapshot_data=<output-file> \n" |
158 | "--isolate_snapshot_data=<output-file> \n" |
159 | "<dart-kernel-file> \n" |
160 | " \n" |
161 | "To create an AOT application snapshot as assembly suitable for compilation \n" |
162 | "as a static or dynamic library: \n" |
163 | "--snapshot_kind=app-aot-assembly \n" |
164 | "--assembly=<output-file> \n" |
165 | "[--strip] \n" |
166 | "[--obfuscate] \n" |
167 | "[--save-debugging-info=<debug-filename>] \n" |
168 | "[--save-obfuscation-map=<map-filename>] \n" |
169 | "<dart-kernel-file> \n" |
170 | " \n" |
171 | "To create an AOT application snapshot as an ELF shared library: \n" |
172 | "--snapshot_kind=app-aot-elf \n" |
173 | "--elf=<output-file> \n" |
174 | "[--strip] \n" |
175 | "[--obfuscate] \n" |
176 | "[--save-debugging-info=<debug-filename>] \n" |
177 | "[--save-obfuscation-map=<map-filename>] \n" |
178 | "<dart-kernel-file> \n" |
179 | " \n" |
180 | "AOT snapshots can be obfuscated: that is all identifiers will be renamed \n" |
181 | "during compilation. This mode is enabled with --obfuscate flag. Mapping \n" |
182 | "between original and obfuscated names can be serialized as a JSON array \n" |
183 | "using --save-obfuscation-map=<filename> option. See dartbug.com/30524 \n" |
184 | "for implementation details and limitations of the obfuscation pass. \n" |
185 | " \n" |
186 | "\n" ); |
187 | if (verbose) { |
188 | Syslog::PrintErr( |
189 | format: "The following options are only used for VM development and may\n" |
190 | "be changed in any future version:\n" ); |
191 | const char* print_flags = "--print_flags" ; |
192 | char* error = Dart_SetVMFlags(argc: 1, argv: &print_flags); |
193 | ASSERT(error == nullptr); |
194 | } |
195 | } |
196 | // clang-format on |
197 | |
198 | // Parse out the command line arguments. Returns -1 if the arguments |
199 | // are incorrect, 0 otherwise. |
200 | static int ParseArguments(int argc, |
201 | char** argv, |
202 | CommandLineOptions* vm_options, |
203 | CommandLineOptions* inputs) { |
204 | // Skip the binary name. |
205 | int i = 1; |
206 | |
207 | // Parse out the vm options. |
208 | while ((i < argc) && OptionProcessor::IsValidShortFlag(name: argv[i])) { |
209 | if (OptionProcessor::TryProcess(option: argv[i], options: vm_options)) { |
210 | i += 1; |
211 | continue; |
212 | } |
213 | vm_options->AddArgument(argument: argv[i]); |
214 | i += 1; |
215 | } |
216 | |
217 | // Parse out the kernel inputs. |
218 | while (i < argc) { |
219 | inputs->AddArgument(argument: argv[i]); |
220 | i++; |
221 | } |
222 | |
223 | if (help) { |
224 | PrintUsage(); |
225 | Platform::Exit(exit_code: 0); |
226 | } else if (version) { |
227 | Syslog::PrintErr(format: "Dart SDK version: %s\n" , Dart_VersionString()); |
228 | Platform::Exit(exit_code: 0); |
229 | } |
230 | |
231 | // Verify consistency of arguments. |
232 | if (inputs->count() < 1) { |
233 | Syslog::PrintErr(format: "At least one input is required\n" ); |
234 | return -1; |
235 | } |
236 | |
237 | switch (snapshot_kind) { |
238 | case kCore: { |
239 | if ((vm_snapshot_data_filename == nullptr) || |
240 | (isolate_snapshot_data_filename == nullptr)) { |
241 | Syslog::PrintErr( |
242 | format: "Building a core snapshot requires specifying output files for " |
243 | "--vm_snapshot_data and --isolate_snapshot_data.\n\n" ); |
244 | return -1; |
245 | } |
246 | break; |
247 | } |
248 | case kCoreJIT: { |
249 | if ((vm_snapshot_data_filename == nullptr) || |
250 | (vm_snapshot_instructions_filename == nullptr) || |
251 | (isolate_snapshot_data_filename == nullptr) || |
252 | (isolate_snapshot_instructions_filename == nullptr)) { |
253 | Syslog::PrintErr( |
254 | format: "Building a core JIT snapshot requires specifying output " |
255 | "files for --vm_snapshot_data, --vm_snapshot_instructions, " |
256 | "--isolate_snapshot_data and --isolate_snapshot_instructions.\n\n" ); |
257 | return -1; |
258 | } |
259 | break; |
260 | } |
261 | case kApp: |
262 | case kAppJIT: { |
263 | if ((load_vm_snapshot_data_filename == nullptr) || |
264 | (isolate_snapshot_data_filename == nullptr) || |
265 | (isolate_snapshot_instructions_filename == nullptr)) { |
266 | Syslog::PrintErr( |
267 | format: "Building an app JIT snapshot requires specifying input files for " |
268 | "--load_vm_snapshot_data and --load_vm_snapshot_instructions, an " |
269 | " output file for --isolate_snapshot_data, and an output " |
270 | "file for --isolate_snapshot_instructions.\n\n" ); |
271 | return -1; |
272 | } |
273 | break; |
274 | } |
275 | case kAppAOTElf: { |
276 | if (elf_filename == nullptr) { |
277 | Syslog::PrintErr( |
278 | format: "Building an AOT snapshot as ELF requires specifying " |
279 | "an output file for --elf.\n\n" ); |
280 | return -1; |
281 | } |
282 | break; |
283 | } |
284 | case kAppAOTAssembly: |
285 | case kVMAOTAssembly: { |
286 | if (assembly_filename == nullptr) { |
287 | Syslog::PrintErr( |
288 | format: "Building an AOT snapshot as assembly requires specifying " |
289 | "an output file for --assembly.\n\n" ); |
290 | return -1; |
291 | } |
292 | break; |
293 | } |
294 | } |
295 | |
296 | if (!obfuscate && obfuscation_map_filename != nullptr) { |
297 | Syslog::PrintErr( |
298 | format: "--save-obfuscation_map=<...> should only be specified when " |
299 | "obfuscation is enabled by the --obfuscate flag.\n\n" ); |
300 | return -1; |
301 | } |
302 | |
303 | if (!IsSnapshottingForPrecompilation()) { |
304 | if (obfuscate) { |
305 | Syslog::PrintErr( |
306 | format: "Obfuscation can only be enabled when building an AOT snapshot.\n\n" ); |
307 | return -1; |
308 | } |
309 | |
310 | if (debugging_info_filename != nullptr) { |
311 | Syslog::PrintErr( |
312 | format: "--save-debugging-info=<...> can only be enabled when building an " |
313 | "AOT snapshot.\n\n" ); |
314 | return -1; |
315 | } |
316 | |
317 | if (strip) { |
318 | Syslog::PrintErr( |
319 | format: "Stripping can only be enabled when building an AOT snapshot.\n\n" ); |
320 | return -1; |
321 | } |
322 | } |
323 | |
324 | return 0; |
325 | } |
326 | |
327 | PRINTF_ATTRIBUTE(1, 2) static void PrintErrAndExit(const char* format, ...) { |
328 | va_list args; |
329 | va_start(args, format); |
330 | Syslog::VPrintErr(format, args); |
331 | va_end(args); |
332 | |
333 | Dart_ExitScope(); |
334 | Dart_ShutdownIsolate(); |
335 | exit(kErrorExitCode); |
336 | } |
337 | |
338 | static File* OpenFile(const char* filename) { |
339 | File* file = File::Open(namespc: nullptr, path: filename, mode: File::kWriteTruncate); |
340 | if (file == nullptr) { |
341 | PrintErrAndExit(format: "Error: Unable to write file: %s\n\n" , filename); |
342 | } |
343 | return file; |
344 | } |
345 | |
346 | static void WriteFile(const char* filename, |
347 | const uint8_t* buffer, |
348 | const intptr_t size) { |
349 | File* file = OpenFile(filename); |
350 | RefCntReleaseScope<File> rs(file); |
351 | if (!file->WriteFully(buffer, num_bytes: size)) { |
352 | PrintErrAndExit(format: "Error: Unable to write file: %s\n\n" , filename); |
353 | } |
354 | } |
355 | |
356 | static void ReadFile(const char* filename, uint8_t** buffer, intptr_t* size) { |
357 | File* file = File::Open(namespc: nullptr, path: filename, mode: File::kRead); |
358 | if (file == nullptr) { |
359 | PrintErrAndExit(format: "Error: Unable to read file: %s\n" , filename); |
360 | } |
361 | RefCntReleaseScope<File> rs(file); |
362 | *size = file->Length(); |
363 | *buffer = reinterpret_cast<uint8_t*>(malloc(size: *size)); |
364 | if (!file->ReadFully(buffer: *buffer, num_bytes: *size)) { |
365 | PrintErrAndExit(format: "Error: Unable to read file: %s\n" , filename); |
366 | } |
367 | } |
368 | |
369 | static void MallocFinalizer(void* isolate_callback_data, void* peer) { |
370 | free(peer); |
371 | } |
372 | |
373 | static void MaybeLoadExtraInputs(const CommandLineOptions& inputs) { |
374 | for (intptr_t i = 1; i < inputs.count(); i++) { |
375 | uint8_t* buffer = nullptr; |
376 | intptr_t size = 0; |
377 | ReadFile(filename: inputs.GetArgument(index: i), buffer: &buffer, size: &size); |
378 | Dart_Handle td = Dart_NewExternalTypedDataWithFinalizer( |
379 | Dart_TypedData_kUint8, buffer, size, buffer, size, MallocFinalizer); |
380 | CHECK_RESULT(td); |
381 | Dart_Handle result = Dart_LoadLibrary(kernel_buffer: td); |
382 | CHECK_RESULT(result); |
383 | } |
384 | } |
385 | |
386 | static void MaybeLoadCode() { |
387 | if (compile_all && |
388 | ((snapshot_kind == kCoreJIT) || (snapshot_kind == kAppJIT))) { |
389 | Dart_Handle result = Dart_CompileAll(); |
390 | CHECK_RESULT(result); |
391 | } |
392 | } |
393 | |
394 | static void CreateAndWriteCoreSnapshot() { |
395 | ASSERT(snapshot_kind == kCore); |
396 | ASSERT(vm_snapshot_data_filename != nullptr); |
397 | ASSERT(isolate_snapshot_data_filename != nullptr); |
398 | |
399 | Dart_Handle result; |
400 | uint8_t* vm_snapshot_data_buffer = nullptr; |
401 | intptr_t vm_snapshot_data_size = 0; |
402 | uint8_t* isolate_snapshot_data_buffer = nullptr; |
403 | intptr_t isolate_snapshot_data_size = 0; |
404 | |
405 | // First create a snapshot. |
406 | result = Dart_CreateSnapshot(&vm_snapshot_data_buffer, &vm_snapshot_data_size, |
407 | &isolate_snapshot_data_buffer, |
408 | &isolate_snapshot_data_size, |
409 | /*is_core=*/true); |
410 | CHECK_RESULT(result); |
411 | |
412 | // Now write the vm isolate and isolate snapshots out to the |
413 | // specified file and exit. |
414 | WriteFile(filename: vm_snapshot_data_filename, buffer: vm_snapshot_data_buffer, |
415 | size: vm_snapshot_data_size); |
416 | if (vm_snapshot_instructions_filename != nullptr) { |
417 | // Create empty file for the convenience of build systems. Makes things |
418 | // polymorphic with generating core-jit snapshots. |
419 | WriteFile(filename: vm_snapshot_instructions_filename, buffer: nullptr, size: 0); |
420 | } |
421 | WriteFile(filename: isolate_snapshot_data_filename, buffer: isolate_snapshot_data_buffer, |
422 | size: isolate_snapshot_data_size); |
423 | if (isolate_snapshot_instructions_filename != nullptr) { |
424 | // Create empty file for the convenience of build systems. Makes things |
425 | // polymorphic with generating core-jit snapshots. |
426 | WriteFile(filename: isolate_snapshot_instructions_filename, buffer: nullptr, size: 0); |
427 | } |
428 | } |
429 | |
430 | static std::unique_ptr<MappedMemory> MapFile(const char* filename, |
431 | File::MapType type, |
432 | const uint8_t** buffer) { |
433 | File* file = File::Open(namespc: nullptr, path: filename, mode: File::kRead); |
434 | if (file == nullptr) { |
435 | Syslog::PrintErr(format: "Failed to open: %s\n" , filename); |
436 | exit(kErrorExitCode); |
437 | } |
438 | RefCntReleaseScope<File> rs(file); |
439 | intptr_t length = file->Length(); |
440 | if (length == 0) { |
441 | // Can't map an empty file. |
442 | *buffer = nullptr; |
443 | return nullptr; |
444 | } |
445 | MappedMemory* mapping = file->Map(type, position: 0, length); |
446 | if (mapping == nullptr) { |
447 | Syslog::PrintErr(format: "Failed to read: %s\n" , filename); |
448 | exit(kErrorExitCode); |
449 | } |
450 | *buffer = reinterpret_cast<const uint8_t*>(mapping->address()); |
451 | return std::unique_ptr<MappedMemory>(mapping); |
452 | } |
453 | |
454 | static void CreateAndWriteCoreJITSnapshot() { |
455 | ASSERT(snapshot_kind == kCoreJIT); |
456 | ASSERT(vm_snapshot_data_filename != nullptr); |
457 | ASSERT(vm_snapshot_instructions_filename != nullptr); |
458 | ASSERT(isolate_snapshot_data_filename != nullptr); |
459 | ASSERT(isolate_snapshot_instructions_filename != nullptr); |
460 | |
461 | Dart_Handle result; |
462 | uint8_t* vm_snapshot_data_buffer = nullptr; |
463 | intptr_t vm_snapshot_data_size = 0; |
464 | uint8_t* vm_snapshot_instructions_buffer = nullptr; |
465 | intptr_t vm_snapshot_instructions_size = 0; |
466 | uint8_t* isolate_snapshot_data_buffer = nullptr; |
467 | intptr_t isolate_snapshot_data_size = 0; |
468 | uint8_t* isolate_snapshot_instructions_buffer = nullptr; |
469 | intptr_t isolate_snapshot_instructions_size = 0; |
470 | |
471 | // First create a snapshot. |
472 | result = Dart_CreateCoreJITSnapshotAsBlobs( |
473 | &vm_snapshot_data_buffer, &vm_snapshot_data_size, |
474 | &vm_snapshot_instructions_buffer, &vm_snapshot_instructions_size, |
475 | &isolate_snapshot_data_buffer, &isolate_snapshot_data_size, |
476 | &isolate_snapshot_instructions_buffer, |
477 | &isolate_snapshot_instructions_size); |
478 | CHECK_RESULT(result); |
479 | |
480 | // Now write the vm isolate and isolate snapshots out to the |
481 | // specified file and exit. |
482 | WriteFile(filename: vm_snapshot_data_filename, buffer: vm_snapshot_data_buffer, |
483 | size: vm_snapshot_data_size); |
484 | WriteFile(filename: vm_snapshot_instructions_filename, buffer: vm_snapshot_instructions_buffer, |
485 | size: vm_snapshot_instructions_size); |
486 | WriteFile(filename: isolate_snapshot_data_filename, buffer: isolate_snapshot_data_buffer, |
487 | size: isolate_snapshot_data_size); |
488 | WriteFile(filename: isolate_snapshot_instructions_filename, |
489 | buffer: isolate_snapshot_instructions_buffer, |
490 | size: isolate_snapshot_instructions_size); |
491 | } |
492 | |
493 | static void CreateAndWriteAppSnapshot() { |
494 | ASSERT(snapshot_kind == kApp); |
495 | ASSERT(isolate_snapshot_data_filename != nullptr); |
496 | |
497 | Dart_Handle result; |
498 | uint8_t* isolate_snapshot_data_buffer = nullptr; |
499 | intptr_t isolate_snapshot_data_size = 0; |
500 | |
501 | result = Dart_CreateSnapshot(nullptr, nullptr, &isolate_snapshot_data_buffer, |
502 | &isolate_snapshot_data_size, /*is_core=*/false); |
503 | CHECK_RESULT(result); |
504 | |
505 | WriteFile(filename: isolate_snapshot_data_filename, buffer: isolate_snapshot_data_buffer, |
506 | size: isolate_snapshot_data_size); |
507 | if (isolate_snapshot_instructions_filename != nullptr) { |
508 | // Create empty file for the convenience of build systems. Makes things |
509 | // polymorphic with generating core-jit snapshots. |
510 | WriteFile(filename: isolate_snapshot_instructions_filename, buffer: nullptr, size: 0); |
511 | } |
512 | } |
513 | |
514 | static void CreateAndWriteAppJITSnapshot() { |
515 | ASSERT(snapshot_kind == kAppJIT); |
516 | ASSERT(isolate_snapshot_data_filename != nullptr); |
517 | ASSERT(isolate_snapshot_instructions_filename != nullptr); |
518 | |
519 | Dart_Handle result; |
520 | uint8_t* isolate_snapshot_data_buffer = nullptr; |
521 | intptr_t isolate_snapshot_data_size = 0; |
522 | uint8_t* isolate_snapshot_instructions_buffer = nullptr; |
523 | intptr_t isolate_snapshot_instructions_size = 0; |
524 | |
525 | result = Dart_CreateAppJITSnapshotAsBlobs( |
526 | &isolate_snapshot_data_buffer, &isolate_snapshot_data_size, |
527 | &isolate_snapshot_instructions_buffer, |
528 | &isolate_snapshot_instructions_size); |
529 | CHECK_RESULT(result); |
530 | |
531 | WriteFile(filename: isolate_snapshot_data_filename, buffer: isolate_snapshot_data_buffer, |
532 | size: isolate_snapshot_data_size); |
533 | WriteFile(filename: isolate_snapshot_instructions_filename, |
534 | buffer: isolate_snapshot_instructions_buffer, |
535 | size: isolate_snapshot_instructions_size); |
536 | } |
537 | |
538 | static void StreamingWriteCallback(void* callback_data, |
539 | const uint8_t* buffer, |
540 | intptr_t size) { |
541 | File* file = reinterpret_cast<File*>(callback_data); |
542 | if ((file != nullptr) && !file->WriteFully(buffer, num_bytes: size)) { |
543 | PrintErrAndExit(format: "Error: Unable to write snapshot file\n\n" ); |
544 | } |
545 | } |
546 | |
547 | static void StreamingCloseCallback(void* callback_data) { |
548 | File* file = reinterpret_cast<File*>(callback_data); |
549 | file->Release(); |
550 | } |
551 | |
552 | static File* OpenLoadingUnitManifest() { |
553 | File* manifest_file = OpenFile(filename: loading_unit_manifest_filename); |
554 | if (!manifest_file->Print(format: "{ \"loadingUnits\": [\n" )) { |
555 | PrintErrAndExit(format: "Error: Unable to write file: %s\n\n" , |
556 | loading_unit_manifest_filename); |
557 | } |
558 | return manifest_file; |
559 | } |
560 | |
561 | static void WriteLoadingUnitManifest(File* manifest_file, |
562 | intptr_t id, |
563 | const char* path) { |
564 | TextBuffer line(128); |
565 | if (id != 1) { |
566 | line.AddString(s: ",\n" ); |
567 | } |
568 | line.Printf("{ \"id\": %" Pd ", \"path\": \"" , id); |
569 | line.AddEscapedString(s: path); |
570 | line.AddString(s: "\", \"libraries\": [\n" ); |
571 | Dart_Handle uris = Dart_LoadingUnitLibraryUris(id); |
572 | CHECK_RESULT(uris); |
573 | intptr_t length; |
574 | CHECK_RESULT(Dart_ListLength(uris, &length)); |
575 | for (intptr_t i = 0; i < length; i++) { |
576 | const char* uri; |
577 | CHECK_RESULT(Dart_StringToCString(Dart_ListGetAt(uris, i), &uri)); |
578 | if (i != 0) { |
579 | line.AddString(s: ",\n" ); |
580 | } |
581 | line.AddString(s: "\"" ); |
582 | line.AddEscapedString(s: uri); |
583 | line.AddString(s: "\"" ); |
584 | } |
585 | line.AddString(s: "]}" ); |
586 | if (!manifest_file->Print(format: "%s\n" , line.buffer())) { |
587 | PrintErrAndExit(format: "Error: Unable to write file: %s\n\n" , |
588 | loading_unit_manifest_filename); |
589 | } |
590 | } |
591 | |
592 | static void CloseLoadingUnitManifest(File* manifest_file) { |
593 | if (!manifest_file->Print(format: "] }\n" )) { |
594 | PrintErrAndExit(format: "Error: Unable to write file: %s\n\n" , |
595 | loading_unit_manifest_filename); |
596 | } |
597 | manifest_file->Release(); |
598 | } |
599 | |
600 | static void NextLoadingUnit(void* callback_data, |
601 | intptr_t loading_unit_id, |
602 | void** write_callback_data, |
603 | void** write_debug_callback_data, |
604 | const char* main_filename, |
605 | const char* suffix) { |
606 | char* filename = loading_unit_id == 1 |
607 | ? Utils::StrDup(main_filename) |
608 | : Utils::SCreate("%s-%" Pd ".part.%s" , main_filename, |
609 | loading_unit_id, suffix); |
610 | File* file = OpenFile(filename); |
611 | *write_callback_data = file; |
612 | |
613 | if (debugging_info_filename != nullptr) { |
614 | char* debug_filename = |
615 | loading_unit_id == 1 |
616 | ? Utils::StrDup(debugging_info_filename) |
617 | : Utils::SCreate("%s-%" Pd ".part.so" , debugging_info_filename, |
618 | loading_unit_id); |
619 | File* debug_file = OpenFile(filename: debug_filename); |
620 | *write_debug_callback_data = debug_file; |
621 | free(debug_filename); |
622 | } |
623 | |
624 | WriteLoadingUnitManifest(manifest_file: reinterpret_cast<File*>(callback_data), |
625 | id: loading_unit_id, path: filename); |
626 | |
627 | free(filename); |
628 | } |
629 | |
630 | static void NextAsmCallback(void* callback_data, |
631 | intptr_t loading_unit_id, |
632 | void** write_callback_data, |
633 | void** write_debug_callback_data) { |
634 | NextLoadingUnit(callback_data, loading_unit_id, write_callback_data, |
635 | write_debug_callback_data, main_filename: assembly_filename, suffix: "S" ); |
636 | } |
637 | |
638 | static void NextElfCallback(void* callback_data, |
639 | intptr_t loading_unit_id, |
640 | void** write_callback_data, |
641 | void** write_debug_callback_data) { |
642 | NextLoadingUnit(callback_data, loading_unit_id, write_callback_data, |
643 | write_debug_callback_data, main_filename: elf_filename, suffix: "so" ); |
644 | } |
645 | |
646 | static void CreateAndWritePrecompiledSnapshot() { |
647 | ASSERT(IsSnapshottingForPrecompilation()); |
648 | Dart_Handle result; |
649 | |
650 | // Precompile with specified embedder entry points |
651 | result = Dart_Precompile(); |
652 | CHECK_RESULT(result); |
653 | |
654 | // Create a precompiled snapshot. |
655 | if (snapshot_kind == kAppAOTAssembly) { |
656 | if (strip && (debugging_info_filename == nullptr)) { |
657 | Syslog::PrintErr( |
658 | format: "Warning: Generating assembly code without DWARF debugging" |
659 | " information.\n" ); |
660 | } |
661 | if (loading_unit_manifest_filename == nullptr) { |
662 | File* file = OpenFile(filename: assembly_filename); |
663 | RefCntReleaseScope<File> rs(file); |
664 | File* debug_file = nullptr; |
665 | if (debugging_info_filename != nullptr) { |
666 | debug_file = OpenFile(filename: debugging_info_filename); |
667 | } |
668 | result = Dart_CreateAppAOTSnapshotAsAssembly(StreamingWriteCallback, file, |
669 | strip, debug_file); |
670 | if (debug_file != nullptr) debug_file->Release(); |
671 | CHECK_RESULT(result); |
672 | } else { |
673 | File* manifest_file = OpenLoadingUnitManifest(); |
674 | result = Dart_CreateAppAOTSnapshotAsAssemblies( |
675 | NextAsmCallback, manifest_file, strip, StreamingWriteCallback, |
676 | StreamingCloseCallback); |
677 | CHECK_RESULT(result); |
678 | CloseLoadingUnitManifest(manifest_file); |
679 | } |
680 | if (obfuscate && !strip) { |
681 | Syslog::PrintErr( |
682 | format: "Warning: The generated assembly code contains unobfuscated DWARF " |
683 | "debugging information.\n" |
684 | " To avoid this, use --strip to remove it.\n" ); |
685 | } |
686 | } else if (snapshot_kind == kAppAOTElf) { |
687 | if (strip && (debugging_info_filename == nullptr)) { |
688 | Syslog::PrintErr( |
689 | format: "Warning: Generating ELF library without DWARF debugging" |
690 | " information.\n" ); |
691 | } |
692 | if (loading_unit_manifest_filename == nullptr) { |
693 | File* file = OpenFile(filename: elf_filename); |
694 | RefCntReleaseScope<File> rs(file); |
695 | File* debug_file = nullptr; |
696 | if (debugging_info_filename != nullptr) { |
697 | debug_file = OpenFile(filename: debugging_info_filename); |
698 | } |
699 | result = Dart_CreateAppAOTSnapshotAsElf(StreamingWriteCallback, file, |
700 | strip, debug_file); |
701 | if (debug_file != nullptr) debug_file->Release(); |
702 | CHECK_RESULT(result); |
703 | } else { |
704 | File* manifest_file = OpenLoadingUnitManifest(); |
705 | result = Dart_CreateAppAOTSnapshotAsElfs(NextElfCallback, manifest_file, |
706 | strip, StreamingWriteCallback, |
707 | StreamingCloseCallback); |
708 | CHECK_RESULT(result); |
709 | CloseLoadingUnitManifest(manifest_file); |
710 | } |
711 | if (obfuscate && !strip) { |
712 | Syslog::PrintErr( |
713 | format: "Warning: The generated ELF library contains unobfuscated DWARF " |
714 | "debugging information.\n" |
715 | " To avoid this, use --strip to remove it and " |
716 | "--save-debugging-info=<...> to save it to a separate file.\n" ); |
717 | } |
718 | } else { |
719 | UNREACHABLE(); |
720 | } |
721 | |
722 | // Serialize obfuscation map if requested. |
723 | if (obfuscation_map_filename != nullptr) { |
724 | ASSERT(obfuscate); |
725 | uint8_t* buffer = nullptr; |
726 | intptr_t size = 0; |
727 | result = Dart_GetObfuscationMap(&buffer, &size); |
728 | CHECK_RESULT(result); |
729 | WriteFile(filename: obfuscation_map_filename, buffer, size); |
730 | } |
731 | } |
732 | |
733 | static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) { |
734 | uint8_t* kernel_buffer = nullptr; |
735 | intptr_t kernel_buffer_size = 0; |
736 | ReadFile(filename: inputs.GetArgument(index: 0), buffer: &kernel_buffer, size: &kernel_buffer_size); |
737 | |
738 | Dart_IsolateFlags isolate_flags; |
739 | Dart_IsolateFlagsInitialize(flags: &isolate_flags); |
740 | isolate_flags.null_safety = |
741 | Dart_DetectNullSafety(nullptr, nullptr, nullptr, nullptr, nullptr, |
742 | kernel_buffer, kernel_buffer_size); |
743 | if (IsSnapshottingForPrecompilation()) { |
744 | isolate_flags.obfuscate = obfuscate; |
745 | } |
746 | |
747 | auto isolate_group_data = std::unique_ptr<IsolateGroupData>( |
748 | new IsolateGroupData(nullptr, nullptr, nullptr, false)); |
749 | Dart_Isolate isolate; |
750 | char* error = nullptr; |
751 | |
752 | bool loading_kernel_failed = false; |
753 | if (isolate_snapshot_data == nullptr) { |
754 | // We need to capture the vmservice library in the core snapshot, so load it |
755 | // in the main isolate as well. |
756 | isolate_flags.load_vmservice_library = true; |
757 | isolate = Dart_CreateIsolateGroupFromKernel( |
758 | nullptr, nullptr, kernel_buffer, kernel_buffer_size, &isolate_flags, |
759 | isolate_group_data.get(), /*isolate_data=*/nullptr, &error); |
760 | loading_kernel_failed = (isolate == nullptr); |
761 | } else { |
762 | isolate = Dart_CreateIsolateGroup(nullptr, nullptr, isolate_snapshot_data, |
763 | isolate_snapshot_instructions, |
764 | &isolate_flags, isolate_group_data.get(), |
765 | /*isolate_data=*/nullptr, &error); |
766 | } |
767 | if (isolate == nullptr) { |
768 | Syslog::PrintErr(format: "%s\n" , error); |
769 | free(error); |
770 | free(kernel_buffer); |
771 | // The only real reason when `gen_snapshot` fails to create an isolate from |
772 | // a valid kernel file is if loading the kernel results in a "compile-time" |
773 | // error. |
774 | // |
775 | // There are other possible reasons, like memory allocation failures, but |
776 | // those are very uncommon. |
777 | // |
778 | // The Dart API doesn't allow us to distinguish the different error cases, |
779 | // so we'll use [kCompilationErrorExitCode] for failed kernel loading, since |
780 | // a compile-time error is the most probable cause. |
781 | return loading_kernel_failed ? kCompilationErrorExitCode : kErrorExitCode; |
782 | } |
783 | |
784 | Dart_EnterScope(); |
785 | Dart_Handle result = |
786 | Dart_SetEnvironmentCallback(callback: DartUtils::EnvironmentCallback); |
787 | CHECK_RESULT(result); |
788 | |
789 | // The root library has to be set to generate AOT snapshots, and sometimes we |
790 | // set one for the core snapshot too. |
791 | // If the input dill file has a root library, then Dart_LoadScript will |
792 | // ignore this dummy uri and set the root library to the one reported in |
793 | // the dill file. Since dill files are not dart script files, |
794 | // trying to resolve the root library URI based on the dill file name |
795 | // would not help. |
796 | // |
797 | // If the input dill file does not have a root library, then |
798 | // Dart_LoadScript will error. |
799 | // |
800 | // TODO(kernel): Dart_CreateIsolateGroupFromKernel should respect the root |
801 | // library in the kernel file, though this requires auditing the other |
802 | // loading paths in the embedders that had to work around this. |
803 | result = Dart_SetRootLibrary( |
804 | Dart_LoadLibraryFromKernel(kernel_buffer, kernel_buffer_size)); |
805 | CHECK_RESULT(result); |
806 | |
807 | MaybeLoadExtraInputs(inputs); |
808 | |
809 | MaybeLoadCode(); |
810 | |
811 | switch (snapshot_kind) { |
812 | case kCore: |
813 | CreateAndWriteCoreSnapshot(); |
814 | break; |
815 | case kCoreJIT: |
816 | CreateAndWriteCoreJITSnapshot(); |
817 | break; |
818 | case kApp: |
819 | CreateAndWriteAppSnapshot(); |
820 | break; |
821 | case kAppJIT: |
822 | CreateAndWriteAppJITSnapshot(); |
823 | break; |
824 | case kAppAOTAssembly: |
825 | case kAppAOTElf: |
826 | CreateAndWritePrecompiledSnapshot(); |
827 | break; |
828 | case kVMAOTAssembly: { |
829 | File* file = OpenFile(filename: assembly_filename); |
830 | RefCntReleaseScope<File> rs(file); |
831 | result = Dart_CreateVMAOTSnapshotAsAssembly(StreamingWriteCallback, file); |
832 | CHECK_RESULT(result); |
833 | break; |
834 | } |
835 | default: |
836 | UNREACHABLE(); |
837 | } |
838 | |
839 | Dart_ExitScope(); |
840 | Dart_ShutdownIsolate(); |
841 | |
842 | free(kernel_buffer); |
843 | return 0; |
844 | } |
845 | |
846 | int main(int argc, char** argv) { |
847 | const int = 7; |
848 | CommandLineOptions vm_options(argc + EXTRA_VM_ARGUMENTS); |
849 | CommandLineOptions inputs(argc); |
850 | |
851 | // When running from the command line we assume that we are optimizing for |
852 | // throughput, and therefore use a larger new gen semi space size and a faster |
853 | // new gen growth factor unless others have been specified. |
854 | if (kWordSize <= 4) { |
855 | vm_options.AddArgument(argument: "--new_gen_semi_max_size=16" ); |
856 | } else { |
857 | vm_options.AddArgument(argument: "--new_gen_semi_max_size=32" ); |
858 | } |
859 | vm_options.AddArgument(argument: "--new_gen_growth_factor=4" ); |
860 | vm_options.AddArgument(argument: "--deterministic" ); |
861 | |
862 | // Parse command line arguments. |
863 | if (ParseArguments(argc, argv, vm_options: &vm_options, inputs: &inputs) < 0) { |
864 | PrintUsage(); |
865 | return kErrorExitCode; |
866 | } |
867 | DartUtils::SetEnvironment(environment); |
868 | |
869 | if (!Platform::Initialize()) { |
870 | Syslog::PrintErr(format: "Initialization failed\n" ); |
871 | return kErrorExitCode; |
872 | } |
873 | Console::SaveConfig(); |
874 | Loader::InitOnce(); |
875 | DartUtils::SetOriginalWorkingDirectory(); |
876 | // Start event handler. |
877 | TimerUtils::InitOnce(); |
878 | EventHandler::Start(); |
879 | |
880 | if (IsSnapshottingForPrecompilation()) { |
881 | vm_options.AddArgument(argument: "--precompilation" ); |
882 | } else if ((snapshot_kind == kCoreJIT) || (snapshot_kind == kAppJIT)) { |
883 | // Core-jit and app-jit snapshot can be deployed to another machine, |
884 | // so generated code should not depend on the CPU features |
885 | // of the system where snapshot was generated. |
886 | vm_options.AddArgument(argument: "--target-unknown-cpu" ); |
887 | #if !defined(TARGET_ARCH_IA32) |
888 | vm_options.AddArgument(argument: "--link_natives_lazily" ); |
889 | #endif |
890 | } |
891 | |
892 | char* error = Dart_SetVMFlags(argc: vm_options.count(), argv: vm_options.arguments()); |
893 | if (error != nullptr) { |
894 | Syslog::PrintErr(format: "Setting VM flags failed: %s\n" , error); |
895 | free(error); |
896 | return kErrorExitCode; |
897 | } |
898 | |
899 | Dart_InitializeParams init_params; |
900 | memset(&init_params, 0, sizeof(init_params)); |
901 | init_params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; |
902 | init_params.file_open = DartUtils::OpenFile; |
903 | init_params.file_read = DartUtils::ReadFile; |
904 | init_params.file_write = DartUtils::WriteFile; |
905 | init_params.file_close = DartUtils::CloseFile; |
906 | init_params.entropy_source = DartUtils::EntropySource; |
907 | init_params.start_kernel_isolate = false; |
908 | #if defined(DART_HOST_OS_FUCHSIA) |
909 | init_params.vmex_resource = Platform::GetVMEXResource(); |
910 | #endif |
911 | |
912 | std::unique_ptr<MappedMemory> mapped_vm_snapshot_data; |
913 | std::unique_ptr<MappedMemory> mapped_vm_snapshot_instructions; |
914 | std::unique_ptr<MappedMemory> mapped_isolate_snapshot_data; |
915 | std::unique_ptr<MappedMemory> mapped_isolate_snapshot_instructions; |
916 | if (load_vm_snapshot_data_filename != nullptr) { |
917 | mapped_vm_snapshot_data = |
918 | MapFile(load_vm_snapshot_data_filename, File::kReadOnly, |
919 | &init_params.vm_snapshot_data); |
920 | } |
921 | if (load_vm_snapshot_instructions_filename != nullptr) { |
922 | mapped_vm_snapshot_instructions = |
923 | MapFile(load_vm_snapshot_instructions_filename, File::kReadExecute, |
924 | &init_params.vm_snapshot_instructions); |
925 | } |
926 | if (load_isolate_snapshot_data_filename != nullptr) { |
927 | mapped_isolate_snapshot_data = |
928 | MapFile(load_isolate_snapshot_data_filename, File::kReadOnly, |
929 | &isolate_snapshot_data); |
930 | } |
931 | if (load_isolate_snapshot_instructions_filename != nullptr) { |
932 | mapped_isolate_snapshot_instructions = |
933 | MapFile(load_isolate_snapshot_instructions_filename, File::kReadExecute, |
934 | &isolate_snapshot_instructions); |
935 | } |
936 | |
937 | error = Dart_Initialize(params: &init_params); |
938 | if (error != nullptr) { |
939 | Syslog::PrintErr(format: "VM initialization failed: %s\n" , error); |
940 | free(error); |
941 | return kErrorExitCode; |
942 | } |
943 | |
944 | int result = CreateIsolateAndSnapshot(inputs); |
945 | if (result != 0) { |
946 | return result; |
947 | } |
948 | |
949 | error = Dart_Cleanup(); |
950 | if (error != nullptr) { |
951 | Syslog::PrintErr(format: "VM cleanup failed: %s\n" , error); |
952 | free(error); |
953 | } |
954 | EventHandler::Stop(); |
955 | return 0; |
956 | } |
957 | |
958 | } // namespace bin |
959 | } // namespace dart |
960 | |
961 | int main(int argc, char** argv) { |
962 | return dart::bin::main(argc, argv); |
963 | } |
964 | |