Improve memory utilization tests

Added tests for large size classes and expanded the tests to
cover wider range of allocation sizes.
This commit is contained in:
Yinan Zhang 2019-05-13 14:59:33 -07:00 committed by Qi Wang
parent 2d6d099fed
commit 4c63b0e76a
2 changed files with 195 additions and 116 deletions

View File

@ -3143,15 +3143,15 @@ label_return:
* (f) total number of regions in the bin the extent belongs to. * (f) total number of regions in the bin the extent belongs to.
* *
* Note that "(e)" and "(f)" are only available when stats are enabled; * Note that "(e)" and "(f)" are only available when stats are enabled;
* otherwise both are set zero. * otherwise their values are undefined.
* *
* This API is mainly intended for small class allocations, where extents are * This API is mainly intended for small class allocations, where extents are
* used as slab. * used as slab.
* *
* In case of large class allocations, "(a)" will be NULL, and "(e)" and "(f)" * In case of large class allocations, "(a)" will be NULL, and "(e)" and "(f)"
* will be zero. The other three fields will be properly set though the values * will be zero (if stats are enabled; otherwise undefined). The other three
* are trivial: "(b)" will be 0, "(c)" will be 1, and "(d)" will be the usable * fields will be properly set though the values are trivial: "(b)" will be 0,
* size. * "(c)" will be 1, and "(d)" will be the usable size.
* *
* The input pointer and size are respectively passed in by newp and newlen, * The input pointer and size are respectively passed in by newp and newlen,
* and the output fields and size are respectively oldp and *oldlenp. * and the output fields and size are respectively oldp and *oldlenp.

View File

@ -25,8 +25,17 @@
#define TEST_UTIL_BATCH_VALID TEST_UTIL_VALID("batch_query") #define TEST_UTIL_BATCH_VALID TEST_UTIL_VALID("batch_query")
#define TEST_MAX_SIZE (1 << 20)
TEST_BEGIN(test_query) { TEST_BEGIN(test_query) {
void *p = mallocx(1, 0); size_t sz;
/*
* Select some sizes that can span both small and large sizes, and are
* numerically unrelated to any size boundaries.
*/
for (sz = 7; sz <= TEST_MAX_SIZE && sz <= SC_LARGE_MAXCLASS;
sz += (sz <= SC_SMALL_MAXCLASS ? 1009 : 99989)) {
void *p = mallocx(sz, 0);
void **in = &p; void **in = &p;
size_t in_sz = sizeof(const void *); size_t in_sz = sizeof(const void *);
size_t out_sz = sizeof(void *) + sizeof(size_t) * 5; size_t out_sz = sizeof(void *) + sizeof(size_t) * 5;
@ -34,9 +43,12 @@ TEST_BEGIN(test_query) {
void *out_ref = mallocx(out_sz, 0); void *out_ref = mallocx(out_sz, 0);
size_t out_sz_ref = out_sz; size_t out_sz_ref = out_sz;
assert_ptr_not_null(p, "test pointer allocation failed"); assert_ptr_not_null(p,
assert_ptr_not_null(out, "test output allocation failed"); "test pointer allocation failed");
assert_ptr_not_null(out_ref, "test reference output 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 SLABCUR_READ(out) (*(void **)out)
#define COUNTS(out) ((size_t *)((void **)out + 1)) #define COUNTS(out) ((size_t *)((void **)out + 1))
@ -52,49 +64,85 @@ TEST_BEGIN(test_query) {
memcpy(out_ref, out, out_sz); memcpy(out_ref, out, out_sz);
/* Test invalid argument(s) errors */ /* Test invalid argument(s) errors */
TEST_UTIL_QUERY_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL"); TEST_UTIL_QUERY_EINVAL(NULL, &out_sz, in, in_sz,
TEST_UTIL_QUERY_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL"); "old is NULL");
TEST_UTIL_QUERY_EINVAL(out, &out_sz, NULL, in_sz, "newp is NULL"); TEST_UTIL_QUERY_EINVAL(out, NULL, in, in_sz,
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, 0, "newlen is zero"); "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; in_sz -= 1;
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz, "invalid newlen"); TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz,
"invalid newlen");
in_sz += 1; in_sz += 1;
out_sz_ref = out_sz -= 2 * sizeof(size_t); out_sz_ref = out_sz -= 2 * sizeof(size_t);
TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz, "invalid *oldlenp"); TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, in_sz,
"invalid *oldlenp");
out_sz_ref = out_sz += 2 * sizeof(size_t); out_sz_ref = out_sz += 2 * sizeof(size_t);
/* Examine output for valid call */ /* Examine output for valid call */
TEST_UTIL_VALID("query"); TEST_UTIL_VALID("query");
assert_zu_le(sz, SIZE_READ(out),
"Extent size should be at least allocation size");
assert_zu_eq(SIZE_READ(out) & (PAGE - 1), 0,
"Extent size should be a multiple of page size");
if (sz <= SC_SMALL_MAXCLASS) {
assert_zu_le(NFREE_READ(out), NREGS_READ(out), assert_zu_le(NFREE_READ(out), NREGS_READ(out),
"Extent free count exceeded region count"); "Extent free count exceeded region count");
assert_zu_le(NREGS_READ(out), SIZE_READ(out), assert_zu_le(NREGS_READ(out), SIZE_READ(out),
"Extent region count exceeded size"); "Extent region count exceeded size");
assert_zu_ne(NREGS_READ(out), 0, assert_zu_ne(NREGS_READ(out), 0,
"Extent region count must be positive"); "Extent region count must be positive");
assert_zu_ne(SIZE_READ(out), 0, "Extent size must be positive"); 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");
if (config_stats) { if (config_stats) {
assert_zu_le(BIN_NFREE_READ(out), BIN_NREGS_READ(out), assert_zu_le(BIN_NFREE_READ(out),
BIN_NREGS_READ(out),
"Bin free count exceeded region count"); "Bin free count exceeded region count");
assert_zu_ne(BIN_NREGS_READ(out), 0, assert_zu_ne(BIN_NREGS_READ(out), 0,
"Bin region count must be positive"); "Bin region count must be positive");
assert_zu_le(NFREE_READ(out), BIN_NFREE_READ(out), assert_zu_le(NFREE_READ(out),
BIN_NFREE_READ(out),
"Extent free count exceeded bin free count"); "Extent free count exceeded bin free count");
assert_zu_le(NREGS_READ(out), BIN_NREGS_READ(out), assert_zu_le(NREGS_READ(out),
"Extent region count exceeded bin region count"); BIN_NREGS_READ(out),
assert_zu_eq(BIN_NREGS_READ(out) % NREGS_READ(out), 0, "Extent region count exceeded "
"Bin region count isn't a multiple of extent region count"); "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(
BIN_NFREE_READ(out) - NFREE_READ(out),
BIN_NREGS_READ(out) - NREGS_READ(out),
"Free count in other extents in the bin "
"exceeded region count in other extents "
"in the bin");
assert_zu_le(NREGS_READ(out) - NFREE_READ(out), assert_zu_le(NREGS_READ(out) - NFREE_READ(out),
BIN_NREGS_READ(out) - BIN_NFREE_READ(out), BIN_NREGS_READ(out) - BIN_NFREE_READ(out),
"Extent utilized count exceeded bin utilized count"); "Extent utilized count exceeded "
} else { "bin utilized count");
assert_zu_eq(BIN_NFREE_READ(out), 0, }
"Bin free count should be zero when stats are disabled"); } else {
assert_zu_eq(BIN_NREGS_READ(out), 0, assert_zu_eq(NFREE_READ(out), 0,
"Bin region count should be zero when stats are disabled"); "Extent free count should be zero");
assert_zu_eq(NREGS_READ(out), 1,
"Extent region count should be one");
assert_ptr_null(SLABCUR_READ(out),
"Current slab must be null for large size classes");
if (config_stats) {
assert_zu_eq(BIN_NFREE_READ(out), 0,
"Bin free count must be zero for "
"large sizes");
assert_zu_eq(BIN_NREGS_READ(out), 0,
"Bin region count must be zero for "
"large sizes");
}
} }
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_NREGS_READ
#undef BIN_NFREE_READ #undef BIN_NFREE_READ
@ -107,12 +155,20 @@ TEST_BEGIN(test_query) {
free(out_ref); free(out_ref);
free(out); free(out);
free(p); free(p);
}
} }
TEST_END TEST_END
TEST_BEGIN(test_batch) { TEST_BEGIN(test_batch) {
void *p = mallocx(1, 0); size_t sz;
void *q = mallocx(1, 0); /*
* 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}; void *in[] = {p, q};
size_t in_sz = sizeof(const void *) * 2; size_t in_sz = sizeof(const void *) * 2;
size_t out[] = {-1, -1, -1, -1, -1, -1}; size_t out[] = {-1, -1, -1, -1, -1, -1};
@ -124,10 +180,14 @@ TEST_BEGIN(test_batch) {
assert_ptr_not_null(q, "test pointer allocation failed"); assert_ptr_not_null(q, "test pointer allocation failed");
/* Test invalid argument(s) errors */ /* Test invalid argument(s) errors */
TEST_UTIL_BATCH_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL"); TEST_UTIL_BATCH_EINVAL(NULL, &out_sz, in, in_sz,
TEST_UTIL_BATCH_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL"); "old is NULL");
TEST_UTIL_BATCH_EINVAL(out, &out_sz, NULL, in_sz, "newp is NULL"); TEST_UTIL_BATCH_EINVAL(out, NULL, in, in_sz,
TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, 0, "newlen is zero"); "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; in_sz -= 1;
TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, in_sz, TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, in_sz,
"newlen is not an exact multiple"); "newlen is not an exact multiple");
@ -152,22 +212,38 @@ TEST_BEGIN(test_batch) {
out_sz_ref = out_sz /= 2; out_sz_ref = out_sz /= 2;
in_sz /= 2; in_sz /= 2;
TEST_UTIL_BATCH_VALID; TEST_UTIL_BATCH_VALID;
assert_zu_le(sz, SIZE_READ(out, 0),
"Extent size should be at least allocation size");
assert_zu_eq(SIZE_READ(out, 0) & (PAGE - 1), 0,
"Extent size should be a multiple of page size");
if (sz <= SC_SMALL_MAXCLASS) {
assert_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0), assert_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0),
"Extent free count exceeded region count"); "Extent free count exceeded region count");
assert_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0), assert_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0),
"Extent region count exceeded size"); "Extent region count exceeded size");
assert_zu_ne(NREGS_READ(out, 0), 0, assert_zu_ne(NREGS_READ(out, 0), 0,
"Extent region count must be positive"); "Extent region count must be positive");
assert_zu_ne(SIZE_READ(out, 0), 0, "Extent size must be positive"); } else {
TEST_EQUAL_REF(1, "Should not overwrite content beyond what's needed"); assert_zu_eq(NFREE_READ(out, 0), 0,
"Extent free count should be zero");
assert_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; in_sz *= 2;
out_sz_ref = out_sz *= 2; out_sz_ref = out_sz *= 2;
memcpy(out_ref, out, 3 * sizeof(size_t)); memcpy(out_ref, out, 3 * sizeof(size_t));
TEST_UTIL_BATCH_VALID; TEST_UTIL_BATCH_VALID;
TEST_EQUAL_REF(0, "Statistics should be stable across calls"); TEST_EQUAL_REF(0, "Statistics should be stable across calls");
if (sz <= SC_SMALL_MAXCLASS) {
assert_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1), assert_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1),
"Extent free count exceeded region count"); "Extent free count exceeded region count");
} else {
assert_zu_eq(NFREE_READ(out, 0), 0,
"Extent free count should be zero");
}
assert_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1), assert_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1),
"Extent region count should be same for same region size"); "Extent region count should be same for same region size");
assert_zu_eq(SIZE_READ(out, 0), SIZE_READ(out, 1), assert_zu_eq(SIZE_READ(out, 0), SIZE_READ(out, 1),
@ -181,10 +257,13 @@ TEST_BEGIN(test_batch) {
free(q); free(q);
free(p); free(p);
}
} }
TEST_END TEST_END
int int
main(void) { main(void) {
assert_zu_lt(SC_SMALL_MAXCLASS, TEST_MAX_SIZE,
"Test case cannot cover large classes");
return test(test_query, test_batch); return test(test_query, test_batch);
} }