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