blob: ddd52afdb6f795ba7398276861ba450947f33eae [file] [log] [blame]
// 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_