mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
154 lines
5.8 KiB
C++
154 lines
5.8 KiB
C++
// 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,
|
|
bool enable_timeline_event_handler,
|
|
bool trace_systrace) {
|
|
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;
|
|
}
|
|
|
|
if (enable_timeline_event_handler) {
|
|
if (!trace_systrace) {
|
|
// Systrace on all platforms except Fuchsia ignores the timestamp provided
|
|
// here. On Android in particular, calls to get the system clock show up
|
|
// in profiles.
|
|
// Fuchsia does not use the TraceSetTimelineMicrosSource.
|
|
fml::tracing::TraceSetTimelineMicrosSource(Dart_TimelineGetMicros);
|
|
} else {
|
|
fml::tracing::TraceSetTimelineMicrosSource(
|
|
[]() -> int64_t { return -1; });
|
|
}
|
|
fml::tracing::TraceSetTimelineEventHandler(LogDartTimelineEvent);
|
|
}
|
|
|
|
fml::TimePoint::SetClockSource(flutter::DartTimelineTicksSinceEpoch);
|
|
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);
|
|
}
|
|
}
|