[debugger] Added exception channel support.
This fleshes-out the exception handler and all the hooks a
ZirconExceptionWatcher would need to start receiving exceptions from
this exception delivery mechanism.
This is not hooked to the main code, as this CL is big enough as it is.
TEST=No functional change.
Change-Id: Icc15a025039491cf14e24c2a03a6ad59bde79113
diff --git a/src/developer/debug/shared/event_handlers.cc b/src/developer/debug/shared/event_handlers.cc
index 8dcf5e96..e481db0 100644
--- a/src/developer/debug/shared/event_handlers.cc
+++ b/src/developer/debug/shared/event_handlers.cc
@@ -6,6 +6,7 @@
#include <lib/async-loop/loop.h>
#include <lib/async/default.h>
+#include <lib/zx/exception.h>
#include "src/developer/debug/shared/logging/logging.h"
#include "src/developer/debug/shared/message_loop_target.h"
@@ -15,6 +16,28 @@
namespace debug_ipc {
+namespace {
+
+// |signals| are the signals we're going to observe.
+std::unique_ptr<async_wait_t>
+CreateSignalHandle(int id, 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;
@@ -32,26 +55,23 @@
zx_status_t SignalHandler::Init(int id, zx_handle_t object,
zx_signals_t signals) {
- handle_ = std::make_unique<async_wait_t>();
- *handle_ = {}; // Need to zero it out.
- handle_->handler = Handler;
- handle_->object = object;
- handle_->trigger = signals;
-
+ handle_ = CreateSignalHandle(id, object, signals, Handler);
watch_info_id_ = id;
- return WaitForSignals();
+
+ // We start listening.
+ return StartListening(handle_.get());
}
-zx_status_t SignalHandler::WaitForSignals() const {
- async_wait_t* wait = handle_.get();
- zx_status_t status = async_begin_wait(async_get_default_dispatcher(), wait);
- return status;
-}
-
+// static
void SignalHandler::Handler(async_dispatcher_t*, async_wait_t* wait,
zx_status_t status,
const zx_packet_signal_t* signal) {
- FXL_DCHECK(status == ZX_OK);
+ if (status != ZX_OK) {
+ FXL_LOG(WARNING) << "Got error on receiving exception: "
+ << ZxStatusToString(status);
+ FXL_NOTREACHED();
+ return;
+ }
auto* loop = MessageLoopTarget::Current();
FXL_DCHECK(loop);
@@ -66,7 +86,8 @@
FXL_DCHECK(watch_info);
// async-loop will remove the handler for this event, so we need to re-add it.
- signal_handler.WaitForSignals();
+ status = StartListening(signal_handler.handle_.get());
+ FXL_DCHECK(status == ZX_OK) << "Got: " << ZxStatusToString(status);
switch (watch_info->type) {
case WatchType::kFdio:
loop->OnFdioSignal(watch_info_id, *watch_info, signal->observed);
@@ -126,6 +147,7 @@
return status;
}
+// static
void ExceptionHandler::Handler(async_dispatcher_t*,
async_exception_t* exception, zx_status_t status,
const zx_port_packet_t* packet) {
@@ -169,24 +191,80 @@
ChannelExceptionHandler::ChannelExceptionHandler(ChannelExceptionHandler&&) =
default;
+
ChannelExceptionHandler& ChannelExceptionHandler::operator=(
ChannelExceptionHandler&&) = default;
zx_status_t ChannelExceptionHandler::Init(int id, zx_handle_t object,
zx_signals_t signals) {
- FXL_NOTREACHED() << "Not implemented.";
- return ZX_ERR_NOT_SUPPORTED;
+ zx_status_t status = zx_task_create_exception_channel(
+ object, ZX_EXCEPTION_CHANNEL_DEBUGGER,
+ exception_channel_.reset_and_get_address());
+ if (status != ZX_OK)
+ return status;
+
+ handle_ = CreateSignalHandle(id, exception_channel_.get(), signals, Handler);
+ watch_info_id_ = id;
+ return StartListening(handle_.get());
}
-zx_status_t ChannelExceptionHandler::WaitForSignals() const {
- FXL_NOTREACHED() << "Not implemented.";
- return ZX_ERR_NOT_SUPPORTED;
-}
-
-void ChannelExceptionHandler::Handler(async_dispatcher_t*, async_wait_t* wait,
- zx_status_t status,
+// static
+void ChannelExceptionHandler::Handler(async_dispatcher_t* dispatcher,
+ async_wait_t* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
- FXL_NOTREACHED() << "Not implemented.";
+ if (status != ZX_OK) {
+ FXL_LOG(WARNING) << "Got error on receiving exception: "
+ << ZxStatusToString(status);
+ FXL_NOTREACHED();
+ return;
+ }
+
+ auto* loop = MessageLoopTarget::Current();
+ FXL_DCHECK(loop);
+
+ // Search for the AsyncHandle that triggered this signal.
+ auto handler_it = loop->exception_channel_handlers().find(wait);
+ FXL_DCHECK(handler_it != loop->exception_channel_handlers().end());
+ const ChannelExceptionHandler& handler = handler_it->second;
+
+ int watch_info_id = handler.watch_info_id();
+ auto* watch_info = loop->FindWatchInfo(watch_info_id);
+ FXL_DCHECK(watch_info);
+
+ // async-loop will remove the handler for this event, so we need to re-add it.
+ status = StartListening(handler.handle_.get());
+ FXL_DCHECK(status == ZX_OK) << "Got: " << ZxStatusToString(status);
+
+ // We should only receive exceptions here.
+ if (watch_info->type != WatchType::kProcessExceptions &&
+ watch_info->type != WatchType::kJobExceptions) {
+ FXL_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;
+
+ FXL_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) {
+ FXL_LOG(WARNING) << "Got error when reading from exception channel: "
+ << ZxStatusToString(status);
+ FXL_NOTREACHED();
+ return;
+ }
+
+ loop->HandleChannelException(handler, std::move(exception), exception_info);
}
} // namespace debug_ipc
diff --git a/src/developer/debug/shared/event_handlers.h b/src/developer/debug/shared/event_handlers.h
index cbf5c1e..be1224c 100644
--- a/src/developer/debug/shared/event_handlers.h
+++ b/src/developer/debug/shared/event_handlers.h
@@ -7,11 +7,15 @@
#include <lib/async/cpp/exception.h>
#include <lib/async/cpp/wait.h>
+#include <lib/zx/channel.h>
#include <memory>
#include "src/lib/fxl/macros.h"
+// Group of classes dedicated at handling async events associated with zircon's
+// message loop.
+
namespace debug_ipc {
enum class WatchType : uint32_t;
@@ -22,8 +26,10 @@
// 0 is an invalid ID for watchers, so is safe to use here.
constexpr uint64_t kTaskSignalKey = 0;
-// Group of classes dedicated at handling async events associated with zircon's
-// message loop.
+
+// Function called when a SignalHandler gets a signal it's waiting for.
+using SignalHandlerFunc = void (*)(async_dispatcher_t*, async_wait_t*,
+ zx_status_t, const zx_packet_signal_t*);
class SignalHandler {
public:
@@ -43,8 +49,6 @@
const async_wait_t* handle() const { return handle_.get(); }
private:
- zx_status_t WaitForSignals() const;
-
int watch_info_id_ = -1;
std::unique_ptr<async_wait_t> handle_;
};
@@ -90,11 +94,11 @@
const async_wait_t* handle() const { return handle_.get(); }
private:
- zx_status_t WaitForSignals() const;
-
int watch_info_id_ = -1;
std::unique_ptr<async_wait_t> handle_;
+ zx::channel exception_channel_;
+
FXL_DISALLOW_COPY_AND_ASSIGN(ChannelExceptionHandler);
};
diff --git a/src/developer/debug/shared/message_loop_target.cc b/src/developer/debug/shared/message_loop_target.cc
index f6c747c..7de8e59 100644
--- a/src/developer/debug/shared/message_loop_target.cc
+++ b/src/developer/debug/shared/message_loop_target.cc
@@ -375,6 +375,40 @@
}
}
+void MessageLoopTarget::HandleChannelException(
+ const ChannelExceptionHandler& handler, zx::exception&& exception,
+ zx_exception_info_t exception_info) {
+ WatchInfo* watch_info = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = watches_.find(handler.watch_info_id());
+ FXL_DCHECK(it != watches_.end());
+ watch_info = &it->second;
+ }
+
+ if (watch_info->type != WatchType::kProcessExceptions &&
+ watch_info->type != WatchType::kJobExceptions) {
+ FXL_NOTREACHED() << "Should only receive exceptions.";
+ return;
+ }
+
+ // We should only receive exceptions here.
+ switch (watch_info->type) {
+ case WatchType::kTask:
+ case WatchType::kFdio:
+ case WatchType::kSocket:
+ FXL_NOTREACHED() << "Should only receive exceptions.";
+ return;
+ case WatchType::kProcessExceptions:
+ case WatchType::kJobExceptions:
+ break;
+ }
+
+ FXL_DCHECK(watch_info->exception_watcher);
+ watch_info->exception_watcher->OnException(std::move(exception),
+ exception_info);
+}
+
uint64_t MessageLoopTarget::GetMonotonicNowNS() const {
zx::time ret;
zx::clock::get(&ret);
diff --git a/src/developer/debug/shared/message_loop_target.h b/src/developer/debug/shared/message_loop_target.h
index 44790d5..58bece2 100644
--- a/src/developer/debug/shared/message_loop_target.h
+++ b/src/developer/debug/shared/message_loop_target.h
@@ -10,6 +10,7 @@
#include <lib/zx/event.h>
#include <lib/zx/port.h>
#include <lib/zx/thread.h>
+#include <zircon/syscalls/exception.h>
#include <vector>
@@ -133,8 +134,9 @@
void HandleException(const ExceptionHandler&, zx_port_packet_t packet);
// Handlers exceptions channel.
- void HandleExceptionChannel(const ChannelExceptionHandler&,
- zx::exception&& exception);
+ void HandleChannelException(const ChannelExceptionHandler&,
+ zx::exception&& exception,
+ zx_exception_info_t exception_info);
// Handle an event of the given type.
void OnFdioSignal(int watch_id, const WatchInfo& info, zx_signals_t observed);
@@ -200,6 +202,7 @@
friend class SignalHandler;
friend class ExceptionHandler;
+ friend class ChannelExceptionHandler;
};
// EventHandlers need access to the WatchInfo implementation.
diff --git a/src/developer/debug/shared/zircon_exception_watcher.h b/src/developer/debug/shared/zircon_exception_watcher.h
index 6be254e..08dba9a 100644
--- a/src/developer/debug/shared/zircon_exception_watcher.h
+++ b/src/developer/debug/shared/zircon_exception_watcher.h
@@ -7,6 +7,9 @@
#include <zircon/types.h>
+#include <zircon/syscalls/exception.h>
+#include <lib/zx/exception.h>
+
namespace debug_ipc {
// Callbacks for exceptions from a process exception port.
@@ -20,6 +23,10 @@
virtual void OnThreadExiting(zx_koid_t process_koid, zx_koid_t thread_koid) {}
virtual void OnException(zx_koid_t process_koid, zx_koid_t thread_koid,
uint32_t type) {}
+
+ // New exception handling that uses the exception tokens.
+ virtual void OnException(zx::exception&& exception_token,
+ zx_exception_info_t exception_info) {}
};
} // namespace debug_ipc