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