Commit | Line | Data |
---|---|---|
f7add556 | 1 | #include <stdlib.h> |
d944c4ee | 2 | #include <linux/types.h> |
f7add556 JO |
3 | #include <sys/stat.h> |
4 | #include <fcntl.h> | |
5 | #include <string.h> | |
4ebbcb84 JO |
6 | #include <sys/time.h> |
7 | #include <sys/resource.h> | |
8 | #include <api/fs/fs.h> | |
9 | #include "util.h" | |
69d2591a | 10 | #include "machine.h" |
f7add556 | 11 | #include "symbol.h" |
c81251e8 | 12 | #include "tests.h" |
84f5d36f | 13 | #include "debug.h" |
f7add556 | 14 | |
f7add556 JO |
15 | static char *test_file(int size) |
16 | { | |
822c45db JO |
17 | #define TEMPL "/tmp/perf-test-XXXXXX" |
18 | static char buf_templ[sizeof(TEMPL)]; | |
f7add556 JO |
19 | char *templ = buf_templ; |
20 | int fd, i; | |
21 | unsigned char *buf; | |
22 | ||
822c45db JO |
23 | strcpy(buf_templ, TEMPL); |
24 | #undef TEMPL | |
25 | ||
7b45f21c | 26 | fd = mkstemp(templ); |
f95e0818 JO |
27 | if (fd < 0) { |
28 | perror("mkstemp failed"); | |
29 | return NULL; | |
30 | } | |
f7add556 JO |
31 | |
32 | buf = malloc(size); | |
33 | if (!buf) { | |
34 | close(fd); | |
35 | return NULL; | |
36 | } | |
37 | ||
38 | for (i = 0; i < size; i++) | |
39 | buf[i] = (unsigned char) ((int) i % 10); | |
40 | ||
41 | if (size != write(fd, buf, size)) | |
42 | templ = NULL; | |
43 | ||
1df9297c | 44 | free(buf); |
f7add556 JO |
45 | close(fd); |
46 | return templ; | |
47 | } | |
48 | ||
49 | #define TEST_FILE_SIZE (DSO__DATA_CACHE_SIZE * 20) | |
50 | ||
51 | struct test_data_offset { | |
52 | off_t offset; | |
53 | u8 data[10]; | |
54 | int size; | |
55 | }; | |
56 | ||
57 | struct test_data_offset offsets[] = { | |
58 | /* Fill first cache page. */ | |
59 | { | |
60 | .offset = 10, | |
61 | .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | |
62 | .size = 10, | |
63 | }, | |
64 | /* Read first cache page. */ | |
65 | { | |
66 | .offset = 10, | |
67 | .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | |
68 | .size = 10, | |
69 | }, | |
70 | /* Fill cache boundary pages. */ | |
71 | { | |
72 | .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10, | |
73 | .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | |
74 | .size = 10, | |
75 | }, | |
76 | /* Read cache boundary pages. */ | |
77 | { | |
78 | .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10, | |
79 | .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | |
80 | .size = 10, | |
81 | }, | |
82 | /* Fill final cache page. */ | |
83 | { | |
84 | .offset = TEST_FILE_SIZE - 10, | |
85 | .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | |
86 | .size = 10, | |
87 | }, | |
88 | /* Read final cache page. */ | |
89 | { | |
90 | .offset = TEST_FILE_SIZE - 10, | |
91 | .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | |
92 | .size = 10, | |
93 | }, | |
94 | /* Read final cache page. */ | |
95 | { | |
96 | .offset = TEST_FILE_SIZE - 3, | |
97 | .data = { 7, 8, 9, 0, 0, 0, 0, 0, 0, 0 }, | |
98 | .size = 3, | |
99 | }, | |
100 | }; | |
101 | ||
4bb11d01 NK |
102 | /* move it from util/dso.c for compatibility */ |
103 | static int dso__data_fd(struct dso *dso, struct machine *machine) | |
104 | { | |
105 | int fd = dso__data_get_fd(dso, machine); | |
106 | ||
107 | if (fd >= 0) | |
108 | dso__data_put_fd(dso); | |
109 | ||
110 | return fd; | |
111 | } | |
112 | ||
721a1f53 | 113 | int test__dso_data(int subtest __maybe_unused) |
f7add556 JO |
114 | { |
115 | struct machine machine; | |
116 | struct dso *dso; | |
117 | char *file = test_file(TEST_FILE_SIZE); | |
118 | size_t i; | |
119 | ||
120 | TEST_ASSERT_VAL("No test file", file); | |
121 | ||
122 | memset(&machine, 0, sizeof(machine)); | |
123 | ||
124 | dso = dso__new((const char *)file); | |
125 | ||
63d3c6f3 NK |
126 | TEST_ASSERT_VAL("Failed to access to dso", |
127 | dso__data_fd(dso, &machine) >= 0); | |
128 | ||
f7add556 JO |
129 | /* Basic 10 bytes tests. */ |
130 | for (i = 0; i < ARRAY_SIZE(offsets); i++) { | |
131 | struct test_data_offset *data = &offsets[i]; | |
132 | ssize_t size; | |
133 | u8 buf[10]; | |
134 | ||
135 | memset(buf, 0, 10); | |
136 | size = dso__data_read_offset(dso, &machine, data->offset, | |
137 | buf, 10); | |
138 | ||
139 | TEST_ASSERT_VAL("Wrong size", size == data->size); | |
140 | TEST_ASSERT_VAL("Wrong data", !memcmp(buf, data->data, 10)); | |
141 | } | |
142 | ||
143 | /* Read cross multiple cache pages. */ | |
144 | { | |
145 | ssize_t size; | |
146 | int c; | |
147 | u8 *buf; | |
148 | ||
149 | buf = malloc(TEST_FILE_SIZE); | |
150 | TEST_ASSERT_VAL("ENOMEM\n", buf); | |
151 | ||
152 | /* First iteration to fill caches, second one to read them. */ | |
153 | for (c = 0; c < 2; c++) { | |
154 | memset(buf, 0, TEST_FILE_SIZE); | |
155 | size = dso__data_read_offset(dso, &machine, 10, | |
156 | buf, TEST_FILE_SIZE); | |
157 | ||
158 | TEST_ASSERT_VAL("Wrong size", | |
159 | size == (TEST_FILE_SIZE - 10)); | |
160 | ||
161 | for (i = 0; i < (size_t)size; i++) | |
162 | TEST_ASSERT_VAL("Wrong data", | |
163 | buf[i] == (i % 10)); | |
164 | } | |
165 | ||
166 | free(buf); | |
167 | } | |
168 | ||
d3a7c489 | 169 | dso__put(dso); |
f7add556 JO |
170 | unlink(file); |
171 | return 0; | |
172 | } | |
4ebbcb84 JO |
173 | |
174 | static long open_files_cnt(void) | |
175 | { | |
176 | char path[PATH_MAX]; | |
177 | struct dirent *dent; | |
178 | DIR *dir; | |
179 | long nr = 0; | |
180 | ||
181 | scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint()); | |
182 | pr_debug("fd path: %s\n", path); | |
183 | ||
184 | dir = opendir(path); | |
185 | TEST_ASSERT_VAL("failed to open fd directory", dir); | |
186 | ||
187 | while ((dent = readdir(dir)) != NULL) { | |
188 | if (!strcmp(dent->d_name, ".") || | |
189 | !strcmp(dent->d_name, "..")) | |
190 | continue; | |
191 | ||
192 | nr++; | |
193 | } | |
194 | ||
195 | closedir(dir); | |
196 | return nr - 1; | |
197 | } | |
198 | ||
199 | static struct dso **dsos; | |
200 | ||
201 | static int dsos__create(int cnt, int size) | |
202 | { | |
203 | int i; | |
204 | ||
ca7ce82a | 205 | dsos = malloc(sizeof(*dsos) * cnt); |
4ebbcb84 JO |
206 | TEST_ASSERT_VAL("failed to alloc dsos array", dsos); |
207 | ||
208 | for (i = 0; i < cnt; i++) { | |
209 | char *file; | |
210 | ||
211 | file = test_file(size); | |
212 | TEST_ASSERT_VAL("failed to get dso file", file); | |
213 | ||
214 | dsos[i] = dso__new(file); | |
215 | TEST_ASSERT_VAL("failed to get dso", dsos[i]); | |
216 | } | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
221 | static void dsos__delete(int cnt) | |
222 | { | |
223 | int i; | |
224 | ||
225 | for (i = 0; i < cnt; i++) { | |
226 | struct dso *dso = dsos[i]; | |
227 | ||
228 | unlink(dso->name); | |
d3a7c489 | 229 | dso__put(dso); |
4ebbcb84 JO |
230 | } |
231 | ||
232 | free(dsos); | |
233 | } | |
234 | ||
235 | static int set_fd_limit(int n) | |
236 | { | |
237 | struct rlimit rlim; | |
238 | ||
239 | if (getrlimit(RLIMIT_NOFILE, &rlim)) | |
240 | return -1; | |
241 | ||
242 | pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n); | |
243 | ||
244 | rlim.rlim_cur = n; | |
245 | return setrlimit(RLIMIT_NOFILE, &rlim); | |
246 | } | |
247 | ||
721a1f53 | 248 | int test__dso_data_cache(int subtest __maybe_unused) |
4ebbcb84 JO |
249 | { |
250 | struct machine machine; | |
251 | long nr_end, nr = open_files_cnt(); | |
252 | int dso_cnt, limit, i, fd; | |
253 | ||
254 | memset(&machine, 0, sizeof(machine)); | |
255 | ||
256 | /* set as system limit */ | |
257 | limit = nr * 4; | |
258 | TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit)); | |
259 | ||
66af43d5 NK |
260 | /* and this is now our dso open FDs limit */ |
261 | dso_cnt = limit / 2; | |
4ebbcb84 JO |
262 | TEST_ASSERT_VAL("failed to create dsos\n", |
263 | !dsos__create(dso_cnt, TEST_FILE_SIZE)); | |
264 | ||
265 | for (i = 0; i < (dso_cnt - 1); i++) { | |
266 | struct dso *dso = dsos[i]; | |
267 | ||
268 | /* | |
63d3c6f3 NK |
269 | * Open dsos via dso__data_fd(), it opens the data |
270 | * file and keep it open (unless open file limit). | |
4ebbcb84 | 271 | */ |
63d3c6f3 NK |
272 | fd = dso__data_fd(dso, &machine); |
273 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | |
274 | ||
4ebbcb84 | 275 | if (i % 2) { |
4ebbcb84 JO |
276 | #define BUFSIZE 10 |
277 | u8 buf[BUFSIZE]; | |
278 | ssize_t n; | |
279 | ||
280 | n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE); | |
281 | TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE); | |
282 | } | |
283 | } | |
284 | ||
66af43d5 NK |
285 | /* verify the first one is already open */ |
286 | TEST_ASSERT_VAL("dsos[0] is not open", dsos[0]->data.fd != -1); | |
287 | ||
288 | /* open +1 dso to reach the allowed limit */ | |
4ebbcb84 JO |
289 | fd = dso__data_fd(dsos[i], &machine); |
290 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | |
291 | ||
292 | /* should force the first one to be closed */ | |
293 | TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1); | |
294 | ||
295 | /* cleanup everything */ | |
296 | dsos__delete(dso_cnt); | |
297 | ||
298 | /* Make sure we did not leak any file descriptor. */ | |
299 | nr_end = open_files_cnt(); | |
300 | pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); | |
301 | TEST_ASSERT_VAL("failed leadking files", nr == nr_end); | |
302 | return 0; | |
303 | } | |
45dc1bb5 | 304 | |
721a1f53 | 305 | int test__dso_data_reopen(int subtest __maybe_unused) |
45dc1bb5 JO |
306 | { |
307 | struct machine machine; | |
308 | long nr_end, nr = open_files_cnt(); | |
309 | int fd, fd_extra; | |
310 | ||
311 | #define dso_0 (dsos[0]) | |
312 | #define dso_1 (dsos[1]) | |
313 | #define dso_2 (dsos[2]) | |
314 | ||
315 | memset(&machine, 0, sizeof(machine)); | |
316 | ||
317 | /* | |
318 | * Test scenario: | |
319 | * - create 3 dso objects | |
320 | * - set process file descriptor limit to current | |
321 | * files count + 3 | |
322 | * - test that the first dso gets closed when we | |
323 | * reach the files count limit | |
324 | */ | |
325 | ||
326 | /* Make sure we are able to open 3 fds anyway */ | |
327 | TEST_ASSERT_VAL("failed to set file limit", | |
328 | !set_fd_limit((nr + 3))); | |
329 | ||
330 | TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE)); | |
331 | ||
332 | /* open dso_0 */ | |
333 | fd = dso__data_fd(dso_0, &machine); | |
334 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | |
335 | ||
336 | /* open dso_1 */ | |
337 | fd = dso__data_fd(dso_1, &machine); | |
338 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | |
339 | ||
340 | /* | |
341 | * open extra file descriptor and we just | |
342 | * reached the files count limit | |
343 | */ | |
344 | fd_extra = open("/dev/null", O_RDONLY); | |
345 | TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0); | |
346 | ||
347 | /* open dso_2 */ | |
348 | fd = dso__data_fd(dso_2, &machine); | |
349 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | |
350 | ||
351 | /* | |
352 | * dso_0 should get closed, because we reached | |
353 | * the file descriptor limit | |
354 | */ | |
355 | TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1); | |
356 | ||
357 | /* open dso_0 */ | |
358 | fd = dso__data_fd(dso_0, &machine); | |
359 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | |
360 | ||
361 | /* | |
362 | * dso_1 should get closed, because we reached | |
363 | * the file descriptor limit | |
364 | */ | |
365 | TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1); | |
366 | ||
367 | /* cleanup everything */ | |
368 | close(fd_extra); | |
369 | dsos__delete(3); | |
370 | ||
371 | /* Make sure we did not leak any file descriptor. */ | |
372 | nr_end = open_files_cnt(); | |
373 | pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); | |
374 | TEST_ASSERT_VAL("failed leadking files", nr == nr_end); | |
375 | return 0; | |
376 | } |