Flutter Engine
dart_vm.cc
Go to the documentation of this file.
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 <sstream>
11 #include <vector>
12 
13 #include "flutter/common/settings.h"
14 #include "flutter/fml/compiler_specific.h"
15 #include "flutter/fml/file.h"
16 #include "flutter/fml/logging.h"
17 #include "flutter/fml/mapping.h"
18 #include "flutter/fml/size.h"
19 #include "flutter/fml/synchronization/count_down_latch.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/dart_vm_initializer.h"
28 #include "flutter/runtime/ptrace_check.h"
29 #include "third_party/dart/runtime/include/bin/dart_io_api.h"
30 #include "third_party/skia/include/core/SkExecutor.h"
38 
39 namespace dart {
40 namespace observatory {
41 
42 #if !OS_FUCHSIA && !FLUTTER_RELEASE
43 
44 // These two symbols are defined in |observatory_archive.cc| which is generated
45 // by the |//third_party/dart/runtime/observatory:archive_observatory| rule.
46 // Both of these symbols will be part of the data segment and therefore are read
47 // only.
48 extern unsigned int observatory_assets_archive_len;
49 extern const uint8_t* observatory_assets_archive;
50 
51 #endif // !OS_FUCHSIA && !FLUTTER_RELEASE
52 
53 } // namespace observatory
54 } // namespace dart
55 
56 namespace flutter {
57 
58 // Arguments passed to the Dart VM in all configurations.
59 static const char* kDartLanguageArgs[] = {
60  // clang-format off
61  "--enable_mirrors=false",
62  "--background_compilation",
63  "--lazy_async_stacks",
64  // clang-format on
65 };
66 
67 // TODO(74520): Remove flag once isolate group work is completed (or add it to
68 // JIT mode).
69 static const char* kDartPrecompilationArgs[] = {"--precompilation",
70  "--enable-isolate-groups"};
71 
73 static const char* kDartWriteProtectCodeArgs[] = {
74  "--no_write_protect_code",
75 };
76 
78 static const char* kDartDisableIntegerDivisionArgs[] = {
79  "--no_use_integer_division",
80 };
81 
82 static const char* kDartAssertArgs[] = {
83  // clang-format off
84  "--enable_asserts",
85  // clang-format on
86 };
87 
88 static const char* kDartStartPausedArgs[]{
89  "--pause_isolates_on_start",
90 };
91 
92 static const char* kDartDisableServiceAuthCodesArgs[]{
93  "--disable-service-auth-codes",
94 };
95 
96 static const char* kDartEndlessTraceBufferArgs[]{
97  "--timeline_recorder=endless",
98 };
99 
100 // This is the same as --timeline_recorder=systrace.
101 static const char* kDartSystraceTraceBufferArgs[] = {
102  "--systrace_timeline",
103 };
104 
106 static const char* kDartDefaultTraceStreamsArgs[]{
107  "--timeline_streams=Dart,Embedder,GC",
108 };
109 
110 static const char* kDartStartupTraceStreamsArgs[]{
111  "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM,API",
112 };
113 
114 static const char* kDartSystraceTraceStreamsArgs[] = {
115  "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM,API",
116 };
117 
118 static std::string DartOldGenHeapSizeArgs(uint64_t heap_size) {
119  std::ostringstream oss;
120  oss << "--old_gen_heap_size=" << heap_size;
121  return oss.str();
122 }
123 
124 constexpr char kFileUriPrefix[] = "file://";
125 constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1;
126 
127 bool DartFileModifiedCallback(const char* source_url, int64_t since_ms) {
128  if (strncmp(source_url, kFileUriPrefix, kFileUriPrefixLength) != 0u) {
129  // Assume modified.
130  return true;
131  }
132 
133  const char* path = source_url + kFileUriPrefixLength;
134  struct stat info;
135  if (stat(path, &info) < 0) {
136  return true;
137  }
138 
139  // If st_mtime is zero, it's more likely that the file system doesn't support
140  // mtime than that the file was actually modified in the 1970s.
141  if (!info.st_mtime) {
142  return true;
143  }
144 
145  // It's very unclear what time bases we're with here. The Dart API doesn't
146  // document the time base for since_ms. Reading the code, the value varies by
147  // platform, with a typical source being something like gettimeofday.
148  //
149  // We add one to st_mtime because st_mtime has less precision than since_ms
150  // and we want to treat the file as modified if the since time is between
151  // ticks of the mtime.
152  fml::TimeDelta mtime = fml::TimeDelta::FromSeconds(info.st_mtime + 1);
154 
155  return mtime > since;
156 }
157 
159 
161 #if FLUTTER_RELEASE
162  return nullptr;
163 #elif OS_FUCHSIA
164  fml::UniqueFD fd = fml::OpenFile("pkg/data/observatory.tar", false,
167  if (mapping.GetSize() == 0 || mapping.GetMapping() == nullptr) {
168  FML_LOG(ERROR) << "Fail to load Observatory archive";
169  return nullptr;
170  }
171  return tonic::DartConverter<tonic::Uint8List>::ToDart(mapping.GetMapping(),
172  mapping.GetSize());
173 #else
177 #endif
178 }
179 
180 static const char kStdoutStreamId[] = "Stdout";
181 static const char kStderrStreamId[] = "Stderr";
182 
183 static bool ServiceStreamListenCallback(const char* stream_id) {
184  if (strcmp(stream_id, kStdoutStreamId) == 0) {
185  dart::bin::SetCaptureStdout(true);
186  return true;
187  } else if (strcmp(stream_id, kStderrStreamId) == 0) {
188  dart::bin::SetCaptureStderr(true);
189  return true;
190  }
191  return false;
192 }
193 
194 static void ServiceStreamCancelCallback(const char* stream_id) {
195  if (strcmp(stream_id, kStdoutStreamId) == 0) {
196  dart::bin::SetCaptureStdout(false);
197  } else if (strcmp(stream_id, kStderrStreamId) == 0) {
198  dart::bin::SetCaptureStderr(false);
199  }
200 }
201 
202 bool DartVM::IsRunningPrecompiledCode() {
203  return Dart_IsPrecompiledRuntime();
204 }
205 
206 static std::vector<const char*> ProfilingFlags(bool enable_profiling) {
207 // Disable Dart's built in profiler when building a debug build. This
208 // works around a race condition that would sometimes stop a crash's
209 // stack trace from being printed on Android.
210 #ifndef NDEBUG
211  enable_profiling = false;
212 #endif
213 
214  // We want to disable profiling by default because it overwhelms LLDB. But
215  // the VM enables the same by default. In either case, we have some profiling
216  // flags.
217  if (enable_profiling) {
218  return {
219  // This is the default. But just be explicit.
220  "--profiler",
221  // This instructs the profiler to walk C++ frames, and to include
222  // them in the profile.
223  "--profile-vm",
224 #if OS_IOS && ARCH_CPU_ARM_FAMILY && ARCH_CPU_ARMEL
225  // Set the profiler interrupt period to 500Hz instead of the
226  // default 1000Hz on 32-bit iOS devices to reduce average and worst
227  // case frame build times.
228  //
229  // Note: profile_period is time in microseconds between sampling
230  // events, not frequency. Frequency is calculated 1/period (or
231  // 1,000,000 / 2,000 -> 500Hz in this case).
232  "--profile_period=2000",
233 #else
234  "--profile_period=1000",
235 #endif // OS_IOS && ARCH_CPU_ARM_FAMILY && ARCH_CPU_ARMEL
236  };
237  } else {
238  return {"--no-profiler"};
239  }
240 }
241 
242 void PushBackAll(std::vector<const char*>* args,
243  const char** argv,
244  size_t argc) {
245  for (size_t i = 0; i < argc; ++i) {
246  args->push_back(argv[i]);
247  }
248 }
249 
250 static void EmbedderInformationCallback(Dart_EmbedderInformation* info) {
251  info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION;
252  dart::bin::GetIOEmbedderInformation(info);
253  info->name = "Flutter";
254 }
255 
256 std::shared_ptr<DartVM> DartVM::Create(
257  Settings settings,
259  fml::RefPtr<const DartSnapshot> isolate_snapshot,
260  std::shared_ptr<IsolateNameServer> isolate_name_server) {
261  auto vm_data = DartVMData::Create(settings, //
262  std::move(vm_snapshot), //
263  std::move(isolate_snapshot) //
264  );
265 
266  if (!vm_data) {
267  FML_LOG(ERROR) << "Could not set up VM data to bootstrap the VM from.";
268  return {};
269  }
270 
271  // Note: std::make_shared unviable due to hidden constructor.
272  return std::shared_ptr<DartVM>(
273  new DartVM(std::move(vm_data), std::move(isolate_name_server)));
274 }
275 
276 static std::atomic_size_t gVMLaunchCount;
277 
278 size_t DartVM::GetVMLaunchCount() {
279  return gVMLaunchCount;
280 }
281 
282 DartVM::DartVM(std::shared_ptr<const DartVMData> vm_data,
283  std::shared_ptr<IsolateNameServer> isolate_name_server)
284  : settings_(vm_data->GetSettings()),
285  concurrent_message_loop_(fml::ConcurrentMessageLoop::Create()),
286  skia_concurrent_executor_(
287  [runner = concurrent_message_loop_->GetTaskRunner()](
288  fml::closure work) { runner->PostTask(work); }),
289  vm_data_(vm_data),
290  isolate_name_server_(std::move(isolate_name_server)),
291  service_protocol_(std::make_shared<ServiceProtocol>()) {
292  TRACE_EVENT0("flutter", "DartVMInitializer");
293 
294  gVMLaunchCount++;
295 
296  // Setting the executor is not thread safe but Dart VM initialization is. So
297  // this call is thread-safe.
298  SkExecutor::SetDefault(&skia_concurrent_executor_);
299 
300  FML_DCHECK(vm_data_);
301  FML_DCHECK(isolate_name_server_);
302  FML_DCHECK(service_protocol_);
303 
304  {
305  TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo");
306  dart::bin::BootstrapDartIo();
307 
308  if (!settings_.temp_directory_path.empty()) {
309  dart::bin::SetSystemTempDirectory(settings_.temp_directory_path.c_str());
310  }
311  }
312 
313  std::vector<const char*> args;
314 
315  // Instruct the VM to ignore unrecognized flags.
316  // There is a lot of diversity in a lot of combinations when it
317  // comes to the arguments the VM supports. And, if the VM comes across a flag
318  // it does not recognize, it exits immediately.
319  args.push_back("--ignore-unrecognized-flags");
320 
321  for (auto* const profiler_flag :
322  ProfilingFlags(settings_.enable_dart_profiling)) {
323  args.push_back(profiler_flag);
324  }
325 
326  PushBackAll(&args, kDartLanguageArgs, fml::size(kDartLanguageArgs));
327 
328  if (IsRunningPrecompiledCode()) {
329  PushBackAll(&args, kDartPrecompilationArgs,
330  fml::size(kDartPrecompilationArgs));
331  }
332 
333  // Enable Dart assertions if we are not running precompiled code. We run non-
334  // precompiled code only in the debug product mode.
335  bool enable_asserts = !settings_.disable_dart_asserts;
336 
337 #if !OS_FUCHSIA
338  if (IsRunningPrecompiledCode()) {
339  enable_asserts = false;
340  }
341 #endif // !OS_FUCHSIA
342 
343 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
344 #if !OS_IOS && !OS_MACOSX
345  // Debug mode uses the JIT, disable code page write protection to avoid
346  // memory page protection changes before and after every compilation.
347  PushBackAll(&args, kDartWriteProtectCodeArgs,
348  fml::size(kDartWriteProtectCodeArgs));
349 #else
350  const bool tracing_result = EnableTracingIfNecessary(settings_);
351  // This check should only trip if the embedding made no attempts to enable
352  // tracing. At this point, it is too late display user visible messages. Just
353  // log and die.
354  FML_CHECK(tracing_result)
355  << "Tracing not enabled before attempting to run JIT mode VM.";
356 #if TARGET_CPU_ARM
357  // Tell Dart in JIT mode to not use integer division on armv7
358  // Ideally, this would be detected at runtime by Dart.
359  // TODO(dnfield): Remove this code
360  // https://github.com/dart-lang/sdk/issues/24743
361  PushBackAll(&args, kDartDisableIntegerDivisionArgs,
362  fml::size(kDartDisableIntegerDivisionArgs));
363 #endif // TARGET_CPU_ARM
364 #endif // !OS_IOS && !OS_MACOSX
365 #endif // (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
366 
367  if (enable_asserts) {
368  PushBackAll(&args, kDartAssertArgs, fml::size(kDartAssertArgs));
369  }
370 
371  if (settings_.start_paused) {
373  }
374 
375  if (settings_.disable_service_auth_codes) {
378  }
379 
380  if (settings_.endless_trace_buffer || settings_.trace_startup) {
381  // If we are tracing startup, make sure the trace buffer is endless so we
382  // don't lose early traces.
385  }
386 
387  if (settings_.trace_systrace) {
388  PushBackAll(&args, kDartSystraceTraceBufferArgs,
389  fml::size(kDartSystraceTraceBufferArgs));
390  PushBackAll(&args, kDartSystraceTraceStreamsArgs,
391  fml::size(kDartSystraceTraceStreamsArgs));
392  }
393 
394  if (settings_.trace_startup) {
397  }
398 
399 #if defined(OS_FUCHSIA)
400  PushBackAll(&args, kDartSystraceTraceBufferArgs,
401  fml::size(kDartSystraceTraceBufferArgs));
402  PushBackAll(&args, kDartSystraceTraceStreamsArgs,
403  fml::size(kDartSystraceTraceStreamsArgs));
404 #else
405  if (!settings_.trace_systrace && !settings_.trace_startup) {
408  }
409 #endif // defined(OS_FUCHSIA)
410 
411  std::string old_gen_heap_size_args;
412  if (settings_.old_gen_heap_size >= 0) {
413  old_gen_heap_size_args =
414  DartOldGenHeapSizeArgs(settings_.old_gen_heap_size);
415  args.push_back(old_gen_heap_size_args.c_str());
416  }
417 
418  for (size_t i = 0; i < settings_.dart_flags.size(); i++) {
419  args.push_back(settings_.dart_flags[i].c_str());
420  }
421 
422  char* flags_error = Dart_SetVMFlags(args.size(), args.data());
423  if (flags_error) {
424  FML_LOG(FATAL) << "Error while setting Dart VM flags: " << flags_error;
425  ::free(flags_error);
426  }
427 
428  DartUI::InitForGlobal();
429 
430  {
431  TRACE_EVENT0("flutter", "Dart_Initialize");
432  Dart_InitializeParams params = {};
433  params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
434  params.vm_snapshot_data = vm_data_->GetVMSnapshot().GetDataMapping();
435  params.vm_snapshot_instructions =
436  vm_data_->GetVMSnapshot().GetInstructionsMapping();
437  params.create_group = reinterpret_cast<decltype(params.create_group)>(
438  DartIsolate::DartIsolateGroupCreateCallback);
439  params.initialize_isolate =
440  reinterpret_cast<decltype(params.initialize_isolate)>(
441  DartIsolate::DartIsolateInitializeCallback);
442  params.shutdown_isolate =
443  reinterpret_cast<decltype(params.shutdown_isolate)>(
444  DartIsolate::DartIsolateShutdownCallback);
445  params.cleanup_isolate = reinterpret_cast<decltype(params.cleanup_isolate)>(
446  DartIsolate::DartIsolateCleanupCallback);
447  params.cleanup_group = reinterpret_cast<decltype(params.cleanup_group)>(
448  DartIsolate::DartIsolateGroupCleanupCallback);
449  params.thread_exit = ThreadExitCallback;
450  params.get_service_assets = GetVMServiceAssetsArchiveCallback;
451  params.entropy_source = dart::bin::GetEntropy;
453  // Send the earliest available timestamp in the application lifecycle to
454  // timeline. The difference between this timestamp and the time we render
455  // the very first frame gives us a good idea about Flutter's startup time.
456  // Use a duration event so about:tracing will consider this event when
457  // deciding the earliest event to use as time 0.
458  if (settings_.engine_start_timestamp.count()) {
459  Dart_TimelineEvent(
460  "FlutterEngineMainEnter", // label
461  settings_.engine_start_timestamp.count(), // timestamp0
462  Dart_TimelineGetMicros(), // timestamp1_or_async_id
463  Dart_Timeline_Event_Duration, // event type
464  0, // argument_count
465  nullptr, // argument_names
466  nullptr // argument_values
467  );
468  }
469  }
470 
471  Dart_SetFileModifiedCallback(&DartFileModifiedCallback);
472 
473  // Allow streaming of stdout and stderr by the Dart vm.
474  Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback,
476 
477  Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback);
478 
479  if (settings_.dart_library_sources_kernel != nullptr) {
480  std::unique_ptr<fml::Mapping> dart_library_sources =
481  settings_.dart_library_sources_kernel();
482  // Set sources for dart:* libraries for debugging.
483  Dart_SetDartLibrarySourcesKernel(dart_library_sources->GetMapping(),
484  dart_library_sources->GetSize());
485  }
486 }
487 
488 DartVM::~DartVM() {
489  // Setting the executor is not thread safe but Dart VM shutdown is. So
490  // this call is thread-safe.
491  SkExecutor::SetDefault(nullptr);
492 
493  if (Dart_CurrentIsolate() != nullptr) {
494  Dart_ExitIsolate();
495  }
496 
498 
499  dart::bin::CleanupDartIo();
500 }
501 
502 std::shared_ptr<const DartVMData> DartVM::GetVMData() const {
503  return vm_data_;
504 }
505 
506 const Settings& DartVM::GetSettings() const {
507  return settings_;
508 }
509 
510 std::shared_ptr<ServiceProtocol> DartVM::GetServiceProtocol() const {
511  return service_protocol_;
512 }
513 
514 std::shared_ptr<IsolateNameServer> DartVM::GetIsolateNameServer() const {
515  return isolate_name_server_;
516 }
517 
518 std::shared_ptr<fml::ConcurrentTaskRunner>
519 DartVM::GetConcurrentWorkerTaskRunner() const {
520  return concurrent_message_loop_->GetTaskRunner();
521 }
522 
523 std::shared_ptr<fml::ConcurrentMessageLoop> DartVM::GetConcurrentMessageLoop() {
524  return concurrent_message_loop_;
525 }
526 
527 } // namespace flutter
G_BEGIN_DECLS FlValue * args
bool DartFileModifiedCallback(const char *source_url, int64_t since_ms)
Definition: dart_vm.cc:127
static const char * kDartEndlessTraceBufferArgs[]
Definition: dart_vm.cc:96
static constexpr TimeDelta FromSeconds(int64_t seconds)
Definition: time_delta.h:49
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
#define FML_DCHECK(condition)
Definition: logging.h:86
static const char * kDartSystraceTraceStreamsArgs[]
Definition: dart_vm.cc:114
constexpr char kFileUriPrefix[]
Definition: dart_vm.cc:124
static std::atomic_size_t gVMLaunchCount
Definition: dart_vm.cc:276
static const char * kDartDisableServiceAuthCodesArgs[]
Definition: dart_vm.cc:92
static const char * kDartAssertArgs[]
Definition: dart_vm.cc:82
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
#define FML_LOG(severity)
Definition: logging.h:65
static void Initialize(Dart_InitializeParams *params)
static FML_ALLOW_UNUSED_TYPE const char * kDartWriteProtectCodeArgs[]
Definition: dart_vm.cc:73
static void EmbedderInformationCallback(Dart_EmbedderInformation *info)
Definition: dart_vm.cc:250
static std::string DartOldGenHeapSizeArgs(uint64_t heap_size)
Definition: dart_vm.cc:118
static const char * kDartSystraceTraceBufferArgs[]
Definition: dart_vm.cc:101
Definition: dart_vm.cc:39
static const char kStdoutStreamId[]
Definition: dart_vm.cc:180
std::function< void()> closure
Definition: closure.h:14
static void ServiceStreamCancelCallback(const char *stream_id)
Definition: dart_vm.cc:194
#define FML_ALLOW_UNUSED_TYPE
constexpr size_t kFileUriPrefixLength
Definition: dart_vm.cc:125
static std::shared_ptr< ConcurrentMessageLoop > Create(size_t worker_count=std::thread::hardware_concurrency())
static FML_ALLOW_UNUSED_TYPE const char * kDartDisableIntegerDivisionArgs[]
Definition: dart_vm.cc:78
static FML_ALLOW_UNUSED_TYPE const char * kDartDefaultTraceStreamsArgs[]
Definition: dart_vm.cc:106
static const char kStderrStreamId[]
Definition: dart_vm.cc:181
static const char * kDartStartupTraceStreamsArgs[]
Definition: dart_vm.cc:110
bool EnableTracingIfNecessary(const Settings &vm_settings)
Enables tracing in the process so that JIT mode VMs may be launched. Explicitly enabling tracing is n...
Definition: ptrace_check.h:45
static const char * kDartStartPausedArgs[]
Definition: dart_vm.cc:88
Describes a running instance of the Dart VM. There may only be one running instance of the Dart VM in...
Definition: dart_vm.h:61
#define FML_CHECK(condition)
Definition: logging.h:68
void ThreadExitCallback()
Definition: dart_vm.cc:158
void PushBackAll(std::vector< const char *> *args, const char **argv, size_t argc)
Definition: dart_vm.cc:242
static std::vector< const char * > ProfilingFlags(bool enable_profiling)
Definition: dart_vm.cc:206
static bool ServiceStreamListenCallback(const char *stream_id)
Definition: dart_vm.cc:183
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
fml::UniqueFD OpenFile(const char *path, bool create_if_necessary, FilePermission permission)
This can open a directory on POSIX, but not on Windows.
Definition: file_posix.cc:65
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
static const char * kDartPrecompilationArgs[]
Definition: dart_vm.cc:69
const uint8_t * observatory_assets_archive
Dart_Handle ToDart(const T &object)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data observatory The hostname IP address on which the Dart Observatory should be served If not defaults to or::depending on whether ipv6 is specified disable observatory
Definition: switches.h:83
Settings settings_
unsigned int observatory_assets_archive_len
static const char * kDartLanguageArgs[]
Definition: dart_vm.cc:59
Dart_Handle GetVMServiceAssetsArchiveCallback()
Definition: dart_vm.cc:160