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