• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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