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_initializer.h"
6
7#include <atomic>
8
9#include "flutter/fml/logging.h"
10#include "flutter/fml/synchronization/shared_mutex.h"
11#include "flutter/fml/trace_event.h"
12#include "flutter/lib/ui/ui_dart_state.h"
13#include "flutter/lib/ui/window/platform_configuration.h"
14#include "third_party/tonic/converter/dart_converter.h"
15#include "third_party/tonic/logging/dart_error.h"
16
17#include "dart_timestamp_provider.h"
18
19namespace {
20// Tracks whether Dart has been initialized and if it is safe to call Dart
21// APIs.
22static std::atomic<bool> gDartInitialized;
23
24void LogUnhandledException(Dart_Handle exception_handle,
25 Dart_Handle stack_trace_handle) {
26 const std::string error =
27 tonic::StdStringFromDart(handle: Dart_ToString(object: exception_handle));
28 const std::string stack_trace =
29 tonic::StdStringFromDart(handle: Dart_ToString(object: stack_trace_handle));
30
31 auto state = flutter::UIDartState::Current();
32 if (state && state->unhandled_exception_callback()) {
33 auto callback = state->unhandled_exception_callback();
34 if (callback(error, stack_trace)) {
35 return;
36 }
37 }
38
39 // Either the exception handler was not set or it could not handle the
40 // error, just log the exception.
41 FML_LOG(ERROR) << "Unhandled Exception: " << error << std::endl
42 << stack_trace;
43}
44
45void ReportUnhandledException(Dart_Handle exception_handle,
46 Dart_Handle stack_trace_handle) {
47 // Hooks.dart will call the error handler on PlatformDispatcher if it is
48 // not null. If it is null, returns false, fall into the !handled branch
49 // below and log.
50 // If it is not null, defer to the return value of that closure
51 // to determine whether to report via logging.
52 bool handled = false;
53 auto state = flutter::UIDartState::Current();
54 if (!state || !state->platform_configuration()) {
55 LogUnhandledException(exception_handle, stack_trace_handle);
56 return;
57 }
58 auto on_error = state->platform_configuration()->on_error();
59 if (on_error) {
60 FML_DCHECK(!Dart_IsNull(on_error));
61 Dart_Handle args[2];
62 args[0] = exception_handle;
63 args[1] = stack_trace_handle;
64 Dart_Handle on_error_result = Dart_InvokeClosure(closure: on_error, number_of_arguments: 2, arguments: args);
65
66 // An exception was thrown by the exception handler.
67 if (Dart_IsError(handle: on_error_result)) {
68 LogUnhandledException(exception_handle: Dart_ErrorGetException(handle: on_error_result),
69 stack_trace_handle: Dart_ErrorGetStackTrace(handle: on_error_result));
70
71 handled = false;
72 } else {
73 handled = tonic::DartConverter<bool>::FromDart(handle: on_error_result);
74 }
75 if (!handled) {
76 LogUnhandledException(exception_handle, stack_trace_handle);
77 }
78 }
79}
80} // namespace
81
82void DartVMInitializer::Initialize(Dart_InitializeParams* params,
83 bool enable_timeline_event_handler,
84 bool trace_systrace) {
85 FML_DCHECK(!gDartInitialized);
86
87 char* error = Dart_Initialize(params);
88 if (error) {
89 FML_LOG(FATAL) << "Error while initializing the Dart VM: " << error;
90 ::free(ptr: error);
91 } else {
92 gDartInitialized = true;
93 }
94
95 if (enable_timeline_event_handler) {
96 if (!trace_systrace) {
97 // Systrace on all platforms except Fuchsia ignores the timestamp provided
98 // here. On Android in particular, calls to get the system clock show up
99 // in profiles.
100 // Fuchsia does not use the TraceSetTimelineMicrosSource.
101 fml::tracing::TraceSetTimelineMicrosSource(source: Dart_TimelineGetMicros);
102 } else {
103 fml::tracing::TraceSetTimelineMicrosSource(
104 source: []() -> int64_t { return -1; });
105 }
106 fml::tracing::TraceSetTimelineEventHandler(handler: LogDartTimelineEvent);
107 }
108
109 fml::TimePoint::SetClockSource(flutter::DartTimelineTicksSinceEpoch);
110 tonic::SetUnhandledExceptionReporter(&ReportUnhandledException);
111}
112
113void DartVMInitializer::Cleanup() {
114 FML_DCHECK(gDartInitialized);
115
116 // Dart_RecordTimelineEvent is unsafe during a concurrent call to Dart_Cleanup
117 // because Dart_Cleanup will destroy the timeline recorder. Clear the
118 // initialized flag so that future calls to LogDartTimelineEvent will not
119 // call Dart_RecordTimelineEvent.
120 //
121 // Note that this is inherently racy. If a thread sees that gDartInitialized
122 // is set and proceeds to call Dart_RecordTimelineEvent shortly before another
123 // thread calls Dart_Cleanup, then the Dart_RecordTimelineEvent call may crash
124 // if Dart_Cleanup deletes the timeline before Dart_RecordTimelineEvent
125 // completes. In practice this is unlikely because Dart_Cleanup does
126 // significant other work before deleting the timeline.
127 //
128 // The engine can not safely guard Dart_Cleanup and LogDartTimelineEvent with
129 // a lock due to the risk of deadlocks. Dart_Cleanup waits for various
130 // Dart-owned threads to shut down. If one of those threads invokes an engine
131 // callback that calls LogDartTimelineEvent while the Dart_Cleanup thread owns
132 // the lock, then Dart_Cleanup would deadlock.
133 gDartInitialized = false;
134
135 char* error = Dart_Cleanup();
136 if (error) {
137 FML_LOG(FATAL) << "Error while cleaning up the Dart VM: " << error;
138 ::free(ptr: error);
139 }
140}
141
142void DartVMInitializer::LogDartTimelineEvent(const char* label,
143 int64_t timestamp0,
144 int64_t timestamp1_or_async_id,
145 intptr_t flow_id_count,
146 const int64_t* flow_ids,
147 Dart_Timeline_Event_Type type,
148 intptr_t argument_count,
149 const char** argument_names,
150 const char** argument_values) {
151 if (gDartInitialized) {
152 Dart_RecordTimelineEvent(label, timestamp0, timestamp1_or_id: timestamp1_or_async_id,
153 flow_id_count, flow_ids, type, argument_count,
154 argument_names, argument_values);
155 }
156}
157

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com

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