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

source code of flutter_engine/flutter/runtime/dart_vm.cc