linux/tools/perf/util/dlfilter.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * dlfilter.c: Interface to perf script --dlfilter shared object
   4 * Copyright (c) 2021, Intel Corporation.
   5 */
   6#include <dlfcn.h>
   7#include <stdlib.h>
   8#include <string.h>
   9#include <dirent.h>
  10#include <subcmd/exec-cmd.h>
  11#include <linux/zalloc.h>
  12#include <linux/build_bug.h>
  13
  14#include "debug.h"
  15#include "event.h"
  16#include "evsel.h"
  17#include "dso.h"
  18#include "map.h"
  19#include "thread.h"
  20#include "trace-event.h"
  21#include "symbol.h"
  22#include "srcline.h"
  23#include "dlfilter.h"
  24#include "../include/perf/perf_dlfilter.h"
  25
  26static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
  27{
  28        struct symbol *sym = al->sym;
  29
  30        d_al->size = sizeof(*d_al);
  31        if (al->map) {
  32                struct dso *dso = al->map->dso;
  33
  34                if (symbol_conf.show_kernel_path && dso->long_name)
  35                        d_al->dso = dso->long_name;
  36                else
  37                        d_al->dso = dso->name;
  38                d_al->is_64_bit = dso->is_64_bit;
  39                d_al->buildid_size = dso->bid.size;
  40                d_al->buildid = dso->bid.data;
  41        } else {
  42                d_al->dso = NULL;
  43                d_al->is_64_bit = 0;
  44                d_al->buildid_size = 0;
  45                d_al->buildid = NULL;
  46        }
  47        if (sym) {
  48                d_al->sym = sym->name;
  49                d_al->sym_start = sym->start;
  50                d_al->sym_end = sym->end;
  51                if (al->addr < sym->end)
  52                        d_al->symoff = al->addr - sym->start;
  53                else
  54                        d_al->symoff = al->addr - al->map->start - sym->start;
  55                d_al->sym_binding = sym->binding;
  56        } else {
  57                d_al->sym = NULL;
  58                d_al->sym_start = 0;
  59                d_al->sym_end = 0;
  60                d_al->symoff = 0;
  61                d_al->sym_binding = 0;
  62        }
  63        d_al->addr = al->addr;
  64        d_al->comm = NULL;
  65        d_al->filtered = 0;
  66}
  67
  68static struct addr_location *get_al(struct dlfilter *d)
  69{
  70        struct addr_location *al = d->al;
  71
  72        if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
  73                return NULL;
  74        return al;
  75}
  76
  77static struct thread *get_thread(struct dlfilter *d)
  78{
  79        struct addr_location *al = get_al(d);
  80
  81        return al ? al->thread : NULL;
  82}
  83
  84static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx)
  85{
  86        struct dlfilter *d = (struct dlfilter *)ctx;
  87        struct perf_dlfilter_al *d_al = d->d_ip_al;
  88        struct addr_location *al;
  89
  90        if (!d->ctx_valid)
  91                return NULL;
  92
  93        /* 'size' is also used to indicate already initialized */
  94        if (d_al->size)
  95                return d_al;
  96
  97        al = get_al(d);
  98        if (!al)
  99                return NULL;
 100
 101        al_to_d_al(al, d_al);
 102
 103        d_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->ip);
 104        d_al->comm = al->thread ? thread__comm_str(al->thread) : ":-1";
 105        d_al->filtered = al->filtered;
 106
 107        return d_al;
 108}
 109
 110static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx)
 111{
 112        struct dlfilter *d = (struct dlfilter *)ctx;
 113        struct perf_dlfilter_al *d_addr_al = d->d_addr_al;
 114        struct addr_location *addr_al = d->addr_al;
 115
 116        if (!d->ctx_valid || !d->d_sample->addr_correlates_sym)
 117                return NULL;
 118
 119        /* 'size' is also used to indicate already initialized */
 120        if (d_addr_al->size)
 121                return d_addr_al;
 122
 123        if (!addr_al->thread) {
 124                struct thread *thread = get_thread(d);
 125
 126                if (!thread)
 127                        return NULL;
 128                thread__resolve(thread, addr_al, d->sample);
 129        }
 130
 131        al_to_d_al(addr_al, d_addr_al);
 132
 133        d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr);
 134
 135        return d_addr_al;
 136}
 137
 138static char **dlfilter__args(void *ctx, int *dlargc)
 139{
 140        struct dlfilter *d = (struct dlfilter *)ctx;
 141
 142        if (dlargc)
 143                *dlargc = 0;
 144        else
 145                return NULL;
 146
 147        if (!d->ctx_valid && !d->in_start && !d->in_stop)
 148                return NULL;
 149
 150        *dlargc = d->dlargc;
 151        return d->dlargv;
 152}
 153
 154static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p)
 155{
 156        struct dlfilter *d = (struct dlfilter *)ctx;
 157        struct perf_dlfilter_al d_al;
 158        struct addr_location al;
 159        struct thread *thread;
 160        __u32 sz;
 161
 162        if (!d->ctx_valid || !d_al_p)
 163                return -1;
 164
 165        thread = get_thread(d);
 166        if (!thread)
 167                return -1;
 168
 169        thread__find_symbol_fb(thread, d->sample->cpumode, address, &al);
 170
 171        al_to_d_al(&al, &d_al);
 172
 173        d_al.is_kernel_ip = machine__kernel_ip(d->machine, address);
 174
 175        sz = d_al_p->size;
 176        memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al)));
 177        d_al_p->size = sz;
 178
 179        return 0;
 180}
 181
 182static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
 183{
 184        struct dlfilter *d = (struct dlfilter *)ctx;
 185
 186        if (!len)
 187                return NULL;
 188
 189        *len = 0;
 190
 191        if (!d->ctx_valid)
 192                return NULL;
 193
 194        if (d->sample->ip && !d->sample->insn_len) {
 195                struct addr_location *al = d->al;
 196
 197                if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
 198                        return NULL;
 199
 200                if (al->thread->maps && al->thread->maps->machine)
 201                        script_fetch_insn(d->sample, al->thread, al->thread->maps->machine);
 202        }
 203
 204        if (!d->sample->insn_len)
 205                return NULL;
 206
 207        *len = d->sample->insn_len;
 208
 209        return (__u8 *)d->sample->insn;
 210}
 211
 212static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
 213{
 214        struct dlfilter *d = (struct dlfilter *)ctx;
 215        struct addr_location *al;
 216        unsigned int line = 0;
 217        char *srcfile = NULL;
 218        struct map *map;
 219        u64 addr;
 220
 221        if (!d->ctx_valid || !line_no)
 222                return NULL;
 223
 224        al = get_al(d);
 225        if (!al)
 226                return NULL;
 227
 228        map = al->map;
 229        addr = al->addr;
 230
 231        if (map && map->dso)
 232                srcfile = get_srcline_split(map->dso, map__rip_2objdump(map, addr), &line);
 233
 234        *line_no = line;
 235        return srcfile;
 236}
 237
 238static struct perf_event_attr *dlfilter__attr(void *ctx)
 239{
 240        struct dlfilter *d = (struct dlfilter *)ctx;
 241
 242        if (!d->ctx_valid)
 243                return NULL;
 244
 245        return &d->evsel->core.attr;
 246}
 247
 248static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
 249{
 250        struct dlfilter *d = (struct dlfilter *)ctx;
 251        struct addr_location *al;
 252        struct addr_location a;
 253        struct map *map;
 254        u64 offset;
 255
 256        if (!d->ctx_valid)
 257                return -1;
 258
 259        al = get_al(d);
 260        if (!al)
 261                return -1;
 262
 263        map = al->map;
 264
 265        if (map && ip >= map->start && ip < map->end &&
 266            machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip))
 267                goto have_map;
 268
 269        thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a);
 270        if (!a.map)
 271                return -1;
 272
 273        map = a.map;
 274have_map:
 275        offset = map->map_ip(map, ip);
 276        if (ip + len >= map->end)
 277                len = map->end - ip;
 278        return dso__data_read_offset(map->dso, d->machine, offset, buf, len);
 279}
 280
 281static const struct perf_dlfilter_fns perf_dlfilter_fns = {
 282        .resolve_ip      = dlfilter__resolve_ip,
 283        .resolve_addr    = dlfilter__resolve_addr,
 284        .args            = dlfilter__args,
 285        .resolve_address = dlfilter__resolve_address,
 286        .insn            = dlfilter__insn,
 287        .srcline         = dlfilter__srcline,
 288        .attr            = dlfilter__attr,
 289        .object_code     = dlfilter__object_code,
 290};
 291
 292static char *find_dlfilter(const char *file)
 293{
 294        char path[PATH_MAX];
 295        char *exec_path;
 296
 297        if (strchr(file, '/'))
 298                goto out;
 299
 300        if (!access(file, R_OK)) {
 301                /*
 302                 * Prepend "./" so that dlopen will find the file in the
 303                 * current directory.
 304                 */
 305                snprintf(path, sizeof(path), "./%s", file);
 306                file = path;
 307                goto out;
 308        }
 309
 310        exec_path = get_argv_exec_path();
 311        if (!exec_path)
 312                goto out;
 313        snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
 314        free(exec_path);
 315        if (!access(path, R_OK))
 316                file = path;
 317out:
 318        return strdup(file);
 319}
 320
 321#define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
 322
 323static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
 324{
 325        CHECK_FLAG(BRANCH);
 326        CHECK_FLAG(CALL);
 327        CHECK_FLAG(RETURN);
 328        CHECK_FLAG(CONDITIONAL);
 329        CHECK_FLAG(SYSCALLRET);
 330        CHECK_FLAG(ASYNC);
 331        CHECK_FLAG(INTERRUPT);
 332        CHECK_FLAG(TX_ABORT);
 333        CHECK_FLAG(TRACE_BEGIN);
 334        CHECK_FLAG(TRACE_END);
 335        CHECK_FLAG(IN_TX);
 336        CHECK_FLAG(VMENTRY);
 337        CHECK_FLAG(VMEXIT);
 338
 339        memset(d, 0, sizeof(*d));
 340        d->file = find_dlfilter(file);
 341        if (!d->file)
 342                return -1;
 343        d->dlargc = dlargc;
 344        d->dlargv = dlargv;
 345        return 0;
 346}
 347
 348static void dlfilter__exit(struct dlfilter *d)
 349{
 350        zfree(&d->file);
 351}
 352
 353static int dlfilter__open(struct dlfilter *d)
 354{
 355        d->handle = dlopen(d->file, RTLD_NOW);
 356        if (!d->handle) {
 357                pr_err("dlopen failed for: '%s'\n", d->file);
 358                return -1;
 359        }
 360        d->start = dlsym(d->handle, "start");
 361        d->filter_event = dlsym(d->handle, "filter_event");
 362        d->filter_event_early = dlsym(d->handle, "filter_event_early");
 363        d->stop = dlsym(d->handle, "stop");
 364        d->fns = dlsym(d->handle, "perf_dlfilter_fns");
 365        if (d->fns)
 366                memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
 367        return 0;
 368}
 369
 370static int dlfilter__close(struct dlfilter *d)
 371{
 372        return dlclose(d->handle);
 373}
 374
 375struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
 376{
 377        struct dlfilter *d = malloc(sizeof(*d));
 378
 379        if (!d)
 380                return NULL;
 381
 382        if (dlfilter__init(d, file, dlargc, dlargv))
 383                goto err_free;
 384
 385        if (dlfilter__open(d))
 386                goto err_exit;
 387
 388        return d;
 389
 390err_exit:
 391        dlfilter__exit(d);
 392err_free:
 393        free(d);
 394        return NULL;
 395}
 396
 397static void dlfilter__free(struct dlfilter *d)
 398{
 399        if (d) {
 400                dlfilter__exit(d);
 401                free(d);
 402        }
 403}
 404
 405int dlfilter__start(struct dlfilter *d, struct perf_session *session)
 406{
 407        if (d) {
 408                d->session = session;
 409                if (d->start) {
 410                        int ret;
 411
 412                        d->in_start = true;
 413                        ret = d->start(&d->data, d);
 414                        d->in_start = false;
 415                        return ret;
 416                }
 417        }
 418        return 0;
 419}
 420
 421static int dlfilter__stop(struct dlfilter *d)
 422{
 423        if (d && d->stop) {
 424                int ret;
 425
 426                d->in_stop = true;
 427                ret = d->stop(d->data, d);
 428                d->in_stop = false;
 429                return ret;
 430        }
 431        return 0;
 432}
 433
 434void dlfilter__cleanup(struct dlfilter *d)
 435{
 436        if (d) {
 437                dlfilter__stop(d);
 438                dlfilter__close(d);
 439                dlfilter__free(d);
 440        }
 441}
 442
 443#define ASSIGN(x) d_sample.x = sample->x
 444
 445int dlfilter__do_filter_event(struct dlfilter *d,
 446                              union perf_event *event,
 447                              struct perf_sample *sample,
 448                              struct evsel *evsel,
 449                              struct machine *machine,
 450                              struct addr_location *al,
 451                              struct addr_location *addr_al,
 452                              bool early)
 453{
 454        struct perf_dlfilter_sample d_sample;
 455        struct perf_dlfilter_al d_ip_al;
 456        struct perf_dlfilter_al d_addr_al;
 457        int ret;
 458
 459        d->event       = event;
 460        d->sample      = sample;
 461        d->evsel       = evsel;
 462        d->machine     = machine;
 463        d->al          = al;
 464        d->addr_al     = addr_al;
 465        d->d_sample    = &d_sample;
 466        d->d_ip_al     = &d_ip_al;
 467        d->d_addr_al   = &d_addr_al;
 468
 469        d_sample.size  = sizeof(d_sample);
 470        d_ip_al.size   = 0; /* To indicate d_ip_al is not initialized */
 471        d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */
 472
 473        ASSIGN(ip);
 474        ASSIGN(pid);
 475        ASSIGN(tid);
 476        ASSIGN(time);
 477        ASSIGN(addr);
 478        ASSIGN(id);
 479        ASSIGN(stream_id);
 480        ASSIGN(period);
 481        ASSIGN(weight);
 482        ASSIGN(ins_lat);
 483        ASSIGN(p_stage_cyc);
 484        ASSIGN(transaction);
 485        ASSIGN(insn_cnt);
 486        ASSIGN(cyc_cnt);
 487        ASSIGN(cpu);
 488        ASSIGN(flags);
 489        ASSIGN(data_src);
 490        ASSIGN(phys_addr);
 491        ASSIGN(data_page_size);
 492        ASSIGN(code_page_size);
 493        ASSIGN(cgroup);
 494        ASSIGN(cpumode);
 495        ASSIGN(misc);
 496        ASSIGN(raw_size);
 497        ASSIGN(raw_data);
 498
 499        if (sample->branch_stack) {
 500                d_sample.brstack_nr = sample->branch_stack->nr;
 501                d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
 502        } else {
 503                d_sample.brstack_nr = 0;
 504                d_sample.brstack = NULL;
 505        }
 506
 507        if (sample->callchain) {
 508                d_sample.raw_callchain_nr = sample->callchain->nr;
 509                d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
 510        } else {
 511                d_sample.raw_callchain_nr = 0;
 512                d_sample.raw_callchain = NULL;
 513        }
 514
 515        d_sample.addr_correlates_sym =
 516                (evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
 517                sample_addr_correlates_sym(&evsel->core.attr);
 518
 519        d_sample.event = evsel__name(evsel);
 520
 521        d->ctx_valid = true;
 522
 523        if (early)
 524                ret = d->filter_event_early(d->data, &d_sample, d);
 525        else
 526                ret = d->filter_event(d->data, &d_sample, d);
 527
 528        d->ctx_valid = false;
 529
 530        return ret;
 531}
 532
 533bool get_filter_desc(const char *dirname, const char *name, char **desc,
 534                     char **long_desc)
 535{
 536        char path[PATH_MAX];
 537        void *handle;
 538        const char *(*desc_fn)(const char **long_description);
 539
 540        snprintf(path, sizeof(path), "%s/%s", dirname, name);
 541        handle = dlopen(path, RTLD_NOW);
 542        if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early")))
 543                return false;
 544        desc_fn = dlsym(handle, "filter_description");
 545        if (desc_fn) {
 546                const char *dsc;
 547                const char *long_dsc;
 548
 549                dsc = desc_fn(&long_dsc);
 550                if (dsc)
 551                        *desc = strdup(dsc);
 552                if (long_dsc)
 553                        *long_desc = strdup(long_dsc);
 554        }
 555        dlclose(handle);
 556        return true;
 557}
 558
 559static void list_filters(const char *dirname)
 560{
 561        struct dirent *entry;
 562        DIR *dir;
 563
 564        dir = opendir(dirname);
 565        if (!dir)
 566                return;
 567
 568        while ((entry = readdir(dir)) != NULL)
 569        {
 570                size_t n = strlen(entry->d_name);
 571                char *long_desc = NULL;
 572                char *desc = NULL;
 573
 574                if (entry->d_type == DT_DIR || n < 4 ||
 575                    strcmp(".so", entry->d_name + n - 3))
 576                        continue;
 577                if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
 578                        continue;
 579                printf("  %-36s %s\n", entry->d_name, desc ? desc : "");
 580                if (verbose) {
 581                        char *p = long_desc;
 582                        char *line;
 583
 584                        while ((line = strsep(&p, "\n")) != NULL)
 585                                printf("%39s%s\n", "", line);
 586                }
 587                free(long_desc);
 588                free(desc);
 589        }
 590
 591        closedir(dir);
 592}
 593
 594int list_available_dlfilters(const struct option *opt __maybe_unused,
 595                             const char *s __maybe_unused,
 596                             int unset __maybe_unused)
 597{
 598        char path[PATH_MAX];
 599        char *exec_path;
 600
 601        printf("List of available dlfilters:\n");
 602
 603        list_filters(".");
 604
 605        exec_path = get_argv_exec_path();
 606        if (!exec_path)
 607                goto out;
 608        snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
 609
 610        list_filters(path);
 611
 612        free(exec_path);
 613out:
 614        exit(0);
 615}
 616