TEST_END TEST_BEGIN(test_nstime_divide) { nstime_t nsta, nstb, nstc; nstime_init2(&nsta, 42, 43); nstime_copy(&nstb, &nsta); nstime_imultiply(&nsta, 10); assert_u64_eq(nstime_divide(&nsta, &nstb), 10, "Incorrect division result"); nstime_init2(&nsta, 42, 43); nstime_copy(&nstb, &nsta); nstime_imultiply(&nsta, 10); nstime_init(&nstc, 1); nstime_add(&nsta, &nstc); assert_u64_eq(nstime_divide(&nsta, &nstb), 10, "Incorrect division result"); nstime_init2(&nsta, 42, 43); nstime_copy(&nstb, &nsta); nstime_imultiply(&nsta, 10); nstime_init(&nstc, 1); nstime_subtract(&nsta, &nstc); assert_u64_eq(nstime_divide(&nsta, &nstb), 9, "Incorrect division result"); }
TEST_END TEST_BEGIN(test_nstime_init2) { nstime_t nst; nstime_init2(&nst, 42, 43); assert_u64_eq(nstime_sec(&nst), 42, "sec incorrectly read"); assert_u64_eq(nstime_nsec(&nst), 43, "nsec incorrectly read"); }
TEST_END TEST_BEGIN(test_nstime_copy) { nstime_t nsta, nstb; nstime_init2(&nsta, 42, 43); nstime_init(&nstb, 0); nstime_copy(&nstb, &nsta); assert_u64_eq(nstime_sec(&nstb), 42, "sec incorrectly copied"); assert_u64_eq(nstime_nsec(&nstb), 43, "nsec incorrectly copied"); }
TEST_END TEST_BEGIN(test_hooks_expand_simple) { /* "Simple" in the sense that we're not in a realloc variant. */ hooks_t hooks = {NULL, NULL, &test_expand_hook, (void *)123}; void *handle = hook_install(TSDN_NULL, &hooks); assert_ptr_ne(handle, NULL, "Hook installation failed"); void *volatile ptr; /* xallocx() */ reset(); ptr = malloc(1); size_t new_usize = xallocx(ptr, 100, 200, MALLOCX_TCACHE_NONE); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_expand_xallocx, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong pointer expanded"); assert_u64_eq(arg_old_usize, nallocx(1, 0), "Wrong old usize"); assert_u64_eq(arg_new_usize, sallocx(ptr, 0), "Wrong new usize"); assert_u64_eq(new_usize, arg_result_raw, "Wrong result"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong arg"); assert_u64_eq(100, arg_args_raw[1], "Wrong arg"); assert_u64_eq(200, arg_args_raw[2], "Wrong arg"); assert_u64_eq(MALLOCX_TCACHE_NONE, arg_args_raw[3], "Wrong arg"); hook_remove(TSDN_NULL, handle); }
TEST_END TEST_BEGIN(test_hooks_realloc_as_malloc_or_free) { hooks_t hooks = {&test_alloc_hook, &test_dalloc_hook, &test_expand_hook, (void *)123}; void *handle = hook_install(TSDN_NULL, &hooks); assert_ptr_ne(handle, NULL, "Hook installation failed"); void *volatile ptr; /* realloc(NULL, size) as malloc */ reset(); ptr = realloc(NULL, 1); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument"); free(ptr); /* realloc(ptr, 0) as free */ ptr = malloc(1); reset(); realloc(ptr, 0); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_dalloc_realloc, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong pointer freed"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg"); assert_u64_eq((uintptr_t)0, arg_args_raw[1], "Wrong raw arg"); /* realloc(NULL, 0) as malloc(0) */ reset(); ptr = realloc(NULL, 0); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)0, arg_args_raw[1], "Wrong argument"); free(ptr); hook_remove(TSDN_NULL, handle); }
TEST_END TEST_BEGIN(test_hooks_dalloc_simple) { /* "Simple" in the sense that we're not in a realloc variant. */ hooks_t hooks = {NULL, &test_dalloc_hook, NULL, (void *)123}; void *handle = hook_install(TSDN_NULL, &hooks); assert_ptr_ne(handle, NULL, "Hook installation failed"); void *volatile ptr; /* free() */ reset(); ptr = malloc(1); free(ptr); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_dalloc_free, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong pointer freed"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg"); /* dallocx() */ reset(); ptr = malloc(1); dallocx(ptr, MALLOCX_TCACHE_NONE); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_dalloc_dallocx, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong pointer freed"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg"); assert_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[1], "Wrong raw arg"); /* sdallocx() */ reset(); ptr = malloc(1); sdallocx(ptr, 1, MALLOCX_TCACHE_NONE); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_dalloc_sdallocx, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong pointer freed"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg"); assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong raw arg"); assert_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[2], "Wrong raw arg"); hook_remove(TSDN_NULL, handle); }
TEST_END TEST_BEGIN(test_smoothstep_monotonic) { uint64_t prev_h; unsigned i; /* * The smoothstep function is monotonic in [0..1], i.e. its slope is * non-negative. In practice we want to parametrize table generation * such that piecewise slope is greater than zero, but do not require * that here. */ prev_h = 0; for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) { uint64_t h = smoothstep_tab[i]; assert_u64_ge(h, prev_h, "Piecewise non-monotonic, i=%u", i); prev_h = h; } assert_u64_eq(smoothstep_tab[SMOOTHSTEP_NSTEPS-1], (KQU(1) << SMOOTHSTEP_BFP), "Last step must equal 1"); }
TEST_END static void do_realloc_test(void *(*ralloc)(void *, size_t, int), int flags, int expand_type, int dalloc_type) { hooks_t hooks = {&test_alloc_hook, &test_dalloc_hook, &test_expand_hook, (void *)123}; void *handle = hook_install(TSDN_NULL, &hooks); assert_ptr_ne(handle, NULL, "Hook installation failed"); void *volatile ptr; void *volatile ptr2; /* Realloc in-place, small. */ ptr = malloc(129); reset(); ptr2 = ralloc(ptr, 130, flags); assert_ptr_eq(ptr, ptr2, "Small realloc moved"); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, expand_type, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong address"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)130, arg_args_raw[1], "Wrong argument"); free(ptr); /* * Realloc in-place, large. Since we can't guarantee the large case * across all platforms, we stay resilient to moving results. */ ptr = malloc(2 * 1024 * 1024); free(ptr); ptr2 = malloc(1 * 1024 * 1024); reset(); ptr = ralloc(ptr2, 2 * 1024 * 1024, flags); /* ptr is the new address, ptr2 is the old address. */ if (ptr == ptr2) { assert_d_eq(call_count, 1, "Hook not called"); assert_d_eq(arg_type, expand_type, "Wrong hook type"); } else { assert_d_eq(call_count, 2, "Wrong hooks called"); assert_ptr_eq(ptr, arg_result, "Wrong address"); assert_d_eq(arg_type, dalloc_type, "Wrong hook type"); } assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_ptr_eq(ptr2, arg_address, "Wrong address"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)ptr2, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1], "Wrong argument"); free(ptr); /* Realloc with move, small. */ ptr = malloc(8); reset(); ptr2 = ralloc(ptr, 128, flags); assert_ptr_ne(ptr, ptr2, "Small realloc didn't move"); assert_d_eq(call_count, 2, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, dalloc_type, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong address"); assert_ptr_eq(ptr2, arg_result, "Wrong address"); assert_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)128, arg_args_raw[1], "Wrong argument"); free(ptr2); /* Realloc with move, large. */ ptr = malloc(1); reset(); ptr2 = ralloc(ptr, 2 * 1024 * 1024, flags); assert_ptr_ne(ptr, ptr2, "Large realloc didn't move"); assert_d_eq(call_count, 2, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, dalloc_type, "Wrong hook type"); assert_ptr_eq(ptr, arg_address, "Wrong address"); assert_ptr_eq(ptr2, arg_result, "Wrong address"); assert_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1], "Wrong argument"); free(ptr2); hook_remove(TSDN_NULL, handle); }
TEST_END TEST_BEGIN(test_hooks_alloc_simple) { /* "Simple" in the sense that we're not in a realloc variant. */ hooks_t hooks = {&test_alloc_hook, NULL, NULL, (void *)123}; void *handle = hook_install(TSDN_NULL, &hooks); assert_ptr_ne(handle, NULL, "Hook installation failed"); /* Stop malloc from being optimized away. */ volatile int err; void *volatile ptr; /* malloc */ reset(); ptr = malloc(1); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_malloc, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument"); free(ptr); /* posix_memalign */ reset(); err = posix_memalign((void **)&ptr, 1024, 1); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_posix_memalign, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)err, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)&ptr, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)1024, arg_args_raw[1], "Wrong argument"); assert_u64_eq((uintptr_t)1, arg_args_raw[2], "Wrong argument"); free(ptr); /* aligned_alloc */ reset(); ptr = aligned_alloc(1024, 1); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_aligned_alloc, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument"); free(ptr); /* calloc */ reset(); ptr = calloc(11, 13); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_calloc, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)11, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)13, arg_args_raw[1], "Wrong argument"); free(ptr); /* memalign */ #ifdef JEMALLOC_OVERRIDE_MEMALIGN reset(); ptr = memalign(1024, 1); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_memalign, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument"); free(ptr); #endif /* JEMALLOC_OVERRIDE_MEMALIGN */ /* valloc */ #ifdef JEMALLOC_OVERRIDE_VALLOC reset(); ptr = valloc(1); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_valloc, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument"); free(ptr); #endif /* JEMALLOC_OVERRIDE_VALLOC */ /* mallocx */ reset(); ptr = mallocx(1, MALLOCX_LG_ALIGN(10)); assert_d_eq(call_count, 1, "Hook not called"); assert_ptr_eq(arg_extra, (void *)123, "Wrong extra"); assert_d_eq(arg_type, (int)hook_alloc_mallocx, "Wrong hook type"); assert_ptr_eq(ptr, arg_result, "Wrong result"); assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw, "Wrong raw result"); assert_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument"); assert_u64_eq((uintptr_t)MALLOCX_LG_ALIGN(10), arg_args_raw[1], "Wrong flags"); free(ptr); hook_remove(TSDN_NULL, handle); }
void * thd_start(void *arg) { int err; void *p; uint64_t a0, a1, d0, d1; uint64_t *ap0, *ap1, *dp0, *dp1; size_t sz, usize; sz = sizeof(a0); if ((err = mallctl("thread.allocated", &a0, &sz, NULL, 0))) { if (err == ENOENT) goto label_ENOENT; test_fail("%s(): Error in mallctl(): %s", __func__, strerror(err)); } sz = sizeof(ap0); if ((err = mallctl("thread.allocatedp", &ap0, &sz, NULL, 0))) { if (err == ENOENT) goto label_ENOENT; test_fail("%s(): Error in mallctl(): %s", __func__, strerror(err)); } assert_u64_eq(*ap0, a0, "\"thread.allocatedp\" should provide a pointer to internal " "storage"); sz = sizeof(d0); if ((err = mallctl("thread.deallocated", &d0, &sz, NULL, 0))) { if (err == ENOENT) goto label_ENOENT; test_fail("%s(): Error in mallctl(): %s", __func__, strerror(err)); } sz = sizeof(dp0); if ((err = mallctl("thread.deallocatedp", &dp0, &sz, NULL, 0))) { if (err == ENOENT) goto label_ENOENT; test_fail("%s(): Error in mallctl(): %s", __func__, strerror(err)); } assert_u64_eq(*dp0, d0, "\"thread.deallocatedp\" should provide a pointer to internal " "storage"); p = malloc(1); assert_ptr_not_null(p, "Unexpected malloc() error"); sz = sizeof(a1); mallctl("thread.allocated", &a1, &sz, NULL, 0); sz = sizeof(ap1); mallctl("thread.allocatedp", &ap1, &sz, NULL, 0); assert_u64_eq(*ap1, a1, "Dereferenced \"thread.allocatedp\" value should equal " "\"thread.allocated\" value"); assert_ptr_eq(ap0, ap1, "Pointer returned by \"thread.allocatedp\" should not change"); usize = malloc_usable_size(p); assert_u64_le(a0 + usize, a1, "Allocated memory counter should increase by at least the amount " "explicitly allocated"); free(p); sz = sizeof(d1); mallctl("thread.deallocated", &d1, &sz, NULL, 0); sz = sizeof(dp1); mallctl("thread.deallocatedp", &dp1, &sz, NULL, 0); assert_u64_eq(*dp1, d1, "Dereferenced \"thread.deallocatedp\" value should equal " "\"thread.deallocated\" value"); assert_ptr_eq(dp0, dp1, "Pointer returned by \"thread.deallocatedp\" should not change"); assert_u64_le(d0 + usize, d1, "Deallocated memory counter should increase by at least the amount " "explicitly deallocated"); return (NULL); label_ENOENT: assert_false(config_stats, "ENOENT should only be returned if stats are disabled"); test_skip("\"thread.allocated\" mallctl not available"); return (NULL); }