#include "test/jemalloc_test.h" #define TEST_UTIL_EINVAL(node, a, b, c, d, why_inval) \ do { \ assert_d_eq( \ mallctl("experimental.utilization." node, a, b, c, d), \ EINVAL, "Should fail when " why_inval); \ assert_zu_eq(out_sz, out_sz_ref, \ "Output size touched when given invalid arguments"); \ assert_d_eq(memcmp(out, out_ref, out_sz_ref), 0, \ "Output content touched when given invalid arguments"); \ } while (0) #define TEST_UTIL_BATCH_EINVAL(a, b, c, d, why_inval) \ TEST_UTIL_EINVAL("batch_query", a, b, c, d, why_inval) #define TEST_UTIL_VALID(node) \ do { \ assert_d_eq(mallctl("experimental.utilization." node, out, \ &out_sz, in, in_sz), \ 0, "Should return 0 on correct arguments"); \ expect_zu_eq(out_sz, out_sz_ref, "incorrect output size"); \ expect_d_ne(memcmp(out, out_ref, out_sz_ref), 0, \ "Output content should be changed"); \ } while (0) #define TEST_UTIL_BATCH_VALID TEST_UTIL_VALID("batch_query") #define TEST_MAX_SIZE (1 << 20) TEST_BEGIN(test_batch) { size_t sz; /* * Select some sizes that can span both small and large sizes, and are * numerically unrelated to any size boundaries. */ for (sz = 17; sz <= TEST_MAX_SIZE && sz <= SC_LARGE_MAXCLASS; sz += (sz <= SC_SMALL_MAXCLASS ? 1019 : 99991)) { void *p = mallocx(sz, 0); void *q = mallocx(sz, 0); void *in[] = {p, q}; size_t in_sz = sizeof(const void *) * 2; size_t out[] = {-1, -1, -1, -1, -1, -1}; size_t out_sz = sizeof(size_t) * 6; size_t out_ref[] = {-1, -1, -1, -1, -1, -1}; size_t out_sz_ref = out_sz; assert_ptr_not_null(p, "test pointer allocation failed"); assert_ptr_not_null(q, "test pointer allocation failed"); /* Test invalid argument(s) errors */ TEST_UTIL_BATCH_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL"); TEST_UTIL_BATCH_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL"); TEST_UTIL_BATCH_EINVAL( out, &out_sz, NULL, in_sz, "newp is NULL"); TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, 0, "newlen is zero"); in_sz -= 1; TEST_UTIL_BATCH_EINVAL( out, &out_sz, in, in_sz, "newlen is not an exact multiple"); in_sz += 1; out_sz_ref = out_sz -= 2 * sizeof(size_t); TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, in_sz, "*oldlenp is not an exact multiple"); out_sz_ref = out_sz += 2 * sizeof(size_t); in_sz -= sizeof(const void *); TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, in_sz, "*oldlenp and newlen do not match"); in_sz += sizeof(const void *); /* Examine output for valid calls */ #define TEST_EQUAL_REF(i, message) \ assert_d_eq(memcmp(out + (i) * 3, out_ref + (i) * 3, 3), 0, message) #define NFREE_READ(out, i) out[(i) * 3] #define NREGS_READ(out, i) out[(i) * 3 + 1] #define SIZE_READ(out, i) out[(i) * 3 + 2] out_sz_ref = out_sz /= 2; in_sz /= 2; TEST_UTIL_BATCH_VALID; expect_zu_le(sz, SIZE_READ(out, 0), "Extent size should be at least allocation size"); expect_zu_eq(SIZE_READ(out, 0) & (PAGE - 1), 0, "Extent size should be a multiple of page size"); /* Profiling breaks our slab count expectations. */ if (sz <= SC_SMALL_MAXCLASS && !opt_prof) { expect_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0), "Extent free count exceeded region count"); expect_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0), "Extent region count exceeded size"); expect_zu_ne(NREGS_READ(out, 0), 0, "Extent region count must be positive"); } else if (sz > SC_SMALL_MAXCLASS) { expect_zu_eq(NFREE_READ(out, 0), 0, "Extent free count should be zero"); expect_zu_eq(NREGS_READ(out, 0), 1, "Extent region count should be one"); } TEST_EQUAL_REF( 1, "Should not overwrite content beyond what's needed"); in_sz *= 2; out_sz_ref = out_sz *= 2; memcpy(out_ref, out, 3 * sizeof(size_t)); TEST_UTIL_BATCH_VALID; TEST_EQUAL_REF(0, "Statistics should be stable across calls"); if (sz <= SC_SMALL_MAXCLASS) { expect_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1), "Extent free count exceeded region count"); } else { expect_zu_eq(NFREE_READ(out, 0), 0, "Extent free count should be zero"); } expect_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1), "Extent region count should be same for same region size"); expect_zu_eq(SIZE_READ(out, 0), SIZE_READ(out, 1), "Extent size should be same for same region size"); #undef SIZE_READ #undef NREGS_READ #undef NFREE_READ #undef TEST_EQUAL_REF free(q); free(p); } } TEST_END int main(void) { assert_zu_lt(SC_SMALL_MAXCLASS + 100000, TEST_MAX_SIZE, "Test case cannot cover large classes"); return test(test_batch); }