linux/tools/perf/tests/dso-data.c
<<
>>
Prefs
   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
  18static 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
  54struct test_data_offset {
  55        off_t offset;
  56        u8 data[10];
  57        int size;
  58};
  59
  60struct test_data_offset offsets[] = {
  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        {
  75                .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
  76                .data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
  77                .size   = 10,
  78        },
  79        /* Read cache boundary pages. */
  80        {
  81                .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
  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 */
 106static 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
 116int 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
 177static 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
 202static struct dso **dsos;
 203
 204static 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
 224static 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
 238static 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
 251int 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
 311int 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}
 386