Blame jemalloc/include/jemalloc/internal/hook.h

Packit Service 724aca
#ifndef JEMALLOC_INTERNAL_HOOK_H
Packit Service 724aca
#define JEMALLOC_INTERNAL_HOOK_H
Packit Service 724aca
Packit Service 724aca
#include "jemalloc/internal/tsd.h"
Packit Service 724aca
Packit Service 724aca
/*
Packit Service 724aca
 * This API is *extremely* experimental, and may get ripped out, changed in API-
Packit Service 724aca
 * and ABI-incompatible ways, be insufficiently or incorrectly documented, etc.
Packit Service 724aca
 *
Packit Service 724aca
 * It allows hooking the stateful parts of the API to see changes as they
Packit Service 724aca
 * happen.
Packit Service 724aca
 *
Packit Service 724aca
 * Allocation hooks are called after the allocation is done, free hooks are
Packit Service 724aca
 * called before the free is done, and expand hooks are called after the
Packit Service 724aca
 * allocation is expanded.
Packit Service 724aca
 *
Packit Service 724aca
 * For realloc and rallocx, if the expansion happens in place, the expansion
Packit Service 724aca
 * hook is called.  If it is moved, then the alloc hook is called on the new
Packit Service 724aca
 * location, and then the free hook is called on the old location (i.e. both
Packit Service 724aca
 * hooks are invoked in between the alloc and the dalloc).
Packit Service 724aca
 *
Packit Service 724aca
 * If we return NULL from OOM, then usize might not be trustworthy.  Calling
Packit Service 724aca
 * realloc(NULL, size) only calls the alloc hook, and calling realloc(ptr, 0)
Packit Service 724aca
 * only calls the free hook.  (Calling realloc(NULL, 0) is treated as malloc(0),
Packit Service 724aca
 * and only calls the alloc hook).
Packit Service 724aca
 *
Packit Service 724aca
 * Reentrancy:
Packit Service 724aca
 *   Reentrancy is guarded against from within the hook implementation.  If you
Packit Service 724aca
 *   call allocator functions from within a hook, the hooks will not be invoked
Packit Service 724aca
 *   again.
Packit Service 724aca
 * Threading:
Packit Service 724aca
 *   The installation of a hook synchronizes with all its uses.  If you can
Packit Service 724aca
 *   prove the installation of a hook happens-before a jemalloc entry point,
Packit Service 724aca
 *   then the hook will get invoked (unless there's a racing removal).
Packit Service 724aca
 *
Packit Service 724aca
 *   Hook insertion appears to be atomic at a per-thread level (i.e. if a thread
Packit Service 724aca
 *   allocates and has the alloc hook invoked, then a subsequent free on the
Packit Service 724aca
 *   same thread will also have the free hook invoked).
Packit Service 724aca
 *
Packit Service 724aca
 *   The *removal* of a hook does *not* block until all threads are done with
Packit Service 724aca
 *   the hook.  Hook authors have to be resilient to this, and need some
Packit Service 724aca
 *   out-of-band mechanism for cleaning up any dynamically allocated memory
Packit Service 724aca
 *   associated with their hook.
Packit Service 724aca
 * Ordering:
Packit Service 724aca
 *   Order of hook execution is unspecified, and may be different than insertion
Packit Service 724aca
 *   order.
Packit Service 724aca
 */
Packit Service 724aca
Packit Service 724aca
#define HOOK_MAX 4
Packit Service 724aca
Packit Service 724aca
enum hook_alloc_e {
Packit Service 724aca
	hook_alloc_malloc,
Packit Service 724aca
	hook_alloc_posix_memalign,
Packit Service 724aca
	hook_alloc_aligned_alloc,
Packit Service 724aca
	hook_alloc_calloc,
Packit Service 724aca
	hook_alloc_memalign,
Packit Service 724aca
	hook_alloc_valloc,
Packit Service 724aca
	hook_alloc_mallocx,
Packit Service 724aca
Packit Service 724aca
	/* The reallocating functions have both alloc and dalloc variants */
Packit Service 724aca
	hook_alloc_realloc,
Packit Service 724aca
	hook_alloc_rallocx,
Packit Service 724aca
};
Packit Service 724aca
/*
Packit Service 724aca
 * We put the enum typedef after the enum, since this file may get included by
Packit Service 724aca
 * jemalloc_cpp.cpp, and C++ disallows enum forward declarations.
Packit Service 724aca
 */
Packit Service 724aca
typedef enum hook_alloc_e hook_alloc_t;
Packit Service 724aca
Packit Service 724aca
enum hook_dalloc_e {
Packit Service 724aca
	hook_dalloc_free,
Packit Service 724aca
	hook_dalloc_dallocx,
Packit Service 724aca
	hook_dalloc_sdallocx,
Packit Service 724aca
Packit Service 724aca
	/*
Packit Service 724aca
	 * The dalloc halves of reallocation (not called if in-place expansion
Packit Service 724aca
	 * happens).
Packit Service 724aca
	 */
Packit Service 724aca
	hook_dalloc_realloc,
Packit Service 724aca
	hook_dalloc_rallocx,
Packit Service 724aca
};
Packit Service 724aca
typedef enum hook_dalloc_e hook_dalloc_t;
Packit Service 724aca
Packit Service 724aca
Packit Service 724aca
enum hook_expand_e {
Packit Service 724aca
	hook_expand_realloc,
Packit Service 724aca
	hook_expand_rallocx,
Packit Service 724aca
	hook_expand_xallocx,
Packit Service 724aca
};
Packit Service 724aca
typedef enum hook_expand_e hook_expand_t;
Packit Service 724aca
Packit Service 724aca
typedef void (*hook_alloc)(
Packit Service 724aca
    void *extra, hook_alloc_t type, void *result, uintptr_t result_raw,
Packit Service 724aca
    uintptr_t args_raw[3]);
Packit Service 724aca
Packit Service 724aca
typedef void (*hook_dalloc)(
Packit Service 724aca
    void *extra, hook_dalloc_t type, void *address, uintptr_t args_raw[3]);
Packit Service 724aca
Packit Service 724aca
typedef void (*hook_expand)(
Packit Service 724aca
    void *extra, hook_expand_t type, void *address, size_t old_usize,
Packit Service 724aca
    size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
Packit Service 724aca
Packit Service 724aca
typedef struct hooks_s hooks_t;
Packit Service 724aca
struct hooks_s {
Packit Service 724aca
	hook_alloc alloc_hook;
Packit Service 724aca
	hook_dalloc dalloc_hook;
Packit Service 724aca
	hook_expand expand_hook;
Packit Service 724aca
	void *extra;
Packit Service 724aca
};
Packit Service 724aca
Packit Service 724aca
/*
Packit Service 724aca
 * Begin implementation details; everything above this point might one day live
Packit Service 724aca
 * in a public API.  Everything below this point never will.
Packit Service 724aca
 */
Packit Service 724aca
Packit Service 724aca
/*
Packit Service 724aca
 * The realloc pathways haven't gotten any refactoring love in a while, and it's
Packit Service 724aca
 * fairly difficult to pass information from the entry point to the hooks.  We
Packit Service 724aca
 * put the informaiton the hooks will need into a struct to encapsulate
Packit Service 724aca
 * everything.
Packit Service 724aca
 *
Packit Service 724aca
 * Much of these pathways are force-inlined, so that the compiler can avoid
Packit Service 724aca
 * materializing this struct until we hit an extern arena function.  For fairly
Packit Service 724aca
 * goofy reasons, *many* of the realloc paths hit an extern arena function.
Packit Service 724aca
 * These paths are cold enough that it doesn't matter; eventually, we should
Packit Service 724aca
 * rewrite the realloc code to make the expand-in-place and the
Packit Service 724aca
 * free-then-realloc paths more orthogonal, at which point we don't need to
Packit Service 724aca
 * spread the hook logic all over the place.
Packit Service 724aca
 */
Packit Service 724aca
typedef struct hook_ralloc_args_s hook_ralloc_args_t;
Packit Service 724aca
struct hook_ralloc_args_s {
Packit Service 724aca
	/* I.e. as opposed to rallocx. */
Packit Service 724aca
	bool is_realloc;
Packit Service 724aca
	/*
Packit Service 724aca
	 * The expand hook takes 4 arguments, even if only 3 are actually used;
Packit Service 724aca
	 * we add an extra one in case the user decides to memcpy without
Packit Service 724aca
	 * looking too closely at the hooked function.
Packit Service 724aca
	 */
Packit Service 724aca
	uintptr_t args[4];
Packit Service 724aca
};
Packit Service 724aca
Packit Service 724aca
/*
Packit Service 724aca
 * Returns an opaque handle to be used when removing the hook.  NULL means that
Packit Service 724aca
 * we couldn't install the hook.
Packit Service 724aca
 */
Packit Service 724aca
bool hook_boot();
Packit Service 724aca
Packit Service 724aca
void *hook_install(tsdn_t *tsdn, hooks_t *hooks);
Packit Service 724aca
/* Uninstalls the hook with the handle previously returned from hook_install. */
Packit Service 724aca
void hook_remove(tsdn_t *tsdn, void *opaque);
Packit Service 724aca
Packit Service 724aca
/* Hooks */
Packit Service 724aca
Packit Service 724aca
void hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
Packit Service 724aca
    uintptr_t args_raw[3]);
Packit Service 724aca
Packit Service 724aca
void hook_invoke_dalloc(hook_dalloc_t type, void *address,
Packit Service 724aca
    uintptr_t args_raw[3]);
Packit Service 724aca
Packit Service 724aca
void hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
Packit Service 724aca
    size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
Packit Service 724aca
Packit Service 724aca
#endif /* JEMALLOC_INTERNAL_HOOK_H */