• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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