[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