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