linux/tools/perf/dlfilters/dlfilter-test-api-v0.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * dlfilter-test-api-v0.c: test original (v0) API for perf --dlfilter shared object
   4 * Copyright (c) 2021, Intel Corporation.
   5 */
   6#include <stdio.h>
   7#include <stdlib.h>
   8#include <string.h>
   9#include <stdbool.h>
  10
  11/*
  12 * Copy original (v0) API instead of including current API
  13 */
  14#include <linux/perf_event.h>
  15#include <linux/types.h>
  16
  17/* Definitions for perf_dlfilter_sample flags */
  18enum {
  19        PERF_DLFILTER_FLAG_BRANCH       = 1ULL << 0,
  20        PERF_DLFILTER_FLAG_CALL         = 1ULL << 1,
  21        PERF_DLFILTER_FLAG_RETURN       = 1ULL << 2,
  22        PERF_DLFILTER_FLAG_CONDITIONAL  = 1ULL << 3,
  23        PERF_DLFILTER_FLAG_SYSCALLRET   = 1ULL << 4,
  24        PERF_DLFILTER_FLAG_ASYNC        = 1ULL << 5,
  25        PERF_DLFILTER_FLAG_INTERRUPT    = 1ULL << 6,
  26        PERF_DLFILTER_FLAG_TX_ABORT     = 1ULL << 7,
  27        PERF_DLFILTER_FLAG_TRACE_BEGIN  = 1ULL << 8,
  28        PERF_DLFILTER_FLAG_TRACE_END    = 1ULL << 9,
  29        PERF_DLFILTER_FLAG_IN_TX        = 1ULL << 10,
  30        PERF_DLFILTER_FLAG_VMENTRY      = 1ULL << 11,
  31        PERF_DLFILTER_FLAG_VMEXIT       = 1ULL << 12,
  32};
  33
  34/*
  35 * perf sample event information (as per perf script and <linux/perf_event.h>)
  36 */
  37struct perf_dlfilter_sample {
  38        __u32 size; /* Size of this structure (for compatibility checking) */
  39        __u16 ins_lat;          /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
  40        __u16 p_stage_cyc;      /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
  41        __u64 ip;
  42        __s32 pid;
  43        __s32 tid;
  44        __u64 time;
  45        __u64 addr;
  46        __u64 id;
  47        __u64 stream_id;
  48        __u64 period;
  49        __u64 weight;           /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
  50        __u64 transaction;      /* Refer PERF_SAMPLE_TRANSACTION in <linux/perf_event.h> */
  51        __u64 insn_cnt; /* For instructions-per-cycle (IPC) */
  52        __u64 cyc_cnt;          /* For instructions-per-cycle (IPC) */
  53        __s32 cpu;
  54        __u32 flags;            /* Refer PERF_DLFILTER_FLAG_* above */
  55        __u64 data_src;         /* Refer PERF_SAMPLE_DATA_SRC in <linux/perf_event.h> */
  56        __u64 phys_addr;        /* Refer PERF_SAMPLE_PHYS_ADDR in <linux/perf_event.h> */
  57        __u64 data_page_size;   /* Refer PERF_SAMPLE_DATA_PAGE_SIZE in <linux/perf_event.h> */
  58        __u64 code_page_size;   /* Refer PERF_SAMPLE_CODE_PAGE_SIZE in <linux/perf_event.h> */
  59        __u64 cgroup;           /* Refer PERF_SAMPLE_CGROUP in <linux/perf_event.h> */
  60        __u8  cpumode;          /* Refer CPUMODE_MASK etc in <linux/perf_event.h> */
  61        __u8  addr_correlates_sym; /* True => resolve_addr() can be called */
  62        __u16 misc;             /* Refer perf_event_header in <linux/perf_event.h> */
  63        __u32 raw_size;         /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */
  64        const void *raw_data;   /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */
  65        __u64 brstack_nr;       /* Number of brstack entries */
  66        const struct perf_branch_entry *brstack; /* Refer <linux/perf_event.h> */
  67        __u64 raw_callchain_nr; /* Number of raw_callchain entries */
  68        const __u64 *raw_callchain; /* Refer <linux/perf_event.h> */
  69        const char *event;
  70};
  71
  72/*
  73 * Address location (as per perf script)
  74 */
  75struct perf_dlfilter_al {
  76        __u32 size; /* Size of this structure (for compatibility checking) */
  77        __u32 symoff;
  78        const char *sym;
  79        __u64 addr; /* Mapped address (from dso) */
  80        __u64 sym_start;
  81        __u64 sym_end;
  82        const char *dso;
  83        __u8  sym_binding; /* STB_LOCAL, STB_GLOBAL or STB_WEAK, refer <elf.h> */
  84        __u8  is_64_bit; /* Only valid if dso is not NULL */
  85        __u8  is_kernel_ip; /* True if in kernel space */
  86        __u32 buildid_size;
  87        __u8 *buildid;
  88        /* Below members are only populated by resolve_ip() */
  89        __u8 filtered; /* True if this sample event will be filtered out */
  90        const char *comm;
  91};
  92
  93struct perf_dlfilter_fns {
  94        /* Return information about ip */
  95        const struct perf_dlfilter_al *(*resolve_ip)(void *ctx);
  96        /* Return information about addr (if addr_correlates_sym) */
  97        const struct perf_dlfilter_al *(*resolve_addr)(void *ctx);
  98        /* Return arguments from --dlarg option */
  99        char **(*args)(void *ctx, int *dlargc);
 100        /*
 101         * Return information about address (al->size must be set before
 102         * calling). Returns 0 on success, -1 otherwise.
 103         */
 104        __s32 (*resolve_address)(void *ctx, __u64 address, struct perf_dlfilter_al *al);
 105        /* Return instruction bytes and length */
 106        const __u8 *(*insn)(void *ctx, __u32 *length);
 107        /* Return source file name and line number */
 108        const char *(*srcline)(void *ctx, __u32 *line_number);
 109        /* Return perf_event_attr, refer <linux/perf_event.h> */
 110        struct perf_event_attr *(*attr)(void *ctx);
 111        /* Read object code, return numbers of bytes read */
 112        __s32 (*object_code)(void *ctx, __u64 ip, void *buf, __u32 len);
 113        /* Reserved */
 114        void *(*reserved[120])(void *);
 115};
 116
 117struct perf_dlfilter_fns perf_dlfilter_fns;
 118
 119static int verbose;
 120
 121#define pr_debug(fmt, ...) do { \
 122                if (verbose) \
 123                        fprintf(stderr, fmt, ##__VA_ARGS__); \
 124        } while (0)
 125
 126static int test_fail(const char *msg)
 127{
 128        pr_debug("%s\n", msg);
 129        return -1;
 130}
 131
 132#define CHECK(x) do { \
 133                if (!(x)) \
 134                        return test_fail("Check '" #x "' failed\n"); \
 135        } while (0)
 136
 137struct filter_data {
 138        __u64 ip;
 139        __u64 addr;
 140        int do_early;
 141        int early_filter_cnt;
 142        int filter_cnt;
 143};
 144
 145static struct filter_data *filt_dat;
 146
 147int start(void **data, void *ctx)
 148{
 149        int dlargc;
 150        char **dlargv;
 151        struct filter_data *d;
 152        static bool called;
 153
 154        verbose = 1;
 155
 156        CHECK(!filt_dat && !called);
 157        called = true;
 158
 159        d = calloc(1, sizeof(*d));
 160        if (!d)
 161                test_fail("Failed to allocate memory");
 162        filt_dat = d;
 163        *data = d;
 164
 165        dlargv = perf_dlfilter_fns.args(ctx, &dlargc);
 166
 167        CHECK(dlargc == 6);
 168        CHECK(!strcmp(dlargv[0], "first"));
 169        verbose = strtol(dlargv[1], NULL, 0);
 170        d->ip = strtoull(dlargv[2], NULL, 0);
 171        d->addr = strtoull(dlargv[3], NULL, 0);
 172        d->do_early = strtol(dlargv[4], NULL, 0);
 173        CHECK(!strcmp(dlargv[5], "last"));
 174
 175        pr_debug("%s API\n", __func__);
 176
 177        return 0;
 178}
 179
 180#define CHECK_SAMPLE(x) do { \
 181                if (sample->x != expected.x) \
 182                        return test_fail("'" #x "' not expected value\n"); \
 183        } while (0)
 184
 185static int check_sample(struct filter_data *d, const struct perf_dlfilter_sample *sample)
 186{
 187        struct perf_dlfilter_sample expected = {
 188                .ip             = d->ip,
 189                .pid            = 12345,
 190                .tid            = 12346,
 191                .time           = 1234567890,
 192                .addr           = d->addr,
 193                .id             = 99,
 194                .stream_id      = 101,
 195                .period         = 543212345,
 196                .cpu            = 31,
 197                .cpumode        = PERF_RECORD_MISC_USER,
 198                .addr_correlates_sym = 1,
 199                .misc           = PERF_RECORD_MISC_USER,
 200        };
 201
 202        CHECK(sample->size >= sizeof(struct perf_dlfilter_sample));
 203
 204        CHECK_SAMPLE(ip);
 205        CHECK_SAMPLE(pid);
 206        CHECK_SAMPLE(tid);
 207        CHECK_SAMPLE(time);
 208        CHECK_SAMPLE(addr);
 209        CHECK_SAMPLE(id);
 210        CHECK_SAMPLE(stream_id);
 211        CHECK_SAMPLE(period);
 212        CHECK_SAMPLE(cpu);
 213        CHECK_SAMPLE(cpumode);
 214        CHECK_SAMPLE(addr_correlates_sym);
 215        CHECK_SAMPLE(misc);
 216
 217        CHECK(!sample->raw_data);
 218        CHECK_SAMPLE(brstack_nr);
 219        CHECK(!sample->brstack);
 220        CHECK_SAMPLE(raw_callchain_nr);
 221        CHECK(!sample->raw_callchain);
 222
 223#define EVENT_NAME "branches:"
 224        CHECK(!strncmp(sample->event, EVENT_NAME, strlen(EVENT_NAME)));
 225
 226        return 0;
 227}
 228
 229static int check_al(void *ctx)
 230{
 231        const struct perf_dlfilter_al *al;
 232
 233        al = perf_dlfilter_fns.resolve_ip(ctx);
 234        if (!al)
 235                return test_fail("resolve_ip() failed");
 236
 237        CHECK(al->sym && !strcmp("foo", al->sym));
 238        CHECK(!al->symoff);
 239
 240        return 0;
 241}
 242
 243static int check_addr_al(void *ctx)
 244{
 245        const struct perf_dlfilter_al *addr_al;
 246
 247        addr_al = perf_dlfilter_fns.resolve_addr(ctx);
 248        if (!addr_al)
 249                return test_fail("resolve_addr() failed");
 250
 251        CHECK(addr_al->sym && !strcmp("bar", addr_al->sym));
 252        CHECK(!addr_al->symoff);
 253
 254        return 0;
 255}
 256
 257static int check_attr(void *ctx)
 258{
 259        struct perf_event_attr *attr = perf_dlfilter_fns.attr(ctx);
 260
 261        CHECK(attr);
 262        CHECK(attr->type == PERF_TYPE_HARDWARE);
 263        CHECK(attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
 264
 265        return 0;
 266}
 267
 268static int do_checks(void *data, const struct perf_dlfilter_sample *sample, void *ctx, bool early)
 269{
 270        struct filter_data *d = data;
 271
 272        CHECK(data && filt_dat == data);
 273
 274        if (early) {
 275                CHECK(!d->early_filter_cnt);
 276                d->early_filter_cnt += 1;
 277        } else {
 278                CHECK(!d->filter_cnt);
 279                CHECK(d->early_filter_cnt);
 280                CHECK(d->do_early != 2);
 281                d->filter_cnt += 1;
 282        }
 283
 284        if (check_sample(data, sample))
 285                return -1;
 286
 287        if (check_attr(ctx))
 288                return -1;
 289
 290        if (early && !d->do_early)
 291                return 0;
 292
 293        if (check_al(ctx) || check_addr_al(ctx))
 294                return -1;
 295
 296        if (early)
 297                return d->do_early == 2;
 298
 299        return 1;
 300}
 301
 302int filter_event_early(void *data, const struct perf_dlfilter_sample *sample, void *ctx)
 303{
 304        pr_debug("%s API\n", __func__);
 305
 306        return do_checks(data, sample, ctx, true);
 307}
 308
 309int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx)
 310{
 311        struct filter_data *d = data;
 312
 313        pr_debug("%s API\n", __func__);
 314
 315        return do_checks(data, sample, ctx, false);
 316}
 317
 318int stop(void *data, void *ctx)
 319{
 320        static bool called;
 321
 322        pr_debug("%s API\n", __func__);
 323
 324        CHECK(data && filt_dat == data && !called);
 325        called = true;
 326
 327        free(data);
 328        filt_dat = NULL;
 329        return 0;
 330}
 331
 332const char *filter_description(const char **long_description)
 333{
 334        *long_description = "Filter used by the 'dlfilter C API' perf test";
 335        return "dlfilter to test v0 C API";
 336}
 337