Home | History | Annotate | Line # | Download | only in unit
      1 #include "test/jemalloc_test.h"
      2 
      3 #define TEST_UTIL_EINVAL(node, a, b, c, d, why_inval)                          \
      4 	do {                                                                   \
      5 		assert_d_eq(                                                   \
      6 		    mallctl("experimental.utilization." node, a, b, c, d),     \
      7 		    EINVAL, "Should fail when " why_inval);                    \
      8 		assert_zu_eq(out_sz, out_sz_ref,                               \
      9 		    "Output size touched when given invalid arguments");       \
     10 		assert_d_eq(memcmp(out, out_ref, out_sz_ref), 0,               \
     11 		    "Output content touched when given invalid arguments");    \
     12 	} while (0)
     13 
     14 #define TEST_UTIL_QUERY_EINVAL(a, b, c, d, why_inval)                          \
     15 	TEST_UTIL_EINVAL("query", a, b, c, d, why_inval)
     16 #define TEST_UTIL_BATCH_EINVAL(a, b, c, d, why_inval)                          \
     17 	TEST_UTIL_EINVAL("batch_query", a, b, c, d, why_inval)
     18 
     19 #define TEST_UTIL_VALID(node)                                                  \
     20 	do {                                                                   \
     21 		assert_d_eq(mallctl("experimental.utilization." node, out,     \
     22 		                &out_sz, in, in_sz),                           \
     23 		    0, "Should return 0 on correct arguments");                \
     24 		expect_zu_eq(out_sz, out_sz_ref, "incorrect output size");     \
     25 		expect_d_ne(memcmp(out, out_ref, out_sz_ref), 0,               \
     26 		    "Output content should be changed");                       \
     27 	} while (0)
     28 
     29 #define TEST_UTIL_BATCH_VALID TEST_UTIL_VALID("batch_query")
     30 
     31 #define TEST_MAX_SIZE (1 << 20)
     32 
     33 TEST_BEGIN(test_query) {
     34 	size_t sz;
     35 	/*
     36 	 * Select some sizes that can span both small and large sizes, and are
     37 	 * numerically unrelated to any size boundaries.
     38 	 */
     39 	for (sz = 7; sz <= TEST_MAX_SIZE && sz <= SC_LARGE_MAXCLASS;
     40 	     sz += (sz <= SC_SMALL_MAXCLASS ? 1009 : 99989)) {
     41 		void  *p = mallocx(sz, 0);
     42 		void **in = &p;
     43 		size_t in_sz = sizeof(const void *);
     44 		size_t out_sz = sizeof(void *) + sizeof(size_t) * 5;
     45 		void  *out = mallocx(out_sz, 0);
     46 		void  *out_ref = mallocx(out_sz, 0);
     47 		size_t out_sz_ref = out_sz;
     48 
     49 		assert_ptr_not_null(p, "test pointer allocation failed");
     50 		assert_ptr_not_null(out, "test output allocation failed");
     51 		assert_ptr_not_null(
     52 		    out_ref, "test reference output allocation failed");
     53 
     54 #define SLABCUR_READ(out) (*(void **)out)
     55 #define COUNTS(out) ((size_t *)((void **)out + 1))
     56 #define NFREE_READ(out) COUNTS(out)[0]
     57 #define NREGS_READ(out) COUNTS(out)[1]
     58 #define SIZE_READ(out) COUNTS(out)[2]
     59 #define BIN_NFREE_READ(out) COUNTS(out)[3]
     60 #define BIN_NREGS_READ(out) COUNTS(out)[4]
     61 
     62 		SLABCUR_READ(out) = NULL;
     63 		NFREE_READ(out) = NREGS_READ(out) = SIZE_READ(out) = -1;
     64 		BIN_NFREE_READ(out) = BIN_NREGS_READ(out) = -1;
     65 		memcpy(out_ref, out, out_sz);
     66 
     67 		/* Test invalid argument(s) errors */
     68 		TEST_UTIL_QUERY_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL");
     69 		TEST_UTIL_QUERY_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL");
     70 		TEST_UTIL_QUERY_EINVAL(
     71 		    out, &out_sz, NULL, in_sz, "newp is NULL");
     72 		TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, 0, "newlen is zero");
     73 		in_sz -= 1;
     74 		TEST_UTIL_QUERY_EINVAL(
     75 		    out, &out_sz, in, in_sz, "invalid newlen");
     76 		in_sz += 1;
     77 		out_sz_ref = out_sz -= 2 * sizeof(size_t);
     78 		TEST_UTIL_QUERY_EINVAL(
     79 		    out, &out_sz, in, in_sz, "invalid *oldlenp");
     80 		out_sz_ref = out_sz += 2 * sizeof(size_t);
     81 
     82 		/* Examine output for valid call */
     83 		TEST_UTIL_VALID("query");
     84 		expect_zu_le(sz, SIZE_READ(out),
     85 		    "Extent size should be at least allocation size");
     86 		expect_zu_eq(SIZE_READ(out) & (PAGE - 1), 0,
     87 		    "Extent size should be a multiple of page size");
     88 
     89 		/*
     90 		 * We don't do much bin checking if prof is on, since profiling
     91 		 * can produce extents that are for small size classes but not
     92 		 * slabs, which interferes with things like region counts.
     93 		 */
     94 		if (!opt_prof && sz <= SC_SMALL_MAXCLASS) {
     95 			expect_zu_le(NFREE_READ(out), NREGS_READ(out),
     96 			    "Extent free count exceeded region count");
     97 			expect_zu_le(NREGS_READ(out), SIZE_READ(out),
     98 			    "Extent region count exceeded size");
     99 			expect_zu_ne(NREGS_READ(out), 0,
    100 			    "Extent region count must be positive");
    101 			expect_true(NFREE_READ(out) == 0
    102 			        || (SLABCUR_READ(out) != NULL
    103 			            && SLABCUR_READ(out) <= p),
    104 			    "Allocation should follow first fit principle");
    105 
    106 			if (config_stats) {
    107 				expect_zu_le(BIN_NFREE_READ(out),
    108 				    BIN_NREGS_READ(out),
    109 				    "Bin free count exceeded region count");
    110 				expect_zu_ne(BIN_NREGS_READ(out), 0,
    111 				    "Bin region count must be positive");
    112 				expect_zu_le(NFREE_READ(out),
    113 				    BIN_NFREE_READ(out),
    114 				    "Extent free count exceeded bin free count");
    115 				expect_zu_le(NREGS_READ(out),
    116 				    BIN_NREGS_READ(out),
    117 				    "Extent region count exceeded "
    118 				    "bin region count");
    119 				expect_zu_eq(
    120 				    BIN_NREGS_READ(out) % NREGS_READ(out), 0,
    121 				    "Bin region count isn't a multiple of "
    122 				    "extent region count");
    123 				expect_zu_le(
    124 				    BIN_NFREE_READ(out) - NFREE_READ(out),
    125 				    BIN_NREGS_READ(out) - NREGS_READ(out),
    126 				    "Free count in other extents in the bin "
    127 				    "exceeded region count in other extents "
    128 				    "in the bin");
    129 				expect_zu_le(NREGS_READ(out) - NFREE_READ(out),
    130 				    BIN_NREGS_READ(out) - BIN_NFREE_READ(out),
    131 				    "Extent utilized count exceeded "
    132 				    "bin utilized count");
    133 			}
    134 		} else if (sz > SC_SMALL_MAXCLASS) {
    135 			expect_zu_eq(NFREE_READ(out), 0,
    136 			    "Extent free count should be zero");
    137 			expect_zu_eq(NREGS_READ(out), 1,
    138 			    "Extent region count should be one");
    139 			expect_ptr_null(SLABCUR_READ(out),
    140 			    "Current slab must be null for large size classes");
    141 			if (config_stats) {
    142 				expect_zu_eq(BIN_NFREE_READ(out), 0,
    143 				    "Bin free count must be zero for "
    144 				    "large sizes");
    145 				expect_zu_eq(BIN_NREGS_READ(out), 0,
    146 				    "Bin region count must be zero for "
    147 				    "large sizes");
    148 			}
    149 		}
    150 
    151 #undef BIN_NREGS_READ
    152 #undef BIN_NFREE_READ
    153 #undef SIZE_READ
    154 #undef NREGS_READ
    155 #undef NFREE_READ
    156 #undef COUNTS
    157 #undef SLABCUR_READ
    158 
    159 		free(out_ref);
    160 		free(out);
    161 		free(p);
    162 	}
    163 }
    164 TEST_END
    165 
    166 TEST_BEGIN(test_batch) {
    167 	size_t sz;
    168 	/*
    169 	 * Select some sizes that can span both small and large sizes, and are
    170 	 * numerically unrelated to any size boundaries.
    171 	 */
    172 	for (sz = 17; sz <= TEST_MAX_SIZE && sz <= SC_LARGE_MAXCLASS;
    173 	     sz += (sz <= SC_SMALL_MAXCLASS ? 1019 : 99991)) {
    174 		void  *p = mallocx(sz, 0);
    175 		void  *q = mallocx(sz, 0);
    176 		void  *in[] = {p, q};
    177 		size_t in_sz = sizeof(const void *) * 2;
    178 		size_t out[] = {-1, -1, -1, -1, -1, -1};
    179 		size_t out_sz = sizeof(size_t) * 6;
    180 		size_t out_ref[] = {-1, -1, -1, -1, -1, -1};
    181 		size_t out_sz_ref = out_sz;
    182 
    183 		assert_ptr_not_null(p, "test pointer allocation failed");
    184 		assert_ptr_not_null(q, "test pointer allocation failed");
    185 
    186 		/* Test invalid argument(s) errors */
    187 		TEST_UTIL_BATCH_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL");
    188 		TEST_UTIL_BATCH_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL");
    189 		TEST_UTIL_BATCH_EINVAL(
    190 		    out, &out_sz, NULL, in_sz, "newp is NULL");
    191 		TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, 0, "newlen is zero");
    192 		in_sz -= 1;
    193 		TEST_UTIL_BATCH_EINVAL(
    194 		    out, &out_sz, in, in_sz, "newlen is not an exact multiple");
    195 		in_sz += 1;
    196 		out_sz_ref = out_sz -= 2 * sizeof(size_t);
    197 		TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, in_sz,
    198 		    "*oldlenp is not an exact multiple");
    199 		out_sz_ref = out_sz += 2 * sizeof(size_t);
    200 		in_sz -= sizeof(const void *);
    201 		TEST_UTIL_BATCH_EINVAL(out, &out_sz, in, in_sz,
    202 		    "*oldlenp and newlen do not match");
    203 		in_sz += sizeof(const void *);
    204 
    205 		/* Examine output for valid calls */
    206 #define TEST_EQUAL_REF(i, message)                                             \
    207 	assert_d_eq(memcmp(out + (i) * 3, out_ref + (i) * 3, 3), 0, message)
    208 
    209 #define NFREE_READ(out, i) out[(i) * 3]
    210 #define NREGS_READ(out, i) out[(i) * 3 + 1]
    211 #define SIZE_READ(out, i) out[(i) * 3 + 2]
    212 
    213 		out_sz_ref = out_sz /= 2;
    214 		in_sz /= 2;
    215 		TEST_UTIL_BATCH_VALID;
    216 		expect_zu_le(sz, SIZE_READ(out, 0),
    217 		    "Extent size should be at least allocation size");
    218 		expect_zu_eq(SIZE_READ(out, 0) & (PAGE - 1), 0,
    219 		    "Extent size should be a multiple of page size");
    220 		/*
    221 		 * See the corresponding comment in test_query; profiling breaks
    222 		 * our slab count expectations.
    223 		 */
    224 		if (sz <= SC_SMALL_MAXCLASS && !opt_prof) {
    225 			expect_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0),
    226 			    "Extent free count exceeded region count");
    227 			expect_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0),
    228 			    "Extent region count exceeded size");
    229 			expect_zu_ne(NREGS_READ(out, 0), 0,
    230 			    "Extent region count must be positive");
    231 		} else if (sz > SC_SMALL_MAXCLASS) {
    232 			expect_zu_eq(NFREE_READ(out, 0), 0,
    233 			    "Extent free count should be zero");
    234 			expect_zu_eq(NREGS_READ(out, 0), 1,
    235 			    "Extent region count should be one");
    236 		}
    237 		TEST_EQUAL_REF(
    238 		    1, "Should not overwrite content beyond what's needed");
    239 		in_sz *= 2;
    240 		out_sz_ref = out_sz *= 2;
    241 
    242 		memcpy(out_ref, out, 3 * sizeof(size_t));
    243 		TEST_UTIL_BATCH_VALID;
    244 		TEST_EQUAL_REF(0, "Statistics should be stable across calls");
    245 		if (sz <= SC_SMALL_MAXCLASS) {
    246 			expect_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1),
    247 			    "Extent free count exceeded region count");
    248 		} else {
    249 			expect_zu_eq(NFREE_READ(out, 0), 0,
    250 			    "Extent free count should be zero");
    251 		}
    252 		expect_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1),
    253 		    "Extent region count should be same for same region size");
    254 		expect_zu_eq(SIZE_READ(out, 0), SIZE_READ(out, 1),
    255 		    "Extent size should be same for same region size");
    256 
    257 #undef SIZE_READ
    258 #undef NREGS_READ
    259 #undef NFREE_READ
    260 
    261 #undef TEST_EQUAL_REF
    262 
    263 		free(q);
    264 		free(p);
    265 	}
    266 }
    267 TEST_END
    268 
    269 int
    270 main(void) {
    271 	assert_zu_lt(SC_SMALL_MAXCLASS + 100000, TEST_MAX_SIZE,
    272 	    "Test case cannot cover large classes");
    273 	return test(test_query, test_batch);
    274 }
    275