| // Copyright 2018 The Fuchsia 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 "topaz/runtime/dart/utils/handle_exception.h" |
| |
| #include <string> |
| |
| #include <fuchsia/crash/cpp/fidl.h> |
| #include <fuchsia/mem/cpp/fidl.h> |
| #include <lib/syslog/global.h> |
| #include <lib/zx/vmo.h> |
| #include <sys/types.h> |
| #include <third_party/tonic/converter/dart_converter.h> |
| #include <zircon/errors.h> |
| #include <zircon/status.h> |
| |
| #include "topaz/runtime/dart/utils/logging.h" |
| |
| namespace { |
| static bool FillBuffer(const std::string& data, fuchsia::mem::Buffer* buffer) { |
| uint64_t num_bytes = data.size(); |
| zx::vmo vmo; |
| |
| if (zx::vmo::create(num_bytes, 0u, &vmo) < 0) { |
| return false; |
| } |
| |
| if (num_bytes > 0) { |
| if (vmo.write(data.data(), 0, num_bytes) < 0) { |
| return false; |
| } |
| } |
| |
| buffer->vmo = std::move(vmo); |
| buffer->size = num_bytes; |
| |
| return true; |
| } |
| |
| template <typename T, size_t N> |
| void CopyToArray(const std::string& s, fidl::Array<T, N>* arr) { |
| const size_t max_size = arr->size(); |
| auto end = s.end(); |
| if (s.size() > max_size) { |
| FX_LOGF(WARNING, LOG_TAG, "truncating '%s' to %d characters", s.c_str(), |
| max_size); |
| end = s.begin() + max_size; |
| } |
| std::copy(s.begin(), end, arr->data()); |
| } |
| |
| fuchsia::crash::ManagedRuntimeException BuildException( |
| const std::string& error, const std::string& stack_trace) { |
| // The runtime type has already been pre-pended to the error message so we |
| // expect the format to be '$RuntimeType: $Message'. |
| std::string error_type; |
| std::string error_message; |
| const size_t delimiter_pos = error.find_first_of(':'); |
| if (delimiter_pos == std::string::npos) { |
| FX_LOGF(ERROR, LOG_TAG, |
| "error parsing Dart exception: expected format '$RuntimeType: " |
| "$Message', got '%s'", |
| error.c_str()); |
| // We still need to specify a type, otherwise the stack trace does not |
| // show up in the crash server UI. |
| error_type = "UnknownError"; |
| error_message = error; |
| } else { |
| error_type = error.substr(0, delimiter_pos); |
| error_message = |
| error.substr(delimiter_pos + 2 /*to get rid of the leading ': '*/); |
| } |
| |
| // Default-initialize to initialize the underlying arrays of characters with |
| // 0s and null-terminate the strings. |
| fuchsia::crash::GenericException exception = {}; |
| CopyToArray(error_type, &exception.type); |
| CopyToArray(error_message, &exception.message); |
| if (!FillBuffer(stack_trace, &exception.stack_trace)) { |
| FX_LOG(ERROR, LOG_TAG, "Failed to convert Dart stack trace to VMO"); |
| } |
| |
| fuchsia::crash::ManagedRuntimeException dart_exception; |
| dart_exception.set_dart(std::move(exception)); |
| return dart_exception; |
| } |
| |
| } // namespace |
| |
| namespace dart_utils { |
| |
| zx_status_t HandleIfException(std::shared_ptr<::sys::ServiceDirectory> services, |
| const std::string& component_url, |
| Dart_Handle result) { |
| if (!Dart_IsError(result) || !Dart_ErrorHasException(result)) { |
| return ZX_OK; |
| } |
| |
| const std::string error = |
| tonic::StdStringFromDart(Dart_ToString(Dart_ErrorGetException(result))); |
| const std::string stack_trace = |
| tonic::StdStringFromDart(Dart_ToString(Dart_ErrorGetStackTrace(result))); |
| |
| return HandleException(services, component_url, error, stack_trace); |
| } |
| |
| zx_status_t HandleException(std::shared_ptr<::sys::ServiceDirectory> services, |
| const std::string& component_url, |
| const std::string& error, |
| const std::string& stack_trace) { |
| fuchsia::crash::ManagedRuntimeException exception = |
| BuildException(error, stack_trace); |
| |
| fuchsia::crash::AnalyzerSyncPtr analyzer; |
| services->Connect(analyzer.NewRequest()); |
| #ifndef NDEBUG |
| if (!analyzer) { |
| FX_LOG(FATAL, LOG_TAG, "Could not connect to analyzer service"); |
| } |
| #endif |
| |
| fuchsia::crash::Analyzer_OnManagedRuntimeException_Result out_result; |
| const zx_status_t status = analyzer->OnManagedRuntimeException( |
| component_url, std::move(exception), &out_result); |
| if (status != ZX_OK) { |
| FX_LOGF(ERROR, LOG_TAG, "Failed to connect to crash analyzer: %d (%s)", |
| status, zx_status_get_string(status)); |
| return ZX_ERR_INTERNAL; |
| } else if (out_result.is_err()) { |
| FX_LOGF(ERROR, LOG_TAG, "Failed to handle Dart exception: %d (%s)", |
| out_result.err(), zx_status_get_string(out_result.err())); |
| return ZX_ERR_INTERNAL; |
| } |
| return ZX_OK; |
| } |
| |
| } // namespace dart_utils |