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 | |
23 | struct 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 |
44 | static 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 |
53 | static 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 |
78 | extern uint8_t _binary_icudtl_dat_start[]; |
79 | extern uint8_t _binary_icudtl_dat_end[]; |
80 | |
81 | static 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 | |
88 | namespace flutter { |
89 | |
90 | void 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 | |
144 | const 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 | |
153 | static 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 | |
166 | template <typename T> |
167 | static 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 | |
186 | std::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 | |
213 | Settings 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 | |