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 = -1; 110 111 if (dso__data_get_fd(dso, machine, &fd)) 112 dso__data_put_fd(dso); 113 114 return fd; 115 } 116 117 static void dsos__delete(struct dsos *dsos) 118 { 119 for (unsigned int i = 0; i < dsos->cnt; i++) { 120 struct dso *dso = dsos->dsos[i]; 121 122 dso__data_close(dso); 123 unlink(dso__name(dso)); 124 } 125 dsos__exit(dsos); 126 } 127 128 static int test__dso_data(struct test_suite *test __maybe_unused, int subtest __maybe_unused) 129 { 130 struct machine machine; 131 struct dso *dso; 132 char *file = test_file(TEST_FILE_SIZE); 133 size_t i; 134 135 TEST_ASSERT_VAL("No test file", file); 136 137 memset(&machine, 0, sizeof(machine)); 138 dsos__init(&machine.dsos); 139 140 dso = dso__new(file); 141 TEST_ASSERT_VAL("Failed to add dso", !dsos__add(&machine.dsos, dso)); 142 TEST_ASSERT_VAL("Failed to access to dso", 143 dso__data_fd(dso, &machine) >= 0); 144 145 /* Basic 10 bytes tests. */ 146 for (i = 0; i < ARRAY_SIZE(offsets); i++) { 147 struct test_data_offset *data = &offsets[i]; 148 ssize_t size; 149 u8 buf[10]; 150 151 memset(buf, 0, 10); 152 size = dso__data_read_offset(dso, &machine, data->offset, 153 buf, 10); 154 155 TEST_ASSERT_VAL("Wrong size", size == data->size); 156 TEST_ASSERT_VAL("Wrong data", !memcmp(buf, data->data, 10)); 157 } 158 159 /* Read cross multiple cache pages. */ 160 { 161 ssize_t size; 162 int c; 163 u8 *buf; 164 165 buf = malloc(TEST_FILE_SIZE); 166 TEST_ASSERT_VAL("ENOMEM\n", buf); 167 168 /* First iteration to fill caches, second one to read them. */ 169 for (c = 0; c < 2; c++) { 170 memset(buf, 0, TEST_FILE_SIZE); 171 size = dso__data_read_offset(dso, &machine, 10, 172 buf, TEST_FILE_SIZE); 173 174 TEST_ASSERT_VAL("Wrong size", 175 size == (TEST_FILE_SIZE - 10)); 176 177 for (i = 0; i < (size_t)size; i++) 178 TEST_ASSERT_VAL("Wrong data", 179 buf[i] == (i % 10)); 180 } 181 182 free(buf); 183 } 184 185 dso__put(dso); 186 dsos__delete(&machine.dsos); 187 unlink(file); 188 return 0; 189 } 190 191 static long open_files_cnt(void) 192 { 193 char path[PATH_MAX]; 194 struct dirent *dent; 195 DIR *dir; 196 long nr = 0; 197 198 scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint()); 199 pr_debug("fd path: %s\n", path); 200 201 dir = opendir(path); 202 TEST_ASSERT_VAL("failed to open fd directory", dir); 203 204 while ((dent = readdir(dir)) != NULL) { 205 if (!strcmp(dent->d_name, ".") || 206 !strcmp(dent->d_name, "..")) 207 continue; 208 209 nr++; 210 } 211 212 closedir(dir); 213 return nr - 1; 214 } 215 216 static int dsos__create(int cnt, int size, struct dsos *dsos) 217 { 218 int i; 219 220 dsos__init(dsos); 221 222 for (i = 0; i < cnt; i++) { 223 struct dso *dso; 224 char *file = test_file(size); 225 226 TEST_ASSERT_VAL("failed to get dso file", file); 227 dso = dso__new(file); 228 TEST_ASSERT_VAL("failed to get dso", dso); 229 TEST_ASSERT_VAL("failed to add dso", !dsos__add(dsos, dso)); 230 dso__put(dso); 231 } 232 233 return 0; 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