| // 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(); } |