xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/hook.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1*7bdf38e5Schristos #include "test/jemalloc_test.h"
2*7bdf38e5Schristos 
3*7bdf38e5Schristos #include "jemalloc/internal/hook.h"
4*7bdf38e5Schristos 
5*7bdf38e5Schristos static void *arg_extra;
6*7bdf38e5Schristos static int arg_type;
7*7bdf38e5Schristos static void *arg_result;
8*7bdf38e5Schristos static void *arg_address;
9*7bdf38e5Schristos static size_t arg_old_usize;
10*7bdf38e5Schristos static size_t arg_new_usize;
11*7bdf38e5Schristos static uintptr_t arg_result_raw;
12*7bdf38e5Schristos static uintptr_t arg_args_raw[4];
13*7bdf38e5Schristos 
14*7bdf38e5Schristos static int call_count = 0;
15*7bdf38e5Schristos 
16*7bdf38e5Schristos static void
17*7bdf38e5Schristos reset_args() {
18*7bdf38e5Schristos 	arg_extra = NULL;
19*7bdf38e5Schristos 	arg_type = 12345;
20*7bdf38e5Schristos 	arg_result = NULL;
21*7bdf38e5Schristos 	arg_address = NULL;
22*7bdf38e5Schristos 	arg_old_usize = 0;
23*7bdf38e5Schristos 	arg_new_usize = 0;
24*7bdf38e5Schristos 	arg_result_raw = 0;
25*7bdf38e5Schristos 	memset(arg_args_raw, 77, sizeof(arg_args_raw));
26*7bdf38e5Schristos }
27*7bdf38e5Schristos 
28*7bdf38e5Schristos static void
29*7bdf38e5Schristos alloc_free_size(size_t sz) {
30*7bdf38e5Schristos 	void *ptr = mallocx(1, 0);
31*7bdf38e5Schristos 	free(ptr);
32*7bdf38e5Schristos 	ptr = mallocx(1, 0);
33*7bdf38e5Schristos 	free(ptr);
34*7bdf38e5Schristos 	ptr = mallocx(1, MALLOCX_TCACHE_NONE);
35*7bdf38e5Schristos 	dallocx(ptr, MALLOCX_TCACHE_NONE);
36*7bdf38e5Schristos }
37*7bdf38e5Schristos 
38*7bdf38e5Schristos /*
39*7bdf38e5Schristos  * We want to support a degree of user reentrancy.  This tests a variety of
40*7bdf38e5Schristos  * allocation scenarios.
41*7bdf38e5Schristos  */
42*7bdf38e5Schristos static void
43*7bdf38e5Schristos be_reentrant() {
44*7bdf38e5Schristos 	/* Let's make sure the tcache is non-empty if enabled. */
45*7bdf38e5Schristos 	alloc_free_size(1);
46*7bdf38e5Schristos 	alloc_free_size(1024);
47*7bdf38e5Schristos 	alloc_free_size(64 * 1024);
48*7bdf38e5Schristos 	alloc_free_size(256 * 1024);
49*7bdf38e5Schristos 	alloc_free_size(1024 * 1024);
50*7bdf38e5Schristos 
51*7bdf38e5Schristos 	/* Some reallocation. */
52*7bdf38e5Schristos 	void *ptr = mallocx(129, 0);
53*7bdf38e5Schristos 	ptr = rallocx(ptr, 130, 0);
54*7bdf38e5Schristos 	free(ptr);
55*7bdf38e5Schristos 
56*7bdf38e5Schristos 	ptr = mallocx(2 * 1024 * 1024, 0);
57*7bdf38e5Schristos 	free(ptr);
58*7bdf38e5Schristos 	ptr = mallocx(1 * 1024 * 1024, 0);
59*7bdf38e5Schristos 	ptr = rallocx(ptr, 2 * 1024 * 1024, 0);
60*7bdf38e5Schristos 	free(ptr);
61*7bdf38e5Schristos 
62*7bdf38e5Schristos 	ptr = mallocx(1, 0);
63*7bdf38e5Schristos 	ptr = rallocx(ptr, 1000, 0);
64*7bdf38e5Schristos 	free(ptr);
65*7bdf38e5Schristos }
66*7bdf38e5Schristos 
67*7bdf38e5Schristos static void
68*7bdf38e5Schristos set_args_raw(uintptr_t *args_raw, int nargs) {
69*7bdf38e5Schristos 	memcpy(arg_args_raw, args_raw, sizeof(uintptr_t) * nargs);
70*7bdf38e5Schristos }
71*7bdf38e5Schristos 
72*7bdf38e5Schristos static void
73*7bdf38e5Schristos expect_args_raw(uintptr_t *args_raw_expected, int nargs) {
74*7bdf38e5Schristos 	int cmp = memcmp(args_raw_expected, arg_args_raw,
75*7bdf38e5Schristos 	    sizeof(uintptr_t) * nargs);
76*7bdf38e5Schristos 	expect_d_eq(cmp, 0, "Raw args mismatch");
77*7bdf38e5Schristos }
78*7bdf38e5Schristos 
79*7bdf38e5Schristos static void
80*7bdf38e5Schristos reset() {
81*7bdf38e5Schristos 	call_count = 0;
82*7bdf38e5Schristos 	reset_args();
83*7bdf38e5Schristos }
84*7bdf38e5Schristos 
85*7bdf38e5Schristos static void
86*7bdf38e5Schristos test_alloc_hook(void *extra, hook_alloc_t type, void *result,
87*7bdf38e5Schristos     uintptr_t result_raw, uintptr_t args_raw[3]) {
88*7bdf38e5Schristos 	call_count++;
89*7bdf38e5Schristos 	arg_extra = extra;
90*7bdf38e5Schristos 	arg_type = (int)type;
91*7bdf38e5Schristos 	arg_result = result;
92*7bdf38e5Schristos 	arg_result_raw = result_raw;
93*7bdf38e5Schristos 	set_args_raw(args_raw, 3);
94*7bdf38e5Schristos 	be_reentrant();
95*7bdf38e5Schristos }
96*7bdf38e5Schristos 
97*7bdf38e5Schristos static void
98*7bdf38e5Schristos test_dalloc_hook(void *extra, hook_dalloc_t type, void *address,
99*7bdf38e5Schristos     uintptr_t args_raw[3]) {
100*7bdf38e5Schristos 	call_count++;
101*7bdf38e5Schristos 	arg_extra = extra;
102*7bdf38e5Schristos 	arg_type = (int)type;
103*7bdf38e5Schristos 	arg_address = address;
104*7bdf38e5Schristos 	set_args_raw(args_raw, 3);
105*7bdf38e5Schristos 	be_reentrant();
106*7bdf38e5Schristos }
107*7bdf38e5Schristos 
108*7bdf38e5Schristos static void
109*7bdf38e5Schristos test_expand_hook(void *extra, hook_expand_t type, void *address,
110*7bdf38e5Schristos     size_t old_usize, size_t new_usize, uintptr_t result_raw,
111*7bdf38e5Schristos     uintptr_t args_raw[4]) {
112*7bdf38e5Schristos 	call_count++;
113*7bdf38e5Schristos 	arg_extra = extra;
114*7bdf38e5Schristos 	arg_type = (int)type;
115*7bdf38e5Schristos 	arg_address = address;
116*7bdf38e5Schristos 	arg_old_usize = old_usize;
117*7bdf38e5Schristos 	arg_new_usize = new_usize;
118*7bdf38e5Schristos 	arg_result_raw = result_raw;
119*7bdf38e5Schristos 	set_args_raw(args_raw, 4);
120*7bdf38e5Schristos 	be_reentrant();
121*7bdf38e5Schristos }
122*7bdf38e5Schristos 
123*7bdf38e5Schristos TEST_BEGIN(test_hooks_basic) {
124*7bdf38e5Schristos 	/* Just verify that the record their arguments correctly. */
125*7bdf38e5Schristos 	hooks_t hooks = {
126*7bdf38e5Schristos 		&test_alloc_hook, &test_dalloc_hook, &test_expand_hook,
127*7bdf38e5Schristos 		(void *)111};
128*7bdf38e5Schristos 	void *handle = hook_install(TSDN_NULL, &hooks);
129*7bdf38e5Schristos 	uintptr_t args_raw[4] = {10, 20, 30, 40};
130*7bdf38e5Schristos 
131*7bdf38e5Schristos 	/* Alloc */
132*7bdf38e5Schristos 	reset_args();
133*7bdf38e5Schristos 	hook_invoke_alloc(hook_alloc_posix_memalign, (void *)222, 333,
134*7bdf38e5Schristos 	    args_raw);
135*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)111, "Passed wrong user pointer");
136*7bdf38e5Schristos 	expect_d_eq((int)hook_alloc_posix_memalign, arg_type,
137*7bdf38e5Schristos 	    "Passed wrong alloc type");
138*7bdf38e5Schristos 	expect_ptr_eq((void *)222, arg_result, "Passed wrong result address");
139*7bdf38e5Schristos 	expect_u64_eq(333, arg_result_raw, "Passed wrong result");
140*7bdf38e5Schristos 	expect_args_raw(args_raw, 3);
141*7bdf38e5Schristos 
142*7bdf38e5Schristos 	/* Dalloc */
143*7bdf38e5Schristos 	reset_args();
144*7bdf38e5Schristos 	hook_invoke_dalloc(hook_dalloc_sdallocx, (void *)222, args_raw);
145*7bdf38e5Schristos 	expect_d_eq((int)hook_dalloc_sdallocx, arg_type,
146*7bdf38e5Schristos 	    "Passed wrong dalloc type");
147*7bdf38e5Schristos 	expect_ptr_eq((void *)111, arg_extra, "Passed wrong user pointer");
148*7bdf38e5Schristos 	expect_ptr_eq((void *)222, arg_address, "Passed wrong address");
149*7bdf38e5Schristos 	expect_args_raw(args_raw, 3);
150*7bdf38e5Schristos 
151*7bdf38e5Schristos 	/* Expand */
152*7bdf38e5Schristos 	reset_args();
153*7bdf38e5Schristos 	hook_invoke_expand(hook_expand_xallocx, (void *)222, 333, 444, 555,
154*7bdf38e5Schristos 	    args_raw);
155*7bdf38e5Schristos 	expect_d_eq((int)hook_expand_xallocx, arg_type,
156*7bdf38e5Schristos 	    "Passed wrong expand type");
157*7bdf38e5Schristos 	expect_ptr_eq((void *)111, arg_extra, "Passed wrong user pointer");
158*7bdf38e5Schristos 	expect_ptr_eq((void *)222, arg_address, "Passed wrong address");
159*7bdf38e5Schristos 	expect_zu_eq(333, arg_old_usize, "Passed wrong old usize");
160*7bdf38e5Schristos 	expect_zu_eq(444, arg_new_usize, "Passed wrong new usize");
161*7bdf38e5Schristos 	expect_zu_eq(555, arg_result_raw, "Passed wrong result");
162*7bdf38e5Schristos 	expect_args_raw(args_raw, 4);
163*7bdf38e5Schristos 
164*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle);
165*7bdf38e5Schristos }
166*7bdf38e5Schristos TEST_END
167*7bdf38e5Schristos 
168*7bdf38e5Schristos TEST_BEGIN(test_hooks_null) {
169*7bdf38e5Schristos 	/* Null hooks should be ignored, not crash. */
170*7bdf38e5Schristos 	hooks_t hooks1 = {NULL, NULL, NULL, NULL};
171*7bdf38e5Schristos 	hooks_t hooks2 = {&test_alloc_hook, NULL, NULL, NULL};
172*7bdf38e5Schristos 	hooks_t hooks3 = {NULL, &test_dalloc_hook, NULL, NULL};
173*7bdf38e5Schristos 	hooks_t hooks4 = {NULL, NULL, &test_expand_hook, NULL};
174*7bdf38e5Schristos 
175*7bdf38e5Schristos 	void *handle1 = hook_install(TSDN_NULL, &hooks1);
176*7bdf38e5Schristos 	void *handle2 = hook_install(TSDN_NULL, &hooks2);
177*7bdf38e5Schristos 	void *handle3 = hook_install(TSDN_NULL, &hooks3);
178*7bdf38e5Schristos 	void *handle4 = hook_install(TSDN_NULL, &hooks4);
179*7bdf38e5Schristos 
180*7bdf38e5Schristos 	expect_ptr_ne(handle1, NULL, "Hook installation failed");
181*7bdf38e5Schristos 	expect_ptr_ne(handle2, NULL, "Hook installation failed");
182*7bdf38e5Schristos 	expect_ptr_ne(handle3, NULL, "Hook installation failed");
183*7bdf38e5Schristos 	expect_ptr_ne(handle4, NULL, "Hook installation failed");
184*7bdf38e5Schristos 
185*7bdf38e5Schristos 	uintptr_t args_raw[4] = {10, 20, 30, 40};
186*7bdf38e5Schristos 
187*7bdf38e5Schristos 	call_count = 0;
188*7bdf38e5Schristos 	hook_invoke_alloc(hook_alloc_malloc, NULL, 0, args_raw);
189*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Called wrong number of times");
190*7bdf38e5Schristos 
191*7bdf38e5Schristos 	call_count = 0;
192*7bdf38e5Schristos 	hook_invoke_dalloc(hook_dalloc_free, NULL, args_raw);
193*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Called wrong number of times");
194*7bdf38e5Schristos 
195*7bdf38e5Schristos 	call_count = 0;
196*7bdf38e5Schristos 	hook_invoke_expand(hook_expand_realloc, NULL, 0, 0, 0, args_raw);
197*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Called wrong number of times");
198*7bdf38e5Schristos 
199*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle1);
200*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle2);
201*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle3);
202*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle4);
203*7bdf38e5Schristos }
204*7bdf38e5Schristos TEST_END
205*7bdf38e5Schristos 
206*7bdf38e5Schristos TEST_BEGIN(test_hooks_remove) {
207*7bdf38e5Schristos 	hooks_t hooks = {&test_alloc_hook, NULL, NULL, NULL};
208*7bdf38e5Schristos 	void *handle = hook_install(TSDN_NULL, &hooks);
209*7bdf38e5Schristos 	expect_ptr_ne(handle, NULL, "Hook installation failed");
210*7bdf38e5Schristos 	call_count = 0;
211*7bdf38e5Schristos 	uintptr_t args_raw[4] = {10, 20, 30, 40};
212*7bdf38e5Schristos 	hook_invoke_alloc(hook_alloc_malloc, NULL, 0, args_raw);
213*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not invoked");
214*7bdf38e5Schristos 
215*7bdf38e5Schristos 	call_count = 0;
216*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle);
217*7bdf38e5Schristos 	hook_invoke_alloc(hook_alloc_malloc, NULL, 0, NULL);
218*7bdf38e5Schristos 	expect_d_eq(call_count, 0, "Hook invoked after removal");
219*7bdf38e5Schristos 
220*7bdf38e5Schristos }
221*7bdf38e5Schristos TEST_END
222*7bdf38e5Schristos 
223*7bdf38e5Schristos TEST_BEGIN(test_hooks_alloc_simple) {
224*7bdf38e5Schristos 	/* "Simple" in the sense that we're not in a realloc variant. */
225*7bdf38e5Schristos 	hooks_t hooks = {&test_alloc_hook, NULL, NULL, (void *)123};
226*7bdf38e5Schristos 	void *handle = hook_install(TSDN_NULL, &hooks);
227*7bdf38e5Schristos 	expect_ptr_ne(handle, NULL, "Hook installation failed");
228*7bdf38e5Schristos 
229*7bdf38e5Schristos 	/* Stop malloc from being optimized away. */
230*7bdf38e5Schristos 	volatile int err;
231*7bdf38e5Schristos 	void *volatile ptr;
232*7bdf38e5Schristos 
233*7bdf38e5Schristos 	/* malloc */
234*7bdf38e5Schristos 	reset();
235*7bdf38e5Schristos 	ptr = malloc(1);
236*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
237*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
238*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_malloc, "Wrong hook type");
239*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
240*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
241*7bdf38e5Schristos 	    "Wrong raw result");
242*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
243*7bdf38e5Schristos 	free(ptr);
244*7bdf38e5Schristos 
245*7bdf38e5Schristos 	/* posix_memalign */
246*7bdf38e5Schristos 	reset();
247*7bdf38e5Schristos 	err = posix_memalign((void **)&ptr, 1024, 1);
248*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
249*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
250*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_posix_memalign,
251*7bdf38e5Schristos 	    "Wrong hook type");
252*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
253*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)err, (uintptr_t)arg_result_raw,
254*7bdf38e5Schristos 	    "Wrong raw result");
255*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)&ptr, arg_args_raw[0], "Wrong argument");
256*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1024, arg_args_raw[1], "Wrong argument");
257*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[2], "Wrong argument");
258*7bdf38e5Schristos 	free(ptr);
259*7bdf38e5Schristos 
260*7bdf38e5Schristos 	/* aligned_alloc */
261*7bdf38e5Schristos 	reset();
262*7bdf38e5Schristos 	ptr = aligned_alloc(1024, 1);
263*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
264*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
265*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_aligned_alloc,
266*7bdf38e5Schristos 	    "Wrong hook type");
267*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
268*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
269*7bdf38e5Schristos 	    "Wrong raw result");
270*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument");
271*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
272*7bdf38e5Schristos 	free(ptr);
273*7bdf38e5Schristos 
274*7bdf38e5Schristos 	/* calloc */
275*7bdf38e5Schristos 	reset();
276*7bdf38e5Schristos 	ptr = calloc(11, 13);
277*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
278*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
279*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_calloc, "Wrong hook type");
280*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
281*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
282*7bdf38e5Schristos 	    "Wrong raw result");
283*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)11, arg_args_raw[0], "Wrong argument");
284*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)13, arg_args_raw[1], "Wrong argument");
285*7bdf38e5Schristos 	free(ptr);
286*7bdf38e5Schristos 
287*7bdf38e5Schristos 	/* memalign */
288*7bdf38e5Schristos #ifdef JEMALLOC_OVERRIDE_MEMALIGN
289*7bdf38e5Schristos 	reset();
290*7bdf38e5Schristos 	ptr = memalign(1024, 1);
291*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
292*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
293*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_memalign, "Wrong hook type");
294*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
295*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
296*7bdf38e5Schristos 	    "Wrong raw result");
297*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument");
298*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
299*7bdf38e5Schristos 	free(ptr);
300*7bdf38e5Schristos #endif /* JEMALLOC_OVERRIDE_MEMALIGN */
301*7bdf38e5Schristos 
302*7bdf38e5Schristos 	/* valloc */
303*7bdf38e5Schristos #ifdef JEMALLOC_OVERRIDE_VALLOC
304*7bdf38e5Schristos 	reset();
305*7bdf38e5Schristos 	ptr = valloc(1);
306*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
307*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
308*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_valloc, "Wrong hook type");
309*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
310*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
311*7bdf38e5Schristos 	    "Wrong raw result");
312*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
313*7bdf38e5Schristos 	free(ptr);
314*7bdf38e5Schristos #endif /* JEMALLOC_OVERRIDE_VALLOC */
315*7bdf38e5Schristos 
316*7bdf38e5Schristos 	/* mallocx */
317*7bdf38e5Schristos 	reset();
318*7bdf38e5Schristos 	ptr = mallocx(1, MALLOCX_LG_ALIGN(10));
319*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
320*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
321*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_mallocx, "Wrong hook type");
322*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
323*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
324*7bdf38e5Schristos 	    "Wrong raw result");
325*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
326*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)MALLOCX_LG_ALIGN(10), arg_args_raw[1],
327*7bdf38e5Schristos 	    "Wrong flags");
328*7bdf38e5Schristos 	free(ptr);
329*7bdf38e5Schristos 
330*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle);
331*7bdf38e5Schristos }
332*7bdf38e5Schristos TEST_END
333*7bdf38e5Schristos 
334*7bdf38e5Schristos TEST_BEGIN(test_hooks_dalloc_simple) {
335*7bdf38e5Schristos 	/* "Simple" in the sense that we're not in a realloc variant. */
336*7bdf38e5Schristos 	hooks_t hooks = {NULL, &test_dalloc_hook, NULL, (void *)123};
337*7bdf38e5Schristos 	void *handle = hook_install(TSDN_NULL, &hooks);
338*7bdf38e5Schristos 	expect_ptr_ne(handle, NULL, "Hook installation failed");
339*7bdf38e5Schristos 
340*7bdf38e5Schristos 	void *volatile ptr;
341*7bdf38e5Schristos 
342*7bdf38e5Schristos 	/* free() */
343*7bdf38e5Schristos 	reset();
344*7bdf38e5Schristos 	ptr = malloc(1);
345*7bdf38e5Schristos 	free(ptr);
346*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
347*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
348*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_dalloc_free, "Wrong hook type");
349*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_address, "Wrong pointer freed");
350*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
351*7bdf38e5Schristos 
352*7bdf38e5Schristos 	/* dallocx() */
353*7bdf38e5Schristos 	reset();
354*7bdf38e5Schristos 	ptr = malloc(1);
355*7bdf38e5Schristos 	dallocx(ptr, MALLOCX_TCACHE_NONE);
356*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
357*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
358*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_dalloc_dallocx, "Wrong hook type");
359*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_address, "Wrong pointer freed");
360*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
361*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[1],
362*7bdf38e5Schristos 	    "Wrong raw arg");
363*7bdf38e5Schristos 
364*7bdf38e5Schristos 	/* sdallocx() */
365*7bdf38e5Schristos 	reset();
366*7bdf38e5Schristos 	ptr = malloc(1);
367*7bdf38e5Schristos 	sdallocx(ptr, 1, MALLOCX_TCACHE_NONE);
368*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
369*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
370*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_dalloc_sdallocx, "Wrong hook type");
371*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_address, "Wrong pointer freed");
372*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
373*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong raw arg");
374*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[2],
375*7bdf38e5Schristos 	    "Wrong raw arg");
376*7bdf38e5Schristos 
377*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle);
378*7bdf38e5Schristos }
379*7bdf38e5Schristos TEST_END
380*7bdf38e5Schristos 
381*7bdf38e5Schristos TEST_BEGIN(test_hooks_expand_simple) {
382*7bdf38e5Schristos 	/* "Simple" in the sense that we're not in a realloc variant. */
383*7bdf38e5Schristos 	hooks_t hooks = {NULL, NULL, &test_expand_hook, (void *)123};
384*7bdf38e5Schristos 	void *handle = hook_install(TSDN_NULL, &hooks);
385*7bdf38e5Schristos 	expect_ptr_ne(handle, NULL, "Hook installation failed");
386*7bdf38e5Schristos 
387*7bdf38e5Schristos 	void *volatile ptr;
388*7bdf38e5Schristos 
389*7bdf38e5Schristos 	/* xallocx() */
390*7bdf38e5Schristos 	reset();
391*7bdf38e5Schristos 	ptr = malloc(1);
392*7bdf38e5Schristos 	size_t new_usize = xallocx(ptr, 100, 200, MALLOCX_TCACHE_NONE);
393*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
394*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
395*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_expand_xallocx, "Wrong hook type");
396*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_address, "Wrong pointer expanded");
397*7bdf38e5Schristos 	expect_u64_eq(arg_old_usize, nallocx(1, 0), "Wrong old usize");
398*7bdf38e5Schristos 	expect_u64_eq(arg_new_usize, sallocx(ptr, 0), "Wrong new usize");
399*7bdf38e5Schristos 	expect_u64_eq(new_usize, arg_result_raw, "Wrong result");
400*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong arg");
401*7bdf38e5Schristos 	expect_u64_eq(100, arg_args_raw[1], "Wrong arg");
402*7bdf38e5Schristos 	expect_u64_eq(200, arg_args_raw[2], "Wrong arg");
403*7bdf38e5Schristos 	expect_u64_eq(MALLOCX_TCACHE_NONE, arg_args_raw[3], "Wrong arg");
404*7bdf38e5Schristos 
405*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle);
406*7bdf38e5Schristos }
407*7bdf38e5Schristos TEST_END
408*7bdf38e5Schristos 
409*7bdf38e5Schristos TEST_BEGIN(test_hooks_realloc_as_malloc_or_free) {
410*7bdf38e5Schristos 	hooks_t hooks = {&test_alloc_hook, &test_dalloc_hook,
411*7bdf38e5Schristos 		&test_expand_hook, (void *)123};
412*7bdf38e5Schristos 	void *handle = hook_install(TSDN_NULL, &hooks);
413*7bdf38e5Schristos 	expect_ptr_ne(handle, NULL, "Hook installation failed");
414*7bdf38e5Schristos 
415*7bdf38e5Schristos 	void *volatile ptr;
416*7bdf38e5Schristos 
417*7bdf38e5Schristos 	/* realloc(NULL, size) as malloc */
418*7bdf38e5Schristos 	reset();
419*7bdf38e5Schristos 	ptr = realloc(NULL, 1);
420*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
421*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
422*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type");
423*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
424*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
425*7bdf38e5Schristos 	    "Wrong raw result");
426*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument");
427*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
428*7bdf38e5Schristos 	free(ptr);
429*7bdf38e5Schristos 
430*7bdf38e5Schristos 	/* realloc(ptr, 0) as free */
431*7bdf38e5Schristos 	if (opt_zero_realloc_action == zero_realloc_action_free) {
432*7bdf38e5Schristos 		ptr = malloc(1);
433*7bdf38e5Schristos 		reset();
434*7bdf38e5Schristos 		realloc(ptr, 0);
435*7bdf38e5Schristos 		expect_d_eq(call_count, 1, "Hook not called");
436*7bdf38e5Schristos 		expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
437*7bdf38e5Schristos 		expect_d_eq(arg_type, (int)hook_dalloc_realloc,
438*7bdf38e5Schristos 		    "Wrong hook type");
439*7bdf38e5Schristos 		expect_ptr_eq(ptr, arg_address,
440*7bdf38e5Schristos 		    "Wrong pointer freed");
441*7bdf38e5Schristos 		expect_u64_eq((uintptr_t)ptr, arg_args_raw[0],
442*7bdf38e5Schristos 		    "Wrong raw arg");
443*7bdf38e5Schristos 		expect_u64_eq((uintptr_t)0, arg_args_raw[1],
444*7bdf38e5Schristos 		    "Wrong raw arg");
445*7bdf38e5Schristos 	}
446*7bdf38e5Schristos 
447*7bdf38e5Schristos 	/* realloc(NULL, 0) as malloc(0) */
448*7bdf38e5Schristos 	reset();
449*7bdf38e5Schristos 	ptr = realloc(NULL, 0);
450*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
451*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
452*7bdf38e5Schristos 	expect_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type");
453*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_result, "Wrong result");
454*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
455*7bdf38e5Schristos 	    "Wrong raw result");
456*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument");
457*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)0, arg_args_raw[1], "Wrong argument");
458*7bdf38e5Schristos 	free(ptr);
459*7bdf38e5Schristos 
460*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle);
461*7bdf38e5Schristos }
462*7bdf38e5Schristos TEST_END
463*7bdf38e5Schristos 
464*7bdf38e5Schristos static void
465*7bdf38e5Schristos do_realloc_test(void *(*ralloc)(void *, size_t, int), int flags,
466*7bdf38e5Schristos     int expand_type, int dalloc_type) {
467*7bdf38e5Schristos 	hooks_t hooks = {&test_alloc_hook, &test_dalloc_hook,
468*7bdf38e5Schristos 		&test_expand_hook, (void *)123};
469*7bdf38e5Schristos 	void *handle = hook_install(TSDN_NULL, &hooks);
470*7bdf38e5Schristos 	expect_ptr_ne(handle, NULL, "Hook installation failed");
471*7bdf38e5Schristos 
472*7bdf38e5Schristos 	void *volatile ptr;
473*7bdf38e5Schristos 	void *volatile ptr2;
474*7bdf38e5Schristos 
475*7bdf38e5Schristos 	/* Realloc in-place, small. */
476*7bdf38e5Schristos 	ptr = malloc(129);
477*7bdf38e5Schristos 	reset();
478*7bdf38e5Schristos 	ptr2 = ralloc(ptr, 130, flags);
479*7bdf38e5Schristos 	expect_ptr_eq(ptr, ptr2, "Small realloc moved");
480*7bdf38e5Schristos 
481*7bdf38e5Schristos 	expect_d_eq(call_count, 1, "Hook not called");
482*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
483*7bdf38e5Schristos 	expect_d_eq(arg_type, expand_type, "Wrong hook type");
484*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_address, "Wrong address");
485*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
486*7bdf38e5Schristos 	    "Wrong raw result");
487*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
488*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)130, arg_args_raw[1], "Wrong argument");
489*7bdf38e5Schristos 	free(ptr);
490*7bdf38e5Schristos 
491*7bdf38e5Schristos 	/*
492*7bdf38e5Schristos 	 * Realloc in-place, large.  Since we can't guarantee the large case
493*7bdf38e5Schristos 	 * across all platforms, we stay resilient to moving results.
494*7bdf38e5Schristos 	 */
495*7bdf38e5Schristos 	ptr = malloc(2 * 1024 * 1024);
496*7bdf38e5Schristos 	free(ptr);
497*7bdf38e5Schristos 	ptr2 = malloc(1 * 1024 * 1024);
498*7bdf38e5Schristos 	reset();
499*7bdf38e5Schristos 	ptr = ralloc(ptr2, 2 * 1024 * 1024, flags);
500*7bdf38e5Schristos 	/* ptr is the new address, ptr2 is the old address. */
501*7bdf38e5Schristos 	if (ptr == ptr2) {
502*7bdf38e5Schristos 		expect_d_eq(call_count, 1, "Hook not called");
503*7bdf38e5Schristos 		expect_d_eq(arg_type, expand_type, "Wrong hook type");
504*7bdf38e5Schristos 	} else {
505*7bdf38e5Schristos 		expect_d_eq(call_count, 2, "Wrong hooks called");
506*7bdf38e5Schristos 		expect_ptr_eq(ptr, arg_result, "Wrong address");
507*7bdf38e5Schristos 		expect_d_eq(arg_type, dalloc_type, "Wrong hook type");
508*7bdf38e5Schristos 	}
509*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
510*7bdf38e5Schristos 	expect_ptr_eq(ptr2, arg_address, "Wrong address");
511*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
512*7bdf38e5Schristos 	    "Wrong raw result");
513*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr2, arg_args_raw[0], "Wrong argument");
514*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
515*7bdf38e5Schristos 	    "Wrong argument");
516*7bdf38e5Schristos 	free(ptr);
517*7bdf38e5Schristos 
518*7bdf38e5Schristos 	/* Realloc with move, small. */
519*7bdf38e5Schristos 	ptr = malloc(8);
520*7bdf38e5Schristos 	reset();
521*7bdf38e5Schristos 	ptr2 = ralloc(ptr, 128, flags);
522*7bdf38e5Schristos 	expect_ptr_ne(ptr, ptr2, "Small realloc didn't move");
523*7bdf38e5Schristos 
524*7bdf38e5Schristos 	expect_d_eq(call_count, 2, "Hook not called");
525*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
526*7bdf38e5Schristos 	expect_d_eq(arg_type, dalloc_type, "Wrong hook type");
527*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_address, "Wrong address");
528*7bdf38e5Schristos 	expect_ptr_eq(ptr2, arg_result, "Wrong address");
529*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
530*7bdf38e5Schristos 	    "Wrong raw result");
531*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
532*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)128, arg_args_raw[1], "Wrong argument");
533*7bdf38e5Schristos 	free(ptr2);
534*7bdf38e5Schristos 
535*7bdf38e5Schristos 	/* Realloc with move, large. */
536*7bdf38e5Schristos 	ptr = malloc(1);
537*7bdf38e5Schristos 	reset();
538*7bdf38e5Schristos 	ptr2 = ralloc(ptr, 2 * 1024 * 1024, flags);
539*7bdf38e5Schristos 	expect_ptr_ne(ptr, ptr2, "Large realloc didn't move");
540*7bdf38e5Schristos 
541*7bdf38e5Schristos 	expect_d_eq(call_count, 2, "Hook not called");
542*7bdf38e5Schristos 	expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
543*7bdf38e5Schristos 	expect_d_eq(arg_type, dalloc_type, "Wrong hook type");
544*7bdf38e5Schristos 	expect_ptr_eq(ptr, arg_address, "Wrong address");
545*7bdf38e5Schristos 	expect_ptr_eq(ptr2, arg_result, "Wrong address");
546*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
547*7bdf38e5Schristos 	    "Wrong raw result");
548*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
549*7bdf38e5Schristos 	expect_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
550*7bdf38e5Schristos 	    "Wrong argument");
551*7bdf38e5Schristos 	free(ptr2);
552*7bdf38e5Schristos 
553*7bdf38e5Schristos 	hook_remove(TSDN_NULL, handle);
554*7bdf38e5Schristos }
555*7bdf38e5Schristos 
556*7bdf38e5Schristos static void *
557*7bdf38e5Schristos realloc_wrapper(void *ptr, size_t size, UNUSED int flags) {
558*7bdf38e5Schristos 	return realloc(ptr, size);
559*7bdf38e5Schristos }
560*7bdf38e5Schristos 
561*7bdf38e5Schristos TEST_BEGIN(test_hooks_realloc) {
562*7bdf38e5Schristos 	do_realloc_test(&realloc_wrapper, 0, hook_expand_realloc,
563*7bdf38e5Schristos 	    hook_dalloc_realloc);
564*7bdf38e5Schristos }
565*7bdf38e5Schristos TEST_END
566*7bdf38e5Schristos 
567*7bdf38e5Schristos TEST_BEGIN(test_hooks_rallocx) {
568*7bdf38e5Schristos 	do_realloc_test(&rallocx, MALLOCX_TCACHE_NONE, hook_expand_rallocx,
569*7bdf38e5Schristos 	    hook_dalloc_rallocx);
570*7bdf38e5Schristos }
571*7bdf38e5Schristos TEST_END
572*7bdf38e5Schristos 
573*7bdf38e5Schristos int
574*7bdf38e5Schristos main(void) {
575*7bdf38e5Schristos 	/* We assert on call counts. */
576*7bdf38e5Schristos 	return test_no_reentrancy(
577*7bdf38e5Schristos 	    test_hooks_basic,
578*7bdf38e5Schristos 	    test_hooks_null,
579*7bdf38e5Schristos 	    test_hooks_remove,
580*7bdf38e5Schristos 	    test_hooks_alloc_simple,
581*7bdf38e5Schristos 	    test_hooks_dalloc_simple,
582*7bdf38e5Schristos 	    test_hooks_expand_simple,
583*7bdf38e5Schristos 	    test_hooks_realloc_as_malloc_or_free,
584*7bdf38e5Schristos 	    test_hooks_realloc,
585*7bdf38e5Schristos 	    test_hooks_rallocx);
586*7bdf38e5Schristos }
587