Separate tests for extent utilization API

As title.
This commit is contained in:
Yinan Zhang 2019-04-09 11:01:26 -07:00 committed by Qi Wang
parent d3d7a8ef09
commit 7ee3897740
4 changed files with 194 additions and 197 deletions

View File

@ -178,6 +178,7 @@ TESTS_UNIT := \
$(srcroot)test/unit/div.c \ $(srcroot)test/unit/div.c \
$(srcroot)test/unit/emitter.c \ $(srcroot)test/unit/emitter.c \
$(srcroot)test/unit/extent_quantize.c \ $(srcroot)test/unit/extent_quantize.c \
$(srcroot)test/unit/extent_util.c \
$(srcroot)test/unit/fork.c \ $(srcroot)test/unit/fork.c \
$(srcroot)test/unit/hash.c \ $(srcroot)test/unit/hash.c \
$(srcroot)test/unit/hook.c \ $(srcroot)test/unit/hook.c \

View File

@ -3131,7 +3131,7 @@ label_return:
* #define BIN_NREGS_READ(out) COUNTS(out)[4] * #define BIN_NREGS_READ(out) COUNTS(out)[4]
* *
* and then write e.g. NFREE_READ(oldp) to fetch the output. See the unit test * and then write e.g. NFREE_READ(oldp) to fetch the output. See the unit test
* test_utilization_query in test/unit/mallctl.c for an example. * test_query in test/unit/extent_util.c for an example.
* *
* For a typical defragmentation workflow making use of this API for * For a typical defragmentation workflow making use of this API for
* understanding the fragmentation level, please refer to the comment for * understanding the fragmentation level, please refer to the comment for
@ -3223,7 +3223,7 @@ label_return:
* #define SIZE_READ(out, i) out[(i) * 3 + 2] * #define SIZE_READ(out, i) out[(i) * 3 + 2]
* *
* and then write e.g. NFREE_READ(oldp, i) to fetch the output. See the unit * and then write e.g. NFREE_READ(oldp, i) to fetch the output. See the unit
* test test_utilization_batch in test/unit/mallctl.c for a concrete example. * test test_batch in test/unit/extent_util.c for a concrete example.
* *
* A typical workflow would be composed of the following steps: * A typical workflow would be composed of the following steps:
* *

190
test/unit/extent_util.c Normal file
View File

@ -0,0 +1,190 @@
#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_QUERY_EINVAL(a, b, c, d, why_inval) \
TEST_UTIL_EINVAL("query", a, b, c, d, why_inval)
#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"); \
assert_zu_eq(out_sz, out_sz_ref, "incorrect output size"); \
assert_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")
TEST_BEGIN(test_query) {
void *p = mallocx(1, 0);
void **in = &p;
size_t in_sz = sizeof(const void *);
size_t out_sz = sizeof(void *) + sizeof(size_t) * 5;
void *out = mallocx(out_sz, 0);
void *out_ref = mallocx(out_sz, 0);
size_t out_sz_ref = out_sz;
assert_ptr_not_null(p, "test pointer allocation failed");
assert_ptr_not_null(out, "test output allocation failed");
assert_ptr_not_null(out_ref, "test reference output allocation failed");
#define SLABCUR_READ(out) (*(void **)out)
#define COUNTS(out) ((size_t *)((void **)out + 1))
#define NFREE_READ(out) COUNTS(out)[0]
#define NREGS_READ(out) COUNTS(out)[1]
#define SIZE_READ(out) COUNTS(out)[2]
#define BIN_NFREE_READ(out) COUNTS(out)[3]
#define BIN_NREGS_READ(out) COUNTS(out)[4]
SLABCUR_READ(out) = NULL;
NFREE_READ(out) = NREGS_READ(out) = SIZE_READ(out) = -1;
BIN_NFREE_READ(out) = BIN_NREGS_READ(out) = -1;
memcpy(out_ref, out, out_sz);
/* Test invalid argument(s) errors */
TEST_UTIL_QUERY_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL");
TEST_UTIL_QUERY_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL");
TEST_UTIL_QUERY_EINVAL(out, &out_sz, NULL, in_sz, "newp is NULL");
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, 0, "newlen is zero");
in_sz -= 1;
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz, "invalid newlen");
in_sz += 1;
out_sz_ref = out_sz -= 2 * sizeof(size_t);
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz, "invalid *oldlenp");
out_sz_ref = out_sz += 2 * sizeof(size_t);
/* Examine output for valid call */
TEST_UTIL_VALID("query");
assert_zu_le(NFREE_READ(out), NREGS_READ(out),
"Extent free count exceeded region count");
assert_zu_le(NREGS_READ(out), SIZE_READ(out),
"Extent region count exceeded size");
assert_zu_ne(NREGS_READ(out), 0,
"Extent region count must be positive");
assert_zu_ne(SIZE_READ(out), 0, "Extent size must be positive");
if (config_stats) {
assert_zu_le(BIN_NFREE_READ(out), BIN_NREGS_READ(out),
"Bin free count exceeded region count");
assert_zu_ne(BIN_NREGS_READ(out), 0,
"Bin region count must be positive");
assert_zu_le(NFREE_READ(out), BIN_NFREE_READ(out),
"Extent free count exceeded bin free count");
assert_zu_le(NREGS_READ(out), BIN_NREGS_READ(out),
"Extent region count exceeded bin region count");
assert_zu_eq(BIN_NREGS_READ(out) % NREGS_READ(out), 0,
"Bin region count isn't a multiple of extent region count");
assert_zu_le(NREGS_READ(out) - NFREE_READ(out),
BIN_NREGS_READ(out) - BIN_NFREE_READ(out),
"Extent utilized count exceeded bin utilized count");
} else {
assert_zu_eq(BIN_NFREE_READ(out), 0,
"Bin free count should be zero when stats are disabled");
assert_zu_eq(BIN_NREGS_READ(out), 0,
"Bin region count should be zero when stats are disabled");
}
assert_ptr_not_null(SLABCUR_READ(out), "Current slab is null");
assert_true(NFREE_READ(out) == 0 || SLABCUR_READ(out) <= p,
"Allocation should follow first fit principle");
#undef BIN_NREGS_READ
#undef BIN_NFREE_READ
#undef SIZE_READ
#undef NREGS_READ
#undef NFREE_READ
#undef COUNTS
#undef SLABCUR_READ
free(out_ref);
free(out);
free(p);
}
TEST_END
TEST_BEGIN(test_batch) {
void *p = mallocx(1, 0);
void *q = mallocx(1, 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;
assert_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0),
"Extent free count exceeded region count");
assert_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0),
"Extent region count exceeded size");
assert_zu_ne(NREGS_READ(out, 0), 0,
"Extent region count must be positive");
assert_zu_ne(SIZE_READ(out, 0), 0, "Extent size must be positive");
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");
assert_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1),
"Extent free count exceeded region count");
assert_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1),
"Extent region count should be same for same region size");
assert_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) {
return test(test_query, test_batch);
}

View File

@ -853,198 +853,6 @@ TEST_BEGIN(test_hooks_exhaustion) {
} }
TEST_END TEST_END
#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_VALID(node) do { \
assert_d_eq(mallctl("experimental.utilization." node, \
out, &out_sz, in, in_sz), 0, \
"Should return 0 on correct arguments"); \
assert_zu_eq(out_sz, out_sz_ref, "incorrect output size"); \
assert_d_ne(memcmp(out, out_ref, out_sz_ref), 0, \
"Output content should be changed"); \
} while (0)
TEST_BEGIN(test_utilization_query) {
void *p = mallocx(1, 0);
void **in = &p;
size_t in_sz = sizeof(const void *);
size_t out_sz = sizeof(void *) + sizeof(size_t) * 5;
void *out = mallocx(out_sz, 0);
void *out_ref = mallocx(out_sz, 0);
size_t out_sz_ref = out_sz;
assert_ptr_not_null(p, "test pointer allocation failed");
assert_ptr_not_null(out, "test output allocation failed");
assert_ptr_not_null(out_ref, "test reference output allocation failed");
#define SLABCUR_READ(out) (*(void **)out)
#define COUNTS(out) ((size_t *)((void **)out + 1))
#define NFREE_READ(out) COUNTS(out)[0]
#define NREGS_READ(out) COUNTS(out)[1]
#define SIZE_READ(out) COUNTS(out)[2]
#define BIN_NFREE_READ(out) COUNTS(out)[3]
#define BIN_NREGS_READ(out) COUNTS(out)[4]
SLABCUR_READ(out) = NULL;
NFREE_READ(out) = NREGS_READ(out) = SIZE_READ(out) = -1;
BIN_NFREE_READ(out) = BIN_NREGS_READ(out) = -1;
memcpy(out_ref, out, out_sz);
/* Test invalid argument(s) errors */
#define TEST_UTIL_QUERY_EINVAL(a, b, c, d, why_inval) \
TEST_UTIL_EINVAL("query", a, b, c, d, why_inval)
TEST_UTIL_QUERY_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL");
TEST_UTIL_QUERY_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL");
TEST_UTIL_QUERY_EINVAL(out, &out_sz, NULL, in_sz, "newp is NULL");
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, 0, "newlen is zero");
in_sz -= 1;
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz, "invalid newlen");
in_sz += 1;
out_sz_ref = out_sz -= 2 * sizeof(size_t);
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz, "invalid *oldlenp");
out_sz_ref = out_sz += 2 * sizeof(size_t);
#undef TEST_UTIL_QUERY_EINVAL
/* Examine output for valid call */
TEST_UTIL_VALID("query");
assert_zu_le(NFREE_READ(out), NREGS_READ(out),
"Extent free count exceeded region count");
assert_zu_le(NREGS_READ(out), SIZE_READ(out),
"Extent region count exceeded size");
assert_zu_ne(NREGS_READ(out), 0,
"Extent region count must be positive");
assert_zu_ne(SIZE_READ(out), 0, "Extent size must be positive");
if (config_stats) {
assert_zu_le(BIN_NFREE_READ(out), BIN_NREGS_READ(out),
"Bin free count exceeded region count");
assert_zu_ne(BIN_NREGS_READ(out), 0,
"Bin region count must be positive");
assert_zu_le(NFREE_READ(out), BIN_NFREE_READ(out),
"Extent free count exceeded bin free count");
assert_zu_le(NREGS_READ(out), BIN_NREGS_READ(out),
"Extent region count exceeded bin region count");
assert_zu_eq(BIN_NREGS_READ(out) % NREGS_READ(out), 0,
"Bin region count isn't a multiple of extent region count");
assert_zu_le(NREGS_READ(out) - NFREE_READ(out),
BIN_NREGS_READ(out) - BIN_NFREE_READ(out),
"Extent utilized count exceeded bin utilized count");
} else {
assert_zu_eq(BIN_NFREE_READ(out), 0,
"Bin free count should be zero when stats are disabled");
assert_zu_eq(BIN_NREGS_READ(out), 0,
"Bin region count should be zero when stats are disabled");
}
assert_ptr_not_null(SLABCUR_READ(out), "Current slab is null");
assert_true(NFREE_READ(out) == 0 || SLABCUR_READ(out) <= p,
"Allocation should follow first fit principle");
#undef BIN_NREGS_READ
#undef BIN_NFREE_READ
#undef SIZE_READ
#undef NREGS_READ
#undef NFREE_READ
#undef COUNTS
#undef SLABCUR_READ
free(out_ref);
free(out);
free(p);
}
TEST_END
TEST_BEGIN(test_utilization_batch_query) {
void *p = mallocx(1, 0);
void *q = mallocx(1, 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 */
#define TEST_UTIL_BATCH_EINVAL(a, b, c, d, why_inval) \
TEST_UTIL_EINVAL("batch_query", a, b, c, d, why_inval)
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 *);
#undef TEST_UTIL_BATCH_EINVAL
/* Examine output for valid calls */
#define TEST_UTIL_BATCH_VALID TEST_UTIL_VALID("batch_query")
#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;
assert_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0),
"Extent free count exceeded region count");
assert_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0),
"Extent region count exceeded size");
assert_zu_ne(NREGS_READ(out, 0), 0,
"Extent region count must be positive");
assert_zu_ne(SIZE_READ(out, 0), 0, "Extent size must be positive");
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");
assert_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1),
"Extent free count exceeded region count");
assert_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1),
"Extent region count should be same for same region size");
assert_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
#undef TEST_UTIL_BATCH_VALID
free(q);
free(p);
}
TEST_END
#undef TEST_UTIL_VALID
#undef TEST_UTIL_EINVAL
int int
main(void) { main(void) {
return test( return test(
@ -1075,7 +883,5 @@ main(void) {
test_arenas_lookup, test_arenas_lookup,
test_stats_arenas, test_stats_arenas,
test_hooks, test_hooks,
test_hooks_exhaustion, test_hooks_exhaustion);
test_utilization_query,
test_utilization_batch_query);
} }