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