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 "dsos.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 static int test__dso_data(struct test_suite *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 dsos__init(&machine.dsos); 128 129 dso = dso__new(file); 130 TEST_ASSERT_VAL("Failed to add dso", !dsos__add(&machine.dsos, dso)); 131 TEST_ASSERT_VAL("Failed to access to dso", 132 dso__data_fd(dso, &machine) >= 0); 133 134 /* Basic 10 bytes tests. */ 135 for (i = 0; i < ARRAY_SIZE(offsets); i++) { 136 struct test_data_offset *data = &offsets[i]; 137 ssize_t size; 138 u8 buf[10]; 139 140 memset(buf, 0, 10); 141 size = dso__data_read_offset(dso, &machine, data->offset, 142 buf, 10); 143 144 TEST_ASSERT_VAL("Wrong size", size == data->size); 145 TEST_ASSERT_VAL("Wrong data", !memcmp(buf, data->data, 10)); 146 } 147 148 /* Read cross multiple cache pages. */ 149 { 150 ssize_t size; 151 int c; 152 u8 *buf; 153 154 buf = malloc(TEST_FILE_SIZE); 155 TEST_ASSERT_VAL("ENOMEM\n", buf); 156 157 /* First iteration to fill caches, second one to read them. */ 158 for (c = 0; c < 2; c++) { 159 memset(buf, 0, TEST_FILE_SIZE); 160 size = dso__data_read_offset(dso, &machine, 10, 161 buf, TEST_FILE_SIZE); 162 163 TEST_ASSERT_VAL("Wrong size", 164 size == (TEST_FILE_SIZE - 10)); 165 166 for (i = 0; i < (size_t)size; i++) 167 TEST_ASSERT_VAL("Wrong data", 168 buf[i] == (i % 10)); 169 } 170 171 free(buf); 172 } 173 174 dso__put(dso); 175 dsos__exit(&machine.dsos); 176 unlink(file); 177 return 0; 178 } 179 180 static long open_files_cnt(void) 181 { 182 char path[PATH_MAX]; 183 struct dirent *dent; 184 DIR *dir; 185 long nr = 0; 186 187 scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint()); 188 pr_debug("fd path: %s\n", path); 189 190 dir = opendir(path); 191 TEST_ASSERT_VAL("failed to open fd directory", dir); 192 193 while ((dent = readdir(dir)) != NULL) { 194 if (!strcmp(dent->d_name, ".") || 195 !strcmp(dent->d_name, "..")) 196 continue; 197 198 nr++; 199 } 200 201 closedir(dir); 202 return nr - 1; 203 } 204 205 static int dsos__create(int cnt, int size, struct dsos *dsos) 206 { 207 int i; 208 209 dsos__init(dsos); 210 211 for (i = 0; i < cnt; i++) { 212 struct dso *dso; 213 char *file = test_file(size); 214 215 TEST_ASSERT_VAL("failed to get dso file", file); 216 dso = dso__new(file); 217 TEST_ASSERT_VAL("failed to get dso", dso); 218 TEST_ASSERT_VAL("failed to add dso", !dsos__add(dsos, dso)); 219 dso__put(dso); 220 } 221 222 return 0; 223 } 224 225 static void dsos__delete(struct dsos *dsos) 226 { 227 for (unsigned int i = 0; i < dsos->cnt; i++) { 228 struct dso *dso = dsos->dsos[i]; 229 230 dso__data_close(dso); 231 unlink(dso__name(dso)); 232 } 233 dsos__exit(dsos); 234 } 235 236 static int set_fd_limit(int n) 237 { 238 struct rlimit rlim; 239 240 if (getrlimit(RLIMIT_NOFILE, &rlim)) 241 return -1; 242 243 pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n); 244 245 rlim.rlim_cur = n; 246 return setrlimit(RLIMIT_NOFILE, &rlim); 247 } 248 249 static int test__dso_data_cache(struct test_suite *test __maybe_unused, int subtest __maybe_unused) 250 { 251 struct machine machine; 252 long nr_end, nr = open_files_cnt(); 253 int dso_cnt, limit, i, fd; 254 255 /* Rest the internal dso open counter limit. */ 256 reset_fd_limit(); 257 258 memset(&machine, 0, sizeof(machine)); 259 260 /* set as system limit */ 261 limit = nr * 4; 262 TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit)); 263 264 /* and this is now our dso open FDs limit */ 265 dso_cnt = limit / 2; 266 TEST_ASSERT_VAL("failed to create dsos\n", 267 !dsos__create(dso_cnt, TEST_FILE_SIZE, &machine.dsos)); 268 269 for (i = 0; i < (dso_cnt - 1); i++) { 270 struct dso *dso = machine.dsos.dsos[i]; 271 272 /* 273 * Open dsos via dso__data_fd(), it opens the data 274 * file and keep it open (unless open file limit). 275 */ 276 fd = dso__data_fd(dso, &machine); 277 TEST_ASSERT_VAL("failed to get fd", fd > 0); 278 279 if (i % 2) { 280 #define BUFSIZE 10 281 u8 buf[BUFSIZE]; 282 ssize_t n; 283 284 n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE); 285 TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE); 286 } 287 } 288 289 /* verify the first one is already open */ 290 TEST_ASSERT_VAL("dsos[0] is not open", dso__data(machine.dsos.dsos[0])->fd != -1); 291 292 /* open +1 dso to reach the allowed limit */ 293 fd = dso__data_fd(machine.dsos.dsos[i], &machine); 294 TEST_ASSERT_VAL("failed to get fd", fd > 0); 295 296 /* should force the first one to be closed */ 297 TEST_ASSERT_VAL("failed to close dsos[0]", dso__data(machine.dsos.dsos[0])->fd == -1); 298 299 /* cleanup everything */ 300 dsos__delete(&machine.dsos); 301 302 /* Make sure we did not leak any file descriptor. */ 303 nr_end = open_files_cnt(); 304 pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); 305 TEST_ASSERT_VAL("failed leaking files", nr == nr_end); 306 return 0; 307 } 308 309 static long new_limit(int count) 310 { 311 int fd = open("/dev/null", O_RDONLY); 312 long ret = fd; 313 if (count > 0) 314 ret = new_limit(--count); 315 close(fd); 316 return ret; 317 } 318 319 static int test__dso_data_reopen(struct test_suite *test __maybe_unused, int subtest __maybe_unused) 320 { 321 struct machine machine; 322 long nr_end, nr = open_files_cnt(), lim = new_limit(3); 323 int fd, fd_extra; 324 325 #define dso_0 (machine.dsos.dsos[0]) 326 #define dso_1 (machine.dsos.dsos[1]) 327 #define dso_2 (machine.dsos.dsos[2]) 328 329 /* Rest the internal dso open counter limit. */ 330 reset_fd_limit(); 331 332 memset(&machine, 0, sizeof(machine)); 333 334 /* 335 * Test scenario: 336 * - create 3 dso objects 337 * - set process file descriptor limit to current 338 * files count + 3 339 * - test that the first dso gets closed when we 340 * reach the files count limit 341 */ 342 343 /* Make sure we are able to open 3 fds anyway */ 344 TEST_ASSERT_VAL("failed to set file limit", 345 !set_fd_limit((lim))); 346 347 TEST_ASSERT_VAL("failed to create dsos\n", 348 !dsos__create(3, TEST_FILE_SIZE, &machine.dsos)); 349 350 /* open dso_0 */ 351 fd = dso__data_fd(dso_0, &machine); 352 TEST_ASSERT_VAL("failed to get fd", fd > 0); 353 354 /* open dso_1 */ 355 fd = dso__data_fd(dso_1, &machine); 356 TEST_ASSERT_VAL("failed to get fd", fd > 0); 357 358 /* 359 * open extra file descriptor and we just 360 * reached the files count limit 361 */ 362 fd_extra = open("/dev/null", O_RDONLY); 363 TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0); 364 365 /* open dso_2 */ 366 fd = dso__data_fd(dso_2, &machine); 367 TEST_ASSERT_VAL("failed to get fd", fd > 0); 368 369 /* 370 * dso_0 should get closed, because we reached 371 * the file descriptor limit 372 */ 373 TEST_ASSERT_VAL("failed to close dso_0", dso__data(dso_0)->fd == -1); 374 375 /* open dso_0 */ 376 fd = dso__data_fd(dso_0, &machine); 377 TEST_ASSERT_VAL("failed to get fd", fd > 0); 378 379 /* 380 * dso_1 should get closed, because we reached 381 * the file descriptor limit 382 */ 383 TEST_ASSERT_VAL("failed to close dso_1", dso__data(dso_1)->fd == -1); 384 385 /* cleanup everything */ 386 close(fd_extra); 387 dsos__delete(&machine.dsos); 388 389 /* Make sure we did not leak any file descriptor. */ 390 nr_end = open_files_cnt(); 391 pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); 392 TEST_ASSERT_VAL("failed leaking files", nr == nr_end); 393 return 0; 394 } 395 396 397 static struct test_case tests__dso_data[] = { 398 TEST_CASE("read", dso_data), 399 TEST_CASE("cache", dso_data_cache), 400 TEST_CASE("reopen", dso_data_reopen), 401 { .name = NULL, } 402 }; 403 404 struct test_suite suite__dso_data = { 405 .desc = "DSO data tests", 406 .test_cases = tests__dso_data, 407 }; 408