5399dbf6f6
- split the Dart CLI tool out of the VM into it's own embedder which runs in AOT mode. The pure Dart VM executable is called 'dartvm' and has no Dart CLI functionality in it - the Dart CLI executable parses the CLI commands and invokes the rest of the AOT tools in the same process, for the 'run' and 'test' commands it execs a process which runs 'dartvm' to run - 'dart hello.dart' execs the 'dartvm' process and runs 'hello.dart' - the Dart CLI is not generated for ia32 as we are not shipping a Dart SDK for ia32 anymore (support to execute the 'dartvm' for ia32 architecture is retained) - the Dart CLI tool is not built in the internal Dart SDK builds TEST=ci Some performance improvement numbers 'dart format pkg/dartdev' goes from 1.17 secs to 0.22 secs 'dart doc pkg/dartdev' goes from 100.2 secs to 66.6 secs 'dart fix pkg/dartdev' goes from 19.3 secs to 14.5 secs Change-Id: I66984a26cb2ab014b34dc1873f1f3d2884e13518 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/364202 Commit-Queue: Ben Konyi <bkonyi@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
511 lines
17 KiB
C++
511 lines
17 KiB
C++
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "bin/main_options.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "bin/common_options.h"
|
|
#include "bin/error_exit.h"
|
|
#include "bin/file_system_watcher.h"
|
|
#if defined(DART_IO_SECURE_SOCKET_DISABLED)
|
|
#include "bin/io_service_no_ssl.h"
|
|
#else // defined(DART_IO_SECURE_SOCKET_DISABLED)
|
|
#include "bin/io_service.h"
|
|
#endif // defined(DART_IO_SECURE_SOCKET_DISABLED)
|
|
#include "bin/platform.h"
|
|
#include "bin/utils.h"
|
|
#include "platform/syslog.h"
|
|
#if !defined(DART_IO_SECURE_SOCKET_DISABLED)
|
|
#include "bin/security_context.h"
|
|
#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)
|
|
#include "bin/socket.h"
|
|
#include "include/dart_api.h"
|
|
#include "platform/assert.h"
|
|
#include "platform/globals.h"
|
|
#include "platform/hashmap.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
// These strings must match the enum SnapshotKind in main_options.h.
|
|
static const char* const kSnapshotKindNames[] = {
|
|
"none",
|
|
"kernel",
|
|
"app-jit",
|
|
nullptr,
|
|
};
|
|
|
|
SnapshotKind Options::gen_snapshot_kind_ = kNone;
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
DFE* Options::dfe_ = nullptr;
|
|
|
|
DEFINE_STRING_OPTION_CB(dfe, { Options::dfe()->set_frontend_filename(value); });
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#define OPTION_FIELD(variable) Options::variable##_
|
|
|
|
#define STRING_OPTION_DEFINITION(name, variable) \
|
|
const char* OPTION_FIELD(variable) = nullptr; \
|
|
DEFINE_STRING_OPTION(name, OPTION_FIELD(variable))
|
|
STRING_OPTIONS_LIST(STRING_OPTION_DEFINITION)
|
|
#undef STRING_OPTION_DEFINITION
|
|
|
|
#define BOOL_OPTION_DEFINITION(name, variable) \
|
|
bool OPTION_FIELD(variable) = false; \
|
|
DEFINE_BOOL_OPTION(name, OPTION_FIELD(variable))
|
|
BOOL_OPTIONS_LIST(BOOL_OPTION_DEFINITION)
|
|
#if defined(DEBUG)
|
|
DEBUG_BOOL_OPTIONS_LIST(BOOL_OPTION_DEFINITION)
|
|
#endif
|
|
#undef BOOL_OPTION_DEFINITION
|
|
|
|
#define SHORT_BOOL_OPTION_DEFINITION(short_name, long_name, variable) \
|
|
bool OPTION_FIELD(variable) = false; \
|
|
DEFINE_BOOL_OPTION_SHORT(short_name, long_name, OPTION_FIELD(variable))
|
|
SHORT_BOOL_OPTIONS_LIST(SHORT_BOOL_OPTION_DEFINITION)
|
|
#undef SHORT_BOOL_OPTION_DEFINITION
|
|
|
|
#define ENUM_OPTION_DEFINITION(name, type, variable) \
|
|
DEFINE_ENUM_OPTION(name, type, OPTION_FIELD(variable))
|
|
ENUM_OPTIONS_LIST(ENUM_OPTION_DEFINITION)
|
|
#undef ENUM_OPTION_DEFINITION
|
|
|
|
#define CB_OPTION_DEFINITION(callback) \
|
|
static bool callback##Helper(const char* arg, CommandLineOptions* o) { \
|
|
return Options::callback(arg, o); \
|
|
} \
|
|
DEFINE_CB_OPTION(callback##Helper)
|
|
CB_OPTIONS_LIST(CB_OPTION_DEFINITION)
|
|
#undef CB_OPTION_DEFINITION
|
|
|
|
static void hot_reload_test_mode_callback(CommandLineOptions* vm_options) {
|
|
// Identity reload.
|
|
vm_options->AddArgument("--identity_reload");
|
|
// Start reloading quickly.
|
|
vm_options->AddArgument("--reload_every=4");
|
|
// Reload from optimized and unoptimized code.
|
|
vm_options->AddArgument("--reload_every_optimized=false");
|
|
// Reload less frequently as time goes on.
|
|
vm_options->AddArgument("--reload_every_back_off");
|
|
// Ensure that every isolate has reloaded once before exiting.
|
|
vm_options->AddArgument("--check_reloaded");
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
Options::dfe()->set_use_incremental_compiler(true);
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
DEFINE_BOOL_OPTION_CB(hot_reload_test_mode, hot_reload_test_mode_callback);
|
|
|
|
static void hot_reload_rollback_test_mode_callback(
|
|
CommandLineOptions* vm_options) {
|
|
// Identity reload.
|
|
vm_options->AddArgument("--identity_reload");
|
|
// Start reloading quickly.
|
|
vm_options->AddArgument("--reload_every=4");
|
|
// Reload from optimized and unoptimized code.
|
|
vm_options->AddArgument("--reload_every_optimized=false");
|
|
// Reload less frequently as time goes on.
|
|
vm_options->AddArgument("--reload_every_back_off");
|
|
// Ensure that every isolate has reloaded once before exiting.
|
|
vm_options->AddArgument("--check_reloaded");
|
|
// Force all reloads to fail and execute the rollback code.
|
|
vm_options->AddArgument("--reload_force_rollback");
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
Options::dfe()->set_use_incremental_compiler(true);
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
DEFINE_BOOL_OPTION_CB(hot_reload_rollback_test_mode,
|
|
hot_reload_rollback_test_mode_callback);
|
|
|
|
bool Options::ParseArguments(int argc,
|
|
char** argv,
|
|
bool vm_run_app_snapshot,
|
|
bool parsing_dart_vm_options,
|
|
CommandLineOptions* vm_options,
|
|
char** script_name,
|
|
CommandLineOptions* dart_options,
|
|
bool* print_flags_seen) {
|
|
int i = 0;
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
// DART_VM_OPTIONS is only implemented for compiled executables.
|
|
ASSERT(!parsing_dart_vm_options);
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (!parsing_dart_vm_options) {
|
|
// Start processing arguments after argv[0] which would be the executable.
|
|
i = 1;
|
|
}
|
|
|
|
CommandLineOptions temp_vm_options(vm_options->max_count());
|
|
// Parse out the vm options.
|
|
while (i < argc) {
|
|
bool skipVmOption = false;
|
|
if (!OptionProcessor::TryProcess(argv[i], &temp_vm_options)) {
|
|
// Check if this flag is a potentially valid VM flag.
|
|
if (!OptionProcessor::IsValidFlag(argv[i])) {
|
|
break;
|
|
}
|
|
if (IsOption(argv[i], "print-flags")) {
|
|
*print_flags_seen = true;
|
|
} else if (IsOption(argv[i], "disable-dart-dev")) {
|
|
skipVmOption = true;
|
|
}
|
|
if (!skipVmOption) {
|
|
temp_vm_options.AddArgument(argv[i]);
|
|
}
|
|
} else if (IsOption(argv[i], "profile-microtasks")) {
|
|
temp_vm_options.AddArgument(argv[i]);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
Options::dfe()->set_use_dfe();
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (Options::deterministic()) {
|
|
// Both an embedder and VM flag.
|
|
temp_vm_options.AddArgument("--deterministic");
|
|
}
|
|
|
|
Socket::set_short_socket_read(Options::short_socket_read());
|
|
Socket::set_short_socket_write(Options::short_socket_write());
|
|
#if !defined(DART_IO_SECURE_SOCKET_DISABLED)
|
|
SSLCertContext::set_root_certs_file(Options::root_certs_file());
|
|
SSLCertContext::set_root_certs_cache(Options::root_certs_cache());
|
|
SSLCertContext::set_long_ssl_cert_evaluation(
|
|
Options::long_ssl_cert_evaluation());
|
|
SSLCertContext::set_bypass_trusting_system_roots(
|
|
Options::bypass_trusting_system_roots());
|
|
#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)
|
|
|
|
FileSystemWatcher::set_delayed_filewatch_callback(
|
|
Options::delayed_filewatch_callback());
|
|
|
|
if (Options::deterministic()) {
|
|
IOService::set_max_concurrency(1);
|
|
}
|
|
|
|
// The arguments to the VM are at positions 1 through i-1 in argv.
|
|
Platform::SetExecutableArguments(i, argv);
|
|
|
|
// Get the script name.
|
|
if (i < argc) {
|
|
*script_name = Utils::StrDup(argv[i]);
|
|
i++;
|
|
// Handle argument parsing errors and missing script / command name when not
|
|
// processing options set via DART_VM_OPTIONS.
|
|
} else if (!parsing_dart_vm_options || Options::help_option() || // NOLINT
|
|
Options::version_option()) { // NOLINT
|
|
return false;
|
|
}
|
|
|
|
const char** vm_argv = temp_vm_options.arguments();
|
|
int vm_argc = temp_vm_options.count();
|
|
vm_options->AddArguments(vm_argv, vm_argc);
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
// If we're parsing DART_VM_OPTIONS, there shouldn't be any script set or
|
|
// Dart arguments left to parse.
|
|
if (parsing_dart_vm_options) {
|
|
ASSERT(i == argc);
|
|
return true;
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
// Parse out options to be passed to dart main.
|
|
while (i < argc) {
|
|
dart_options->AddArgument(argv[i]);
|
|
i++;
|
|
}
|
|
if (!parsing_dart_vm_options) {
|
|
// Store the executable name.
|
|
if (Options::executable_name() != nullptr) {
|
|
Platform::SetExecutableName(Options::executable_name());
|
|
Platform::SetResolvedExecutableName(Options::executable_name());
|
|
} else {
|
|
Platform::SetExecutableName(argv[0]);
|
|
}
|
|
}
|
|
|
|
// Verify consistency of arguments.
|
|
|
|
// snapshot_depfile is an alias for depfile. Passing them both is an error.
|
|
if ((snapshot_deps_filename_ != nullptr) && (depfile_ != nullptr)) {
|
|
Syslog::PrintErr("Specify only one of --depfile and --snapshot_depfile\n");
|
|
return false;
|
|
}
|
|
if (snapshot_deps_filename_ != nullptr) {
|
|
depfile_ = snapshot_deps_filename_;
|
|
snapshot_deps_filename_ = nullptr;
|
|
}
|
|
|
|
if ((packages_file_ != nullptr) && (strlen(packages_file_) == 0)) {
|
|
Syslog::PrintErr("Empty package file name specified.\n");
|
|
return false;
|
|
}
|
|
if ((gen_snapshot_kind_ != kNone) && (snapshot_filename_ == nullptr)) {
|
|
Syslog::PrintErr(
|
|
"Generating a snapshot requires a filename (--snapshot).\n");
|
|
return false;
|
|
}
|
|
if ((gen_snapshot_kind_ == kNone) && (depfile_ != nullptr) &&
|
|
(snapshot_filename_ == nullptr) &&
|
|
(depfile_output_filename_ == nullptr)) {
|
|
Syslog::PrintErr(
|
|
"Generating a depfile requires an output filename"
|
|
" (--depfile-output-filename or --snapshot).\n");
|
|
return false;
|
|
}
|
|
if ((gen_snapshot_kind_ != kNone) && vm_run_app_snapshot) {
|
|
Syslog::PrintErr(
|
|
"Specifying an option to generate a snapshot and"
|
|
" run using a snapshot is invalid.\n");
|
|
return false;
|
|
}
|
|
|
|
// If --snapshot is given without --snapshot-kind, default to script snapshot.
|
|
if ((snapshot_filename_ != nullptr) && (gen_snapshot_kind_ == kNone)) {
|
|
gen_snapshot_kind_ = kKernel;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// These strings must match the enum VerbosityLevel in main_options.h.
|
|
VerbosityLevel Options::verbosity_ = kAll;
|
|
bool Options::enable_vm_service_ = false;
|
|
bool Options::enable_dds_ = true;
|
|
|
|
void Options::PrintVersion() {
|
|
_PrintVersion();
|
|
}
|
|
|
|
// clang-format off
|
|
void Options::PrintUsage() {
|
|
_PrintUsage();
|
|
if (!Options::verbose_option()) {
|
|
_PrintNonVerboseUsage();
|
|
} else {
|
|
_PrintVerboseUsage();
|
|
}
|
|
}
|
|
// clang-format on
|
|
|
|
dart::SimpleHashMap* Options::environment_ = nullptr;
|
|
bool Options::ProcessEnvironmentOption(const char* arg,
|
|
CommandLineOptions* vm_options) {
|
|
return OptionProcessor::ProcessEnvironmentOption(arg, vm_options,
|
|
&Options::environment_);
|
|
}
|
|
|
|
void Options::Cleanup() {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
DestroyEnvArgv();
|
|
#endif
|
|
DestroyEnvironment();
|
|
}
|
|
|
|
void Options::DestroyEnvironment() {
|
|
if (environment_ != nullptr) {
|
|
for (SimpleHashMap::Entry* p = environment_->Start(); p != nullptr;
|
|
p = environment_->Next(p)) {
|
|
free(p->key);
|
|
free(p->value);
|
|
}
|
|
delete environment_;
|
|
environment_ = nullptr;
|
|
}
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
// Retrieves the set of arguments stored in the DART_VM_OPTIONS environment
|
|
// variable.
|
|
//
|
|
// DART_VM_OPTIONS should contain a list of comma-separated options and flags
|
|
// with no spaces. Options that support providing multiple values as
|
|
// comma-separated lists (e.g., --timeline-streams=Dart,GC,Compiler,Microtask)
|
|
// are not supported and will cause argument parsing to fail.
|
|
char** Options::GetEnvArguments(int* argc) {
|
|
ASSERT(argc != nullptr);
|
|
const char* env_args_str = std::getenv("DART_VM_OPTIONS");
|
|
if (env_args_str == nullptr) {
|
|
*argc = 0;
|
|
return nullptr;
|
|
}
|
|
|
|
intptr_t n = strlen(env_args_str);
|
|
if (n == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Find the number of arguments based on the number of ','s.
|
|
//
|
|
// WARNING: this won't work for arguments that support CSVs. There's less
|
|
// than a handful of options that support multiple values. If we want to
|
|
// support this case, we need to determine a way to specify groupings of CSVs
|
|
// in environment variables.
|
|
int arg_count = 1;
|
|
for (int i = 0; i < n; ++i) {
|
|
// Ignore the last comma if it's the last character in the string.
|
|
if (env_args_str[i] == ',' && i + 1 != n) {
|
|
arg_count++;
|
|
}
|
|
}
|
|
|
|
env_argv_ = new char*[arg_count];
|
|
env_argc_ = arg_count;
|
|
*argc = arg_count;
|
|
|
|
int current_arg = 0;
|
|
char* token;
|
|
char* rest = const_cast<char*>(env_args_str);
|
|
|
|
// Split out the individual arguments.
|
|
while ((token = strtok_r(rest, ",", &rest)) != nullptr) {
|
|
// TODO(bkonyi): consider stripping leading/trailing whitespace from
|
|
// arguments.
|
|
env_argv_[current_arg++] = Utils::StrNDup(token, rest - token);
|
|
}
|
|
|
|
return env_argv_;
|
|
}
|
|
|
|
char** Options::env_argv_ = nullptr;
|
|
int Options::env_argc_ = 0;
|
|
|
|
void Options::DestroyEnvArgv() {
|
|
for (int i = 0; i < env_argc_; ++i) {
|
|
free(env_argv_[i]);
|
|
}
|
|
delete[] env_argv_;
|
|
env_argv_ = nullptr;
|
|
}
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
bool Options::ExtractPortAndAddress(const char* option_value,
|
|
int* out_port,
|
|
const char** out_ip,
|
|
int default_port,
|
|
const char* default_ip) {
|
|
// [option_value] has to be one of the following formats:
|
|
// - ""
|
|
// - ":8181"
|
|
// - "=8181"
|
|
// - ":8181/192.168.0.1"
|
|
// - "=8181/192.168.0.1"
|
|
// - "=8181/::1"
|
|
|
|
if (*option_value == '\0') {
|
|
*out_ip = default_ip;
|
|
*out_port = default_port;
|
|
return true;
|
|
}
|
|
|
|
if ((*option_value != '=') && (*option_value != ':')) {
|
|
return false;
|
|
}
|
|
|
|
int port = atoi(option_value + 1);
|
|
const char* slash = strstr(option_value, "/");
|
|
if (slash == nullptr) {
|
|
*out_ip = default_ip;
|
|
*out_port = port;
|
|
return true;
|
|
}
|
|
|
|
*out_ip = slash + 1;
|
|
*out_port = port;
|
|
return true;
|
|
}
|
|
|
|
#if !defined(PRODUCT)
|
|
static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost";
|
|
static constexpr int DEFAULT_VM_SERVICE_SERVER_PORT = 8181;
|
|
static constexpr int INVALID_VM_SERVICE_SERVER_PORT = -1;
|
|
const char* Options::vm_service_server_ip_ = DEFAULT_VM_SERVICE_SERVER_IP;
|
|
int Options::vm_service_server_port_ = INVALID_VM_SERVICE_SERVER_PORT;
|
|
#endif // !defined(PRODUCT)
|
|
|
|
bool Options::ProcessEnableVmServiceOption(const char* arg,
|
|
CommandLineOptions* vm_options) {
|
|
const char* value =
|
|
OptionProcessor::ProcessOption(arg, "--enable-vm-service");
|
|
if (value == nullptr) {
|
|
return false;
|
|
}
|
|
#if !defined(PRODUCT)
|
|
if (!ExtractPortAndAddress(
|
|
value, &vm_service_server_port_, &vm_service_server_ip_,
|
|
DEFAULT_VM_SERVICE_SERVER_PORT, DEFAULT_VM_SERVICE_SERVER_IP)) {
|
|
Syslog::PrintErr(
|
|
"unrecognized --enable-vm-service option syntax. "
|
|
"Use --enable-vm-service[=<port number>[/<bind address>]]\n");
|
|
return false;
|
|
}
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
dfe()->set_use_incremental_compiler(true);
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
enable_vm_service_ = true;
|
|
return true;
|
|
#else
|
|
// VM service not available in product mode.
|
|
return false;
|
|
#endif // !defined(PRODUCT)
|
|
}
|
|
|
|
bool Options::ProcessObserveOption(const char* arg,
|
|
CommandLineOptions* vm_options) {
|
|
const char* value = OptionProcessor::ProcessOption(arg, "--observe");
|
|
if (value == nullptr) {
|
|
return false;
|
|
}
|
|
#if !defined(PRODUCT)
|
|
if (!ExtractPortAndAddress(
|
|
value, &vm_service_server_port_, &vm_service_server_ip_,
|
|
DEFAULT_VM_SERVICE_SERVER_PORT, DEFAULT_VM_SERVICE_SERVER_IP)) {
|
|
Syslog::PrintErr(
|
|
"unrecognized --observe option syntax. "
|
|
"Use --observe[=<port number>[/<bind address>]]\n");
|
|
return false;
|
|
}
|
|
|
|
// These options should also be documented in the help message.
|
|
vm_options->AddArgument("--pause-isolates-on-exit");
|
|
vm_options->AddArgument("--pause-isolates-on-unhandled-exceptions");
|
|
vm_options->AddArgument("--profiler");
|
|
vm_options->AddArgument("--warn-on-pause-with-no-debugger");
|
|
vm_options->AddArgument("--timeline-streams=\"Compiler,Dart,GC,Microtask\"");
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
dfe()->set_use_incremental_compiler(true);
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
enable_vm_service_ = true;
|
|
return true;
|
|
#else
|
|
// VM service not available in product mode.
|
|
return false;
|
|
#endif // !defined(PRODUCT)
|
|
}
|
|
|
|
bool Options::ProcessDdsOption(const char* arg,
|
|
CommandLineOptions* vm_options) {
|
|
const char* value = OptionProcessor::ProcessOption(arg, "--dds");
|
|
if (value == nullptr) {
|
|
value = OptionProcessor::ProcessOption(arg, "--no-dds");
|
|
if (value == nullptr) {
|
|
return false;
|
|
}
|
|
enable_dds_ = false;
|
|
} else {
|
|
enable_dds_ = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|