Index: runtime/bin/gen_snapshot.cc |
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc |
index bb6d4f8502e425ff0748b4f84f39ead591311132..1d696ba50969b3da91ec7bc5e09452dd3f727f95 100644 |
--- a/runtime/bin/gen_snapshot.cc |
+++ b/runtime/bin/gen_snapshot.cc |
@@ -38,6 +38,8 @@ namespace bin { |
// if so which file to write the snapshot into. |
static const char* vm_isolate_snapshot_filename = NULL; |
static const char* isolate_snapshot_filename = NULL; |
+static const char* instructions_snapshot_filename = NULL; |
+static const char* vm_entry_points_manifest = NULL; |
rmacnak
2015/10/21 23:06:18
Throughout: s/vm_entry_points/embedder_entry_point
Chinmay
2015/10/22 00:13:16
ack
|
static const char* package_root = NULL; |
@@ -88,6 +90,26 @@ static bool ProcessIsolateSnapshotOption(const char* option) { |
} |
+static bool ProcessInstructionsSnapshotOption(const char* option) { |
+ const char* name = ProcessOption(option, "--instructions_snapshot="); |
+ if (name != NULL) { |
+ instructions_snapshot_filename = name; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+ |
+static bool ProcessVMEntryPointsManifestOption(const char* option) { |
+ const char* name = ProcessOption(option, "--vm_entry_points_manifest="); |
+ if (name != NULL) { |
+ vm_entry_points_manifest = name; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+ |
static bool ProcessPackageRootOption(const char* option) { |
const char* name = ProcessOption(option, "--package_root="); |
if (name != NULL) { |
@@ -127,6 +149,8 @@ static int ParseArguments(int argc, |
while ((i < argc) && IsValidFlag(argv[i], kPrefix, kPrefixLen)) { |
if (ProcessVmIsolateSnapshotOption(argv[i]) || |
ProcessIsolateSnapshotOption(argv[i]) || |
+ ProcessInstructionsSnapshotOption(argv[i]) || |
+ ProcessVMEntryPointsManifestOption(argv[i]) || |
ProcessURLmappingOption(argv[i]) || |
ProcessPackageRootOption(argv[i])) { |
i += 1; |
@@ -154,10 +178,32 @@ static int ParseArguments(int argc, |
return -1; |
} |
+ if (instructions_snapshot_filename != NULL && |
+ vm_entry_points_manifest == NULL) { |
+ Log::PrintErr( |
+ "Specifying an instructions snapshot filename indicates " |
+ "precompilation. But no VM entry points manifest was specified.\n\n"); |
+ return -1; |
+ } |
+ |
+ if (vm_entry_points_manifest != NULL && |
+ instructions_snapshot_filename == NULL) { |
+ Log::PrintErr( |
+ "Specifying the VM entry points manifest indicates precompilation. But " |
+ "no instuctions snapshot was specified.\n\n"); |
+ return -1; |
+ } |
+ |
return 0; |
} |
+static bool IsSnapshottingForPrecompilation(void) { |
+ return vm_entry_points_manifest != NULL && |
+ instructions_snapshot_filename != NULL; |
+} |
+ |
+ |
static void WriteSnapshotFile(const char* filename, |
const uint8_t* buffer, |
const intptr_t size) { |
@@ -420,27 +466,54 @@ static Dart_Handle LoadGenericSnapshotCreationScript( |
static void PrintUsage() { |
- Log::PrintErr( |
-"Usage:\n" |
-"\n" |
-" gen_snapshot [<vm-flags>] [<options>] \\\n" |
-" --snapshot=<out-file> [<dart-script-file>]\n" |
-"\n" |
-" Writes a snapshot of <dart-script-file> to <out-file>. If no\n" |
-" <dart-script-file> is passed, a generic snapshot of all the corelibs is\n" |
-" created. It is required to specify an output file name:\n" |
-"\n" |
-" --snapshot=<file> Generates a complete snapshot. Uses the url\n" |
-" mapping specified on the command line to load\n" |
-" the libraries.\n" |
-"Supported options:\n" |
-"\n" |
-"--package_root=<path>\n" |
-" Where to find packages, that is, \"package:...\" imports.\n" |
-"\n" |
-"--url_mapping=<mapping>\n" |
-" Uses the URL mapping(s) specified on the command line to load the\n" |
-" libraries. For use only with --snapshot=.\n"); |
+#define STRINGERIZE(...) #__VA_ARGS__ |
+ // clang-format off |
+ Log::PrintErr(STRINGERIZE( |
+Usage: \n |
+ gen_snapshot [<vm-flags>] [<options>] [<dart-script-file>] \n |
+ \n |
+ Writes a snapshot of <dart-script-file> to the specified snapshot files. If \n |
+ no <dart-script-file> is passed, a generic snapshot of all the corelibs is \n |
+ created. It is required to specify the VM isolate snapshot and the isolate \n |
+ snapshot. The other flags are related to precompilation and are optional. \n |
+ \n |
+ Precompilation: \n |
+ In order to configure the snapshotter for precompilation, both the \n |
+ instructions snapshot and the VM entry points manifest must be specified. \n |
+ Machine code for the target architecture will be dumped into the \n |
+ instructions snapshot. This must be linked into the target binary in a \n |
+ separate step. The VM entry points manifest lists the standalone entry \n |
+ points into the VM. Not specifying these will cause the tree shaker to \n |
+ disregard the same as being used. The format of this manifest is as follows.\n |
+ Each line in the manifest is a comma separated list of three elements. The \n |
+ first entry is the library URI, the second entry is the class name and the \n |
+ final entry the function name. The file must be terminated with a newline \n |
+ charater. \n |
+ \n |
+ Example: \n |
+ dart:something,SomeClass,doSomething \n |
+ \n |
+ Supported options: \n |
+ --vm_isolate_snapshot=<file> A full snapshot is a compact \n |
+ --isolate_snapshot=<file> representation of the dart vm isolate \n |
+ heap and dart isolate heap states. \n |
+ Both these options are required \n |
+ \n |
+ --package_root=<path> Where to find packages, that is, \n |
+ package:... imports. \n |
+ \n |
+ --url_mapping=<mapping> Uses the URL mapping(s) specified on the\n |
+ command line to load the libraries. \n |
+ \n |
+ --instructions_snapshot=<file> (Precompilation only) Contains the \n |
+ machine code that must be linked into \n |
rmacnak
2015/10/21 23:06:18
s/machine code/assembly
Chinmay
2015/10/22 00:13:16
ack
|
+ the target binary \n |
+ \n |
+ --vm_entry_points_manifest=<file> (Precompilation only) Contains the \n |
+ stanalone VM entry points \n |
+ )); |
+ // clang-format on |
+#undef STRINGERIZE |
} |
@@ -456,6 +529,268 @@ static void VerifyLoaded(Dart_Handle library) { |
} |
+static const char StubNativeFunctionName[] = "StubNativeFunction"; |
+ |
+ |
+void StubNativeFunction(Dart_NativeArguments arguments) { |
+ // This is a stub function for the resolver |
+ ASSERT(false); |
rmacnak
2015/10/21 23:06:18
UNREACHABLE();
Chinmay
2015/10/22 00:13:16
ack
|
+} |
+ |
+ |
+static Dart_NativeFunction StubNativeLookup(Dart_Handle name, |
+ int argument_count, |
Cutch
2015/10/21 22:00:08
align arguments
Chinmay
2015/10/22 00:13:17
ack
|
+ bool* auto_setup_scope) { |
+ return &StubNativeFunction; |
+} |
+ |
+ |
+static const uint8_t* StubNativeSymbol(Dart_NativeFunction nf) { |
+ return reinterpret_cast<const uint8_t *>(StubNativeFunctionName); |
+} |
+ |
+ |
+static void SetupStubNativeResolver(size_t lib_index, |
rmacnak
2015/10/21 23:06:18
TODO(24686): Remove this.
Chinmay
2015/10/22 00:13:16
ack
|
+ const Dart_QualifiedFunctionName* entry) { |
+ Dart_Handle library_string = Dart_NewStringFromCString(entry->library_uri); |
+ DART_CHECK_VALID(library_string); |
+ Dart_Handle library = Dart_LookupLibrary(library_string); |
+ // VM entry points may be setup in libraries that have not been explicitly |
+ // loaded by the application script. In such cases, library lookup will |
+ // fail. Manually load those libraries. |
+ if (Dart_IsError(library)) { |
+ static const uint32_t kLoadBufferMaxSize = 128; |
+ char* load_buffer = |
+ reinterpret_cast<char*>(calloc(kLoadBufferMaxSize, sizeof(char))); |
+ snprintf(load_buffer, |
+ kLoadBufferMaxSize, |
+ "import '%s';", |
+ DartUtils::GetStringValue(library_string)); |
+ Dart_Handle script_handle = Dart_NewStringFromCString(load_buffer); |
+ memset(load_buffer, 0, kLoadBufferMaxSize); |
+ snprintf(load_buffer, |
+ kLoadBufferMaxSize, |
+ "dart:_snapshot_%zu", |
+ lib_index); |
+ Dart_Handle script_url = Dart_NewStringFromCString(load_buffer); |
+ free(load_buffer); |
+ Dart_Handle loaded = Dart_LoadLibrary(script_url, script_handle, 0, 0); |
+ DART_CHECK_VALID(loaded); |
+ |
+ // Do a fresh lookup |
+ library = Dart_LookupLibrary(library_string); |
+ } |
+ |
+ DART_CHECK_VALID(library); |
+ Dart_Handle result = Dart_SetNativeResolver(library, |
+ &StubNativeLookup, |
+ &StubNativeSymbol); |
+ DART_CHECK_VALID(result); |
+} |
+ |
+ |
+static void ImportNativeEntryPointLibrariesIntoRoot( |
+ const Dart_QualifiedFunctionName* entries) { |
+ if (entries == nullptr) { |
rmacnak
2015/10/21 23:06:18
Here and throughout s/nullptr/NULL
nullptr is und
Chinmay
2015/10/22 00:13:17
ack
|
+ return; |
+ } |
+ |
+ size_t index = 0; |
+ while (true) { |
+ Dart_QualifiedFunctionName entry = entries[index++]; |
+ if (entry.library_uri == nullptr) { |
+ // The termination sentinel has null members. |
+ break; |
+ } |
+ Dart_Handle entry_library = |
+ Dart_LookupLibrary(Dart_NewStringFromCString(entry.library_uri)); |
+ DART_CHECK_VALID(entry_library); |
+ Dart_Handle import_result = Dart_LibraryImportLibrary( |
+ entry_library, Dart_RootLibrary(), Dart_EmptyString()); |
+ DART_CHECK_VALID(import_result); |
+ } |
+} |
+ |
+ |
+static void SetupStubNativeResolversForPrecompilation( |
+ const Dart_QualifiedFunctionName* entries) { |
+ |
+ if (entries == nullptr) { |
+ return; |
+ } |
+ |
+ // Setup native resolvers for all libraries found in the manifest. |
+ size_t index = 0; |
+ while (true) { |
+ Dart_QualifiedFunctionName entry = entries[index++]; |
+ if (entry.library_uri == nullptr) { |
+ // The termination sentinel has null members. |
+ break; |
+ } |
+ // Setup stub resolvers on loaded libraries |
+ SetupStubNativeResolver(index, &entry); |
+ } |
+} |
+ |
+ |
+static bool ParseEntryPointsManifestSingleLine( |
Cutch
2015/10/21 22:00:08
This should take an char** error which indicates w
Chinmay
2015/10/22 00:13:16
ack
|
+ const char* line, Dart_QualifiedFunctionName* entry) { |
+ bool success = true; |
+ size_t offset = 0; |
+ for (uint8_t i = 0; i < 3; i++) { |
+ char* component = strchr(line + offset, i == 2 ? '\n' : ','); |
rmacnak
2015/10/21 23:06:18
const char* component
Chinmay
2015/10/22 00:13:16
ack
|
+ if (component == nullptr) { |
+ success = false; |
+ break; |
+ } |
+ |
+ int64_t chars_read = component - (line + offset); |
+ if (chars_read <= 0) { |
+ success = false; |
+ break; |
+ } |
+ |
+ if (entry != nullptr) { |
+ // These allocations are collected in |CleanupEntryPointsCollection|. |
+ char* entry_item = |
+ reinterpret_cast<char*>(calloc(chars_read + 1, sizeof(char))); |
+ memcpy(entry_item, line + offset, chars_read); |
+ |
+ switch (i) { |
+ case 0: // library |
+ entry->library_uri = entry_item; |
+ break; |
+ case 1: // class |
+ entry->class_name = entry_item; |
+ break; |
+ case 2: // function |
+ entry->function_name = entry_item; |
+ break; |
+ default: |
+ free(entry_item); |
+ success = false; |
+ break; |
+ } |
+ } |
+ |
+ offset += chars_read + 1; |
+ } |
+ return success; |
+} |
+ |
+ |
+int64_t ParseEntryPointsManifestLines(FILE* file, |
+ Dart_QualifiedFunctionName* collection) { |
+ int64_t entries = 0; |
+ |
+ static const int kManifestMaxLineLength = 1024; |
+ char* line = reinterpret_cast<char*>(malloc(kManifestMaxLineLength)); |
+ |
+ while (true) { |
+ char* read_line = fgets(line, kManifestMaxLineLength, file); |
+ |
+ if (read_line == nullptr) { |
+ if ((feof(file) != 0) && (ferror(file) != 0)) { |
+ entries = -1; |
+ } |
+ break; |
+ } |
+ |
+ Dart_QualifiedFunctionName* entry = |
+ collection != nullptr ? collection + entries : nullptr; |
+ |
+ if (!ParseEntryPointsManifestSingleLine(read_line, entry)) { |
+ entries = -1; |
Cutch
2015/10/21 22:00:08
This might leak some |entry_item| allocated above
Chinmay
2015/10/22 00:13:16
ack
|
+ break; |
+ } |
+ |
+ entries++; |
+ } |
+ |
+ free(line); |
+ |
+ return entries; |
+} |
+ |
+ |
+static Dart_QualifiedFunctionName* ParseEntryPointsManifestFile( |
+ const char* path) { |
+ if (path == nullptr) { |
+ return nullptr; |
+ } |
+ |
+ FILE* file = fopen(path, "r"); |
+ |
+ if (file == nullptr) { |
+ Log::PrintErr("Could not open entry points manifest file\n"); |
+ return nullptr; |
+ } |
+ |
+ // Parse the file once but don't store the results. This is done to first |
+ // determine the number of entries in the manifest |
+ int64_t entry_count = ParseEntryPointsManifestLines(file, nullptr); |
+ |
+ if (entry_count <= 0) { |
+ Log::PrintErr("Manifest file specified is invalid or contained no entries"); |
+ fclose(file); |
+ return nullptr; |
+ } |
+ |
+ rewind(file); |
+ |
+ // Allocate enough storage for the entries in the file plus a termination |
+ // sentinel and parse it again to populate the allocation |
+ Dart_QualifiedFunctionName* entries = |
+ reinterpret_cast<Dart_QualifiedFunctionName*>( |
+ calloc(entry_count + 1, sizeof(Dart_QualifiedFunctionName))); |
+ |
+ int64_t parsed_entry_count = ParseEntryPointsManifestLines(file, entries); |
+ |
+ if (parsed_entry_count != entry_count) { |
Cutch
2015/10/21 22:00:08
You're calling the same function twice with the sa
Chinmay
2015/10/22 00:13:17
Was being paranoid since I am reading from a file
|
+ free(entries); |
Cutch
2015/10/21 22:00:08
This leaks pointers inside of |entries|. Call Clea
Chinmay
2015/10/22 00:13:17
ack. Removed by the assert.
|
+ fclose(file); |
+ return nullptr; |
+ } |
+ |
+ fclose(file); |
+ |
+ // The entries allocation must be explicitly cleaned up via |
+ // |CleanupEntryPointsCollection| |
+ return entries; |
+} |
+ |
+ |
+static Dart_QualifiedFunctionName* ParseEntryPointsManifestIfPresent() { |
+ Dart_QualifiedFunctionName* entries = |
+ ParseEntryPointsManifestFile(vm_entry_points_manifest); |
+ if (entries == nullptr && IsSnapshottingForPrecompilation()) { |
Cutch
2015/10/21 22:00:08
s/nullptr/NULL here and elsewhere
Chinmay
2015/10/22 00:13:16
ack
|
+ Log::PrintErr( |
+ "Could not find native VM entry points during precompilation"); |
+ exit(255); |
+ } |
+ return entries; |
+} |
+ |
+ |
+void CleanupEntryPointsCollection(Dart_QualifiedFunctionName* entries) { |
+ if (entries == nullptr) { |
+ return; |
+ } |
+ |
+ size_t index = 0; |
+ while (true) { |
+ Dart_QualifiedFunctionName entry = entries[index++]; |
+ if (entry.library_uri == nullptr) { |
Cutch
2015/10/21 22:00:08
s/nullptr/NULL here and elsewhere
Chinmay
2015/10/22 00:13:16
ack
|
+ break; |
+ } |
+ free((char*)entry.library_uri); |
Cutch
2015/10/21 22:00:08
use c++ casts
Chinmay
2015/10/22 00:13:17
ack
|
+ free((char*)entry.class_name); |
+ free((char*)entry.function_name); |
+ } |
+ free(entries); |
+} |
+ |
+ |
static void CreateAndWriteSnapshot() { |
Dart_Handle result; |
uint8_t* vm_isolate_buffer = NULL; |
@@ -485,6 +820,55 @@ static void CreateAndWriteSnapshot() { |
} |
+static void CreateAndWritePrecompiledSnapshot( |
+ Dart_QualifiedFunctionName* standalone_entry_points) { |
+ |
+ if (standalone_entry_points == nullptr) { |
Cutch
2015/10/21 22:00:08
Can you hoist this out or rename it? It's confusin
Chinmay
2015/10/22 00:13:16
ack
|
+ ASSERT(!IsSnapshottingForPrecompilation()); |
+ CreateAndWriteSnapshot(); |
+ return; |
+ } |
+ |
+ Dart_Handle result; |
+ uint8_t* vm_isolate_buffer = NULL; |
+ intptr_t vm_isolate_size = 0; |
+ uint8_t* isolate_buffer = NULL; |
+ intptr_t isolate_size = 0; |
+ uint8_t* instructions_buffer = NULL; |
+ intptr_t instructions_size = 0; |
+ |
+ // Precompile with specified VM entry points |
+ result = Dart_Precompile(standalone_entry_points, true); |
+ CHECK_RESULT(result); |
+ |
+ // Create a precompiled snapshot. This gives us an instruction buffer with |
+ // machine code |
+ result = Dart_CreatePrecompiledSnapshot(&vm_isolate_buffer, |
+ &vm_isolate_size, |
+ &isolate_buffer, |
+ &isolate_size, |
+ &instructions_buffer, |
+ &instructions_size); |
+ CHECK_RESULT(result); |
+ |
+ // Now write the vm isolate, isolate and instructions snapshots out to the |
+ // specified files and exit. |
+ WriteSnapshotFile(vm_isolate_snapshot_filename, |
+ vm_isolate_buffer, |
+ vm_isolate_size); |
+ WriteSnapshotFile(isolate_snapshot_filename, |
+ isolate_buffer, |
+ isolate_size); |
+ WriteSnapshotFile(instructions_snapshot_filename, |
+ instructions_buffer, |
+ instructions_size); |
+ Dart_ExitScope(); |
+ |
+ // Shutdown the isolate. |
+ Dart_ShutdownIsolate(); |
+} |
+ |
+ |
static void SetupForUriResolution() { |
// Set up the library tag handler for this isolate. |
Dart_Handle result = Dart_SetLibraryTagHandler(DartUtils::LibraryTagHandler); |
@@ -589,6 +973,12 @@ int main(int argc, char** argv) { |
// Workaround until issue 21620 is fixed. |
// (https://github.com/dart-lang/sdk/issues/21620) |
vm_options.AddArgument("--no-concurrent_sweep"); |
+ |
+ if (IsSnapshottingForPrecompilation()) { |
+ vm_options.AddArgument("--precompilation"); |
+ vm_options.AddArgument("--write_protect_code=false"); |
rmacnak
2015/10/21 23:06:18
--write_protect_code=false should be unnecessary n
Chinmay
2015/10/22 00:13:16
ack
|
+ } |
+ |
Dart_SetVMFlags(vm_options.count(), vm_options.arguments()); |
// Initialize the Dart VM. |
@@ -672,13 +1062,24 @@ int main(int argc, char** argv) { |
// URL mapping specified on the command line to load the libraries. |
result = Dart_SetLibraryTagHandler(CreateSnapshotLibraryTagHandler); |
CHECK_RESULT(result); |
+ |
+ Dart_QualifiedFunctionName* entry_points = |
+ ParseEntryPointsManifestIfPresent(); |
+ |
+ SetupStubNativeResolversForPrecompilation(entry_points); |
+ |
// Load the specified script. |
library = LoadSnapshotCreationScript(app_script_name); |
VerifyLoaded(library); |
+ |
+ ImportNativeEntryPointLibrariesIntoRoot(entry_points); |
+ |
// Ensure that we mark all libraries as loaded. |
result = Dart_FinalizeLoading(false); |
CHECK_RESULT(result); |
- CreateAndWriteSnapshot(); |
+ CreateAndWritePrecompiledSnapshot(entry_points); |
+ |
+ CleanupEntryPointsCollection(entry_points); |
Dart_EnterIsolate(UriResolverIsolateScope::isolate); |
Dart_ShutdownIsolate(); |