| // Copyright 2018 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. | |
| #ifndef SRC_DEVELOPER_DEBUG_SHARED_MESSAGE_LOOP_FUCHSIA_H_ | |
| #define SRC_DEVELOPER_DEBUG_SHARED_MESSAGE_LOOP_FUCHSIA_H_ | |
| #include <lib/async-loop/cpp/loop.h> | |
| #include <lib/async-loop/default.h> | |
| #include <lib/fdio/unsafe.h> | |
| #include <lib/zx/event.h> | |
| #include <lib/zx/port.h> | |
| #include <lib/zx/thread.h> | |
| #include <zircon/syscalls/exception.h> | |
| #include <vector> | |
| #include "src/developer/debug/shared/event_handlers.h" | |
| #include "src/developer/debug/shared/message_loop.h" | |
| namespace debug { | |
| class ExceptionHandler; | |
| class SignalHandler; | |
| class SocketWatcher; | |
| class ZirconExceptionWatcher; | |
| enum class WatchType : uint32_t { kTask, kFdio, kProcessExceptions, kJobExceptions, kSocket }; | |
| const char* WatchTypeToString(WatchType); | |
| // MessageLoop is a virtual class to enable tests to intercept watch messages. | |
| // See debug_agent/debug_agent_unittest.cc for an example. | |
| class MessageLoopFuchsia : public MessageLoop { | |
| public: | |
| // Associated struct to track information about what type of resource a watch handle is following. | |
| // | |
| // EventHandlers need access to the WatchInfo implementation, hence the reason for it to be | |
| // public. | |
| // | |
| // Definition at the end of the header. | |
| struct WatchInfo; | |
| using SignalHandlerMap = std::map<const async_wait_t*, SignalHandler>; | |
| using ChannelExceptionHandlerMap = std::map<const async_wait_t*, ChannelExceptionHandler>; | |
| MessageLoopFuchsia(); | |
| ~MessageLoopFuchsia(); | |
| bool Init(std::string* error_message) override; | |
| void Cleanup() override; | |
| // Returns the current message loop or null if there isn't one. | |
| static MessageLoopFuchsia* Current(); | |
| // MessageLoop implementation. | |
| WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher watcher) override; | |
| // Watches the given socket for read/write status. The watcher must outlive the returned | |
| // WatchHandle. Must only be called on the message loop thread. | |
| // | |
| // The WatchHandle must not unregister from a callback. The handle might become both readable and | |
| // writable at the same time which will necessitate calling both callbacks. The code does not | |
| // expect the SocketWatcher to disappear in between these callbacks. | |
| virtual zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle, SocketWatcher* watcher, | |
| WatchHandle* out); | |
| // Attaches to the exception port of the given process and issues callbacks on the given watcher. | |
| // The watcher must outlive the returned WatchHandle. Must only be called on the message loop | |
| // thread. | |
| struct WatchProcessConfig { | |
| std::string process_name; | |
| zx_handle_t process_handle; | |
| zx_koid_t process_koid; | |
| ZirconExceptionWatcher* watcher = nullptr; | |
| }; | |
| virtual zx_status_t WatchProcessExceptions(WatchProcessConfig config, WatchHandle* out); | |
| // Attaches to the exception port of the given job and issues callbacks on the given watcher. The | |
| // watcher must outlive the returned WatchHandle. Must only be called on the message loop thread. | |
| struct WatchJobConfig { | |
| std::string job_name; | |
| zx_handle_t job_handle; | |
| zx_koid_t job_koid; | |
| ZirconExceptionWatcher* watcher; | |
| }; | |
| virtual zx_status_t WatchJobExceptions(WatchJobConfig config, WatchHandle* out); | |
| void QuitNow() override; | |
| const SignalHandlerMap& signal_handlers() const { return signal_handlers_; } | |
| const ChannelExceptionHandlerMap& channel_exception_handlers() const { | |
| return channel_exception_handlers_; | |
| } | |
| private: | |
| const WatchInfo* FindWatchInfo(int id) const; | |
| // MessageLoop protected implementation. | |
| uint64_t GetMonotonicNowNS() const override; | |
| void RunImpl() override; | |
| void StopWatching(int id) override; | |
| // Triggers an event signaling that there is a pending event. | |
| void SetHasTasks() override; | |
| // Check for any pending C++ tasks and process them. | |
| // Returns true if there was an event pending to be processed. | |
| bool CheckAndProcessPendingTasks(); | |
| // Handlers exceptions channel. | |
| 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); | |
| void OnJobException(const WatchInfo& info, zx::exception exception, | |
| zx_exception_info_t exception_info); | |
| void OnProcessException(const WatchInfo& info, zx::exception exception, | |
| zx_exception_info_t exception_info); | |
| void OnProcessTerminated(const WatchInfo&, zx_signals_t observed); | |
| void OnSocketSignal(int watch_id, const WatchInfo& info, zx_signals_t observed); | |
| std::map<int, WatchInfo> watches_; | |
| // ID used as an index into watches_. | |
| int next_watch_id_ = 1; | |
| async::Loop loop_; | |
| zx::event task_event_; | |
| SignalHandlerMap signal_handlers_; | |
| // See SignalHandler constructor. | |
| // |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(WatchInfo* info); | |
| // Channel Exception Handlers are similar to SignalHandlers, but have different handling | |
| // semantics. Particularly, they are meant to return out exception_tokens to their handlers. | |
| ChannelExceptionHandlerMap channel_exception_handlers_; | |
| // Listens to the exception channel. Will call |HandleChannelException| on this message loop. | |
| // |options| are the options to be passed to |zx_task_create_exception_channel|. | |
| zx_status_t AddChannelExceptionHandler(int id, zx_handle_t object, uint32_t options, | |
| WatchInfo* info); | |
| void RemoveChannelExceptionHandler(WatchInfo*); | |
| FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoopFuchsia); | |
| friend class SignalHandler; | |
| friend class ChannelExceptionHandler; | |
| }; | |
| // EventHandlers need access to the WatchInfo implementation. | |
| struct MessageLoopFuchsia::WatchInfo { | |
| // Name of the resource being watched. | |
| // Mostly tracked for debugging purposes. | |
| std::string resource_name; | |
| WatchType type = WatchType::kFdio; | |
| // Used when the type is FDIO or socket. | |
| WatchMode mode = WatchMode::kReadWrite; | |
| // FDIO-specific watcher parameters. | |
| int fd = -1; | |
| fdio_t* fdio = nullptr; | |
| FDWatcher fd_watcher = nullptr; | |
| zx_handle_t fd_handle = ZX_HANDLE_INVALID; | |
| // Socket-specific parameters. | |
| SocketWatcher* socket_watcher = nullptr; | |
| zx_handle_t socket_handle = ZX_HANDLE_INVALID; | |
| // Task-exception-specific parameters, can be of job or process type. | |
| ZirconExceptionWatcher* exception_watcher = nullptr; | |
| zx_koid_t task_koid = 0; | |
| zx_handle_t task_handle = ZX_HANDLE_INVALID; | |
| // This makes easier the lookup of the associated ExceptionHandler with this watch id. | |
| const async_wait_t* signal_handler_key = nullptr; | |
| const async_wait_t* exception_channel_handler_key = nullptr; | |
| }; | |
| } // namespace debug | |
| #endif // SRC_DEVELOPER_DEBUG_SHARED_MESSAGE_LOOP_FUCHSIA_H_ |