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