• 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 "flutter/runtime/dart_vm.h"
6 
7 #include <sys/stat.h>
8 
9 #include <mutex>
10 #include <vector>
11 
12 #include "flutter/common/settings.h"
13 #include "flutter/fml/compiler_specific.h"
14 #include "flutter/fml/file.h"
15 #include "flutter/fml/logging.h"
16 #include "flutter/fml/mapping.h"
17 #include "flutter/fml/size.h"
18 #include "flutter/fml/synchronization/count_down_latch.h"
19 #include "flutter/fml/synchronization/thread_annotations.h"
20 #include "flutter/fml/time/time_delta.h"
21 #include "flutter/fml/trace_event.h"
22 #include "flutter/lib/io/dart_io.h"
23 #include "flutter/lib/ui/dart_runtime_hooks.h"
24 #include "flutter/lib/ui/dart_ui.h"
25 #include "flutter/runtime/dart_isolate.h"
26 #include "flutter/runtime/dart_service_isolate.h"
27 #include "flutter/runtime/ptrace_ios.h"
28 #include "flutter/runtime/start_up.h"
29 #include "third_party/dart/runtime/include/bin/dart_io_api.h"
30 #include "third_party/skia/include/core/SkExecutor.h"
31 
32 namespace dart {
33 namespace observatory {
34 
35 #if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE)
36 
37 // These two symbols are defined in |observatory_archive.cc| which is generated
38 // by the |//third_party/dart/runtime/observatory:archive_observatory| rule.
39 // Both of these symbols will be part of the data segment and therefore are read
40 // only.
41 extern unsigned int observatory_assets_archive_len;
42 extern const uint8_t* observatory_assets_archive;
43 
44 #endif  // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE !=
45         // FLUTTER_RUNTIME_MODE_RELEASE)
46 
47 }  // namespace observatory
48 }  // namespace dart
49 
50 namespace flutter {
51 
52 // Arguments passed to the Dart VM in all configurations.
53 static const char* kDartLanguageArgs[] = {
54     // clang-format off
55     "--enable_mirrors=false",
56     "--background_compilation",
57     "--causal_async_stacks",
58     // clang-format on
59 };
60 
61 static const char* kDartPrecompilationArgs[] = {
62     "--precompilation",
63 };
64 
65 FML_ALLOW_UNUSED_TYPE
66 static const char* kDartWriteProtectCodeArgs[] = {
67     "--no_write_protect_code",
68 };
69 
70 FML_ALLOW_UNUSED_TYPE
71 static const char* kDartDisableIntegerDivisionArgs[] = {
72     "--no_use_integer_division",
73 };
74 
75 static const char* kDartAssertArgs[] = {
76     // clang-format off
77     "--enable_asserts",
78     // clang-format on
79 };
80 
81 static const char* kDartStartPausedArgs[]{
82     "--pause_isolates_on_start",
83 };
84 
85 static const char* kDartDisableServiceAuthCodesArgs[]{
86     "--disable-service-auth-codes",
87 };
88 
89 static const char* kDartTraceStartupArgs[]{
90     "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM",
91 };
92 
93 static const char* kDartEndlessTraceBufferArgs[]{
94     "--timeline_recorder=endless",
95 };
96 
97 static const char* kDartSystraceTraceBufferArgs[]{
98     "--timeline_recorder=systrace",
99 };
100 
101 static const char* kDartFuchsiaTraceArgs[] FML_ALLOW_UNUSED_TYPE = {
102     "--systrace_timeline",
103 };
104 
105 static const char* kDartTraceStreamsArgs[] = {
106     "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM",
107 };
108 
109 constexpr char kFileUriPrefix[] = "file://";
110 constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1;
111 
DartFileModifiedCallback(const char * source_url,int64_t since_ms)112 bool DartFileModifiedCallback(const char* source_url, int64_t since_ms) {
113   if (strncmp(source_url, kFileUriPrefix, kFileUriPrefixLength) != 0u) {
114     // Assume modified.
115     return true;
116   }
117 
118   const char* path = source_url + kFileUriPrefixLength;
119   struct stat info;
120   if (stat(path, &info) < 0)
121     return true;
122 
123   // If st_mtime is zero, it's more likely that the file system doesn't support
124   // mtime than that the file was actually modified in the 1970s.
125   if (!info.st_mtime)
126     return true;
127 
128   // It's very unclear what time bases we're with here. The Dart API doesn't
129   // document the time base for since_ms. Reading the code, the value varies by
130   // platform, with a typical source being something like gettimeofday.
131   //
132   // We add one to st_mtime because st_mtime has less precision than since_ms
133   // and we want to treat the file as modified if the since time is between
134   // ticks of the mtime.
135   fml::TimeDelta mtime = fml::TimeDelta::FromSeconds(info.st_mtime + 1);
136   fml::TimeDelta since = fml::TimeDelta::FromMilliseconds(since_ms);
137 
138   return mtime > since;
139 }
140 
ThreadExitCallback()141 void ThreadExitCallback() {}
142 
GetVMServiceAssetsArchiveCallback()143 Dart_Handle GetVMServiceAssetsArchiveCallback() {
144 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE)
145   return nullptr;
146 #elif OS_FUCHSIA
147   fml::UniqueFD fd = fml::OpenFile("pkg/data/observatory.tar", false,
148                                    fml::FilePermission::kRead);
149   fml::FileMapping mapping(fd, {fml::FileMapping::Protection::kRead});
150   if (mapping.GetSize() == 0 || mapping.GetMapping() == nullptr) {
151     FML_LOG(ERROR) << "Fail to load Observatory archive";
152     return nullptr;
153   }
154   return tonic::DartConverter<tonic::Uint8List>::ToDart(mapping.GetMapping(),
155                                                         mapping.GetSize());
156 #else
157   return tonic::DartConverter<tonic::Uint8List>::ToDart(
158       ::dart::observatory::observatory_assets_archive,
159       ::dart::observatory::observatory_assets_archive_len);
160 #endif
161 }
162 
163 static const char kStdoutStreamId[] = "Stdout";
164 static const char kStderrStreamId[] = "Stderr";
165 
ServiceStreamListenCallback(const char * stream_id)166 static bool ServiceStreamListenCallback(const char* stream_id) {
167   if (strcmp(stream_id, kStdoutStreamId) == 0) {
168     dart::bin::SetCaptureStdout(true);
169     return true;
170   } else if (strcmp(stream_id, kStderrStreamId) == 0) {
171     dart::bin::SetCaptureStderr(true);
172     return true;
173   }
174   return false;
175 }
176 
ServiceStreamCancelCallback(const char * stream_id)177 static void ServiceStreamCancelCallback(const char* stream_id) {
178   if (strcmp(stream_id, kStdoutStreamId) == 0) {
179     dart::bin::SetCaptureStdout(false);
180   } else if (strcmp(stream_id, kStderrStreamId) == 0) {
181     dart::bin::SetCaptureStderr(false);
182   }
183 }
184 
IsRunningPrecompiledCode()185 bool DartVM::IsRunningPrecompiledCode() {
186   return Dart_IsPrecompiledRuntime();
187 }
188 
ProfilingFlags(bool enable_profiling)189 static std::vector<const char*> ProfilingFlags(bool enable_profiling) {
190 // Disable Dart's built in profiler when building a debug build. This
191 // works around a race condition that would sometimes stop a crash's
192 // stack trace from being printed on Android.
193 #ifndef NDEBUG
194   enable_profiling = false;
195 #endif
196 
197   // We want to disable profiling by default because it overwhelms LLDB. But
198   // the VM enables the same by default. In either case, we have some profiling
199   // flags.
200   if (enable_profiling) {
201     return {// This is the default. But just be explicit.
202             "--profiler",
203             // This instructs the profiler to walk C++ frames, and to include
204             // them in the profile.
205             "--profile-vm"};
206   } else {
207     return {"--no-profiler"};
208   }
209 }
210 
PushBackAll(std::vector<const char * > * args,const char ** argv,size_t argc)211 void PushBackAll(std::vector<const char*>* args,
212                  const char** argv,
213                  size_t argc) {
214   for (size_t i = 0; i < argc; ++i) {
215     args->push_back(argv[i]);
216   }
217 }
218 
EmbedderInformationCallback(Dart_EmbedderInformation * info)219 static void EmbedderInformationCallback(Dart_EmbedderInformation* info) {
220   info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION;
221   dart::bin::GetIOEmbedderInformation(info);
222   info->name = "Flutter";
223 }
224 
Create(Settings settings,fml::RefPtr<DartSnapshot> vm_snapshot,fml::RefPtr<DartSnapshot> isolate_snapshot,fml::RefPtr<DartSnapshot> shared_snapshot,std::shared_ptr<IsolateNameServer> isolate_name_server)225 std::shared_ptr<DartVM> DartVM::Create(
226     Settings settings,
227     fml::RefPtr<DartSnapshot> vm_snapshot,
228     fml::RefPtr<DartSnapshot> isolate_snapshot,
229     fml::RefPtr<DartSnapshot> shared_snapshot,
230     std::shared_ptr<IsolateNameServer> isolate_name_server) {
231   auto vm_data = DartVMData::Create(settings,                     //
232                                     std::move(vm_snapshot),       //
233                                     std::move(isolate_snapshot),  //
234                                     std::move(shared_snapshot)    //
235   );
236 
237   if (!vm_data) {
238     FML_LOG(ERROR) << "Could not setup VM data to bootstrap the VM from.";
239     return {};
240   }
241 
242   // Note: std::make_shared unviable due to hidden constructor.
243   return std::shared_ptr<DartVM>(
244       new DartVM(std::move(vm_data), std::move(isolate_name_server)));
245 }
246 
247 static std::atomic_size_t gVMLaunchCount;
248 
GetVMLaunchCount()249 size_t DartVM::GetVMLaunchCount() {
250   return gVMLaunchCount;
251 }
252 
DartVM(std::shared_ptr<const DartVMData> vm_data,std::shared_ptr<IsolateNameServer> isolate_name_server)253 DartVM::DartVM(std::shared_ptr<const DartVMData> vm_data,
254                std::shared_ptr<IsolateNameServer> isolate_name_server)
255     : settings_(vm_data->GetSettings()),
256       concurrent_message_loop_(fml::ConcurrentMessageLoop::Create()),
257       skia_concurrent_executor_(
258           [runner = concurrent_message_loop_->GetTaskRunner()](
259               fml::closure work) { runner->PostTask(work); }),
260       vm_data_(vm_data),
261       isolate_name_server_(std::move(isolate_name_server)),
262       service_protocol_(std::make_shared<ServiceProtocol>()) {
263   TRACE_EVENT0("flutter", "DartVMInitializer");
264 
265   gVMLaunchCount++;
266 
267   // Setting the executor is not thread safe but Dart VM initialization is. So
268   // this call is thread-safe.
269   SkExecutor::SetDefault(&skia_concurrent_executor_);
270 
271   FML_DCHECK(vm_data_);
272   FML_DCHECK(isolate_name_server_);
273   FML_DCHECK(service_protocol_);
274 
275   FML_DLOG(INFO) << "Attempting Dart VM launch for mode: "
276                  << (IsRunningPrecompiledCode() ? "AOT" : "Interpreter");
277 
278   {
279     TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo");
280     dart::bin::BootstrapDartIo();
281 
282     if (!settings_.temp_directory_path.empty()) {
283       dart::bin::SetSystemTempDirectory(settings_.temp_directory_path.c_str());
284     }
285   }
286 
287   std::vector<const char*> args;
288 
289   // Instruct the VM to ignore unrecognized flags.
290   // There is a lot of diversity in a lot of combinations when it
291   // comes to the arguments the VM supports. And, if the VM comes across a flag
292   // it does not recognize, it exits immediately.
293   args.push_back("--ignore-unrecognized-flags");
294 
295   for (auto* const profiler_flag :
296        ProfilingFlags(settings_.enable_dart_profiling)) {
297     args.push_back(profiler_flag);
298   }
299 
300   PushBackAll(&args, kDartLanguageArgs, fml::size(kDartLanguageArgs));
301 
302   if (IsRunningPrecompiledCode()) {
303     PushBackAll(&args, kDartPrecompilationArgs,
304                 fml::size(kDartPrecompilationArgs));
305   }
306 
307   // Enable Dart assertions if we are not running precompiled code. We run non-
308   // precompiled code only in the debug product mode.
309   bool enable_asserts = !settings_.disable_dart_asserts;
310 
311 #if !OS_FUCHSIA
312   if (IsRunningPrecompiledCode()) {
313     enable_asserts = false;
314   }
315 #endif  // !OS_FUCHSIA
316 
317 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
318 #if !OS_IOS || TARGET_OS_SIMULATOR
319   // Debug mode uses the JIT, disable code page write protection to avoid
320   // memory page protection changes before and after every compilation.
321   PushBackAll(&args, kDartWriteProtectCodeArgs,
322               fml::size(kDartWriteProtectCodeArgs));
323 #else
324   EnsureDebuggedIOS(settings_);
325 #if TARGET_CPU_ARM
326   // Tell Dart in JIT mode to not use integer division on armv7
327   // Ideally, this would be detected at runtime by Dart.
328   // TODO(dnfield): Remove this code
329   // https://github.com/dart-lang/sdk/issues/24743
330   PushBackAll(&args, kDartDisableIntegerDivisionArgs,
331               fml::size(kDartDisableIntegerDivisionArgs));
332 #endif  // TARGET_CPU_ARM
333 #endif  // !OS_IOS || TARGET_OS_SIMULATOR
334 #endif  // (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
335 
336   if (enable_asserts) {
337     PushBackAll(&args, kDartAssertArgs, fml::size(kDartAssertArgs));
338   }
339 
340   if (settings_.start_paused) {
341     PushBackAll(&args, kDartStartPausedArgs, fml::size(kDartStartPausedArgs));
342   }
343 
344   if (settings_.disable_service_auth_codes) {
345     PushBackAll(&args, kDartDisableServiceAuthCodesArgs,
346                 fml::size(kDartDisableServiceAuthCodesArgs));
347   }
348 
349   if (settings_.endless_trace_buffer || settings_.trace_startup) {
350     // If we are tracing startup, make sure the trace buffer is endless so we
351     // don't lose early traces.
352     PushBackAll(&args, kDartEndlessTraceBufferArgs,
353                 fml::size(kDartEndlessTraceBufferArgs));
354   }
355 
356   if (settings_.trace_systrace) {
357     PushBackAll(&args, kDartSystraceTraceBufferArgs,
358                 fml::size(kDartSystraceTraceBufferArgs));
359     PushBackAll(&args, kDartTraceStreamsArgs, fml::size(kDartTraceStreamsArgs));
360   }
361 
362   if (settings_.trace_startup) {
363     PushBackAll(&args, kDartTraceStartupArgs, fml::size(kDartTraceStartupArgs));
364   }
365 
366 #if defined(OS_FUCHSIA)
367   PushBackAll(&args, kDartFuchsiaTraceArgs, fml::size(kDartFuchsiaTraceArgs));
368   PushBackAll(&args, kDartTraceStreamsArgs, fml::size(kDartTraceStreamsArgs));
369 #endif
370 
371   for (size_t i = 0; i < settings_.dart_flags.size(); i++)
372     args.push_back(settings_.dart_flags[i].c_str());
373 
374   char* flags_error = Dart_SetVMFlags(args.size(), args.data());
375   if (flags_error) {
376     FML_LOG(FATAL) << "Error while setting Dart VM flags: " << flags_error;
377     ::free(flags_error);
378   }
379 
380   DartUI::InitForGlobal();
381 
382   {
383     TRACE_EVENT0("flutter", "Dart_Initialize");
384     Dart_InitializeParams params = {};
385     params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
386     params.vm_snapshot_data = vm_data_->GetVMSnapshot().GetDataMapping();
387     params.vm_snapshot_instructions =
388         vm_data_->GetVMSnapshot().GetInstructionsMapping();
389     params.create_group = reinterpret_cast<decltype(params.create_group)>(
390         DartIsolate::DartIsolateGroupCreateCallback);
391     params.shutdown_isolate =
392         reinterpret_cast<decltype(params.shutdown_isolate)>(
393             DartIsolate::DartIsolateShutdownCallback);
394     params.cleanup_group = reinterpret_cast<decltype(params.cleanup_group)>(
395         DartIsolate::DartIsolateGroupCleanupCallback);
396     params.thread_exit = ThreadExitCallback;
397     params.get_service_assets = GetVMServiceAssetsArchiveCallback;
398     params.entropy_source = dart::bin::GetEntropy;
399     char* init_error = Dart_Initialize(&params);
400     if (init_error) {
401       FML_LOG(FATAL) << "Error while initializing the Dart VM: " << init_error;
402       ::free(init_error);
403     }
404     // Send the earliest available timestamp in the application lifecycle to
405     // timeline. The difference between this timestamp and the time we render
406     // the very first frame gives us a good idea about Flutter's startup time.
407     // Use a duration event so about:tracing will consider this event when
408     // deciding the earliest event to use as time 0.
409     if (engine_main_enter_ts != 0) {
410       Dart_TimelineEvent("FlutterEngineMainEnter",  // label
411                          engine_main_enter_ts,      // timestamp0
412                          engine_main_enter_ts,      // timestamp1_or_async_id
413                          Dart_Timeline_Event_Duration,  // event type
414                          0,                             // argument_count
415                          nullptr,                       // argument_names
416                          nullptr                        // argument_values
417       );
418     }
419   }
420 
421   Dart_SetFileModifiedCallback(&DartFileModifiedCallback);
422 
423   // Allow streaming of stdout and stderr by the Dart vm.
424   Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback,
425                                  &ServiceStreamCancelCallback);
426 
427   Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback);
428 
429   if (settings_.dart_library_sources_kernel != nullptr) {
430     std::unique_ptr<fml::Mapping> dart_library_sources =
431         settings_.dart_library_sources_kernel();
432     // Set sources for dart:* libraries for debugging.
433     Dart_SetDartLibrarySourcesKernel(dart_library_sources->GetMapping(),
434                                      dart_library_sources->GetSize());
435   }
436 
437   FML_DLOG(INFO) << "New Dart VM instance created. Instance count: "
438                  << gVMLaunchCount;
439 }
440 
~DartVM()441 DartVM::~DartVM() {
442   // Setting the executor is not thread safe but Dart VM shutdown is. So
443   // this call is thread-safe.
444   SkExecutor::SetDefault(nullptr);
445 
446   if (Dart_CurrentIsolate() != nullptr) {
447     Dart_ExitIsolate();
448   }
449 
450   char* result = Dart_Cleanup();
451 
452   dart::bin::CleanupDartIo();
453 
454   FML_CHECK(result == nullptr)
455       << "Could not cleanly shut down the Dart VM. Error: \"" << result
456       << "\".";
457   free(result);
458 
459   FML_DLOG(INFO) << "Dart VM instance destroyed. Instance count: "
460                  << gVMLaunchCount;
461 }
462 
GetVMData() const463 std::shared_ptr<const DartVMData> DartVM::GetVMData() const {
464   return vm_data_;
465 }
466 
GetSettings() const467 const Settings& DartVM::GetSettings() const {
468   return settings_;
469 }
470 
GetServiceProtocol() const471 std::shared_ptr<ServiceProtocol> DartVM::GetServiceProtocol() const {
472   return service_protocol_;
473 }
474 
GetIsolateNameServer() const475 std::shared_ptr<IsolateNameServer> DartVM::GetIsolateNameServer() const {
476   return isolate_name_server_;
477 }
478 
479 std::shared_ptr<fml::ConcurrentTaskRunner>
GetConcurrentWorkerTaskRunner() const480 DartVM::GetConcurrentWorkerTaskRunner() const {
481   return concurrent_message_loop_->GetTaskRunner();
482 }
483 
484 }  // namespace flutter
485