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