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