• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "test/jemalloc_test.h"
2 
3 static int
prof_dump_open_intercept(bool propagate_err,const char * filename)4 prof_dump_open_intercept(bool propagate_err, const char *filename) {
5 	int fd;
6 
7 	fd = open("/dev/null", O_WRONLY);
8 	assert_d_ne(fd, -1, "Unexpected open() failure");
9 
10 	return fd;
11 }
12 
13 static void
set_prof_active(bool active)14 set_prof_active(bool active) {
15 	assert_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
16 	    sizeof(active)), 0, "Unexpected mallctl failure");
17 }
18 
19 static size_t
get_lg_prof_sample(void)20 get_lg_prof_sample(void) {
21 	size_t lg_prof_sample;
22 	size_t sz = sizeof(size_t);
23 
24 	assert_d_eq(mallctl("prof.lg_sample", (void *)&lg_prof_sample, &sz,
25 	    NULL, 0), 0,
26 	    "Unexpected mallctl failure while reading profiling sample rate");
27 	return lg_prof_sample;
28 }
29 
30 static void
do_prof_reset(size_t lg_prof_sample)31 do_prof_reset(size_t lg_prof_sample) {
32 	assert_d_eq(mallctl("prof.reset", NULL, NULL,
33 	    (void *)&lg_prof_sample, sizeof(size_t)), 0,
34 	    "Unexpected mallctl failure while resetting profile data");
35 	assert_zu_eq(lg_prof_sample, get_lg_prof_sample(),
36 	    "Expected profile sample rate change");
37 }
38 
TEST_BEGIN(test_prof_reset_basic)39 TEST_BEGIN(test_prof_reset_basic) {
40 	size_t lg_prof_sample_orig, lg_prof_sample, lg_prof_sample_next;
41 	size_t sz;
42 	unsigned i;
43 
44 	test_skip_if(!config_prof);
45 
46 	sz = sizeof(size_t);
47 	assert_d_eq(mallctl("opt.lg_prof_sample", (void *)&lg_prof_sample_orig,
48 	    &sz, NULL, 0), 0,
49 	    "Unexpected mallctl failure while reading profiling sample rate");
50 	assert_zu_eq(lg_prof_sample_orig, 0,
51 	    "Unexpected profiling sample rate");
52 	lg_prof_sample = get_lg_prof_sample();
53 	assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
54 	    "Unexpected disagreement between \"opt.lg_prof_sample\" and "
55 	    "\"prof.lg_sample\"");
56 
57 	/* Test simple resets. */
58 	for (i = 0; i < 2; i++) {
59 		assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
60 		    "Unexpected mallctl failure while resetting profile data");
61 		lg_prof_sample = get_lg_prof_sample();
62 		assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
63 		    "Unexpected profile sample rate change");
64 	}
65 
66 	/* Test resets with prof.lg_sample changes. */
67 	lg_prof_sample_next = 1;
68 	for (i = 0; i < 2; i++) {
69 		do_prof_reset(lg_prof_sample_next);
70 		lg_prof_sample = get_lg_prof_sample();
71 		assert_zu_eq(lg_prof_sample, lg_prof_sample_next,
72 		    "Expected profile sample rate change");
73 		lg_prof_sample_next = lg_prof_sample_orig;
74 	}
75 
76 	/* Make sure the test code restored prof.lg_sample. */
77 	lg_prof_sample = get_lg_prof_sample();
78 	assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
79 	    "Unexpected disagreement between \"opt.lg_prof_sample\" and "
80 	    "\"prof.lg_sample\"");
81 }
82 TEST_END
83 
84 bool prof_dump_header_intercepted = false;
85 prof_cnt_t cnt_all_copy = {0, 0, 0, 0};
86 static bool
prof_dump_header_intercept(tsdn_t * tsdn,bool propagate_err,const prof_cnt_t * cnt_all)87 prof_dump_header_intercept(tsdn_t *tsdn, bool propagate_err,
88     const prof_cnt_t *cnt_all) {
89 	prof_dump_header_intercepted = true;
90 	memcpy(&cnt_all_copy, cnt_all, sizeof(prof_cnt_t));
91 
92 	return false;
93 }
94 
TEST_BEGIN(test_prof_reset_cleanup)95 TEST_BEGIN(test_prof_reset_cleanup) {
96 	void *p;
97 	prof_dump_header_t *prof_dump_header_orig;
98 
99 	test_skip_if(!config_prof);
100 
101 	set_prof_active(true);
102 
103 	assert_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
104 	p = mallocx(1, 0);
105 	assert_ptr_not_null(p, "Unexpected mallocx() failure");
106 	assert_zu_eq(prof_bt_count(), 1, "Expected 1 backtrace");
107 
108 	prof_dump_header_orig = prof_dump_header;
109 	prof_dump_header = prof_dump_header_intercept;
110 	assert_false(prof_dump_header_intercepted, "Unexpected intercept");
111 
112 	assert_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
113 	    0, "Unexpected error while dumping heap profile");
114 	assert_true(prof_dump_header_intercepted, "Expected intercept");
115 	assert_u64_eq(cnt_all_copy.curobjs, 1, "Expected 1 allocation");
116 
117 	assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
118 	    "Unexpected error while resetting heap profile data");
119 	assert_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
120 	    0, "Unexpected error while dumping heap profile");
121 	assert_u64_eq(cnt_all_copy.curobjs, 0, "Expected 0 allocations");
122 	assert_zu_eq(prof_bt_count(), 1, "Expected 1 backtrace");
123 
124 	prof_dump_header = prof_dump_header_orig;
125 
126 	dallocx(p, 0);
127 	assert_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
128 
129 	set_prof_active(false);
130 }
131 TEST_END
132 
133 #define NTHREADS		4
134 #define NALLOCS_PER_THREAD	(1U << 13)
135 #define OBJ_RING_BUF_COUNT	1531
136 #define RESET_INTERVAL		(1U << 10)
137 #define DUMP_INTERVAL		3677
138 static void *
thd_start(void * varg)139 thd_start(void *varg) {
140 	unsigned thd_ind = *(unsigned *)varg;
141 	unsigned i;
142 	void *objs[OBJ_RING_BUF_COUNT];
143 
144 	memset(objs, 0, sizeof(objs));
145 
146 	for (i = 0; i < NALLOCS_PER_THREAD; i++) {
147 		if (i % RESET_INTERVAL == 0) {
148 			assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0),
149 			    0, "Unexpected error while resetting heap profile "
150 			    "data");
151 		}
152 
153 		if (i % DUMP_INTERVAL == 0) {
154 			assert_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
155 			    0, "Unexpected error while dumping heap profile");
156 		}
157 
158 		{
159 			void **pp = &objs[i % OBJ_RING_BUF_COUNT];
160 			if (*pp != NULL) {
161 				dallocx(*pp, 0);
162 				*pp = NULL;
163 			}
164 			*pp = btalloc(1, thd_ind*NALLOCS_PER_THREAD + i);
165 			assert_ptr_not_null(*pp,
166 			    "Unexpected btalloc() failure");
167 		}
168 	}
169 
170 	/* Clean up any remaining objects. */
171 	for (i = 0; i < OBJ_RING_BUF_COUNT; i++) {
172 		void **pp = &objs[i % OBJ_RING_BUF_COUNT];
173 		if (*pp != NULL) {
174 			dallocx(*pp, 0);
175 			*pp = NULL;
176 		}
177 	}
178 
179 	return NULL;
180 }
181 
TEST_BEGIN(test_prof_reset)182 TEST_BEGIN(test_prof_reset) {
183 	size_t lg_prof_sample_orig;
184 	thd_t thds[NTHREADS];
185 	unsigned thd_args[NTHREADS];
186 	unsigned i;
187 	size_t bt_count, tdata_count;
188 
189 	test_skip_if(!config_prof);
190 
191 	bt_count = prof_bt_count();
192 	assert_zu_eq(bt_count, 0,
193 	    "Unexpected pre-existing tdata structures");
194 	tdata_count = prof_tdata_count();
195 
196 	lg_prof_sample_orig = get_lg_prof_sample();
197 	do_prof_reset(5);
198 
199 	set_prof_active(true);
200 
201 	for (i = 0; i < NTHREADS; i++) {
202 		thd_args[i] = i;
203 		thd_create(&thds[i], thd_start, (void *)&thd_args[i]);
204 	}
205 	for (i = 0; i < NTHREADS; i++) {
206 		thd_join(thds[i], NULL);
207 	}
208 
209 	assert_zu_eq(prof_bt_count(), bt_count,
210 	    "Unexpected bactrace count change");
211 	assert_zu_eq(prof_tdata_count(), tdata_count,
212 	    "Unexpected remaining tdata structures");
213 
214 	set_prof_active(false);
215 
216 	do_prof_reset(lg_prof_sample_orig);
217 }
218 TEST_END
219 #undef NTHREADS
220 #undef NALLOCS_PER_THREAD
221 #undef OBJ_RING_BUF_COUNT
222 #undef RESET_INTERVAL
223 #undef DUMP_INTERVAL
224 
225 /* Test sampling at the same allocation site across resets. */
226 #define NITER 10
TEST_BEGIN(test_xallocx)227 TEST_BEGIN(test_xallocx) {
228 	size_t lg_prof_sample_orig;
229 	unsigned i;
230 	void *ptrs[NITER];
231 
232 	test_skip_if(!config_prof);
233 
234 	lg_prof_sample_orig = get_lg_prof_sample();
235 	set_prof_active(true);
236 
237 	/* Reset profiling. */
238 	do_prof_reset(0);
239 
240 	for (i = 0; i < NITER; i++) {
241 		void *p;
242 		size_t sz, nsz;
243 
244 		/* Reset profiling. */
245 		do_prof_reset(0);
246 
247 		/* Allocate small object (which will be promoted). */
248 		p = ptrs[i] = mallocx(1, 0);
249 		assert_ptr_not_null(p, "Unexpected mallocx() failure");
250 
251 		/* Reset profiling. */
252 		do_prof_reset(0);
253 
254 		/* Perform successful xallocx(). */
255 		sz = sallocx(p, 0);
256 		assert_zu_eq(xallocx(p, sz, 0, 0), sz,
257 		    "Unexpected xallocx() failure");
258 
259 		/* Perform unsuccessful xallocx(). */
260 		nsz = nallocx(sz+1, 0);
261 		assert_zu_eq(xallocx(p, nsz, 0, 0), sz,
262 		    "Unexpected xallocx() success");
263 	}
264 
265 	for (i = 0; i < NITER; i++) {
266 		/* dallocx. */
267 		dallocx(ptrs[i], 0);
268 	}
269 
270 	set_prof_active(false);
271 	do_prof_reset(lg_prof_sample_orig);
272 }
273 TEST_END
274 #undef NITER
275 
276 int
main(void)277 main(void) {
278 	/* Intercept dumping prior to running any tests. */
279 	prof_dump_open = prof_dump_open_intercept;
280 
281 	return test_no_reentrancy(
282 	    test_prof_reset_basic,
283 	    test_prof_reset_cleanup,
284 	    test_prof_reset,
285 	    test_xallocx);
286 }
287