dlfilter.c (13033B)
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}