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 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 "--null_assertions",
66 "--strict_null_safety_checks",
67 "--max_subtype_cache_entries",
68};
69// clang-format on
70
71#endif // FLUTTER_RELEASE
72
73// Include again for struct definition.
74#include "flutter/shell/common/switches.h"
75
76// Define symbols for the ICU data that is linked into the Flutter library on
77// Android. This is a workaround for crashes seen when doing dynamic lookups
78// of the engine's own symbols on some older versions of Android.
79#if FML_OS_ANDROID
80extern uint8_t _binary_icudtl_dat_start[];
81extern uint8_t _binary_icudtl_dat_end[];
82
83static std::unique_ptr<fml::Mapping> GetICUStaticMapping() {
84 return std::make_unique<fml::NonOwnedMapping>(
85 _binary_icudtl_dat_start,
86 _binary_icudtl_dat_end - _binary_icudtl_dat_start);
87}
88#endif
89
90namespace flutter {
91
92void PrintUsage(const std::string& executable_name) {
93 std::cerr << std::endl << " " << executable_name << std::endl << std::endl;
94
95 std::cerr << "Versions: " << std::endl << std::endl;
96
97 std::cerr << "Flutter Engine Version: " << GetFlutterEngineVersion()
98 << std::endl;
99 std::cerr << "Skia Version: " << GetSkiaVersion() << std::endl;
100
101 std::cerr << "Dart Version: " << GetDartVersion() << std::endl << std::endl;
102
103 std::cerr << "Available Flags:" << std::endl;
104
105 const uint32_t column_width = 80;
106
107 const uint32_t flags_count = static_cast<uint32_t>(Switch::Sentinel);
108
109 uint32_t max_width = 2;
110 for (uint32_t i = 0; i < flags_count; i++) {
111 auto desc = gSwitchDescs[i];
112 max_width = std::max<uint32_t>(a: desc.flag.size() + 2, b: max_width);
113 }
114
115 const uint32_t help_width = column_width - max_width - 3;
116
117 std::cerr << std::string(column_width, '-') << std::endl;
118 for (uint32_t i = 0; i < flags_count; i++) {
119 auto desc = gSwitchDescs[i];
120
121 std::cerr << std::setw(max_width)
122 << std::string("--") +
123 std::string{desc.flag.data(), desc.flag.size()}
124 << " : ";
125
126 std::istringstream stream(desc.help);
127 int32_t remaining = help_width;
128
129 std::string word;
130 while (stream >> word && remaining > 0) {
131 remaining -= (word.size() + 1);
132 if (remaining <= 0) {
133 std::cerr << std::endl
134 << std::string(max_width, ' ') << " " << word << " ";
135 remaining = help_width;
136 } else {
137 std::cerr << word << " ";
138 }
139 }
140
141 std::cerr << std::endl;
142 }
143 std::cerr << std::string(column_width, '-') << std::endl;
144}
145
146const std::string_view FlagForSwitch(Switch swtch) {
147 for (uint32_t i = 0; i < static_cast<uint32_t>(Switch::Sentinel); i++) {
148 if (gSwitchDescs[i].sw == swtch) {
149 return gSwitchDescs[i].flag;
150 }
151 }
152 return std::string_view();
153}
154
155static std::vector<std::string> ParseCommaDelimited(const std::string& input) {
156 std::istringstream ss(input);
157 std::vector<std::string> result;
158 std::string token;
159 while (std::getline(is&: ss, str&: token, dlm: ',')) {
160 result.push_back(x: token);
161 }
162 return result;
163}
164
165static bool IsAllowedDartVMFlag(const std::string& flag) {
166 for (uint32_t i = 0; i < fml::size(array: kAllowedDartFlags); ++i) {
167 const std::string& allowed = kAllowedDartFlags[i];
168 // Check that the prefix of the flag matches one of the allowed flags. This
169 // is to handle cases where flags take arguments, such as in
170 // "--max_profile_depth 1".
171 //
172 // We don't need to worry about cases like "--safe --sneaky_dangerous" as
173 // the VM will discard these as a single unrecognized flag.
174 if (flag.length() >= allowed.length() &&
175 std::equal(first1: allowed.begin(), last1: allowed.end(), first2: flag.begin())) {
176 return true;
177 }
178 }
179 return false;
180}
181
182template <typename T>
183static bool GetSwitchValue(const fml::CommandLine& command_line,
184 Switch sw,
185 T* result) {
186 std::string switch_string;
187
188 if (!command_line.GetOptionValue(name: FlagForSwitch(swtch: sw), value: &switch_string)) {
189 return false;
190 }
191
192 std::stringstream stream(switch_string);
193 T value = 0;
194 if (stream >> value) {
195 *result = value;
196 return true;
197 }
198
199 return false;
200}
201
202std::unique_ptr<fml::Mapping> GetSymbolMapping(
203 const std::string& symbol_prefix,
204 const std::string& native_lib_path) {
205 const uint8_t* mapping = nullptr;
206 intptr_t size;
207
208 auto lookup_symbol = [&mapping, &size, symbol_prefix](
209 const fml::RefPtr<fml::NativeLibrary>& library) {
210 mapping = library->ResolveSymbol(symbol: (symbol_prefix + "_start").c_str());
211 size = reinterpret_cast<intptr_t>(
212 library->ResolveSymbol(symbol: (symbol_prefix + "_size").c_str()));
213 };
214
215 fml::RefPtr<fml::NativeLibrary> library =
216 fml::NativeLibrary::CreateForCurrentProcess();
217 lookup_symbol(library);
218
219 if (!(mapping && size)) {
220 // Symbol lookup for the current process fails on some devices. As a
221 // fallback, try doing the lookup based on the path to the Flutter library.
222 library = fml::NativeLibrary::Create(path: native_lib_path.c_str());
223 lookup_symbol(library);
224 }
225
226 FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
227 return std::make_unique<fml::NonOwnedMapping>(args&: mapping, args&: size);
228}
229
230Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
231 Settings settings = {};
232
233 // Set executable name.
234 if (command_line.has_argv0()) {
235 settings.executable_name = command_line.argv0();
236 }
237
238 // Enable the VM Service
239 settings.enable_vm_service =
240 !command_line.HasOption(name: FlagForSwitch(swtch: Switch::DisableVMService)) &&
241 // TODO(bkonyi): remove once flutter_tools no longer uses this option.
242 // See https://github.com/dart-lang/sdk/issues/50233
243 !command_line.HasOption(name: FlagForSwitch(swtch: Switch::DisableObservatory));
244
245 // Enable mDNS VM Service Publication
246 settings.enable_vm_service_publication =
247 !command_line.HasOption(
248 name: FlagForSwitch(swtch: Switch::DisableVMServicePublication)) &&
249 !command_line.HasOption(
250 name: FlagForSwitch(swtch: Switch::DisableObservatoryPublication));
251
252 // Set VM Service Host
253 if (command_line.HasOption(name: FlagForSwitch(swtch: Switch::DeviceVMServiceHost))) {
254 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::DeviceVMServiceHost),
255 value: &settings.vm_service_host);
256 } else if (command_line.HasOption(
257 name: FlagForSwitch(swtch: Switch::DeviceObservatoryHost))) {
258 // TODO(bkonyi): remove once flutter_tools no longer uses this option.
259 // See https://github.com/dart-lang/sdk/issues/50233
260 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::DeviceObservatoryHost),
261 value: &settings.vm_service_host);
262 }
263 // Default the VM Service port based on --ipv6 if not set.
264 if (settings.vm_service_host.empty()) {
265 settings.vm_service_host =
266 command_line.HasOption(name: FlagForSwitch(swtch: Switch::IPv6)) ? "::1"
267 : "127.0.0.1";
268 }
269
270 // Set VM Service Port
271 if (command_line.HasOption(name: FlagForSwitch(swtch: Switch::DeviceVMServicePort))) {
272 if (!GetSwitchValue(command_line, sw: Switch::DeviceVMServicePort,
273 result: &settings.vm_service_port)) {
274 FML_LOG(INFO)
275 << "VM Service port specified was malformed. Will default to "
276 << settings.vm_service_port;
277 }
278 } else if (command_line.HasOption(
279 name: FlagForSwitch(swtch: Switch::DeviceObservatoryPort))) {
280 // TODO(bkonyi): remove once flutter_tools no longer uses this option.
281 // See https://github.com/dart-lang/sdk/issues/50233
282 if (!GetSwitchValue(command_line, sw: Switch::DeviceObservatoryPort,
283 result: &settings.vm_service_port)) {
284 FML_LOG(INFO)
285 << "VM Service port specified was malformed. Will default to "
286 << settings.vm_service_port;
287 }
288 }
289
290 settings.may_insecurely_connect_to_all_domains = !command_line.HasOption(
291 name: FlagForSwitch(swtch: Switch::DisallowInsecureConnections));
292
293 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::DomainNetworkPolicy),
294 value: &settings.domain_network_policy);
295
296 // Disable need for authentication codes for VM service communication, if
297 // specified.
298 settings.disable_service_auth_codes =
299 command_line.HasOption(name: FlagForSwitch(swtch: Switch::DisableServiceAuthCodes));
300
301 // Allow fallback to automatic port selection if binding to a specified port
302 // fails.
303 settings.enable_service_port_fallback =
304 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EnableServicePortFallback));
305
306 // Checked mode overrides.
307 settings.disable_dart_asserts =
308 command_line.HasOption(name: FlagForSwitch(swtch: Switch::DisableDartAsserts));
309
310 settings.start_paused =
311 command_line.HasOption(name: FlagForSwitch(swtch: Switch::StartPaused));
312
313 settings.enable_checked_mode =
314 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EnableCheckedMode));
315
316 settings.enable_dart_profiling =
317 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EnableDartProfiling));
318
319 settings.enable_software_rendering =
320 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EnableSoftwareRendering));
321
322 settings.endless_trace_buffer =
323 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EndlessTraceBuffer));
324
325 settings.trace_startup =
326 command_line.HasOption(name: FlagForSwitch(swtch: Switch::TraceStartup));
327
328 settings.enable_serial_gc =
329 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EnableSerialGC));
330
331#if !FLUTTER_RELEASE
332 settings.trace_skia = true;
333
334 if (command_line.HasOption(name: FlagForSwitch(swtch: Switch::TraceSkia))) {
335 // If --trace-skia is specified, then log all Skia events.
336 settings.trace_skia_allowlist.reset();
337 } else {
338 std::string trace_skia_allowlist;
339 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::TraceSkiaAllowlist),
340 value: &trace_skia_allowlist);
341 if (trace_skia_allowlist.size()) {
342 settings.trace_skia_allowlist = ParseCommaDelimited(input: trace_skia_allowlist);
343 } else {
344 settings.trace_skia_allowlist = {"skia.shaders"};
345 }
346 }
347#endif // !FLUTTER_RELEASE
348
349 std::string trace_allowlist;
350 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::TraceAllowlist),
351 value: &trace_allowlist);
352 settings.trace_allowlist = ParseCommaDelimited(input: trace_allowlist);
353
354 settings.trace_systrace =
355 command_line.HasOption(name: FlagForSwitch(swtch: Switch::TraceSystrace));
356
357 settings.skia_deterministic_rendering_on_cpu =
358 command_line.HasOption(name: FlagForSwitch(swtch: Switch::SkiaDeterministicRendering));
359
360 settings.verbose_logging =
361 command_line.HasOption(name: FlagForSwitch(swtch: Switch::VerboseLogging));
362
363 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::FlutterAssetsDir),
364 value: &settings.assets_path);
365
366 std::vector<std::string_view> aot_shared_library_name =
367 command_line.GetOptionValues(name: FlagForSwitch(swtch: Switch::AotSharedLibraryName));
368
369 std::vector<std::string_view> vmservice_shared_library_name =
370 command_line.GetOptionValues(
371 name: FlagForSwitch(swtch: Switch::AotVMServiceSharedLibraryName));
372 for (auto path : vmservice_shared_library_name) {
373 settings.vmservice_snapshot_library_path.emplace_back(args&: path);
374 }
375
376 std::string snapshot_asset_path;
377 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::SnapshotAssetPath),
378 value: &snapshot_asset_path);
379
380 std::string vm_snapshot_data_filename;
381 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::VmSnapshotData),
382 value: &vm_snapshot_data_filename);
383
384 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::Route), value: &settings.route);
385
386 std::string vm_snapshot_instr_filename;
387 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::VmSnapshotInstructions),
388 value: &vm_snapshot_instr_filename);
389
390 std::string isolate_snapshot_data_filename;
391 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::IsolateSnapshotData),
392 value: &isolate_snapshot_data_filename);
393
394 std::string isolate_snapshot_instr_filename;
395 command_line.GetOptionValue(
396 name: FlagForSwitch(swtch: Switch::IsolateSnapshotInstructions),
397 value: &isolate_snapshot_instr_filename);
398
399 if (!aot_shared_library_name.empty()) {
400 for (std::string_view name : aot_shared_library_name) {
401 settings.application_library_path.emplace_back(args&: name);
402 }
403 } else if (!snapshot_asset_path.empty()) {
404 settings.vm_snapshot_data_path =
405 fml::paths::JoinPaths(components: {snapshot_asset_path, vm_snapshot_data_filename});
406 settings.vm_snapshot_instr_path = fml::paths::JoinPaths(
407 components: {snapshot_asset_path, vm_snapshot_instr_filename});
408 settings.isolate_snapshot_data_path = fml::paths::JoinPaths(
409 components: {snapshot_asset_path, isolate_snapshot_data_filename});
410 settings.isolate_snapshot_instr_path = fml::paths::JoinPaths(
411 components: {snapshot_asset_path, isolate_snapshot_instr_filename});
412 }
413
414 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::CacheDirPath),
415 value: &settings.temp_directory_path);
416
417 bool leak_vm = "true" == command_line.GetOptionValueWithDefault(
418 name: FlagForSwitch(swtch: Switch::LeakVM), default_value: "true");
419 settings.leak_vm = leak_vm;
420
421 if (settings.icu_initialization_required) {
422 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::ICUDataFilePath),
423 value: &settings.icu_data_path);
424 if (command_line.HasOption(name: FlagForSwitch(swtch: Switch::ICUSymbolPrefix))) {
425 std::string icu_symbol_prefix, native_lib_path;
426 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::ICUSymbolPrefix),
427 value: &icu_symbol_prefix);
428 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::ICUNativeLibPath),
429 value: &native_lib_path);
430
431#if FML_OS_ANDROID
432 settings.icu_mapper = GetICUStaticMapping;
433#else
434 settings.icu_mapper = [icu_symbol_prefix, native_lib_path] {
435 return GetSymbolMapping(symbol_prefix: icu_symbol_prefix, native_lib_path);
436 };
437#endif
438 }
439 }
440
441 settings.use_test_fonts =
442 command_line.HasOption(name: FlagForSwitch(swtch: Switch::UseTestFonts));
443 settings.use_asset_fonts =
444 !command_line.HasOption(name: FlagForSwitch(swtch: Switch::DisableAssetFonts));
445
446 {
447 std::string enable_impeller_value;
448 if (command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::EnableImpeller),
449 value: &enable_impeller_value)) {
450 settings.enable_impeller =
451 enable_impeller_value.empty() || "true" == enable_impeller_value;
452 }
453 }
454
455 {
456 std::string impeller_backend_value;
457 if (command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::ImpellerBackend),
458 value: &impeller_backend_value)) {
459 if (!impeller_backend_value.empty()) {
460 settings.impeller_backend = impeller_backend_value;
461 }
462 }
463 }
464
465 settings.enable_vulkan_validation =
466 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EnableVulkanValidation));
467
468 settings.enable_embedder_api =
469 command_line.HasOption(name: FlagForSwitch(swtch: Switch::EnableEmbedderAPI));
470
471 settings.prefetched_default_font_manager = command_line.HasOption(
472 name: FlagForSwitch(swtch: Switch::PrefetchedDefaultFontManager));
473
474 std::string all_dart_flags;
475 if (command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::DartFlags),
476 value: &all_dart_flags)) {
477 // Assume that individual flags are comma separated.
478 std::vector<std::string> flags = ParseCommaDelimited(input: all_dart_flags);
479 for (auto flag : flags) {
480 if (!IsAllowedDartVMFlag(flag)) {
481 FML_LOG(FATAL) << "Encountered disallowed Dart VM flag: " << flag;
482 }
483 settings.dart_flags.push_back(x: flag);
484 }
485 }
486
487#if !FLUTTER_RELEASE
488 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::LogTag), value: &settings.log_tag);
489#endif
490
491 settings.dump_skp_on_shader_compilation =
492 command_line.HasOption(name: FlagForSwitch(swtch: Switch::DumpSkpOnShaderCompilation));
493
494 settings.cache_sksl =
495 command_line.HasOption(name: FlagForSwitch(swtch: Switch::CacheSkSL));
496
497 settings.purge_persistent_cache =
498 command_line.HasOption(name: FlagForSwitch(swtch: Switch::PurgePersistentCache));
499
500 if (command_line.HasOption(name: FlagForSwitch(swtch: Switch::OldGenHeapSize))) {
501 std::string old_gen_heap_size;
502 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::OldGenHeapSize),
503 value: &old_gen_heap_size);
504 settings.old_gen_heap_size = std::stoi(str: old_gen_heap_size);
505 }
506
507 if (command_line.HasOption(
508 name: FlagForSwitch(swtch: Switch::ResourceCacheMaxBytesThreshold))) {
509 std::string resource_cache_max_bytes_threshold;
510 command_line.GetOptionValue(
511 name: FlagForSwitch(swtch: Switch::ResourceCacheMaxBytesThreshold),
512 value: &resource_cache_max_bytes_threshold);
513 settings.resource_cache_max_bytes_threshold =
514 std::stoi(str: resource_cache_max_bytes_threshold);
515 }
516
517 if (command_line.HasOption(name: FlagForSwitch(swtch: Switch::MsaaSamples))) {
518 std::string msaa_samples;
519 command_line.GetOptionValue(name: FlagForSwitch(swtch: Switch::MsaaSamples),
520 value: &msaa_samples);
521 if (msaa_samples == "0") {
522 settings.msaa_samples = 0;
523 } else if (msaa_samples == "1") {
524 settings.msaa_samples = 1;
525 } else if (msaa_samples == "2") {
526 settings.msaa_samples = 2;
527 } else if (msaa_samples == "4") {
528 settings.msaa_samples = 4;
529 } else if (msaa_samples == "8") {
530 settings.msaa_samples = 8;
531 } else if (msaa_samples == "16") {
532 settings.msaa_samples = 16;
533 } else {
534 FML_DLOG(ERROR) << "Invalid value for --msaa-samples: '" << msaa_samples
535 << "' (expected 0, 1, 2, 4, 8, or 16).";
536 }
537 }
538
539 return settings;
540}
541
542} // namespace flutter
543

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com

source code of flutter_engine/flutter/shell/common/switches.cc