// 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 #include #include #include #include #include #include "flutter/fml/native_library.h" #include "flutter/fml/paths.h" #include "flutter/shell/version/version.h" // Include once for the default enum definition. #include "flutter/shell/common/switches.h" #undef FLUTTER_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", "--max_subtype_cache_entries", "--enable-asserts", }; // 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 size_t _binary_icudtl_dat_size; static std::unique_ptr GetICUStaticMapping() { return std::make_unique(_binary_icudtl_dat_start, _binary_icudtl_dat_size); } #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(Switch::Sentinel); uint32_t max_width = 2; for (uint32_t i = 0; i < flags_count; i++) { auto desc = gSwitchDescs[i]; max_width = std::max(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(Switch::Sentinel); i++) { if (gSwitchDescs[i].sw == swtch) { return gSwitchDescs[i].flag; } } return std::string_view(); } static std::vector ParseCommaDelimited(const std::string& input) { std::istringstream ss(input); std::vector 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 < std::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 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 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& library) { mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str()); size = reinterpret_cast( library->ResolveSymbol((symbol_prefix + "_size").c_str())); }; fml::RefPtr 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(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 the VM Service settings.enable_vm_service = !command_line.HasOption(FlagForSwitch(Switch::DisableVMService)) && // TODO(bkonyi): remove once flutter_tools no longer uses this option. // See https://github.com/dart-lang/sdk/issues/50233 !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory)); // Enable mDNS VM Service Publication settings.enable_vm_service_publication = !command_line.HasOption( FlagForSwitch(Switch::DisableVMServicePublication)) && !command_line.HasOption( FlagForSwitch(Switch::DisableObservatoryPublication)); // Set VM Service Host if (command_line.HasOption(FlagForSwitch(Switch::DeviceVMServiceHost))) { command_line.GetOptionValue(FlagForSwitch(Switch::DeviceVMServiceHost), &settings.vm_service_host); } else if (command_line.HasOption( FlagForSwitch(Switch::DeviceObservatoryHost))) { // TODO(bkonyi): remove once flutter_tools no longer uses this option. // See https://github.com/dart-lang/sdk/issues/50233 command_line.GetOptionValue(FlagForSwitch(Switch::DeviceObservatoryHost), &settings.vm_service_host); } // Default the VM Service port based on --ipv6 if not set. if (settings.vm_service_host.empty()) { settings.vm_service_host = command_line.HasOption(FlagForSwitch(Switch::IPv6)) ? "::1" : "127.0.0.1"; } // Set VM Service Port if (command_line.HasOption(FlagForSwitch(Switch::DeviceVMServicePort))) { if (!GetSwitchValue(command_line, Switch::DeviceVMServicePort, &settings.vm_service_port)) { FML_LOG(INFO) << "VM Service port specified was malformed. Will default to " << settings.vm_service_port; } } else if (command_line.HasOption( FlagForSwitch(Switch::DeviceObservatoryPort))) { // TODO(bkonyi): remove once flutter_tools no longer uses this option. // See https://github.com/dart-lang/sdk/issues/50233 if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort, &settings.vm_service_port)) { FML_LOG(INFO) << "VM Service port specified was malformed. Will default to " << settings.vm_service_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)); command_line.GetOptionValue(FlagForSwitch(Switch::TraceToFile), &settings.trace_to_file); settings.skia_deterministic_rendering_on_cpu = command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering)); settings.verbose_logging = command_line.HasOption(FlagForSwitch(Switch::VerboseLogging)); settings.merged_platform_ui_thread = command_line.HasOption( FlagForSwitch(Switch::EnableMergedPlatformUIThread)); command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir), &settings.assets_path); std::vector aot_shared_library_name = command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName)); std::vector 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_impeller_value; if (command_line.GetOptionValue(FlagForSwitch(Switch::EnableImpeller), &enable_impeller_value)) { settings.enable_impeller = enable_impeller_value.empty() || "true" == enable_impeller_value; } } { std::string impeller_backend_value; if (command_line.GetOptionValue(FlagForSwitch(Switch::ImpellerBackend), &impeller_backend_value)) { if (!impeller_backend_value.empty()) { settings.requested_rendering_backend = impeller_backend_value; } } } settings.enable_vulkan_validation = command_line.HasOption(FlagForSwitch(Switch::EnableVulkanValidation)); settings.enable_opengl_gpu_tracing = command_line.HasOption(FlagForSwitch(Switch::EnableOpenGLGPUTracing)); settings.enable_vulkan_gpu_tracing = command_line.HasOption(FlagForSwitch(Switch::EnableVulkanGPUTracing)); settings.enable_embedder_api = command_line.HasOption(FlagForSwitch(Switch::EnableEmbedderAPI)); 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 flags = ParseCommaDelimited(all_dart_flags); for (const 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); } settings.enable_platform_isolates = command_line.HasOption(FlagForSwitch(Switch::EnablePlatformIsolates)); settings.disable_surface_control = command_line.HasOption( FlagForSwitch(Switch::DisableAndroidSurfaceControl)); return settings; } } // namespace flutter