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 "runtime/dart/utils/handle_exception.h"
6
7 #include <fuchsia/crash/cpp/fidl.h>
8 #include <fuchsia/mem/cpp/fidl.h>
9 #include <lib/syslog/global.h>
10 #include <lib/zx/vmo.h>
11 #include <sys/types.h>
12 #include <third_party/tonic/converter/dart_converter.h>
13 #include <zircon/status.h>
14
15 #include <string>
16
17 #include "runtime/dart/utils/logging.h"
18
19 namespace {
FillBuffer(const std::string & data,fuchsia::mem::Buffer * buffer)20 static bool FillBuffer(const std::string& data, fuchsia::mem::Buffer* buffer) {
21 uint64_t num_bytes = data.size();
22 zx::vmo vmo;
23
24 if (zx::vmo::create(num_bytes, 0u, &vmo) < 0) {
25 return false;
26 }
27
28 if (num_bytes > 0) {
29 if (vmo.write(data.data(), 0, num_bytes) < 0) {
30 return false;
31 }
32 }
33
34 buffer->vmo = std::move(vmo);
35 buffer->size = num_bytes;
36
37 return true;
38 }
39
40 template <typename T, size_t N>
CopyToArray(const std::string & s,std::array<T,N> * arr)41 void CopyToArray(const std::string& s, std::array<T, N>* arr) {
42 const size_t max_size = arr->size();
43 auto end = s.end();
44 if (s.size() > max_size) {
45 FX_LOGF(WARNING, LOG_TAG, "truncating '%s' to %d characters", s.c_str(),
46 max_size);
47 end = s.begin() + max_size;
48 }
49 std::copy(s.begin(), end, arr->data());
50 }
51
BuildException(const std::string & error,const std::string & stack_trace)52 fuchsia::crash::ManagedRuntimeException BuildException(
53 const std::string& error,
54 const std::string& stack_trace) {
55 // The runtime type has already been pre-pended to the error message so we
56 // expect the format to be '$RuntimeType: $Message'.
57 std::string error_type;
58 std::string error_message;
59 const size_t delimiter_pos = error.find_first_of(':');
60 if (delimiter_pos == std::string::npos) {
61 FX_LOGF(ERROR, LOG_TAG,
62 "error parsing Dart exception: expected format '$RuntimeType: "
63 "$Message', got '%s'",
64 error.c_str());
65 // We still need to specify a type, otherwise the stack trace does not
66 // show up in the crash server UI.
67 error_type = "UnknownError";
68 error_message = error;
69 } else {
70 error_type = error.substr(0, delimiter_pos);
71 error_message =
72 error.substr(delimiter_pos + 2 /*to get rid of the leading ': '*/);
73 }
74
75 // Default-initialize to initialize the underlying arrays of characters with
76 // 0s and null-terminate the strings.
77 fuchsia::crash::GenericException exception = {};
78 CopyToArray(error_type, &exception.type);
79 CopyToArray(error_message, &exception.message);
80 if (!FillBuffer(stack_trace, &exception.stack_trace)) {
81 FX_LOG(ERROR, LOG_TAG, "Failed to convert Dart stack trace to VMO");
82 }
83
84 fuchsia::crash::ManagedRuntimeException dart_exception;
85 dart_exception.set_dart(std::move(exception));
86 return dart_exception;
87 }
88
89 } // namespace
90
91 namespace dart_utils {
92
HandleIfException(std::shared_ptr<::sys::ServiceDirectory> services,const std::string & component_url,Dart_Handle result)93 void HandleIfException(std::shared_ptr<::sys::ServiceDirectory> services,
94 const std::string& component_url,
95 Dart_Handle result) {
96 if (!Dart_IsError(result) || !Dart_ErrorHasException(result)) {
97 return;
98 }
99
100 const std::string error =
101 tonic::StdStringFromDart(Dart_ToString(Dart_ErrorGetException(result)));
102 const std::string stack_trace =
103 tonic::StdStringFromDart(Dart_ToString(Dart_ErrorGetStackTrace(result)));
104
105 return HandleException(services, component_url, error, stack_trace);
106 }
107
HandleException(std::shared_ptr<::sys::ServiceDirectory> services,const std::string & component_url,const std::string & error,const std::string & stack_trace)108 void HandleException(std::shared_ptr<::sys::ServiceDirectory> services,
109 const std::string& component_url,
110 const std::string& error,
111 const std::string& stack_trace) {
112 fuchsia::crash::ManagedRuntimeException exception =
113 BuildException(error, stack_trace);
114
115 fuchsia::crash::AnalyzerPtr analyzer =
116 services->Connect<fuchsia::crash::Analyzer>();
117 #ifndef NDEBUG
118 if (!analyzer) {
119 FX_LOG(FATAL, LOG_TAG, "Could not connect to analyzer service");
120 }
121 #endif
122
123 analyzer->OnManagedRuntimeException(
124 component_url, std::move(exception),
125 [](fuchsia::crash::Analyzer_OnManagedRuntimeException_Result result) {
126 if (result.is_err()) {
127 FX_LOGF(ERROR, LOG_TAG, "Failed to handle Dart exception: %d (%s)",
128 result.err(), zx_status_get_string(result.err()));
129 }
130 });
131 }
132
133 } // namespace dart_utils
134