| // Copyright 2019 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 "src/developer/debug/shared/event_handlers.h" |
| |
| #include <lib/async-loop/default.h> |
| #include <lib/async-loop/loop.h> |
| #include <lib/async/default.h> |
| #include <lib/fit/defer.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/zx/exception.h> |
| |
| #include "src/developer/debug/shared/logging/logging.h" |
| #include "src/developer/debug/shared/message_loop_target.h" |
| #include "src/developer/debug/shared/zircon_utils.h" |
| #include "src/developer/debug/shared/zx_status.h" |
| |
| namespace debug_ipc { |
| |
| namespace { |
| |
| // |signals| are the signals we're going to observe. |
| std::unique_ptr<async_wait_t> CreateSignalHandle(zx_handle_t object, zx_signals_t signals, |
| SignalHandlerFunc handler_func) { |
| auto handle = std::make_unique<async_wait_t>(); |
| *handle = {}; // Need to zero it out. |
| handle->handler = handler_func; |
| handle->object = object; |
| handle->trigger = signals; |
| |
| return handle; |
| } |
| |
| // Sets a particular signal handler to start listening on the async loop. |
| zx_status_t StartListening(async_wait_t* signal_handle) { |
| return async_begin_wait(async_get_default_dispatcher(), signal_handle); |
| } |
| |
| } // namespace |
| |
| // SignalHandler --------------------------------------------------------------- |
| |
| SignalHandler::SignalHandler() = default; |
| SignalHandler::~SignalHandler() { |
| if (!handle_) |
| return; |
| |
| async_wait_t* wait = handle_.get(); |
| auto status = async_cancel_wait(async_get_default_dispatcher(), wait); |
| FX_DCHECK(status == ZX_OK) << "Got: " << ZxStatusToString(status); |
| } |
| |
| SignalHandler::SignalHandler(SignalHandler&&) = default; |
| SignalHandler& SignalHandler::operator=(SignalHandler&&) = default; |
| |
| zx_status_t SignalHandler::Init(int id, zx_handle_t object, zx_signals_t signals) { |
| handle_ = CreateSignalHandle(object, signals, Handler); |
| watch_info_id_ = id; |
| |
| // We start listening. |
| return StartListening(handle_.get()); |
| } |
| |
| // static |
| void SignalHandler::Handler(async_dispatcher_t*, async_wait_t* wait, zx_status_t status, |
| const zx_packet_signal_t* signal) { |
| if (status != ZX_OK) { |
| FX_LOGS(WARNING) << "Got error on receiving exception: " << ZxStatusToString(status); |
| FX_NOTREACHED(); |
| return; |
| } |
| |
| auto* loop = MessageLoopTarget::Current(); |
| FX_DCHECK(loop); |
| |
| // Search for the AsyncHandle that triggered this signal. |
| auto handler_it = loop->signal_handlers().find(wait); |
| FX_DCHECK(handler_it != loop->signal_handlers().end()); |
| const SignalHandler& signal_handler = handler_it->second; |
| |
| int watch_info_id = signal_handler.watch_info_id(); |
| auto* watch_info = loop->FindWatchInfo(watch_info_id); |
| FX_DCHECK(watch_info); |
| |
| // async-loop will remove the handler for this event, so we need to re-add it. |
| status = StartListening(signal_handler.handle_.get()); |
| FX_DCHECK(status == ZX_OK) << "Got: " << ZxStatusToString(status); |
| switch (watch_info->type) { |
| case WatchType::kFdio: |
| loop->OnFdioSignal(watch_info_id, *watch_info, signal->observed); |
| break; |
| case WatchType::kSocket: |
| loop->OnSocketSignal(watch_info_id, *watch_info, signal->observed); |
| break; |
| case WatchType::kTask: |
| FX_DCHECK(watch_info_id == kTaskSignalKey); |
| loop->CheckAndProcessPendingTasks(); |
| break; |
| case WatchType::kProcessExceptions: |
| loop->OnProcessTerminated(*watch_info, signal->observed); |
| break; |
| case WatchType::kJobExceptions: |
| FX_NOTREACHED(); |
| } |
| |
| // "this" might be deleted at this point, so it should never be used. |
| } |
| |
| // ChannelExceptionHandler ----------------------------------------------------- |
| |
| ChannelExceptionHandler::ChannelExceptionHandler() = default; |
| ChannelExceptionHandler::~ChannelExceptionHandler() { |
| if (!handle_) |
| return; |
| |
| async_wait_t* wait = handle_.get(); |
| auto status = async_cancel_wait(async_get_default_dispatcher(), wait); |
| FX_DCHECK(status == ZX_OK) << "Got: " << ZxStatusToString(status); |
| } |
| |
| ChannelExceptionHandler::ChannelExceptionHandler(ChannelExceptionHandler&&) = default; |
| |
| ChannelExceptionHandler& ChannelExceptionHandler::operator=(ChannelExceptionHandler&&) = default; |
| |
| zx_status_t ChannelExceptionHandler::Init(int id, zx_handle_t object, uint32_t options) { |
| zx_status_t status = |
| zx_task_create_exception_channel(object, options, exception_channel_.reset_and_get_address()); |
| if (status != ZX_OK) |
| return status; |
| |
| zx_signals_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED; |
| handle_ = CreateSignalHandle(exception_channel_.get(), signals, Handler); |
| watch_info_id_ = id; |
| return StartListening(handle_.get()); |
| } |
| |
| // static |
| void ChannelExceptionHandler::Handler(async_dispatcher_t* dispatcher, async_wait_t* wait, |
| zx_status_t status, const zx_packet_signal_t* signal) { |
| if (status != ZX_OK) { |
| FX_LOGS(WARNING) << "Got error on receiving exception: " << ZxStatusToString(status); |
| FX_NOTREACHED(); |
| return; |
| } |
| |
| auto* loop = MessageLoopTarget::Current(); |
| FX_DCHECK(loop); |
| |
| // Search for the AsyncHandle that triggered this signal. |
| auto handler_it = loop->channel_exception_handlers().find(wait); |
| FX_DCHECK(handler_it != loop->channel_exception_handlers().end()); |
| const ChannelExceptionHandler& handler = handler_it->second; |
| |
| auto cleanup = fit::defer([handle = handler.handle_.get()] { |
| zx_status_t status = StartListening(handle); |
| FX_DCHECK(status == ZX_OK) << "Got: " << ZxStatusToString(status); |
| }); |
| |
| int watch_info_id = handler.watch_info_id(); |
| auto* watch_info = loop->FindWatchInfo(watch_info_id); |
| FX_DCHECK(watch_info); |
| |
| // async-loop will remove the handler for this event, so we need to re-add it. |
| // We should only receive exceptions here. |
| if (watch_info->type != WatchType::kProcessExceptions && |
| watch_info->type != WatchType::kJobExceptions) { |
| FX_NOTREACHED() << "Should only watch for exceptions on this handler."; |
| return; |
| } |
| |
| bool peer_closed = signal->observed & ZX_CHANNEL_PEER_CLOSED; |
| bool readable = signal->observed & ZX_CHANNEL_READABLE; |
| |
| FX_DCHECK(peer_closed || readable); |
| |
| if (peer_closed) |
| return; |
| |
| // We obtain the exception from the channel. |
| zx::exception exception; |
| zx_exception_info_t exception_info; |
| status = handler.exception_channel_.read(0, &exception_info, exception.reset_and_get_address(), |
| sizeof(exception_info), 1, nullptr, nullptr); |
| if (status != ZX_OK) { |
| FX_LOGS(WARNING) << "Got error when reading from exception channel: " |
| << ZxStatusToString(status); |
| FX_NOTREACHED(); |
| return; |
| } |
| |
| loop->HandleChannelException(handler, std::move(exception), exception_info); |
| } |
| |
| } // namespace debug_ipc |