blob: 1d518824aa8199f4e67b8cc6b95efff7cde1b066 [file] [log] [blame] [edit]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include "flutter/fml/native_library.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/size.h"
#include "flutter/shell/version/version.h"
// Include once for the default enum definition.
#include "flutter/shell/common/switches.h"
#undef SHELL_COMMON_SWITCHES_H_
struct SwitchDesc {
flutter::Switch sw;
const std::string_view flag;
const char* help;
};
#undef DEF_SWITCHES_START
#undef DEF_SWITCH
#undef DEF_SWITCHES_END
// clang-format off
#define DEF_SWITCHES_START static const struct SwitchDesc gSwitchDescs[] = {
#define DEF_SWITCH(p_swtch, p_flag, p_help) \
{ flutter::Switch:: p_swtch, p_flag, p_help },
#define DEF_SWITCHES_END };
// clang-format on
// List of common and safe VM flags to allow to be passed directly to the VM.
#if FLUTTER_RELEASE
// clang-format off
static const std::string kAllowedDartFlags[] = {
"--enable-isolate-groups",
"--no-enable-isolate-groups",
};
// clang-format on
#else
// clang-format off
static const std::string kAllowedDartFlags[] = {
"--enable-isolate-groups",
"--no-enable-isolate-groups",
"--enable_mirrors",
"--enable-service-port-fallback",
"--max_profile_depth",
"--profile_period",
"--random_seed",
"--sample-buffer-duration",
"--trace-kernel",
"--trace-reload",
"--trace-reload-verbose",
"--write-service-info",
"--null_assertions",
"--strict_null_safety_checks",
"--max_subtype_cache_entries",
};
// clang-format on
#endif // FLUTTER_RELEASE
// Include again for struct definition.
#include "flutter/shell/common/switches.h"
// Define symbols for the ICU data that is linked into the Flutter library on
// Android. This is a workaround for crashes seen when doing dynamic lookups
// of the engine's own symbols on some older versions of Android.
#if FML_OS_ANDROID
extern uint8_t _binary_icudtl_dat_start[];
extern uint8_t _binary_icudtl_dat_end[];
static std::unique_ptr<fml::Mapping> GetICUStaticMapping() {
return std::make_unique<fml::NonOwnedMapping>(
_binary_icudtl_dat_start,
_binary_icudtl_dat_end - _binary_icudtl_dat_start);
}
#endif
namespace flutter {
void PrintUsage(const std::string& executable_name) {
std::cerr << std::endl << " " << executable_name << std::endl << std::endl;
std::cerr << "Versions: " << std::endl << std::endl;
std::cerr << "Flutter Engine Version: " << GetFlutterEngineVersion()
<< std::endl;
std::cerr << "Skia Version: " << GetSkiaVersion() << std::endl;
std::cerr << "Dart Version: " << GetDartVersion() << std::endl << std::endl;
std::cerr << "Available Flags:" << std::endl;
const uint32_t column_width = 80;
const uint32_t flags_count = static_cast<uint32_t>(Switch::Sentinel);
uint32_t max_width = 2;
for (uint32_t i = 0; i < flags_count; i++) {
auto desc = gSwitchDescs[i];
max_width = std::max<uint32_t>(desc.flag.size() + 2, max_width);
}
const uint32_t help_width = column_width - max_width - 3;
std::cerr << std::string(column_width, '-') << std::endl;
for (uint32_t i = 0; i < flags_count; i++) {
auto desc = gSwitchDescs[i];
std::cerr << std::setw(max_width)
<< std::string("--") +
std::string{desc.flag.data(), desc.flag.size()}
<< " : ";
std::istringstream stream(desc.help);
int32_t remaining = help_width;
std::string word;
while (stream >> word && remaining > 0) {
remaining -= (word.size() + 1);
if (remaining <= 0) {
std::cerr << std::endl
<< std::string(max_width, ' ') << " " << word << " ";
remaining = help_width;
} else {
std::cerr << word << " ";
}
}
std::cerr << std::endl;
}
std::cerr << std::string(column_width, '-') << std::endl;
}
const std::string_view FlagForSwitch(Switch swtch) {
for (uint32_t i = 0; i < static_cast<uint32_t>(Switch::Sentinel); i++) {
if (gSwitchDescs[i].sw == swtch) {
return gSwitchDescs[i].flag;
}
}
return std::string_view();
}
static std::vector<std::string> ParseCommaDelimited(const std::string& input) {
std::istringstream ss(input);
std::vector<std::string> result;
std::string token;
while (std::getline(ss, token, ',')) {
result.push_back(token);
}
return result;
}
static bool IsAllowedDartVMFlag(const std::string& flag) {
for (uint32_t i = 0; i < fml::size(kAllowedDartFlags); ++i) {
const std::string& allowed = kAllowedDartFlags[i];
// Check that the prefix of the flag matches one of the allowed flags. This
// is to handle cases where flags take arguments, such as in
// "--max_profile_depth 1".
//
// We don't need to worry about cases like "--safe --sneaky_dangerous" as
// the VM will discard these as a single unrecognized flag.
if (flag.length() >= allowed.length() &&
std::equal(allowed.begin(), allowed.end(), flag.begin())) {
return true;
}
}
return false;
}
template <typename T>
static bool GetSwitchValue(const fml::CommandLine& command_line,
Switch sw,
T* result) {
std::string switch_string;
if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) {
return false;
}
std::stringstream stream(switch_string);
T value = 0;
if (stream >> value) {
*result = value;
return true;
}
return false;
}
std::unique_ptr<fml::Mapping> GetSymbolMapping(
const std::string& symbol_prefix,
const std::string& native_lib_path) {
const uint8_t* mapping = nullptr;
intptr_t size;
auto lookup_symbol = [&mapping, &size, symbol_prefix](
const fml::RefPtr<fml::NativeLibrary>& library) {
mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str());
size = reinterpret_cast<intptr_t>(
library->ResolveSymbol((symbol_prefix + "_size").c_str()));
};
fml::RefPtr<fml::NativeLibrary> library =
fml::NativeLibrary::CreateForCurrentProcess();
lookup_symbol(library);
if (!(mapping && size)) {
// Symbol lookup for the current process fails on some devices. As a
// fallback, try doing the lookup based on the path to the Flutter library.
library = fml::NativeLibrary::Create(native_lib_path.c_str());
lookup_symbol(library);
}
FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
return std::make_unique<fml::NonOwnedMapping>(mapping, size);
}
Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
Settings settings = {};
// Set executable name.
if (command_line.has_argv0()) {
settings.executable_name = command_line.argv0();
}
// Enable Observatory
settings.enable_observatory =
!command_line.HasOption(FlagForSwitch(Switch::DisableObservatory));
// Enable mDNS Observatory Publication
settings.enable_observatory_publication = !command_line.HasOption(
FlagForSwitch(Switch::DisableObservatoryPublication));
// Set Observatory Host
if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryHost))) {
command_line.GetOptionValue(FlagForSwitch(Switch::DeviceObservatoryHost),
&settings.observatory_host);
}
// Default the observatory port based on --ipv6 if not set.
if (settings.observatory_host.empty()) {
settings.observatory_host =
command_line.HasOption(FlagForSwitch(Switch::IPv6)) ? "::1"
: "127.0.0.1";
}
// Set Observatory Port
if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) {
if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort,
&settings.observatory_port)) {
FML_LOG(INFO)
<< "Observatory port specified was malformed. Will default to "
<< settings.observatory_port;
}
}
settings.may_insecurely_connect_to_all_domains = !command_line.HasOption(
FlagForSwitch(Switch::DisallowInsecureConnections));
command_line.GetOptionValue(FlagForSwitch(Switch::DomainNetworkPolicy),
&settings.domain_network_policy);
// Disable need for authentication codes for VM service communication, if
// specified.
settings.disable_service_auth_codes =
command_line.HasOption(FlagForSwitch(Switch::DisableServiceAuthCodes));
// Allow fallback to automatic port selection if binding to a specified port
// fails.
settings.enable_service_port_fallback =
command_line.HasOption(FlagForSwitch(Switch::EnableServicePortFallback));
// Checked mode overrides.
settings.disable_dart_asserts =
command_line.HasOption(FlagForSwitch(Switch::DisableDartAsserts));
settings.start_paused =
command_line.HasOption(FlagForSwitch(Switch::StartPaused));
settings.enable_checked_mode =
command_line.HasOption(FlagForSwitch(Switch::EnableCheckedMode));
settings.enable_dart_profiling =
command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling));
settings.enable_software_rendering =
command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering));
settings.endless_trace_buffer =
command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer));
settings.trace_startup =
command_line.HasOption(FlagForSwitch(Switch::TraceStartup));
settings.enable_serial_gc =
command_line.HasOption(FlagForSwitch(Switch::EnableSerialGC));
#if !FLUTTER_RELEASE
settings.trace_skia = true;
if (command_line.HasOption(FlagForSwitch(Switch::TraceSkia))) {
// If --trace-skia is specified, then log all Skia events.
settings.trace_skia_allowlist.reset();
} else {
std::string trace_skia_allowlist;
command_line.GetOptionValue(FlagForSwitch(Switch::TraceSkiaAllowlist),
&trace_skia_allowlist);
if (trace_skia_allowlist.size()) {
settings.trace_skia_allowlist = ParseCommaDelimited(trace_skia_allowlist);
} else {
settings.trace_skia_allowlist = {"skia.shaders"};
}
}
#endif // !FLUTTER_RELEASE
std::string trace_allowlist;
command_line.GetOptionValue(FlagForSwitch(Switch::TraceAllowlist),
&trace_allowlist);
settings.trace_allowlist = ParseCommaDelimited(trace_allowlist);
settings.trace_systrace =
command_line.HasOption(FlagForSwitch(Switch::TraceSystrace));
settings.skia_deterministic_rendering_on_cpu =
command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering));
settings.verbose_logging =
command_line.HasOption(FlagForSwitch(Switch::VerboseLogging));
command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir),
&settings.assets_path);
std::vector<std::string_view> aot_shared_library_name =
command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName));
std::vector<std::string_view> vmservice_shared_library_name =
command_line.GetOptionValues(
FlagForSwitch(Switch::AotVMServiceSharedLibraryName));
for (auto path : vmservice_shared_library_name) {
settings.vmservice_snapshot_library_path.emplace_back(path);
}
std::string snapshot_asset_path;
command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath),
&snapshot_asset_path);
std::string vm_snapshot_data_filename;
command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotData),
&vm_snapshot_data_filename);
command_line.GetOptionValue(FlagForSwitch(Switch::Route), &settings.route);
std::string vm_snapshot_instr_filename;
command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotInstructions),
&vm_snapshot_instr_filename);
std::string isolate_snapshot_data_filename;
command_line.GetOptionValue(FlagForSwitch(Switch::IsolateSnapshotData),
&isolate_snapshot_data_filename);
std::string isolate_snapshot_instr_filename;
command_line.GetOptionValue(
FlagForSwitch(Switch::IsolateSnapshotInstructions),
&isolate_snapshot_instr_filename);
if (!aot_shared_library_name.empty()) {
for (std::string_view name : aot_shared_library_name) {
settings.application_library_path.emplace_back(name);
}
} else if (!snapshot_asset_path.empty()) {
settings.vm_snapshot_data_path =
fml::paths::JoinPaths({snapshot_asset_path, vm_snapshot_data_filename});
settings.vm_snapshot_instr_path = fml::paths::JoinPaths(
{snapshot_asset_path, vm_snapshot_instr_filename});
settings.isolate_snapshot_data_path = fml::paths::JoinPaths(
{snapshot_asset_path, isolate_snapshot_data_filename});
settings.isolate_snapshot_instr_path = fml::paths::JoinPaths(
{snapshot_asset_path, isolate_snapshot_instr_filename});
}
command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath),
&settings.temp_directory_path);
bool leak_vm = "true" == command_line.GetOptionValueWithDefault(
FlagForSwitch(Switch::LeakVM), "true");
settings.leak_vm = leak_vm;
if (settings.icu_initialization_required) {
command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
&settings.icu_data_path);
if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
std::string icu_symbol_prefix, native_lib_path;
command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
&icu_symbol_prefix);
command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath),
&native_lib_path);
#if FML_OS_ANDROID
settings.icu_mapper = GetICUStaticMapping;
#else
settings.icu_mapper = [icu_symbol_prefix, native_lib_path] {
return GetSymbolMapping(icu_symbol_prefix, native_lib_path);
};
#endif
}
}
settings.use_test_fonts =
command_line.HasOption(FlagForSwitch(Switch::UseTestFonts));
settings.use_asset_fonts =
!command_line.HasOption(FlagForSwitch(Switch::DisableAssetFonts));
std::string enable_skparagraph = command_line.GetOptionValueWithDefault(
FlagForSwitch(Switch::EnableSkParagraph), "");
settings.enable_skparagraph = enable_skparagraph != "false";
settings.enable_impeller =
command_line.HasOption(FlagForSwitch(Switch::EnableImpeller));
settings.prefetched_default_font_manager = command_line.HasOption(
FlagForSwitch(Switch::PrefetchedDefaultFontManager));
std::string all_dart_flags;
if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags),
&all_dart_flags)) {
// Assume that individual flags are comma separated.
std::vector<std::string> flags = ParseCommaDelimited(all_dart_flags);
for (auto flag : flags) {
if (!IsAllowedDartVMFlag(flag)) {
FML_LOG(FATAL) << "Encountered disallowed Dart VM flag: " << flag;
}
settings.dart_flags.push_back(flag);
}
}
#if !FLUTTER_RELEASE
command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag);
#endif
settings.dump_skp_on_shader_compilation =
command_line.HasOption(FlagForSwitch(Switch::DumpSkpOnShaderCompilation));
settings.cache_sksl =
command_line.HasOption(FlagForSwitch(Switch::CacheSkSL));
settings.purge_persistent_cache =
command_line.HasOption(FlagForSwitch(Switch::PurgePersistentCache));
if (command_line.HasOption(FlagForSwitch(Switch::OldGenHeapSize))) {
std::string old_gen_heap_size;
command_line.GetOptionValue(FlagForSwitch(Switch::OldGenHeapSize),
&old_gen_heap_size);
settings.old_gen_heap_size = std::stoi(old_gen_heap_size);
}
if (command_line.HasOption(
FlagForSwitch(Switch::ResourceCacheMaxBytesThreshold))) {
std::string resource_cache_max_bytes_threshold;
command_line.GetOptionValue(
FlagForSwitch(Switch::ResourceCacheMaxBytesThreshold),
&resource_cache_max_bytes_threshold);
settings.resource_cache_max_bytes_threshold =
std::stoi(resource_cache_max_bytes_threshold);
}
if (command_line.HasOption(FlagForSwitch(Switch::MsaaSamples))) {
std::string msaa_samples;
command_line.GetOptionValue(FlagForSwitch(Switch::MsaaSamples),
&msaa_samples);
if (msaa_samples == "0") {
settings.msaa_samples = 0;
} else if (msaa_samples == "1") {
settings.msaa_samples = 1;
} else if (msaa_samples == "2") {
settings.msaa_samples = 2;
} else if (msaa_samples == "4") {
settings.msaa_samples = 4;
} else if (msaa_samples == "8") {
settings.msaa_samples = 8;
} else if (msaa_samples == "16") {
settings.msaa_samples = 16;
} else {
FML_DLOG(ERROR) << "Invalid value for --msaa-samples: '" << msaa_samples
<< "' (expected 0, 1, 2, 4, 8, or 16).";
}
}
return settings;
}
} // namespace flutter