[debugger] Added initial exception channel support.

This is the new way exceptions are going to be handled. The introduction
of exception handles should simplify exception handling for the rest of
the debugger.

TEST=No functional change as it is not hooked yet.
     All current debugger test passes.

Change-Id: Ic78e5b13bd79240e92374920225945527a75a734
diff --git a/src/developer/debug/shared/BUILD.gn b/src/developer/debug/shared/BUILD.gn
index 2384fda..3912722 100644
--- a/src/developer/debug/shared/BUILD.gn
+++ b/src/developer/debug/shared/BUILD.gn
@@ -48,6 +48,7 @@
       "event_handlers.h",
       "message_loop_target.cc",
       "message_loop_target.h",
+      "zircon_exception_watcher.h",
       "zircon_utils.cc",
       "zircon_utils.h",
     ]
diff --git a/src/developer/debug/shared/event_handlers.cc b/src/developer/debug/shared/event_handlers.cc
index 11a60e2..8dcf5e96 100644
--- a/src/developer/debug/shared/event_handlers.cc
+++ b/src/developer/debug/shared/event_handlers.cc
@@ -158,4 +158,35 @@
   loop->HandleException(exception_handler, std::move(new_packet));
 }
 
+// ChannelExceptionHandler -----------------------------------------------------
+
+ChannelExceptionHandler::ChannelExceptionHandler() = default;
+ChannelExceptionHandler::~ChannelExceptionHandler() {
+  // TODO(donosoc): This CL is just adding the correct placeholder code.
+  //                Actual implementation is due for another CL.
+  FXL_NOTIMPLEMENTED();
+}
+
+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 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,
+                                      const zx_packet_signal_t* signal) {
+  FXL_NOTREACHED() << "Not implemented.";
+}
+
 }  // namespace debug_ipc
diff --git a/src/developer/debug/shared/event_handlers.h b/src/developer/debug/shared/event_handlers.h
index b27efb0..cbf5c1e 100644
--- a/src/developer/debug/shared/event_handlers.h
+++ b/src/developer/debug/shared/event_handlers.h
@@ -71,6 +71,33 @@
   std::unique_ptr<async_exception_t> handle_;
 };
 
+// This is the exception handler that uses exception token instead of the
+// deprecated exception ports.
+class ChannelExceptionHandler {
+ public:
+  static void Handler(async_dispatcher_t*, async_wait_t*, zx_status_t,
+                      const zx_packet_signal_t*);
+
+  ChannelExceptionHandler();
+  ~ChannelExceptionHandler();
+
+  ChannelExceptionHandler(ChannelExceptionHandler&&);
+  ChannelExceptionHandler& operator=(ChannelExceptionHandler&&);
+
+  zx_status_t Init(int id, zx_handle_t object, zx_signals_t signals);
+
+  int watch_info_id() const { return watch_info_id_; }
+  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_;
+
+  FXL_DISALLOW_COPY_AND_ASSIGN(ChannelExceptionHandler);
+};
+
 }  // namespace debug_ipc
 
 #endif  // SRC_DEVELOPER_DEBUG_SHARED_EVENT_HANDLERS_H_
diff --git a/src/developer/debug/shared/message_loop_target.cc b/src/developer/debug/shared/message_loop_target.cc
index fc41365..f6c747c 100644
--- a/src/developer/debug/shared/message_loop_target.cc
+++ b/src/developer/debug/shared/message_loop_target.cc
@@ -125,6 +125,25 @@
   return ZX_OK;
 }
 
+zx_status_t MessageLoopTarget::AddChannelExceptionHandler(int id,
+                                                          zx_handle_t object,
+                                                          uint32_t options,
+                                                          WatchInfo* info) {
+  ChannelExceptionHandler handler;
+  zx_status_t status = handler.Init(id, object, options);
+  if (status != ZX_OK)
+    return status;
+
+  // The handler should not be there already.
+  FXL_DCHECK(exception_channel_handlers_.find(handler.handle()) ==
+             exception_channel_handlers_.end());
+
+  info->exception_channel_handler_key = handler.handle();
+  exception_channel_handlers_[handler.handle()] = std::move(handler);
+
+  return ZX_OK;
+}
+
 MessageLoop::WatchHandle MessageLoopTarget::WatchFD(WatchMode mode, int fd,
                                                     FDWatcher* watcher) {
   WatchInfo info;
@@ -433,18 +452,18 @@
 
   switch (info.type) {
     case WatchType::kProcessExceptions: {
-      RemoveExceptionHandler(info.exception_handler_key);
-      RemoveSignalHandler(info.signal_handler_key);
+      RemoveExceptionHandler(&info);
+      RemoveSignalHandler(&info);
       break;
     }
     case WatchType::kJobExceptions: {
-      RemoveExceptionHandler(info.exception_handler_key);
+      RemoveExceptionHandler(&info);
       break;
     }
     case WatchType::kTask:
     case WatchType::kFdio:
     case WatchType::kSocket:
-      RemoveSignalHandler(info.signal_handler_key);
+      RemoveSignalHandler(&info);
       break;
   }
   watches_.erase(found);
@@ -472,16 +491,34 @@
   info.fd_watcher->OnFDReady(info.fd, readable, writable, false);
 }
 
-void MessageLoopTarget::RemoveSignalHandler(const async_wait_t* key) {
+void MessageLoopTarget::RemoveSignalHandler(WatchInfo* info) {
+  const async_wait_t* key = info->signal_handler_key;
   FXL_DCHECK(key);
+
   size_t erase_count = signal_handlers_.erase(key);
   FXL_DCHECK(erase_count == 1u);
+
+  info->signal_handler_key = nullptr;
 }
 
-void MessageLoopTarget::RemoveExceptionHandler(const async_exception_t* key) {
+void MessageLoopTarget::RemoveExceptionHandler(WatchInfo* info) {
+  const async_exception_t* key = info->exception_handler_key;
   FXL_DCHECK(key);
+
   size_t erase_count = exception_handlers_.erase(key);
   FXL_DCHECK(erase_count == 1u);
+
+  info->exception_handler_key = nullptr;
+}
+
+void MessageLoopTarget::RemoveChannelExceptionHandler(WatchInfo* info) {
+  const async_wait_t* key = info->exception_channel_handler_key;
+  FXL_DCHECK(key);
+
+  size_t erase_count = exception_channel_handlers_.erase(key);
+  FXL_DCHECK(erase_count == 1u);
+
+  info->exception_channel_handler_key = nullptr;
 }
 
 void MessageLoopTarget::AddException(const ExceptionHandler& handler,
diff --git a/src/developer/debug/shared/message_loop_target.h b/src/developer/debug/shared/message_loop_target.h
index adab270..44790d5 100644
--- a/src/developer/debug/shared/message_loop_target.h
+++ b/src/developer/debug/shared/message_loop_target.h
@@ -44,6 +44,8 @@
   using SignalHandlerMap = std::map<const async_wait_t*, SignalHandler>;
   using ExceptionHandlerMap =
       std::map<const async_exception_t*, ExceptionHandler>;
+  using ChannelExceptionHandlerMap =
+      std::map<const async_wait_t*, ChannelExceptionHandler>;
 
   MessageLoopTarget();
   ~MessageLoopTarget();
@@ -108,6 +110,10 @@
     return exception_handlers_;
   }
 
+  const ChannelExceptionHandlerMap& exception_channel_handlers() const {
+    return exception_channel_handlers_;
+  }
+
  private:
   const WatchInfo* FindWatchInfo(int id) const;
 
@@ -126,6 +132,10 @@
   // posted to the message loop.
   void HandleException(const ExceptionHandler&, zx_port_packet_t packet);
 
+  // Handlers exceptions channel.
+  void HandleExceptionChannel(const ChannelExceptionHandler&,
+                              zx::exception&& exception);
+
   // Handle an event of the given type.
   void OnFdioSignal(int watch_id, const WatchInfo& info, zx_signals_t observed);
   void OnProcessException(const ExceptionHandler&, const WatchInfo& info,
@@ -151,14 +161,29 @@
   // |associated_info| needs to be updated with the fact that it has an
   // associated SignalHandler.
   zx_status_t AddSignalHandler(int, zx_handle_t, zx_signals_t, WatchInfo* info);
-  void RemoveSignalHandler(const async_wait_t* id);
+  void RemoveSignalHandler(WatchInfo* info);
 
   ExceptionHandlerMap exception_handlers_;
   // See ExceptionHandler constructor.
   // |associated_info| needs to be updated with the fact that it has an
   // associated ExceptionHandler.
-  zx_status_t AddExceptionHandler(int, zx_handle_t, uint32_t, WatchInfo* info);
-  void RemoveExceptionHandler(const async_exception_t*);
+  // |options| are the options to be bassed to |zx_task_bind_exception_port|.
+  zx_status_t AddExceptionHandler(int id, zx_handle_t object, uint32_t options,
+                                  WatchInfo* info);
+  void RemoveExceptionHandler(WatchInfo*);
+
+  // Exception Token Handlers are what handle exception channels.
+  // These are similar to SignalHandlers, but have different handling semantics.
+  // Particularly, they are meant to return out exception_tokens to their
+  // handlers.
+  ChannelExceptionHandlerMap exception_channel_handlers_;
+
+  // Listens to the exception channel. Will call |HandleChannelException| on
+  // this message loop.
+  // |options| are the options to be bassed to |zx_task_bind_exception_port|.
+  zx_status_t AddChannelExceptionHandler(int id, zx_handle_t object,
+                                         uint32_t options, WatchInfo* info);
+  void RemoveChannelExceptionHandler(WatchInfo*);
 
   // Every exception source (ExceptionHandler) will get an async_exception_t*
   // that works as a "key" for the async_loop. This async_exception_t* is what
@@ -204,6 +229,7 @@
   // watch id.
   const async_wait_t* signal_handler_key = nullptr;
   const async_exception_t* exception_handler_key = nullptr;
+  const async_wait_t* exception_channel_handler_key = nullptr;
 };
 
 }  // namespace debug_ipc