| // Copyright 2016 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. |
| |
| #pragma once |
| |
| #include <atomic> |
| #include <cstdio> |
| #include <mutex> |
| #include <thread> |
| #include <unordered_map> |
| |
| #include <lib/async/dispatcher.h> |
| #include <lib/fit/function.h> |
| #include <lib/zx/port.h> |
| #include <zircon/syscalls/exception.h> |
| #include <zircon/types.h> |
| |
| #include "lib/fxl/macros.h" |
| #include "lib/fxl/memory/ref_ptr.h" |
| #include "lib/fxl/tasks/task_runner.h" |
| |
| namespace inferior_control { |
| |
| class Thread; |
| |
| // Maintains a dedicated thread for listening to exceptions from multiple |
| // processes and provides an interface that processes can use to subscribe to |
| // exception notifications. |
| class ExceptionPort final { |
| public: |
| // A Key is vended as a result of a call to Bind() |
| using Key = uint64_t; |
| |
| // Handler callback invoked when the kernel reports an exception. For more |
| // information about the possible values and fields of the |type| and |
| // |context| parameters, see <zircon/syscalls/exception.h>. |
| using Callback = fit::function<void(const zx_port_packet_t& packet, |
| const zx_exception_context_t& context)>; |
| |
| explicit ExceptionPort(async_dispatcher_t* dispatcher); |
| ~ExceptionPort(); |
| |
| // Creates an exception port and starts waiting for events on it in a special |
| // thread. Returns false if there is an error during set up. |
| bool Run(); |
| |
| // Quits the listening loop, closes the exception port and joins the |
| // underlying thread. This must be called AFTER a successful call to Run(). |
| void Quit(); |
| |
| // Binds an exception port to |process_handle| and associates |callback| |
| // with it. The returned key can be used to unbind this process later. |
| // On success, a positive Key value will be returned. On failure, 0 will be |
| // returned. |
| // |
| // The |callback| will be posted on the origin thread's message loop, where |
| // the origin thread is the thread on which this ExceptionPort instance was |
| // created. |
| // |
| // This must be called AFTER a successful call to Run(). |
| Key Bind(const zx_handle_t process_handle, Callback callback); |
| |
| // Unbinds a previously bound exception port and returns true on success. |
| // This must be called AFTER a successful call to Run(). |
| bool Unbind(const Key key); |
| |
| zx::unowned_port GetUnownedExceptionPort(); |
| |
| private: |
| struct BindData { |
| BindData() = default; |
| BindData(zx_handle_t process_handle, zx_koid_t process_koid, |
| Callback callback) |
| : process_handle(process_handle), |
| process_koid(process_koid), |
| callback(std::move(callback)) {} |
| |
| zx_handle_t process_handle; |
| zx_koid_t process_koid; |
| Callback callback; |
| }; |
| |
| // Counter used for generating keys. |
| static Key g_key_counter; |
| |
| // The worker function. |
| void Worker(); |
| |
| // Set to false by Quit(). This tells |io_thread_| whether it should terminate |
| // its loop as soon as zx_port_wait returns. |
| std::atomic_bool keep_running_; |
| |
| // The origin dispatcher to post observer callback events to the thread |
| // that created this object. |
| async_dispatcher_t* const origin_dispatcher_; |
| |
| // The exception port handle and a mutex for synchronizing access to it. |
| // |io_thread_| only ever reads from |eport_| but a call to Quit() can set it |
| // to 0. This can really only happen if Quit() is called before Worker() even |
| // runs on the |io_thread_| which is extremely unlikely. But we play safe |
| // anyway. |
| std::mutex eport_mutex_; |
| zx::port eport_; |
| |
| // The thread on which we wait on the exception port. |
| std::thread io_thread_; |
| |
| // All callbacks that are currently bound to this port. |
| std::unordered_map<Key, BindData> callbacks_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(ExceptionPort); |
| }; |
| |
| // Print an exception in user-friendly form. |
| // This is for log messages and interactive programs that wish to report |
| // the exception. |
| // This doesn't have a better place at the moment. |
| void PrintException(FILE* out, const Thread* thread, zx_excp_type_t type, |
| const zx_exception_context_t& context); |
| |
| // Print a signal (or signals) in user-friendly form. |
| // This is for log messages and interactive programs that wish to report |
| // the exception. |
| // This doesn't have a better place at the moment. |
| void PrintSignal(FILE* out, const Thread* thread, zx_signals_t signals); |
| |
| } // namespace inferior_control |