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 26 static 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 = map__dso(al->map); 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 - map__start(al->map) - 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 68 static 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 77 static 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 84 static 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 110 static 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 138 static 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 154 static __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 182 static 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) { 201 struct machine *machine = maps__machine(al->thread->maps); 202 203 if (machine) 204 script_fetch_insn(d->sample, al->thread, machine); 205 } 206 } 207 208 if (!d->sample->insn_len) 209 return NULL; 210 211 *len = d->sample->insn_len; 212 213 return (__u8 *)d->sample->insn; 214 } 215 216 static const char *dlfilter__srcline(void *ctx, __u32 *line_no) 217 { 218 struct dlfilter *d = (struct dlfilter *)ctx; 219 struct addr_location *al; 220 unsigned int line = 0; 221 char *srcfile = NULL; 222 struct map *map; 223 struct dso *dso; 224 u64 addr; 225 226 if (!d->ctx_valid || !line_no) 227 return NULL; 228 229 al = get_al(d); 230 if (!al) 231 return NULL; 232 233 map = al->map; 234 addr = al->addr; 235 dso = map ? map__dso(map) : NULL; 236 237 if (dso) 238 srcfile = get_srcline_split(dso, map__rip_2objdump(map, addr), &line); 239 240 *line_no = line; 241 return srcfile; 242 } 243 244 static struct perf_event_attr *dlfilter__attr(void *ctx) 245 { 246 struct dlfilter *d = (struct dlfilter *)ctx; 247 248 if (!d->ctx_valid) 249 return NULL; 250 251 return &d->evsel->core.attr; 252 } 253 254 static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len) 255 { 256 struct dlfilter *d = (struct dlfilter *)ctx; 257 struct addr_location *al; 258 struct addr_location a; 259 struct map *map; 260 u64 offset; 261 262 if (!d->ctx_valid) 263 return -1; 264 265 al = get_al(d); 266 if (!al) 267 return -1; 268 269 map = al->map; 270 271 if (map && ip >= map__start(map) && ip < map__end(map) && 272 machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip)) 273 goto have_map; 274 275 thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a); 276 if (!a.map) 277 return -1; 278 279 map = a.map; 280 have_map: 281 offset = map__map_ip(map, ip); 282 if (ip + len >= map__end(map)) 283 len = map__end(map) - ip; 284 return dso__data_read_offset(map__dso(map), d->machine, offset, buf, len); 285 } 286 287 static const struct perf_dlfilter_fns perf_dlfilter_fns = { 288 .resolve_ip = dlfilter__resolve_ip, 289 .resolve_addr = dlfilter__resolve_addr, 290 .args = dlfilter__args, 291 .resolve_address = dlfilter__resolve_address, 292 .insn = dlfilter__insn, 293 .srcline = dlfilter__srcline, 294 .attr = dlfilter__attr, 295 .object_code = dlfilter__object_code, 296 }; 297 298 static char *find_dlfilter(const char *file) 299 { 300 char path[PATH_MAX]; 301 char *exec_path; 302 303 if (strchr(file, '/')) 304 goto out; 305 306 if (!access(file, R_OK)) { 307 /* 308 * Prepend "./" so that dlopen will find the file in the 309 * current directory. 310 */ 311 snprintf(path, sizeof(path), "./%s", file); 312 file = path; 313 goto out; 314 } 315 316 exec_path = get_argv_exec_path(); 317 if (!exec_path) 318 goto out; 319 snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file); 320 free(exec_path); 321 if (!access(path, R_OK)) 322 file = path; 323 out: 324 return strdup(file); 325 } 326 327 #define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x) 328 329 static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv) 330 { 331 CHECK_FLAG(BRANCH); 332 CHECK_FLAG(CALL); 333 CHECK_FLAG(RETURN); 334 CHECK_FLAG(CONDITIONAL); 335 CHECK_FLAG(SYSCALLRET); 336 CHECK_FLAG(ASYNC); 337 CHECK_FLAG(INTERRUPT); 338 CHECK_FLAG(TX_ABORT); 339 CHECK_FLAG(TRACE_BEGIN); 340 CHECK_FLAG(TRACE_END); 341 CHECK_FLAG(IN_TX); 342 CHECK_FLAG(VMENTRY); 343 CHECK_FLAG(VMEXIT); 344 345 memset(d, 0, sizeof(*d)); 346 d->file = find_dlfilter(file); 347 if (!d->file) 348 return -1; 349 d->dlargc = dlargc; 350 d->dlargv = dlargv; 351 return 0; 352 } 353 354 static void dlfilter__exit(struct dlfilter *d) 355 { 356 zfree(&d->file); 357 } 358 359 static int dlfilter__open(struct dlfilter *d) 360 { 361 d->handle = dlopen(d->file, RTLD_NOW); 362 if (!d->handle) { 363 pr_err("dlopen failed for: '%s'\n", d->file); 364 return -1; 365 } 366 d->start = dlsym(d->handle, "start"); 367 d->filter_event = dlsym(d->handle, "filter_event"); 368 d->filter_event_early = dlsym(d->handle, "filter_event_early"); 369 d->stop = dlsym(d->handle, "stop"); 370 d->fns = dlsym(d->handle, "perf_dlfilter_fns"); 371 if (d->fns) 372 memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns)); 373 return 0; 374 } 375 376 static int dlfilter__close(struct dlfilter *d) 377 { 378 return dlclose(d->handle); 379 } 380 381 struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv) 382 { 383 struct dlfilter *d = malloc(sizeof(*d)); 384 385 if (!d) 386 return NULL; 387 388 if (dlfilter__init(d, file, dlargc, dlargv)) 389 goto err_free; 390 391 if (dlfilter__open(d)) 392 goto err_exit; 393 394 return d; 395 396 err_exit: 397 dlfilter__exit(d); 398 err_free: 399 free(d); 400 return NULL; 401 } 402 403 static void dlfilter__free(struct dlfilter *d) 404 { 405 if (d) { 406 dlfilter__exit(d); 407 free(d); 408 } 409 } 410 411 int dlfilter__start(struct dlfilter *d, struct perf_session *session) 412 { 413 if (d) { 414 d->session = session; 415 if (d->start) { 416 int ret; 417 418 d->in_start = true; 419 ret = d->start(&d->data, d); 420 d->in_start = false; 421 return ret; 422 } 423 } 424 return 0; 425 } 426 427 static int dlfilter__stop(struct dlfilter *d) 428 { 429 if (d && d->stop) { 430 int ret; 431 432 d->in_stop = true; 433 ret = d->stop(d->data, d); 434 d->in_stop = false; 435 return ret; 436 } 437 return 0; 438 } 439 440 void dlfilter__cleanup(struct dlfilter *d) 441 { 442 if (d) { 443 dlfilter__stop(d); 444 dlfilter__close(d); 445 dlfilter__free(d); 446 } 447 } 448 449 #define ASSIGN(x) d_sample.x = sample->x 450 451 int dlfilter__do_filter_event(struct dlfilter *d, 452 union perf_event *event, 453 struct perf_sample *sample, 454 struct evsel *evsel, 455 struct machine *machine, 456 struct addr_location *al, 457 struct addr_location *addr_al, 458 bool early) 459 { 460 struct perf_dlfilter_sample d_sample; 461 struct perf_dlfilter_al d_ip_al; 462 struct perf_dlfilter_al d_addr_al; 463 int ret; 464 465 d->event = event; 466 d->sample = sample; 467 d->evsel = evsel; 468 d->machine = machine; 469 d->al = al; 470 d->addr_al = addr_al; 471 d->d_sample = &d_sample; 472 d->d_ip_al = &d_ip_al; 473 d->d_addr_al = &d_addr_al; 474 475 d_sample.size = sizeof(d_sample); 476 d_ip_al.size = 0; /* To indicate d_ip_al is not initialized */ 477 d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */ 478 479 ASSIGN(ip); 480 ASSIGN(pid); 481 ASSIGN(tid); 482 ASSIGN(time); 483 ASSIGN(addr); 484 ASSIGN(id); 485 ASSIGN(stream_id); 486 ASSIGN(period); 487 ASSIGN(weight); 488 ASSIGN(ins_lat); 489 ASSIGN(p_stage_cyc); 490 ASSIGN(transaction); 491 ASSIGN(insn_cnt); 492 ASSIGN(cyc_cnt); 493 ASSIGN(cpu); 494 ASSIGN(flags); 495 ASSIGN(data_src); 496 ASSIGN(phys_addr); 497 ASSIGN(data_page_size); 498 ASSIGN(code_page_size); 499 ASSIGN(cgroup); 500 ASSIGN(cpumode); 501 ASSIGN(misc); 502 ASSIGN(raw_size); 503 ASSIGN(raw_data); 504 ASSIGN(machine_pid); 505 ASSIGN(vcpu); 506 507 if (sample->branch_stack) { 508 d_sample.brstack_nr = sample->branch_stack->nr; 509 d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample); 510 } else { 511 d_sample.brstack_nr = 0; 512 d_sample.brstack = NULL; 513 } 514 515 if (sample->callchain) { 516 d_sample.raw_callchain_nr = sample->callchain->nr; 517 d_sample.raw_callchain = (__u64 *)sample->callchain->ips; 518 } else { 519 d_sample.raw_callchain_nr = 0; 520 d_sample.raw_callchain = NULL; 521 } 522 523 d_sample.addr_correlates_sym = 524 (evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) && 525 sample_addr_correlates_sym(&evsel->core.attr); 526 527 d_sample.event = evsel__name(evsel); 528 529 d->ctx_valid = true; 530 531 if (early) 532 ret = d->filter_event_early(d->data, &d_sample, d); 533 else 534 ret = d->filter_event(d->data, &d_sample, d); 535 536 d->ctx_valid = false; 537 538 return ret; 539 } 540 541 bool get_filter_desc(const char *dirname, const char *name, char **desc, 542 char **long_desc) 543 { 544 char path[PATH_MAX]; 545 void *handle; 546 const char *(*desc_fn)(const char **long_description); 547 548 snprintf(path, sizeof(path), "%s/%s", dirname, name); 549 handle = dlopen(path, RTLD_NOW); 550 if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early"))) 551 return false; 552 desc_fn = dlsym(handle, "filter_description"); 553 if (desc_fn) { 554 const char *dsc; 555 const char *long_dsc; 556 557 dsc = desc_fn(&long_dsc); 558 if (dsc) 559 *desc = strdup(dsc); 560 if (long_dsc) 561 *long_desc = strdup(long_dsc); 562 } 563 dlclose(handle); 564 return true; 565 } 566 567 static void list_filters(const char *dirname) 568 { 569 struct dirent *entry; 570 DIR *dir; 571 572 dir = opendir(dirname); 573 if (!dir) 574 return; 575 576 while ((entry = readdir(dir)) != NULL) 577 { 578 size_t n = strlen(entry->d_name); 579 char *long_desc = NULL; 580 char *desc = NULL; 581 582 if (entry->d_type == DT_DIR || n < 4 || 583 strcmp(".so", entry->d_name + n - 3)) 584 continue; 585 if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc)) 586 continue; 587 printf(" %-36s %s\n", entry->d_name, desc ? desc : ""); 588 if (verbose > 0) { 589 char *p = long_desc; 590 char *line; 591 592 while ((line = strsep(&p, "\n")) != NULL) 593 printf("%39s%s\n", "", line); 594 } 595 free(long_desc); 596 free(desc); 597 } 598 599 closedir(dir); 600 } 601 602 int list_available_dlfilters(const struct option *opt __maybe_unused, 603 const char *s __maybe_unused, 604 int unset __maybe_unused) 605 { 606 char path[PATH_MAX]; 607 char *exec_path; 608 609 printf("List of available dlfilters:\n"); 610 611 list_filters("."); 612 613 exec_path = get_argv_exec_path(); 614 if (!exec_path) 615 goto out; 616 snprintf(path, sizeof(path), "%s/dlfilters", exec_path); 617 618 list_filters(path); 619 620 free(exec_path); 621 out: 622 exit(0); 623 } 624