1#ifndef JEMALLOC_INTERNAL_HOOK_H
2#define JEMALLOC_INTERNAL_HOOK_H
3
4#include "jemalloc/internal/tsd.h"
5
6/*
7 * This API is *extremely* experimental, and may get ripped out, changed in API-
8 * and ABI-incompatible ways, be insufficiently or incorrectly documented, etc.
9 *
10 * It allows hooking the stateful parts of the API to see changes as they
11 * happen.
12 *
13 * Allocation hooks are called after the allocation is done, free hooks are
14 * called before the free is done, and expand hooks are called after the
15 * allocation is expanded.
16 *
17 * For realloc and rallocx, if the expansion happens in place, the expansion
18 * hook is called. If it is moved, then the alloc hook is called on the new
19 * location, and then the free hook is called on the old location (i.e. both
20 * hooks are invoked in between the alloc and the dalloc).
21 *
22 * If we return NULL from OOM, then usize might not be trustworthy. Calling
23 * realloc(NULL, size) only calls the alloc hook, and calling realloc(ptr, 0)
24 * only calls the free hook. (Calling realloc(NULL, 0) is treated as malloc(0),
25 * and only calls the alloc hook).
26 *
27 * Reentrancy:
28 * Reentrancy is guarded against from within the hook implementation. If you
29 * call allocator functions from within a hook, the hooks will not be invoked
30 * again.
31 * Threading:
32 * The installation of a hook synchronizes with all its uses. If you can
33 * prove the installation of a hook happens-before a jemalloc entry point,
34 * then the hook will get invoked (unless there's a racing removal).
35 *
36 * Hook insertion appears to be atomic at a per-thread level (i.e. if a thread
37 * allocates and has the alloc hook invoked, then a subsequent free on the
38 * same thread will also have the free hook invoked).
39 *
40 * The *removal* of a hook does *not* block until all threads are done with
41 * the hook. Hook authors have to be resilient to this, and need some
42 * out-of-band mechanism for cleaning up any dynamically allocated memory
43 * associated with their hook.
44 * Ordering:
45 * Order of hook execution is unspecified, and may be different than insertion
46 * order.
47 */
48
49#define HOOK_MAX 4
50
51enum hook_alloc_e {
52 hook_alloc_malloc,
53 hook_alloc_posix_memalign,
54 hook_alloc_aligned_alloc,
55 hook_alloc_calloc,
56 hook_alloc_memalign,
57 hook_alloc_valloc,
58 hook_alloc_pvalloc,
59 hook_alloc_mallocx,
60
61 /* The reallocating functions have both alloc and dalloc variants */
62 hook_alloc_realloc,
63 hook_alloc_rallocx,
64};
65/*
66 * We put the enum typedef after the enum, since this file may get included by
67 * jemalloc_cpp.cpp, and C++ disallows enum forward declarations.
68 */
69typedef enum hook_alloc_e hook_alloc_t;
70
71enum hook_dalloc_e {
72 hook_dalloc_free,
73 hook_dalloc_dallocx,
74 hook_dalloc_sdallocx,
75
76 /*
77 * The dalloc halves of reallocation (not called if in-place expansion
78 * happens).
79 */
80 hook_dalloc_realloc,
81 hook_dalloc_rallocx,
82};
83typedef enum hook_dalloc_e hook_dalloc_t;
84
85
86enum hook_expand_e {
87 hook_expand_realloc,
88 hook_expand_rallocx,
89 hook_expand_xallocx,
90};
91typedef enum hook_expand_e hook_expand_t;
92
93typedef void (*hook_alloc)(
94 void *extra, hook_alloc_t type, void *result, uintptr_t result_raw,
95 uintptr_t args_raw[3]);
96
97typedef void (*hook_dalloc)(
98 void *extra, hook_dalloc_t type, void *address, uintptr_t args_raw[3]);
99
100typedef void (*hook_expand)(
101 void *extra, hook_expand_t type, void *address, size_t old_usize,
102 size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
103
104typedef struct hooks_s hooks_t;
105struct hooks_s {
106 hook_alloc alloc_hook;
107 hook_dalloc dalloc_hook;
108 hook_expand expand_hook;
109 void *extra;
110};
111
112/*
113 * Begin implementation details; everything above this point might one day live
114 * in a public API. Everything below this point never will.
115 */
116
117/*
118 * The realloc pathways haven't gotten any refactoring love in a while, and it's
119 * fairly difficult to pass information from the entry point to the hooks. We
120 * put the informaiton the hooks will need into a struct to encapsulate
121 * everything.
122 *
123 * Much of these pathways are force-inlined, so that the compiler can avoid
124 * materializing this struct until we hit an extern arena function. For fairly
125 * goofy reasons, *many* of the realloc paths hit an extern arena function.
126 * These paths are cold enough that it doesn't matter; eventually, we should
127 * rewrite the realloc code to make the expand-in-place and the
128 * free-then-realloc paths more orthogonal, at which point we don't need to
129 * spread the hook logic all over the place.
130 */
131typedef struct hook_ralloc_args_s hook_ralloc_args_t;
132struct hook_ralloc_args_s {
133 /* I.e. as opposed to rallocx. */
134 bool is_realloc;
135 /*
136 * The expand hook takes 4 arguments, even if only 3 are actually used;
137 * we add an extra one in case the user decides to memcpy without
138 * looking too closely at the hooked function.
139 */
140 uintptr_t args[4];
141};
142
143/*
144 * Returns an opaque handle to be used when removing the hook. NULL means that
145 * we couldn't install the hook.
146 */
147bool hook_boot();
148
149void *hook_install(tsdn_t *tsdn, hooks_t *hooks);
150/* Uninstalls the hook with the handle previously returned from hook_install. */
151void hook_remove(tsdn_t *tsdn, void *opaque);
152
153/* Hooks */
154
155void hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
156 uintptr_t args_raw[3]);
157
158void hook_invoke_dalloc(hook_dalloc_t type, void *address,
159 uintptr_t args_raw[3]);
160
161void hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
162 size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
163
164#endif /* JEMALLOC_INTERNAL_HOOK_H */
165