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 51 enum 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_mallocx, 59 60 /* The reallocating functions have both alloc and dalloc variants */ 61 hook_alloc_realloc, 62 hook_alloc_rallocx, 63 }; 64 /* 65 * We put the enum typedef after the enum, since this file may get included by 66 * jemalloc_cpp.cpp, and C++ disallows enum forward declarations. 67 */ 68 typedef enum hook_alloc_e hook_alloc_t; 69 70 enum hook_dalloc_e { 71 hook_dalloc_free, 72 hook_dalloc_dallocx, 73 hook_dalloc_sdallocx, 74 75 /* 76 * The dalloc halves of reallocation (not called if in-place expansion 77 * happens). 78 */ 79 hook_dalloc_realloc, 80 hook_dalloc_rallocx, 81 }; 82 typedef enum hook_dalloc_e hook_dalloc_t; 83 84 85 enum hook_expand_e { 86 hook_expand_realloc, 87 hook_expand_rallocx, 88 hook_expand_xallocx, 89 }; 90 typedef enum hook_expand_e hook_expand_t; 91 92 typedef void (*hook_alloc)( 93 void *extra, hook_alloc_t type, void *result, uintptr_t result_raw, 94 uintptr_t args_raw[3]); 95 96 typedef void (*hook_dalloc)( 97 void *extra, hook_dalloc_t type, void *address, uintptr_t args_raw[3]); 98 99 typedef void (*hook_expand)( 100 void *extra, hook_expand_t type, void *address, size_t old_usize, 101 size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]); 102 103 typedef struct hooks_s hooks_t; 104 struct hooks_s { 105 hook_alloc alloc_hook; 106 hook_dalloc dalloc_hook; 107 hook_expand expand_hook; 108 void *extra; 109 }; 110 111 /* 112 * Begin implementation details; everything above this point might one day live 113 * in a public API. Everything below this point never will. 114 */ 115 116 /* 117 * The realloc pathways haven't gotten any refactoring love in a while, and it's 118 * fairly difficult to pass information from the entry point to the hooks. We 119 * put the informaiton the hooks will need into a struct to encapsulate 120 * everything. 121 * 122 * Much of these pathways are force-inlined, so that the compiler can avoid 123 * materializing this struct until we hit an extern arena function. For fairly 124 * goofy reasons, *many* of the realloc paths hit an extern arena function. 125 * These paths are cold enough that it doesn't matter; eventually, we should 126 * rewrite the realloc code to make the expand-in-place and the 127 * free-then-realloc paths more orthogonal, at which point we don't need to 128 * spread the hook logic all over the place. 129 */ 130 typedef struct hook_ralloc_args_s hook_ralloc_args_t; 131 struct hook_ralloc_args_s { 132 /* I.e. as opposed to rallocx. */ 133 bool is_realloc; 134 /* 135 * The expand hook takes 4 arguments, even if only 3 are actually used; 136 * we add an extra one in case the user decides to memcpy without 137 * looking too closely at the hooked function. 138 */ 139 uintptr_t args[4]; 140 }; 141 142 /* 143 * Returns an opaque handle to be used when removing the hook. NULL means that 144 * we couldn't install the hook. 145 */ 146 bool hook_boot(); 147 148 void *hook_install(tsdn_t *tsdn, hooks_t *hooks); 149 /* Uninstalls the hook with the handle previously returned from hook_install. */ 150 void hook_remove(tsdn_t *tsdn, void *opaque); 151 152 /* Hooks */ 153 154 void hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw, 155 uintptr_t args_raw[3]); 156 157 void hook_invoke_dalloc(hook_dalloc_t type, void *address, 158 uintptr_t args_raw[3]); 159 160 void hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize, 161 size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]); 162 163 #endif /* JEMALLOC_INTERNAL_HOOK_H */ 164