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