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
22 struct 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 #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
40
41 // List of common and safe VM flags to allow to be passed directly to the VM.
42 // clang-format off
43 static const std::string gDartFlagsWhitelist[] = {
44 "--max_profile_depth",
45 "--profile_period",
46 "--random_seed",
47 "--enable_mirrors",
48 };
49 // clang-format on
50
51 #endif
52
53 // Include again for struct definition.
54 #include "flutter/shell/common/switches.h"
55
56 // Define symbols for the ICU data that is linked into the Flutter library on
57 // Android. This is a workaround for crashes seen when doing dynamic lookups
58 // of the engine's own symbols on some older versions of Android.
59 #if OS_ANDROID
60 extern uint8_t _binary_icudtl_dat_start[];
61 extern uint8_t _binary_icudtl_dat_end[];
62
GetICUStaticMapping()63 static std::unique_ptr<fml::Mapping> GetICUStaticMapping() {
64 return std::make_unique<fml::NonOwnedMapping>(
65 _binary_icudtl_dat_start,
66 _binary_icudtl_dat_end - _binary_icudtl_dat_start);
67 }
68 #endif
69
70 namespace flutter {
71
PrintUsage(const std::string & executable_name)72 void PrintUsage(const std::string& executable_name) {
73 std::cerr << std::endl << " " << executable_name << std::endl << std::endl;
74
75 std::cerr << "Versions: " << std::endl << std::endl;
76
77 std::cerr << "Flutter Engine Version: " << GetFlutterEngineVersion()
78 << std::endl;
79 std::cerr << "Skia Version: " << GetSkiaVersion() << std::endl;
80
81 std::cerr << "Dart Version: " << GetDartVersion() << std::endl << std::endl;
82
83 std::cerr << "Available Flags:" << std::endl;
84
85 const uint32_t column_width = 80;
86
87 const uint32_t flags_count = static_cast<uint32_t>(Switch::Sentinel);
88
89 uint32_t max_width = 2;
90 for (uint32_t i = 0; i < flags_count; i++) {
91 auto desc = gSwitchDescs[i];
92 max_width = std::max<uint32_t>(desc.flag.size() + 2, max_width);
93 }
94
95 const uint32_t help_width = column_width - max_width - 3;
96
97 std::cerr << std::string(column_width, '-') << std::endl;
98 for (uint32_t i = 0; i < flags_count; i++) {
99 auto desc = gSwitchDescs[i];
100
101 std::cerr << std::setw(max_width)
102 << std::string("--") +
103 std::string{desc.flag.data(), desc.flag.size()}
104 << " : ";
105
106 std::istringstream stream(desc.help);
107 int32_t remaining = help_width;
108
109 std::string word;
110 while (stream >> word && remaining > 0) {
111 remaining -= (word.size() + 1);
112 if (remaining <= 0) {
113 std::cerr << std::endl
114 << std::string(max_width, ' ') << " " << word << " ";
115 remaining = help_width;
116 } else {
117 std::cerr << word << " ";
118 }
119 }
120
121 std::cerr << std::endl;
122 }
123 std::cerr << std::string(column_width, '-') << std::endl;
124 }
125
FlagForSwitch(Switch swtch)126 const std::string_view FlagForSwitch(Switch swtch) {
127 for (uint32_t i = 0; i < static_cast<uint32_t>(Switch::Sentinel); i++) {
128 if (gSwitchDescs[i].sw == swtch) {
129 return gSwitchDescs[i].flag;
130 }
131 }
132 return std::string_view();
133 }
134
135 #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
136
IsWhitelistedDartVMFlag(const std::string & flag)137 static bool IsWhitelistedDartVMFlag(const std::string& flag) {
138 for (uint32_t i = 0; i < fml::size(gDartFlagsWhitelist); ++i) {
139 const std::string& allowed = gDartFlagsWhitelist[i];
140 // Check that the prefix of the flag matches one of the whitelisted flags.
141 // We don't need to worry about cases like "--safe --sneaky_dangerous" as
142 // the VM will discard these as a single unrecognized flag.
143 if (std::equal(allowed.begin(), allowed.end(), flag.begin())) {
144 return true;
145 }
146 }
147 return false;
148 }
149
150 #endif
151
152 template <typename T>
GetSwitchValue(const fml::CommandLine & command_line,Switch sw,T * result)153 static bool GetSwitchValue(const fml::CommandLine& command_line,
154 Switch sw,
155 T* result) {
156 std::string switch_string;
157
158 if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) {
159 return false;
160 }
161
162 std::stringstream stream(switch_string);
163 T value = 0;
164 if (stream >> value) {
165 *result = value;
166 return true;
167 }
168
169 return false;
170 }
171
GetSymbolMapping(std::string symbol_prefix,std::string native_lib_path)172 std::unique_ptr<fml::Mapping> GetSymbolMapping(std::string symbol_prefix,
173 std::string native_lib_path) {
174 const uint8_t* mapping;
175 intptr_t size;
176
177 auto lookup_symbol = [&mapping, &size, symbol_prefix](
178 const fml::RefPtr<fml::NativeLibrary>& library) {
179 mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str());
180 size = reinterpret_cast<intptr_t>(
181 library->ResolveSymbol((symbol_prefix + "_size").c_str()));
182 };
183
184 fml::RefPtr<fml::NativeLibrary> library =
185 fml::NativeLibrary::CreateForCurrentProcess();
186 lookup_symbol(library);
187
188 if (!(mapping && size)) {
189 // Symbol lookup for the current process fails on some devices. As a
190 // fallback, try doing the lookup based on the path to the Flutter library.
191 library = fml::NativeLibrary::Create(native_lib_path.c_str());
192 lookup_symbol(library);
193 }
194
195 FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
196 return std::make_unique<fml::NonOwnedMapping>(mapping, size);
197 }
198
SettingsFromCommandLine(const fml::CommandLine & command_line)199 Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
200 Settings settings = {};
201
202 // Enable Observatory
203 settings.enable_observatory =
204 !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory));
205
206 // Set Observatory Host
207 if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryHost))) {
208 command_line.GetOptionValue(FlagForSwitch(Switch::DeviceObservatoryHost),
209 &settings.observatory_host);
210 }
211 // Default the observatory port based on --ipv6 if not set.
212 if (settings.observatory_host.empty()) {
213 settings.observatory_host =
214 command_line.HasOption(FlagForSwitch(Switch::IPv6)) ? "::1"
215 : "127.0.0.1";
216 }
217
218 // Set Observatory Port
219 if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) {
220 if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort,
221 &settings.observatory_port)) {
222 FML_LOG(INFO)
223 << "Observatory port specified was malformed. Will default to "
224 << settings.observatory_port;
225 }
226 }
227
228 // Disable need for authentication codes for VM service communication, if
229 // specified.
230 settings.disable_service_auth_codes =
231 command_line.HasOption(FlagForSwitch(Switch::DisableServiceAuthCodes));
232
233 // Checked mode overrides.
234 settings.disable_dart_asserts =
235 command_line.HasOption(FlagForSwitch(Switch::DisableDartAsserts));
236
237 settings.start_paused =
238 command_line.HasOption(FlagForSwitch(Switch::StartPaused));
239
240 settings.enable_checked_mode =
241 command_line.HasOption(FlagForSwitch(Switch::EnableCheckedMode));
242
243 settings.enable_dart_profiling =
244 command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling));
245
246 settings.enable_software_rendering =
247 command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering));
248
249 settings.endless_trace_buffer =
250 command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer));
251
252 settings.trace_startup =
253 command_line.HasOption(FlagForSwitch(Switch::TraceStartup));
254
255 settings.skia_deterministic_rendering_on_cpu =
256 command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering));
257
258 settings.verbose_logging =
259 command_line.HasOption(FlagForSwitch(Switch::VerboseLogging));
260
261 command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir),
262 &settings.assets_path);
263
264 std::vector<std::string_view> aot_shared_library_name =
265 command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName));
266
267 std::string snapshot_asset_path;
268 command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath),
269 &snapshot_asset_path);
270
271 std::string vm_snapshot_data_filename;
272 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotData),
273 &vm_snapshot_data_filename);
274
275 std::string vm_snapshot_instr_filename;
276 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotInstructions),
277 &vm_snapshot_instr_filename);
278
279 std::string isolate_snapshot_data_filename;
280 command_line.GetOptionValue(FlagForSwitch(Switch::IsolateSnapshotData),
281 &isolate_snapshot_data_filename);
282
283 std::string isolate_snapshot_instr_filename;
284 command_line.GetOptionValue(
285 FlagForSwitch(Switch::IsolateSnapshotInstructions),
286 &isolate_snapshot_instr_filename);
287
288 if (aot_shared_library_name.size() > 0) {
289 for (std::string_view name : aot_shared_library_name) {
290 settings.application_library_path.emplace_back(name);
291 }
292 } else if (snapshot_asset_path.size() > 0) {
293 settings.vm_snapshot_data_path =
294 fml::paths::JoinPaths({snapshot_asset_path, vm_snapshot_data_filename});
295 settings.vm_snapshot_instr_path = fml::paths::JoinPaths(
296 {snapshot_asset_path, vm_snapshot_instr_filename});
297 settings.isolate_snapshot_data_path = fml::paths::JoinPaths(
298 {snapshot_asset_path, isolate_snapshot_data_filename});
299 settings.isolate_snapshot_instr_path = fml::paths::JoinPaths(
300 {snapshot_asset_path, isolate_snapshot_instr_filename});
301 }
302
303 command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath),
304 &settings.temp_directory_path);
305
306 if (settings.icu_initialization_required) {
307 command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
308 &settings.icu_data_path);
309 if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
310 std::string icu_symbol_prefix, native_lib_path;
311 command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
312 &icu_symbol_prefix);
313 command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath),
314 &native_lib_path);
315
316 #if OS_ANDROID
317 settings.icu_mapper = GetICUStaticMapping;
318 #else
319 settings.icu_mapper = [icu_symbol_prefix, native_lib_path] {
320 return GetSymbolMapping(icu_symbol_prefix, native_lib_path);
321 };
322 #endif
323 }
324 }
325
326 settings.use_test_fonts =
327 command_line.HasOption(FlagForSwitch(Switch::UseTestFonts));
328
329 #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
330 command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag);
331 std::string all_dart_flags;
332 if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags),
333 &all_dart_flags)) {
334 std::stringstream stream(all_dart_flags);
335 std::string flag;
336
337 // Assume that individual flags are comma separated.
338 while (std::getline(stream, flag, ',')) {
339 if (!IsWhitelistedDartVMFlag(flag)) {
340 FML_LOG(FATAL) << "Encountered blacklisted Dart VM flag: " << flag;
341 }
342 settings.dart_flags.push_back(flag);
343 }
344 }
345
346 settings.trace_skia =
347 command_line.HasOption(FlagForSwitch(Switch::TraceSkia));
348 settings.trace_systrace =
349 command_line.HasOption(FlagForSwitch(Switch::TraceSystrace));
350 #endif
351
352 settings.dump_skp_on_shader_compilation =
353 command_line.HasOption(FlagForSwitch(Switch::DumpSkpOnShaderCompilation));
354
355 return settings;
356 }
357
358 } // namespace flutter
359