Flutter Engine
The Flutter Engine
switches.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <iomanip>
7#include <iostream>
8#include <iterator>
9#include <sstream>
10#include <string>
11
12#include "flutter/fml/native_library.h"
13#include "flutter/fml/paths.h"
14#include "flutter/fml/size.h"
15#include "flutter/shell/version/version.h"
16
17// Include once for the default enum definition.
18#include "flutter/shell/common/switches.h"
19
20#undef FLUTTER_SHELL_COMMON_SWITCHES_H_
21
22struct SwitchDesc {
23 flutter::Switch sw;
24 const std::string_view flag;
25 const char* help;
26};
27
28#undef DEF_SWITCHES_START
29#undef DEF_SWITCH
30#undef DEF_SWITCHES_END
31
32// clang-format off
33#define DEF_SWITCHES_START static const struct SwitchDesc gSwitchDescs[] = {
34#define DEF_SWITCH(p_swtch, p_flag, p_help) \
35 { flutter::Switch:: p_swtch, p_flag, p_help },
36#define DEF_SWITCHES_END };
37// clang-format on
38
39// List of common and safe VM flags to allow to be passed directly to the VM.
40#if FLUTTER_RELEASE
41
42// clang-format off
43static const std::string kAllowedDartFlags[] = {
44 "--enable-isolate-groups",
45 "--no-enable-isolate-groups",
46};
47// clang-format on
48
49#else
50
51// clang-format off
52static const std::string kAllowedDartFlags[] = {
53 "--enable-isolate-groups",
54 "--no-enable-isolate-groups",
55 "--enable_mirrors",
56 "--enable-service-port-fallback",
57 "--max_profile_depth",
58 "--profile_period",
59 "--random_seed",
60 "--sample-buffer-duration",
61 "--trace-kernel",
62 "--trace-reload",
63 "--trace-reload-verbose",
64 "--write-service-info",
65 "--max_subtype_cache_entries",
66 "--enable-asserts",
67};
68// clang-format on
69
70#endif // FLUTTER_RELEASE
71
72// Include again for struct definition.
73#include "flutter/shell/common/switches.h"
74
75// Define symbols for the ICU data that is linked into the Flutter library on
76// Android. This is a workaround for crashes seen when doing dynamic lookups
77// of the engine's own symbols on some older versions of Android.
78#if FML_OS_ANDROID
79extern uint8_t _binary_icudtl_dat_start[];
80extern size_t _binary_icudtl_dat_size;
81
82static std::unique_ptr<fml::Mapping> GetICUStaticMapping() {
83 return std::make_unique<fml::NonOwnedMapping>(_binary_icudtl_dat_start,
84 _binary_icudtl_dat_size);
85}
86#endif
87
88namespace flutter {
89
90void PrintUsage(const std::string& executable_name) {
91 std::cerr << std::endl << " " << executable_name << std::endl << std::endl;
92
93 std::cerr << "Versions: " << std::endl << std::endl;
94
95 std::cerr << "Flutter Engine Version: " << GetFlutterEngineVersion()
96 << std::endl;
97 std::cerr << "Skia Version: " << GetSkiaVersion() << std::endl;
98
99 std::cerr << "Dart Version: " << GetDartVersion() << std::endl << std::endl;
100
101 std::cerr << "Available Flags:" << std::endl;
102
103 const uint32_t column_width = 80;
104
105 const uint32_t flags_count = static_cast<uint32_t>(Switch::Sentinel);
106
107 uint32_t max_width = 2;
108 for (uint32_t i = 0; i < flags_count; i++) {
109 auto desc = gSwitchDescs[i];
110 max_width = std::max<uint32_t>(desc.flag.size() + 2, max_width);
111 }
112
113 const uint32_t help_width = column_width - max_width - 3;
114
115 std::cerr << std::string(column_width, '-') << std::endl;
116 for (uint32_t i = 0; i < flags_count; i++) {
117 auto desc = gSwitchDescs[i];
118
119 std::cerr << std::setw(max_width)
120 << std::string("--") +
121 std::string{desc.flag.data(), desc.flag.size()}
122 << " : ";
123
124 std::istringstream stream(desc.help);
125 int32_t remaining = help_width;
126
127 std::string word;
128 while (stream >> word && remaining > 0) {
129 remaining -= (word.size() + 1);
130 if (remaining <= 0) {
131 std::cerr << std::endl
132 << std::string(max_width, ' ') << " " << word << " ";
133 remaining = help_width;
134 } else {
135 std::cerr << word << " ";
136 }
137 }
138
139 std::cerr << std::endl;
140 }
141 std::cerr << std::string(column_width, '-') << std::endl;
142}
143
144const std::string_view FlagForSwitch(Switch swtch) {
145 for (uint32_t i = 0; i < static_cast<uint32_t>(Switch::Sentinel); i++) {
146 if (gSwitchDescs[i].sw == swtch) {
147 return gSwitchDescs[i].flag;
148 }
149 }
150 return std::string_view();
151}
152
153static std::vector<std::string> ParseCommaDelimited(const std::string& input) {
154 std::istringstream ss(input);
155 std::vector<std::string> result;
156 std::string token;
157 while (std::getline(ss, token, ',')) {
158 result.push_back(token);
159 }
160 return result;
161}
162
163static bool IsAllowedDartVMFlag(const std::string& flag) {
164 for (uint32_t i = 0; i < fml::size(kAllowedDartFlags); ++i) {
165 const std::string& allowed = kAllowedDartFlags[i];
166 // Check that the prefix of the flag matches one of the allowed flags. This
167 // is to handle cases where flags take arguments, such as in
168 // "--max_profile_depth 1".
169 //
170 // We don't need to worry about cases like "--safe --sneaky_dangerous" as
171 // the VM will discard these as a single unrecognized flag.
172 if (flag.length() >= allowed.length() &&
173 std::equal(allowed.begin(), allowed.end(), flag.begin())) {
174 return true;
175 }
176 }
177 return false;
178}
179
180template <typename T>
181static bool GetSwitchValue(const fml::CommandLine& command_line,
182 Switch sw,
183 T* result) {
184 std::string switch_string;
185
186 if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) {
187 return false;
188 }
189
190 std::stringstream stream(switch_string);
191 T value = 0;
192 if (stream >> value) {
193 *result = value;
194 return true;
195 }
196
197 return false;
198}
199
200std::unique_ptr<fml::Mapping> GetSymbolMapping(
201 const std::string& symbol_prefix,
202 const std::string& native_lib_path) {
203 const uint8_t* mapping = nullptr;
204 intptr_t size;
205
206 auto lookup_symbol = [&mapping, &size, symbol_prefix](
207 const fml::RefPtr<fml::NativeLibrary>& library) {
208 mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str());
209 size = reinterpret_cast<intptr_t>(
210 library->ResolveSymbol((symbol_prefix + "_size").c_str()));
211 };
212
215 lookup_symbol(library);
216
217 if (!(mapping && size)) {
218 // Symbol lookup for the current process fails on some devices. As a
219 // fallback, try doing the lookup based on the path to the Flutter library.
220 library = fml::NativeLibrary::Create(native_lib_path.c_str());
221 lookup_symbol(library);
222 }
223
224 FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
225 return std::make_unique<fml::NonOwnedMapping>(mapping, size);
226}
227
229 Settings settings = {};
230
231 // Set executable name.
232 if (command_line.has_argv0()) {
233 settings.executable_name = command_line.argv0();
234 }
235
236 // Enable the VM Service
237 settings.enable_vm_service =
238 !command_line.HasOption(FlagForSwitch(Switch::DisableVMService)) &&
239 // TODO(bkonyi): remove once flutter_tools no longer uses this option.
240 // See https://github.com/dart-lang/sdk/issues/50233
241 !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory));
242
243 // Enable mDNS VM Service Publication
244 settings.enable_vm_service_publication =
245 !command_line.HasOption(
246 FlagForSwitch(Switch::DisableVMServicePublication)) &&
247 !command_line.HasOption(
248 FlagForSwitch(Switch::DisableObservatoryPublication));
249
250 // Set VM Service Host
251 if (command_line.HasOption(FlagForSwitch(Switch::DeviceVMServiceHost))) {
252 command_line.GetOptionValue(FlagForSwitch(Switch::DeviceVMServiceHost),
253 &settings.vm_service_host);
254 } else if (command_line.HasOption(
255 FlagForSwitch(Switch::DeviceObservatoryHost))) {
256 // TODO(bkonyi): remove once flutter_tools no longer uses this option.
257 // See https://github.com/dart-lang/sdk/issues/50233
258 command_line.GetOptionValue(FlagForSwitch(Switch::DeviceObservatoryHost),
259 &settings.vm_service_host);
260 }
261 // Default the VM Service port based on --ipv6 if not set.
262 if (settings.vm_service_host.empty()) {
263 settings.vm_service_host =
264 command_line.HasOption(FlagForSwitch(Switch::IPv6)) ? "::1"
265 : "127.0.0.1";
266 }
267
268 // Set VM Service Port
269 if (command_line.HasOption(FlagForSwitch(Switch::DeviceVMServicePort))) {
270 if (!GetSwitchValue(command_line, Switch::DeviceVMServicePort,
271 &settings.vm_service_port)) {
272 FML_LOG(INFO)
273 << "VM Service port specified was malformed. Will default to "
274 << settings.vm_service_port;
275 }
276 } else if (command_line.HasOption(
277 FlagForSwitch(Switch::DeviceObservatoryPort))) {
278 // TODO(bkonyi): remove once flutter_tools no longer uses this option.
279 // See https://github.com/dart-lang/sdk/issues/50233
280 if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort,
281 &settings.vm_service_port)) {
282 FML_LOG(INFO)
283 << "VM Service port specified was malformed. Will default to "
284 << settings.vm_service_port;
285 }
286 }
287
288 settings.may_insecurely_connect_to_all_domains = !command_line.HasOption(
289 FlagForSwitch(Switch::DisallowInsecureConnections));
290
291 command_line.GetOptionValue(FlagForSwitch(Switch::DomainNetworkPolicy),
292 &settings.domain_network_policy);
293
294 // Disable need for authentication codes for VM service communication, if
295 // specified.
296 settings.disable_service_auth_codes =
297 command_line.HasOption(FlagForSwitch(Switch::DisableServiceAuthCodes));
298
299 // Allow fallback to automatic port selection if binding to a specified port
300 // fails.
301 settings.enable_service_port_fallback =
302 command_line.HasOption(FlagForSwitch(Switch::EnableServicePortFallback));
303
304 // Checked mode overrides.
305 settings.disable_dart_asserts =
306 command_line.HasOption(FlagForSwitch(Switch::DisableDartAsserts));
307
308 settings.start_paused =
309 command_line.HasOption(FlagForSwitch(Switch::StartPaused));
310
311 settings.enable_checked_mode =
312 command_line.HasOption(FlagForSwitch(Switch::EnableCheckedMode));
313
314 settings.enable_dart_profiling =
315 command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling));
316
317 settings.enable_software_rendering =
318 command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering));
319
320 settings.endless_trace_buffer =
321 command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer));
322
323 settings.trace_startup =
324 command_line.HasOption(FlagForSwitch(Switch::TraceStartup));
325
326 settings.enable_serial_gc =
327 command_line.HasOption(FlagForSwitch(Switch::EnableSerialGC));
328
329#if !FLUTTER_RELEASE
330 settings.trace_skia = true;
331
332 if (command_line.HasOption(FlagForSwitch(Switch::TraceSkia))) {
333 // If --trace-skia is specified, then log all Skia events.
334 settings.trace_skia_allowlist.reset();
335 } else {
336 std::string trace_skia_allowlist;
337 command_line.GetOptionValue(FlagForSwitch(Switch::TraceSkiaAllowlist),
338 &trace_skia_allowlist);
339 if (trace_skia_allowlist.size()) {
340 settings.trace_skia_allowlist = ParseCommaDelimited(trace_skia_allowlist);
341 } else {
342 settings.trace_skia_allowlist = {"skia.shaders"};
343 }
344 }
345#endif // !FLUTTER_RELEASE
346
347 std::string trace_allowlist;
348 command_line.GetOptionValue(FlagForSwitch(Switch::TraceAllowlist),
349 &trace_allowlist);
350 settings.trace_allowlist = ParseCommaDelimited(trace_allowlist);
351
352 settings.trace_systrace =
353 command_line.HasOption(FlagForSwitch(Switch::TraceSystrace));
354
355 command_line.GetOptionValue(FlagForSwitch(Switch::TraceToFile),
356 &settings.trace_to_file);
357
358 settings.skia_deterministic_rendering_on_cpu =
359 command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering));
360
361 settings.verbose_logging =
362 command_line.HasOption(FlagForSwitch(Switch::VerboseLogging));
363
364 command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir),
365 &settings.assets_path);
366
367 std::vector<std::string_view> aot_shared_library_name =
368 command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName));
369
370 std::vector<std::string_view> vmservice_shared_library_name =
371 command_line.GetOptionValues(
372 FlagForSwitch(Switch::AotVMServiceSharedLibraryName));
373 for (auto path : vmservice_shared_library_name) {
374 settings.vmservice_snapshot_library_path.emplace_back(path);
375 }
376
377 std::string snapshot_asset_path;
378 command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath),
379 &snapshot_asset_path);
380
381 std::string vm_snapshot_data_filename;
382 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotData),
383 &vm_snapshot_data_filename);
384
385 command_line.GetOptionValue(FlagForSwitch(Switch::Route), &settings.route);
386
387 std::string vm_snapshot_instr_filename;
388 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotInstructions),
389 &vm_snapshot_instr_filename);
390
391 std::string isolate_snapshot_data_filename;
392 command_line.GetOptionValue(FlagForSwitch(Switch::IsolateSnapshotData),
393 &isolate_snapshot_data_filename);
394
395 std::string isolate_snapshot_instr_filename;
396 command_line.GetOptionValue(
397 FlagForSwitch(Switch::IsolateSnapshotInstructions),
398 &isolate_snapshot_instr_filename);
399
400 if (!aot_shared_library_name.empty()) {
401 for (std::string_view name : aot_shared_library_name) {
402 settings.application_library_path.emplace_back(name);
403 }
404 } else if (!snapshot_asset_path.empty()) {
405 settings.vm_snapshot_data_path =
406 fml::paths::JoinPaths({snapshot_asset_path, vm_snapshot_data_filename});
407 settings.vm_snapshot_instr_path = fml::paths::JoinPaths(
408 {snapshot_asset_path, vm_snapshot_instr_filename});
409 settings.isolate_snapshot_data_path = fml::paths::JoinPaths(
410 {snapshot_asset_path, isolate_snapshot_data_filename});
411 settings.isolate_snapshot_instr_path = fml::paths::JoinPaths(
412 {snapshot_asset_path, isolate_snapshot_instr_filename});
413 }
414
415 command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath),
416 &settings.temp_directory_path);
417
418 bool leak_vm = "true" == command_line.GetOptionValueWithDefault(
419 FlagForSwitch(Switch::LeakVM), "true");
420 settings.leak_vm = leak_vm;
421
422 if (settings.icu_initialization_required) {
423 command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
424 &settings.icu_data_path);
425 if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
426 std::string icu_symbol_prefix, native_lib_path;
427 command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
428 &icu_symbol_prefix);
429 command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath),
430 &native_lib_path);
431
432#if FML_OS_ANDROID
433 settings.icu_mapper = GetICUStaticMapping;
434#else
435 settings.icu_mapper = [icu_symbol_prefix, native_lib_path] {
436 return GetSymbolMapping(icu_symbol_prefix, native_lib_path);
437 };
438#endif
439 }
440 }
441
442 settings.use_test_fonts =
443 command_line.HasOption(FlagForSwitch(Switch::UseTestFonts));
444 settings.use_asset_fonts =
445 !command_line.HasOption(FlagForSwitch(Switch::DisableAssetFonts));
446
447 {
448 std::string enable_impeller_value;
449 if (command_line.GetOptionValue(FlagForSwitch(Switch::EnableImpeller),
450 &enable_impeller_value)) {
451 settings.enable_impeller =
452 enable_impeller_value.empty() || "true" == enable_impeller_value;
453 }
454 }
455
456 {
457 std::string impeller_backend_value;
458 if (command_line.GetOptionValue(FlagForSwitch(Switch::ImpellerBackend),
459 &impeller_backend_value)) {
460 if (!impeller_backend_value.empty()) {
461 settings.requested_rendering_backend = impeller_backend_value;
462 }
463 }
464 }
465
466 settings.enable_vulkan_validation =
467 command_line.HasOption(FlagForSwitch(Switch::EnableVulkanValidation));
468 settings.enable_opengl_gpu_tracing =
469 command_line.HasOption(FlagForSwitch(Switch::EnableOpenGLGPUTracing));
470 settings.enable_vulkan_gpu_tracing =
471 command_line.HasOption(FlagForSwitch(Switch::EnableVulkanGPUTracing));
472
473 settings.enable_embedder_api =
474 command_line.HasOption(FlagForSwitch(Switch::EnableEmbedderAPI));
475
476 settings.prefetched_default_font_manager = command_line.HasOption(
477 FlagForSwitch(Switch::PrefetchedDefaultFontManager));
478
479 std::string all_dart_flags;
480 if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags),
481 &all_dart_flags)) {
482 // Assume that individual flags are comma separated.
483 std::vector<std::string> flags = ParseCommaDelimited(all_dart_flags);
484 for (const auto& flag : flags) {
486 FML_LOG(FATAL) << "Encountered disallowed Dart VM flag: " << flag;
487 }
488 settings.dart_flags.push_back(flag);
489 }
490 }
491
492#if !FLUTTER_RELEASE
493 command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag);
494#endif
495
496 settings.dump_skp_on_shader_compilation =
497 command_line.HasOption(FlagForSwitch(Switch::DumpSkpOnShaderCompilation));
498
499 settings.cache_sksl =
500 command_line.HasOption(FlagForSwitch(Switch::CacheSkSL));
501
502 settings.purge_persistent_cache =
503 command_line.HasOption(FlagForSwitch(Switch::PurgePersistentCache));
504
505 if (command_line.HasOption(FlagForSwitch(Switch::OldGenHeapSize))) {
506 std::string old_gen_heap_size;
507 command_line.GetOptionValue(FlagForSwitch(Switch::OldGenHeapSize),
508 &old_gen_heap_size);
509 settings.old_gen_heap_size = std::stoi(old_gen_heap_size);
510 }
511
512 if (command_line.HasOption(
513 FlagForSwitch(Switch::ResourceCacheMaxBytesThreshold))) {
514 std::string resource_cache_max_bytes_threshold;
515 command_line.GetOptionValue(
516 FlagForSwitch(Switch::ResourceCacheMaxBytesThreshold),
517 &resource_cache_max_bytes_threshold);
518 settings.resource_cache_max_bytes_threshold =
519 std::stoi(resource_cache_max_bytes_threshold);
520 }
521
522 settings.enable_platform_isolates =
523 command_line.HasOption(FlagForSwitch(Switch::EnablePlatformIsolates));
524
525 return settings;
526}
527
528} // namespace flutter
static bool equal(const SkBitmap &a, const SkBitmap &b)
Definition: ImageTest.cpp:1395
const std::string & argv0() const
Definition: command_line.h:94
std::vector< std::string_view > GetOptionValues(std::string_view name) const
Definition: command_line.cc:61
std::string GetOptionValueWithDefault(std::string_view name, std::string_view default_value) const
Definition: command_line.cc:72
bool HasOption(std::string_view name, size_t *index=nullptr) const
Definition: command_line.cc:40
bool GetOptionValue(std::string_view name, std::string *value) const
Definition: command_line.cc:51
bool has_argv0() const
Definition: command_line.h:93
static fml::RefPtr< NativeLibrary > CreateForCurrentProcess()
static fml::RefPtr< NativeLibrary > Create(const char *path)
#define FATAL(error)
FlutterSemanticsFlag flag
FlutterSemanticsFlag flags
uint8_t value
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_CHECK(condition)
Definition: logging.h:85
bool stoi(std::string_view s, SKSL_INT *value)
Definition: SkSLString.cpp:66
intptr_t word
Definition: globals.h:500
const char * GetDartVersion()
Definition: version.cc:19
const char * GetSkiaVersion()
Definition: version.cc:15
static bool IsAllowedDartVMFlag(const std::string &flag)
Definition: switches.cc:163
static bool GetSwitchValue(const fml::CommandLine &command_line, Switch sw, T *result)
Definition: switches.cc:181
void PrintUsage(const std::string &executable_name)
Definition: switches.cc:90
Settings SettingsFromCommandLine(const fml::CommandLine &command_line)
Definition: switches.cc:228
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
const char * GetFlutterEngineVersion()
Definition: version.cc:11
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
static std::vector< std::string > ParseCommaDelimited(const std::string &input)
Definition: switches.cc:153
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
std::unique_ptr< fml::Mapping > GetSymbolMapping(const std::string &symbol_prefix, const std::string &native_lib_path)
Definition: switches.cc:200
const std::string_view FlagForSwitch(Switch swtch)
Definition: switches.cc:144
std::string JoinPaths(std::initializer_list< std::string > components)
Definition: paths.cc:14
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
#define T
Definition: precompiler.cc:65
static const std::string kAllowedDartFlags[]
Definition: switches.cc:52
const std::string_view flag
Definition: switches.cc:24
flutter::Switch sw
Definition: switches.cc:23
const char * help
Definition: switches.cc:25