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