blob: 0d34d9e07e73bb1adbe55d1329f12d41a95d929d [file] [log] [blame]
// Copyright 2020 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 <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <zircon/status.h>
#include <crashsvc/exception_handler.h>
#include <crashsvc/logging.h>
ExceptionHandler::ExceptionHandler(async_dispatcher_t* dispatcher,
zx_handle_t exception_handler_svc)
: dispatcher_(dispatcher),
exception_handler_svc_(exception_handler_svc),
// We are in a build without a server for fuchsia.exception.Handler, e.g., bringup.
drop_exceptions_(exception_handler_svc_ == ZX_HANDLE_INVALID),
connection_() {
SetUpClient();
ConnectToServer();
}
void ExceptionHandler::SetUpClient() {
if (drop_exceptions_) {
return;
}
auto exception_handler_endpoints = fidl::CreateEndpoints<fuchsia_exception::Handler>();
if (!exception_handler_endpoints.is_ok()) {
LogError("Failed to create channel for fuchsia.exception.Handler",
exception_handler_endpoints.status_value());
drop_exceptions_ = true;
return;
}
class EventHandler : public fidl::WireAsyncEventHandler<fuchsia_exception::Handler> {
public:
EventHandler(ExceptionHandler* handler) : handler_(handler) {}
void Unbound(fidl::UnbindInfo info) { handler_->OnUnbind(info); }
private:
ExceptionHandler* handler_;
};
connection_ = fidl::Client<fuchsia_exception::Handler>();
connection_.Bind(std::move(exception_handler_endpoints->client), dispatcher_,
std::make_shared<EventHandler>(this));
server_endpoint_ = std::move(exception_handler_endpoints->server);
}
void ExceptionHandler::OnUnbind(const fidl::UnbindInfo info) {
// If the unbind was not an error, don't reconnect and stop sending exceptions to
// fuchsia.exception.Handler. This should only happen in tests.
if (info.status == ZX_OK || info.status == ZX_ERR_CANCELED) {
drop_exceptions_ = true;
return;
}
LogError("Lost connection to fuchsia.exception.Handler", info.status);
// We immediately bind the |connection_| again, but we don't re-connect to the server of
// fuchsia.exception.Handler, i.e sending the other endpoint of the channel to the server. Instead
// the re-connection will be done on the next exception. The reason we don't re-connect (1)
// immediately is because the server could have been shut down by the system or (2) with a backoff
// is because we don't want to be queueing up exceptions which underlying processes need to be
// terminated.
SetUpClient();
}
void ExceptionHandler::ConnectToServer() {
if (ConnectedToServer() || drop_exceptions_) {
return;
}
if (const zx_status_t status = fdio_service_connect_at(
exception_handler_svc_, fidl::DiscoverableProtocolName<fuchsia_exception::Handler>,
server_endpoint_.channel().release());
status != ZX_OK) {
LogError("unable to connect to fuchsia.exception.Handler", status);
drop_exceptions_ = true;
return;
}
}
void ExceptionHandler::Handle(zx::exception exception, const zx_exception_info_t& info) {
if (drop_exceptions_) {
return;
}
ConnectToServer();
fuchsia_exception::wire::ExceptionInfo exception_info;
exception_info.process_koid = info.pid;
exception_info.thread_koid = info.tid;
exception_info.type = static_cast<fuchsia_exception::wire::ExceptionType>(info.type);
if (const auto result = connection_->OnException(
std::move(exception), exception_info,
[](fidl::WireResponse<fuchsia_exception::Handler::OnException>* response) {});
result.status() != ZX_OK) {
LogError("Failed to pass exception to handler", info, result.status());
}
}
bool ExceptionHandler::ConnectedToServer() const { return !server_endpoint_.is_valid(); }