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