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