408 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 19static 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 TEMPL29 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 55struct test_data_offset {56 off_t offset;57 u8 data[10];58 int size;59};60 61struct 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 */107static 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 117static 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 180static 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 205static 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 225static 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 236static 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 249static 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 data274 * 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 10281 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 309static 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 319static 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 objects337 * - set process file descriptor limit to current338 * files count + 3339 * - test that the first dso gets closed when we340 * reach the files count limit341 */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 just360 * reached the files count limit361 */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 reached371 * the file descriptor limit372 */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 reached381 * the file descriptor limit382 */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 397static 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 404struct test_suite suite__dso_data = {405 .desc = "DSO data tests",406 .test_cases = tests__dso_data,407};408