xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/prof_mdump.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1*7bdf38e5Schristos #include "test/jemalloc_test.h"
2*7bdf38e5Schristos 
3*7bdf38e5Schristos #include "jemalloc/internal/prof_sys.h"
4*7bdf38e5Schristos 
5*7bdf38e5Schristos static const char *test_filename = "test_filename";
6*7bdf38e5Schristos static bool did_prof_dump_open;
7*7bdf38e5Schristos 
8*7bdf38e5Schristos static int
9*7bdf38e5Schristos prof_dump_open_file_intercept(const char *filename, int mode) {
10*7bdf38e5Schristos 	int fd;
11*7bdf38e5Schristos 
12*7bdf38e5Schristos 	did_prof_dump_open = true;
13*7bdf38e5Schristos 
14*7bdf38e5Schristos 	/*
15*7bdf38e5Schristos 	 * Stronger than a strcmp() - verifying that we internally directly use
16*7bdf38e5Schristos 	 * the caller supplied char pointer.
17*7bdf38e5Schristos 	 */
18*7bdf38e5Schristos 	expect_ptr_eq(filename, test_filename,
19*7bdf38e5Schristos 	    "Dump file name should be \"%s\"", test_filename);
20*7bdf38e5Schristos 
21*7bdf38e5Schristos 	fd = open("/dev/null", O_WRONLY);
22*7bdf38e5Schristos 	assert_d_ne(fd, -1, "Unexpected open() failure");
23*7bdf38e5Schristos 
24*7bdf38e5Schristos 	return fd;
25*7bdf38e5Schristos }
26*7bdf38e5Schristos 
27*7bdf38e5Schristos TEST_BEGIN(test_mdump_normal) {
28*7bdf38e5Schristos 	test_skip_if(!config_prof);
29*7bdf38e5Schristos 
30*7bdf38e5Schristos 	prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
31*7bdf38e5Schristos 
32*7bdf38e5Schristos 	void *p = mallocx(1, 0);
33*7bdf38e5Schristos 	assert_ptr_not_null(p, "Unexpected mallocx() failure");
34*7bdf38e5Schristos 
35*7bdf38e5Schristos 	prof_dump_open_file = prof_dump_open_file_intercept;
36*7bdf38e5Schristos 	did_prof_dump_open = false;
37*7bdf38e5Schristos 	expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename,
38*7bdf38e5Schristos 	    sizeof(test_filename)), 0,
39*7bdf38e5Schristos 	    "Unexpected mallctl failure while dumping");
40*7bdf38e5Schristos 	expect_true(did_prof_dump_open, "Expected a profile dump");
41*7bdf38e5Schristos 
42*7bdf38e5Schristos 	dallocx(p, 0);
43*7bdf38e5Schristos 
44*7bdf38e5Schristos 	prof_dump_open_file = open_file_orig;
45*7bdf38e5Schristos }
46*7bdf38e5Schristos TEST_END
47*7bdf38e5Schristos 
48*7bdf38e5Schristos static int
49*7bdf38e5Schristos prof_dump_open_file_error(const char *filename, int mode) {
50*7bdf38e5Schristos 	return -1;
51*7bdf38e5Schristos }
52*7bdf38e5Schristos 
53*7bdf38e5Schristos /*
54*7bdf38e5Schristos  * In the context of test_mdump_output_error, prof_dump_write_file_count is the
55*7bdf38e5Schristos  * total number of times prof_dump_write_file_error() is expected to be called.
56*7bdf38e5Schristos  * In the context of test_mdump_maps_error, prof_dump_write_file_count is the
57*7bdf38e5Schristos  * total number of times prof_dump_write_file_error() is expected to be called
58*7bdf38e5Schristos  * starting from the one that contains an 'M' (beginning the "MAPPED_LIBRARIES"
59*7bdf38e5Schristos  * header).
60*7bdf38e5Schristos  */
61*7bdf38e5Schristos static int prof_dump_write_file_count;
62*7bdf38e5Schristos 
63*7bdf38e5Schristos static ssize_t
64*7bdf38e5Schristos prof_dump_write_file_error(int fd, const void *s, size_t len) {
65*7bdf38e5Schristos 	--prof_dump_write_file_count;
66*7bdf38e5Schristos 
67*7bdf38e5Schristos 	expect_d_ge(prof_dump_write_file_count, 0,
68*7bdf38e5Schristos 	    "Write is called after error occurs");
69*7bdf38e5Schristos 
70*7bdf38e5Schristos 	if (prof_dump_write_file_count == 0) {
71*7bdf38e5Schristos 		return -1;
72*7bdf38e5Schristos 	} else {
73*7bdf38e5Schristos 		/*
74*7bdf38e5Schristos 		 * Any non-negative number indicates success, and for
75*7bdf38e5Schristos 		 * simplicity we just use 0.  When prof_dump_write_file_count
76*7bdf38e5Schristos 		 * is positive, it means that we haven't reached the write that
77*7bdf38e5Schristos 		 * we want to fail; when prof_dump_write_file_count is
78*7bdf38e5Schristos 		 * negative, it means that we've already violated the
79*7bdf38e5Schristos 		 * expect_d_ge(prof_dump_write_file_count, 0) statement above,
80*7bdf38e5Schristos 		 * but instead of aborting, we continue the rest of the test,
81*7bdf38e5Schristos 		 * and we indicate that all the writes after the failed write
82*7bdf38e5Schristos 		 * are successful.
83*7bdf38e5Schristos 		 */
84*7bdf38e5Schristos 		return 0;
85*7bdf38e5Schristos 	}
86*7bdf38e5Schristos }
87*7bdf38e5Schristos 
88*7bdf38e5Schristos static void
89*7bdf38e5Schristos expect_write_failure(int count) {
90*7bdf38e5Schristos 	prof_dump_write_file_count = count;
91*7bdf38e5Schristos 	expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename,
92*7bdf38e5Schristos 	    sizeof(test_filename)), EFAULT, "Dump should err");
93*7bdf38e5Schristos 	expect_d_eq(prof_dump_write_file_count, 0,
94*7bdf38e5Schristos 	    "Dumping stopped after a wrong number of writes");
95*7bdf38e5Schristos }
96*7bdf38e5Schristos 
97*7bdf38e5Schristos TEST_BEGIN(test_mdump_output_error) {
98*7bdf38e5Schristos 	test_skip_if(!config_prof);
99*7bdf38e5Schristos 	test_skip_if(!config_debug);
100*7bdf38e5Schristos 
101*7bdf38e5Schristos 	prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
102*7bdf38e5Schristos 	prof_dump_write_file_t *write_file_orig = prof_dump_write_file;
103*7bdf38e5Schristos 
104*7bdf38e5Schristos 	prof_dump_write_file = prof_dump_write_file_error;
105*7bdf38e5Schristos 
106*7bdf38e5Schristos 	void *p = mallocx(1, 0);
107*7bdf38e5Schristos 	assert_ptr_not_null(p, "Unexpected mallocx() failure");
108*7bdf38e5Schristos 
109*7bdf38e5Schristos 	/*
110*7bdf38e5Schristos 	 * When opening the dump file fails, there shouldn't be any write, and
111*7bdf38e5Schristos 	 * mallctl() should return failure.
112*7bdf38e5Schristos 	 */
113*7bdf38e5Schristos 	prof_dump_open_file = prof_dump_open_file_error;
114*7bdf38e5Schristos 	expect_write_failure(0);
115*7bdf38e5Schristos 
116*7bdf38e5Schristos 	/*
117*7bdf38e5Schristos 	 * When the n-th write fails, there shouldn't be any more write, and
118*7bdf38e5Schristos 	 * mallctl() should return failure.
119*7bdf38e5Schristos 	 */
120*7bdf38e5Schristos 	prof_dump_open_file = prof_dump_open_file_intercept;
121*7bdf38e5Schristos 	expect_write_failure(1); /* First write fails. */
122*7bdf38e5Schristos 	expect_write_failure(2); /* Second write fails. */
123*7bdf38e5Schristos 
124*7bdf38e5Schristos 	dallocx(p, 0);
125*7bdf38e5Schristos 
126*7bdf38e5Schristos 	prof_dump_open_file = open_file_orig;
127*7bdf38e5Schristos 	prof_dump_write_file = write_file_orig;
128*7bdf38e5Schristos }
129*7bdf38e5Schristos TEST_END
130*7bdf38e5Schristos 
131*7bdf38e5Schristos static int
132*7bdf38e5Schristos prof_dump_open_maps_error() {
133*7bdf38e5Schristos 	return -1;
134*7bdf38e5Schristos }
135*7bdf38e5Schristos 
136*7bdf38e5Schristos static bool started_piping_maps_file;
137*7bdf38e5Schristos 
138*7bdf38e5Schristos static ssize_t
139*7bdf38e5Schristos prof_dump_write_maps_file_error(int fd, const void *s, size_t len) {
140*7bdf38e5Schristos 	/* The main dump doesn't contain any capital 'M'. */
141*7bdf38e5Schristos 	if (!started_piping_maps_file && strchr(s, 'M') != NULL) {
142*7bdf38e5Schristos 		started_piping_maps_file = true;
143*7bdf38e5Schristos 	}
144*7bdf38e5Schristos 
145*7bdf38e5Schristos 	if (started_piping_maps_file) {
146*7bdf38e5Schristos 		return prof_dump_write_file_error(fd, s, len);
147*7bdf38e5Schristos 	} else {
148*7bdf38e5Schristos 		/* Return success when we haven't started piping maps. */
149*7bdf38e5Schristos 		return 0;
150*7bdf38e5Schristos 	}
151*7bdf38e5Schristos }
152*7bdf38e5Schristos 
153*7bdf38e5Schristos static void
154*7bdf38e5Schristos expect_maps_write_failure(int count) {
155*7bdf38e5Schristos 	int mfd = prof_dump_open_maps();
156*7bdf38e5Schristos 	if (mfd == -1) {
157*7bdf38e5Schristos 		/* No need to continue if we just can't find the maps file. */
158*7bdf38e5Schristos 		return;
159*7bdf38e5Schristos 	}
160*7bdf38e5Schristos 	close(mfd);
161*7bdf38e5Schristos 	started_piping_maps_file = false;
162*7bdf38e5Schristos 	expect_write_failure(count);
163*7bdf38e5Schristos 	expect_true(started_piping_maps_file, "Should start piping maps");
164*7bdf38e5Schristos }
165*7bdf38e5Schristos 
166*7bdf38e5Schristos TEST_BEGIN(test_mdump_maps_error) {
167*7bdf38e5Schristos 	test_skip_if(!config_prof);
168*7bdf38e5Schristos 	test_skip_if(!config_debug);
169*7bdf38e5Schristos 
170*7bdf38e5Schristos 	prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
171*7bdf38e5Schristos 	prof_dump_write_file_t *write_file_orig = prof_dump_write_file;
172*7bdf38e5Schristos 	prof_dump_open_maps_t *open_maps_orig = prof_dump_open_maps;
173*7bdf38e5Schristos 
174*7bdf38e5Schristos 	prof_dump_open_file = prof_dump_open_file_intercept;
175*7bdf38e5Schristos 	prof_dump_write_file = prof_dump_write_maps_file_error;
176*7bdf38e5Schristos 
177*7bdf38e5Schristos 	void *p = mallocx(1, 0);
178*7bdf38e5Schristos 	assert_ptr_not_null(p, "Unexpected mallocx() failure");
179*7bdf38e5Schristos 
180*7bdf38e5Schristos 	/*
181*7bdf38e5Schristos 	 * When opening the maps file fails, there shouldn't be any maps write,
182*7bdf38e5Schristos 	 * and mallctl() should return success.
183*7bdf38e5Schristos 	 */
184*7bdf38e5Schristos 	prof_dump_open_maps = prof_dump_open_maps_error;
185*7bdf38e5Schristos 	started_piping_maps_file = false;
186*7bdf38e5Schristos 	prof_dump_write_file_count = 0;
187*7bdf38e5Schristos 	expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename,
188*7bdf38e5Schristos 	    sizeof(test_filename)), 0,
189*7bdf38e5Schristos 	    "mallctl should not fail in case of maps file opening failure");
190*7bdf38e5Schristos 	expect_false(started_piping_maps_file, "Shouldn't start piping maps");
191*7bdf38e5Schristos 	expect_d_eq(prof_dump_write_file_count, 0,
192*7bdf38e5Schristos 	    "Dumping stopped after a wrong number of writes");
193*7bdf38e5Schristos 
194*7bdf38e5Schristos 	/*
195*7bdf38e5Schristos 	 * When the n-th maps write fails (given that we are able to find the
196*7bdf38e5Schristos 	 * maps file), there shouldn't be any more maps write, and mallctl()
197*7bdf38e5Schristos 	 * should return failure.
198*7bdf38e5Schristos 	 */
199*7bdf38e5Schristos 	prof_dump_open_maps = open_maps_orig;
200*7bdf38e5Schristos 	expect_maps_write_failure(1); /* First write fails. */
201*7bdf38e5Schristos 	expect_maps_write_failure(2); /* Second write fails. */
202*7bdf38e5Schristos 
203*7bdf38e5Schristos 	dallocx(p, 0);
204*7bdf38e5Schristos 
205*7bdf38e5Schristos 	prof_dump_open_file = open_file_orig;
206*7bdf38e5Schristos 	prof_dump_write_file = write_file_orig;
207*7bdf38e5Schristos }
208*7bdf38e5Schristos TEST_END
209*7bdf38e5Schristos 
210*7bdf38e5Schristos int
211*7bdf38e5Schristos main(void) {
212*7bdf38e5Schristos 	return test(
213*7bdf38e5Schristos 	    test_mdump_normal,
214*7bdf38e5Schristos 	    test_mdump_output_error,
215*7bdf38e5Schristos 	    test_mdump_maps_error);
216*7bdf38e5Schristos }
217