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