blob: bb81e1e4690173b57c58a5edfda42b9ec8f6d06c [file] [log] [blame]
// 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