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// FLUTTER_NOLINT
5
6#include <algorithm>
7#include <iomanip>
8#include <iostream>
9#include <iterator>
10#include <sstream>
11#include <string>
12
13#include "flutter/fml/native_library.h"
14#include "flutter/fml/paths.h"
15#include "flutter/fml/size.h"
16#include "flutter/shell/version/version.h"
17
18// Include once for the default enum definition.
19#include "flutter/shell/common/switches.h"
20
21#undef SHELL_COMMON_SWITCHES_H_
22
23struct SwitchDesc {
24 flutter::Switch sw;
25 const std::string_view flag;
26 const char* help;
27};
28
29#undef DEF_SWITCHES_START
30#undef DEF_SWITCH
31#undef DEF_SWITCHES_END
32
33// clang-format off
34#define DEF_SWITCHES_START static const struct SwitchDesc gSwitchDescs[] = {
35#define DEF_SWITCH(p_swtch, p_flag, p_help) \
36 { flutter::Switch:: p_swtch, p_flag, p_help },
37#define DEF_SWITCHES_END };
38// clang-format on
39
40// List of common and safe VM flags to allow to be passed directly to the VM.
41#if FLUTTER_RELEASE
42
43// clang-format off
44static const std::string gAllowedDartFlags[] = {
45 "--no-causal_async_stacks",
46 "--lazy_async_stacks",
47};
48// clang-format on
49
50#else
51
52// clang-format off
53static const std::string gAllowedDartFlags[] = {
54 "--enable_mirrors",
55 "--enable-service-port-fallback",
56 "--lazy_async_stacks",
57 "--max_profile_depth",
58 "--no-causal_async_stacks",
59 "--profile_period",
60 "--random_seed",
61 "--sample-buffer-duration",
62 "--trace-reload",
63 "--trace-reload-verbose",
64 "--write-service-info",
65 "--null_assertions",
66};
67// clang-format on
68
69#endif // FLUTTER_RELEASE
70
71// Include again for struct definition.
72#include "flutter/shell/common/switches.h"
73
74// Define symbols for the ICU data that is linked into the Flutter library on
75// Android. This is a workaround for crashes seen when doing dynamic lookups
76// of the engine's own symbols on some older versions of Android.
77#if OS_ANDROID
78extern uint8_t _binary_icudtl_dat_start[];
79extern uint8_t _binary_icudtl_dat_end[];
80
81static std::unique_ptr<fml::Mapping> GetICUStaticMapping() {
82 return std::make_unique<fml::NonOwnedMapping>(
83 _binary_icudtl_dat_start,
84 _binary_icudtl_dat_end - _binary_icudtl_dat_start);
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 bool IsAllowedDartVMFlag(const std::string& flag) {
154 for (uint32_t i = 0; i < fml::size(gAllowedDartFlags); ++i) {
155 const std::string& allowed = gAllowedDartFlags[i];
156 // Check that the prefix of the flag matches one of the allowed flags.
157 // We don't need to worry about cases like "--safe --sneaky_dangerous" as
158 // the VM will discard these as a single unrecognized flag.
159 if (std::equal(allowed.begin(), allowed.end(), flag.begin())) {
160 return true;
161 }
162 }
163 return false;
164}
165
166template <typename T>
167static bool GetSwitchValue(const fml::CommandLine& command_line,
168 Switch sw,
169 T* result) {
170 std::string switch_string;
171
172 if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) {
173 return false;
174 }
175
176 std::stringstream stream(switch_string);
177 T value = 0;
178 if (stream >> value) {
179 *result = value;
180 return true;
181 }
182
183 return false;
184}
185
186std::unique_ptr<fml::Mapping> GetSymbolMapping(std::string symbol_prefix,
187 std::string native_lib_path) {
188 const uint8_t* mapping;
189 intptr_t size;
190
191 auto lookup_symbol = [&mapping, &size, symbol_prefix](
192 const fml::RefPtr<fml::NativeLibrary>& library) {
193 mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str());
194 size = reinterpret_cast<intptr_t>(
195 library->ResolveSymbol((symbol_prefix + "_size").c_str()));
196 };
197
198 fml::RefPtr<fml::NativeLibrary> library =
199 fml::NativeLibrary::CreateForCurrentProcess();
200 lookup_symbol(library);
201
202 if (!(mapping && size)) {
203 // Symbol lookup for the current process fails on some devices. As a
204 // fallback, try doing the lookup based on the path to the Flutter library.
205 library = fml::NativeLibrary::Create(native_lib_path.c_str());
206 lookup_symbol(library);
207 }
208
209 FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
210 return std::make_unique<fml::NonOwnedMapping>(mapping, size);
211}
212
213Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
214 Settings settings = {};
215
216 // Enable Observatory
217 settings.enable_observatory =
218 !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory));
219
220 // Set Observatory Host
221 if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryHost))) {
222 command_line.GetOptionValue(FlagForSwitch(Switch::DeviceObservatoryHost),
223 &settings.observatory_host);
224 }
225 // Default the observatory port based on --ipv6 if not set.
226 if (settings.observatory_host.empty()) {
227 settings.observatory_host =
228 command_line.HasOption(FlagForSwitch(Switch::IPv6)) ? "::1"
229 : "127.0.0.1";
230 }
231
232 settings.use_embedded_view =
233 command_line.HasOption(FlagForSwitch(Switch::UseEmbeddedView));
234
235 // Set Observatory Port
236 if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) {
237 if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort,
238 &settings.observatory_port)) {
239 FML_LOG(INFO)
240 << "Observatory port specified was malformed. Will default to "
241 << settings.observatory_port;
242 }
243 }
244
245 settings.may_insecurely_connect_to_all_domains = !command_line.HasOption(
246 FlagForSwitch(Switch::DisallowInsecureConnections));
247
248 command_line.GetOptionValue(FlagForSwitch(Switch::DomainNetworkPolicy),
249 &settings.domain_network_policy);
250
251 // Disable need for authentication codes for VM service communication, if
252 // specified.
253 settings.disable_service_auth_codes =
254 command_line.HasOption(FlagForSwitch(Switch::DisableServiceAuthCodes));
255
256 // Allow fallback to automatic port selection if binding to a specified port
257 // fails.
258 settings.enable_service_port_fallback =
259 command_line.HasOption(FlagForSwitch(Switch::EnableServicePortFallback));
260
261 // Checked mode overrides.
262 settings.disable_dart_asserts =
263 command_line.HasOption(FlagForSwitch(Switch::DisableDartAsserts));
264
265 settings.start_paused =
266 command_line.HasOption(FlagForSwitch(Switch::StartPaused));
267
268 settings.enable_checked_mode =
269 command_line.HasOption(FlagForSwitch(Switch::EnableCheckedMode));
270
271 settings.enable_dart_profiling =
272 command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling));
273
274 settings.enable_software_rendering =
275 command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering));
276
277 settings.endless_trace_buffer =
278 command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer));
279
280 settings.trace_startup =
281 command_line.HasOption(FlagForSwitch(Switch::TraceStartup));
282
283 settings.trace_skia =
284 command_line.HasOption(FlagForSwitch(Switch::TraceSkia));
285
286 command_line.GetOptionValue(FlagForSwitch(Switch::TraceAllowlist),
287 &settings.trace_allowlist);
288
289 if (settings.trace_allowlist.empty()) {
290 command_line.GetOptionValue(FlagForSwitch(Switch::TraceWhitelist),
291 &settings.trace_allowlist);
292 if (!settings.trace_allowlist.empty()) {
293 FML_LOG(INFO)
294 << "--trace-whitelist is deprecated. Use --trace-allowlist instead.";
295 }
296 }
297
298 settings.trace_systrace =
299 command_line.HasOption(FlagForSwitch(Switch::TraceSystrace));
300
301 settings.skia_deterministic_rendering_on_cpu =
302 command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering));
303
304 settings.verbose_logging =
305 command_line.HasOption(FlagForSwitch(Switch::VerboseLogging));
306
307 command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir),
308 &settings.assets_path);
309
310 std::vector<std::string_view> aot_shared_library_name =
311 command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName));
312
313 std::string snapshot_asset_path;
314 command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath),
315 &snapshot_asset_path);
316
317 std::string vm_snapshot_data_filename;
318 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotData),
319 &vm_snapshot_data_filename);
320
321 std::string vm_snapshot_instr_filename;
322 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotInstructions),
323 &vm_snapshot_instr_filename);
324
325 std::string isolate_snapshot_data_filename;
326 command_line.GetOptionValue(FlagForSwitch(Switch::IsolateSnapshotData),
327 &isolate_snapshot_data_filename);
328
329 std::string isolate_snapshot_instr_filename;
330 command_line.GetOptionValue(
331 FlagForSwitch(Switch::IsolateSnapshotInstructions),
332 &isolate_snapshot_instr_filename);
333
334 if (aot_shared_library_name.size() > 0) {
335 for (std::string_view name : aot_shared_library_name) {
336 settings.application_library_path.emplace_back(name);
337 }
338 } else if (snapshot_asset_path.size() > 0) {
339 settings.vm_snapshot_data_path =
340 fml::paths::JoinPaths({snapshot_asset_path, vm_snapshot_data_filename});
341 settings.vm_snapshot_instr_path = fml::paths::JoinPaths(
342 {snapshot_asset_path, vm_snapshot_instr_filename});
343 settings.isolate_snapshot_data_path = fml::paths::JoinPaths(
344 {snapshot_asset_path, isolate_snapshot_data_filename});
345 settings.isolate_snapshot_instr_path = fml::paths::JoinPaths(
346 {snapshot_asset_path, isolate_snapshot_instr_filename});
347 }
348
349 command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath),
350 &settings.temp_directory_path);
351
352 if (settings.icu_initialization_required) {
353 command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
354 &settings.icu_data_path);
355 if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
356 std::string icu_symbol_prefix, native_lib_path;
357 command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
358 &icu_symbol_prefix);
359 command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath),
360 &native_lib_path);
361
362#if OS_ANDROID
363 settings.icu_mapper = GetICUStaticMapping;
364#else
365 settings.icu_mapper = [icu_symbol_prefix, native_lib_path] {
366 return GetSymbolMapping(icu_symbol_prefix, native_lib_path);
367 };
368#endif
369 }
370 }
371
372 settings.use_test_fonts =
373 command_line.HasOption(FlagForSwitch(Switch::UseTestFonts));
374
375 std::string all_dart_flags;
376 if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags),
377 &all_dart_flags)) {
378 std::stringstream stream(all_dart_flags);
379 std::string flag;
380
381 // Assume that individual flags are comma separated.
382 while (std::getline(stream, flag, ',')) {
383 if (!IsAllowedDartVMFlag(flag)) {
384 FML_LOG(FATAL) << "Encountered disallowed Dart VM flag: " << flag;
385 }
386 settings.dart_flags.push_back(flag);
387 }
388 }
389
390#if !FLUTTER_RELEASE
391 command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag);
392#endif
393
394 settings.dump_skp_on_shader_compilation =
395 command_line.HasOption(FlagForSwitch(Switch::DumpSkpOnShaderCompilation));
396
397 settings.cache_sksl =
398 command_line.HasOption(FlagForSwitch(Switch::CacheSkSL));
399
400 settings.purge_persistent_cache =
401 command_line.HasOption(FlagForSwitch(Switch::PurgePersistentCache));
402
403 return settings;
404}
405
406} // namespace flutter
407