1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2026, Netflix, Inc. 5 * 6 * This software was developed by Ali Mashtizadeh under the sponsorship from 7 * Netflix, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 35 #include <assert.h> 36 #include <err.h> 37 #include <fcntl.h> 38 #include <gelf.h> 39 #include <inttypes.h> 40 #include <libelf.h> 41 #include <pmclog.h> 42 #include <sysexits.h> 43 #include <unistd.h> 44 45 #include <cxxabi.h> 46 #include <cstring> 47 #include <iostream> 48 #include <map> 49 #include <set> 50 #include <string> 51 #include <sstream> 52 #include <unordered_map> 53 #include <unordered_set> 54 55 #include <dev/hwpmc/hwpmc_ibs.h> 56 #include "util.hh" 57 #include "view.hh" 58 59 std::string 60 syminfo::to_string(bool show_line) 61 { 62 int status; 63 char *demangled; 64 std::stringstream ss; 65 66 if (name == "") { 67 ss << "0x" << std::hex << offset; 68 } else if (name.length() >= 2 && name[0] == '_' && name[1] == 'Z') { 69 demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status); 70 if (status == 0) { 71 ss << demangled; 72 free(demangled); 73 } else { 74 ss << name; 75 } 76 } else { 77 ss << name; 78 } 79 80 if (!show_line) 81 return ss.str(); 82 83 if (line) { 84 ss << ":" << std::dec << line; 85 } else if (funcoff) { 86 ss << "+0x" << std::hex << funcoff; 87 } 88 89 return ss.str(); 90 } 91 92 pmcview::pmcview() : tscfreq(0), pmcid(), pmcinfo(), procs(), tidtopid(), 93 images(), sysroot(""), filter() 94 { 95 char *root; 96 97 root = getenv("SYSROOT"); 98 if (root) { 99 sysroot = root; 100 } 101 } 102 103 pmcview::~pmcview() 104 { 105 } 106 107 void 108 pmcview::setfilter(const pmcfilter &pmcfilter) 109 { 110 filter = pmcfilter; 111 } 112 113 static int 114 readlog(int logfd, void *buf, size_t len) 115 { 116 int status; 117 char *cur = (char *)buf; 118 size_t left = len; 119 120 while (left != 0) { 121 status = read(logfd, cur, left); 122 if (status < 0) { 123 if (errno == EINTR || errno == EAGAIN) 124 continue; 125 else 126 return status; 127 } 128 if (status == 0) 129 return status; 130 131 cur += status; 132 left -= status; 133 } 134 135 return len; 136 } 137 138 int 139 pmcview::process_sysinfo(int logfd, const pmchdr_infohdr &infohdr) 140 { 141 int status; 142 pmchdr_sysinfo sysinfo; 143 144 if (infohdr.length != sizeof(sysinfo)) 145 errx(EX_IOERR, "PMC log headers have an unexpected size"); 146 147 status = readlog(logfd, &sysinfo, sizeof(sysinfo)); 148 if (status < 0 || status != infohdr.length) 149 errx(EX_IOERR, "readlog"); 150 151 cpumodel = sysinfo.cpumodel; 152 osrelease = sysinfo.osrelease; 153 buildid = sysinfo.buildid; 154 155 return 0; 156 } 157 158 int 159 pmcview::process_pmcinfo(int logfd, const pmchdr_infohdr &infohdr) 160 { 161 int status; 162 union { 163 pmchdr_pmcinfo pmcinfo; 164 __unused char reserve_max_size[256]; 165 }; 166 167 status = readlog(logfd, &pmcinfo, infohdr.length); 168 if (status < 0 || status != infohdr.length) 169 errx(EX_IOERR, "readlog"); 170 171 extpmcinfo.emplace_back(pmcinfo.rate, std::string(pmcinfo.pmc)); 172 173 return 0; 174 } 175 176 int 177 pmcview::process_cpuidinfo(int logfd, const pmchdr_infohdr &infohdr) 178 { 179 int status; 180 uint32_t *cpuidinfo; 181 int offset, len; 182 uint32_t root, maxleaf, count; 183 184 len = infohdr.length / 4; 185 cpuidinfo = new uint32_t[len]; 186 187 status = readlog(logfd, cpuidinfo, infohdr.length); 188 if (status < 0 || status != infohdr.length) 189 errx(EX_IOERR, "readlog"); 190 191 offset = 0; 192 193 while (offset < len) { 194 maxleaf = cpuidinfo[offset]; 195 196 /* 197 * In x86 the roots contains the maximum leaf number present. 198 */ 199 root = maxleaf & 0xFFFF0000; 200 count = maxleaf & 0x0000FFFF; 201 202 for (uint32_t i = 0; i <= count; i++) { 203 cpuid[root + i] = { cpuidinfo[4 * i + offset], 204 cpuidinfo[4 * i + 1 + offset], 205 cpuidinfo[4 * i + 2 + offset], 206 cpuidinfo[4 * i + 3 + offset] }; 207 } 208 209 offset += 4 * (count + 1); 210 } 211 212 delete[] cpuidinfo; 213 214 return 0; 215 } 216 217 void 218 pmcview::process(int logfd) 219 { 220 int status; 221 pmchdr_header hdr; 222 struct pmclog_parse_state *ps; 223 224 // Read header 225 status = readlog(logfd, &hdr, sizeof(hdr)); 226 if (status < 0 || status != sizeof(hdr)) 227 err(EX_IOERR, "readlog"); 228 if (hdr.magic != PMC_HEADER_MAGIC) 229 errx(EX_DATAERR, "PMC log magic mismatch!"); 230 if (hdr.version > PMC_HEADER_VERSION) 231 errx(EX_DATAERR, "PMC log version newer than supported!"); 232 233 // Save architecture 234 arch = hdr.arch; 235 236 while (1) { 237 pmchdr_infohdr infohdr; 238 239 status = readlog(logfd, &infohdr, sizeof(infohdr)); 240 if (status < 0 || status != sizeof(infohdr)) 241 errx(EX_IOERR, "readlog"); 242 243 if (infohdr.type == INFOHDR_TYPE_DONE) { 244 break; 245 } else if (infohdr.type == INFOHDR_TYPE_SYSINFO) { 246 process_sysinfo(logfd, infohdr); 247 } else if (infohdr.type == INFOHDR_TYPE_PMCINFO) { 248 process_pmcinfo(logfd, infohdr); 249 } else if (infohdr.type == INFOHDR_TYPE_CPUID) { 250 process_cpuidinfo(logfd, infohdr); 251 } 252 } 253 254 ps = static_cast<struct pmclog_parse_state*>(pmclog_open(logfd)); 255 if (ps == NULL) { 256 errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", 257 strerror(errno)); 258 } 259 260 process(ps); 261 262 pmclog_close(ps); 263 } 264 265 void 266 pmcview::process(struct pmclog_ev &p) 267 { 268 switch (p.pl_type) { 269 case PMCLOG_TYPE_INITIALIZE: 270 process(p.pl_u.pl_i); 271 return; 272 case PMCLOG_TYPE_CLOSELOG: 273 process(p.pl_u.pl_cl); 274 return; 275 case PMCLOG_TYPE_PMCALLOCATE: 276 process(p.pl_u.pl_a); 277 return; 278 case PMCLOG_TYPE_PMCALLOCATEDYN: 279 process(p.pl_u.pl_ad); 280 return; 281 case PMCLOG_TYPE_PROC_CREATE: 282 process(p.pl_u.pl_pc); 283 return; 284 case PMCLOG_TYPE_PROCEXIT: 285 process(p.pl_u.pl_e); 286 return; 287 case PMCLOG_TYPE_PROCEXEC: 288 process(p.pl_u.pl_x); 289 return; 290 case PMCLOG_TYPE_PROCFORK: 291 process(p.pl_u.pl_f); 292 return; 293 case PMCLOG_TYPE_SYSEXIT: 294 process(p.pl_u.pl_se); 295 return; 296 case PMCLOG_TYPE_MAP_IN: 297 process(p.pl_u.pl_mi); 298 return; 299 case PMCLOG_TYPE_MAP_OUT: 300 process(p.pl_u.pl_mo); 301 return; 302 case PMCLOG_TYPE_THR_CREATE: 303 process(p.pl_u.pl_tc); 304 return; 305 case PMCLOG_TYPE_THR_EXIT: 306 process(p.pl_u.pl_te); 307 return; 308 case PMCLOG_TYPE_CALLCHAIN: 309 process(p.pl_u.pl_cc); 310 return; 311 case PMCLOG_TYPE_PMCATTACH: [[fallthrough]]; 312 case PMCLOG_TYPE_PMCDETACH: 313 case PMCLOG_TYPE_USERDATA: 314 case PMCLOG_TYPE_PROCCSW: 315 case PMCLOG_TYPE_DROPNOTIFY: 316 return; 317 }; 318 } 319 320 void 321 pmcview::process(struct pmclog_parse_state *p) 322 { 323 struct pmclog_ev ev; 324 325 while (pmclog_read(p, &ev) == 0) { 326 process(ev); 327 } 328 } 329 330 void 331 pmcview::process(struct pmclog_ev_initialize &p) 332 { 333 tscfreq = p.pl_tsc_freq; 334 } 335 336 void 337 pmcview::process(__unused struct pmclog_ev_closelog &p) 338 { 339 } 340 341 void 342 pmcview::process(struct pmclog_ev_pmcallocate &p) 343 { 344 pmcid[p.pl_pmcid] = p.pl_event; 345 if (pmcinfo.find(p.pl_event) == pmcinfo.end()) { 346 pmcinfo.emplace(p.pl_event, p); 347 } 348 } 349 350 void 351 pmcview::process(struct pmclog_ev_pmcallocatedyn &p) 352 { 353 pmcid[p.pl_pmcid] = p.pl_event; 354 if (pmcinfo.find(p.pl_event) == pmcinfo.end()) { 355 pmcinfo.emplace(p.pl_event, p); 356 } 357 } 358 359 void 360 pmcview::process(struct pmclog_ev_proccreate &p) 361 { 362 procs[p.pl_pid] = procinfo(p); 363 364 proccreate(p.pl_pid); 365 } 366 367 void 368 pmcview::process(struct pmclog_ev_procexit &p) 369 { 370 /* 371 * XXX: Seems we can recieve samples after the process exits we need 372 * some way to delay cleanup, then after the delayed cleanup discard 373 * delayed events. 374 */ 375 procs.erase(p.pl_pid); 376 377 procexit(p.pl_pid); 378 } 379 380 void 381 pmcview::process(struct pmclog_ev_procexec &p) 382 { 383 procs[p.pl_pid].map.clear(); 384 procs[p.pl_pid].name = basename(p.pl_pathname); 385 procs[p.pl_pid].fullpath = p.pl_pathname; 386 procs[p.pl_pid].baseaddr = p.pl_baseaddr; 387 procs[p.pl_pid].dynaddr = p.pl_dynaddr; 388 389 image im = loadimage(p.pl_pathname); 390 391 mapimage(p.pl_pid, im, im.vaddr + p.pl_dynaddr); 392 393 /* 394 * Map the dynamic runtime loader 395 */ 396 if (im.isdynamic) { 397 image rtldim = loadimage(im.loader); 398 399 mapimage(p.pl_pid, rtldim, p.pl_baseaddr); 400 } 401 402 procexec(p.pl_pid); 403 } 404 405 void 406 pmcview::process(struct pmclog_ev_procfork &p) 407 { 408 procs[p.pl_newpid] = procs[p.pl_oldpid]; 409 410 proccreate(p.pl_newpid); 411 } 412 413 void 414 pmcview::process(struct pmclog_ev_sysexit &p) 415 { 416 procs.erase(p.pl_pid); 417 } 418 419 void 420 pmcview::process(struct pmclog_ev_threadcreate &p) 421 { 422 procs[p.pl_pid].threads[p.pl_tid] = threadinfo(p.pl_tdname); 423 tidtopid[p.pl_tid] = p.pl_pid; 424 } 425 426 void 427 pmcview::process(struct pmclog_ev_threadexit &p) 428 { 429 pid_t pid = tidtopid[p.pl_tid]; 430 procs[pid].threads.erase(p.pl_tid); 431 tidtopid.erase(p.pl_tid); 432 } 433 434 /* 435 * Load the ELF file to compute the values needed for the memory map and check 436 * if the dwarf symbols are available. 437 */ 438 image 439 pmcview::loadimage(const std::string &path) 440 { 441 std::string fullpath; 442 image im; 443 GElf_Ehdr eh; 444 Elf *e; 445 const char *elf; 446 uint64_t start; 447 uint64_t end; 448 size_t shstrndx; 449 int fd, i; 450 bool foundexec; 451 452 if (images.find(path) != images.end()) 453 return images[path]; 454 455 im = image(); 456 457 if (path == "unknown") { 458 return (im); 459 } 460 461 fullpath = sysroot + path; 462 463 if (access(fullpath.c_str(), R_OK) != 0) 464 return (im); 465 466 fd = open(fullpath.c_str(), O_RDONLY, 0); 467 if (fd < 0) { 468 warnx("WARNING: Cannot open \"%s\".", 469 fullpath.c_str()); 470 return (im); 471 } 472 473 e = elf_begin(fd, ELF_C_READ, NULL); 474 if (e == NULL) { 475 warnx("WARNING: Cannot read \"%s\".", 476 fullpath.c_str()); 477 close(fd); 478 return (im); 479 } 480 481 if (gelf_getehdr(e, &eh) != &eh) { 482 warnx("WARNING: Cannot read the ELF header for \"%s\": %s.", 483 fullpath.c_str(), elf_errmsg(-1)); 484 goto done; 485 } 486 487 if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN && eh.e_type != ET_REL) { 488 warnx("WARNING: ELF file type is unsupported for \"%s\".", 489 fullpath.c_str());; 490 goto done; 491 } 492 493 elf = elf_rawfile(e, NULL); 494 if (elf == NULL) { 495 warnx("WARNING: Cannot read the ELF file for \"%s\": %s.", 496 fullpath.c_str(), elf_errmsg(-1)); 497 goto done; 498 } 499 500 if (eh.e_type != ET_REL) { 501 foundexec = false; 502 for (i = 0; i < eh.e_phnum; i++) { 503 GElf_Phdr ph; 504 505 if (gelf_getphdr(e, i, &ph) != &ph) { 506 warnx("WARNING: Cannot read program header for \"%s\": %s.", 507 fullpath.c_str(), elf_errmsg(-1)); 508 goto done; 509 } 510 511 if (ph.p_type == PT_DYNAMIC) { 512 im.isdynamic = 1; 513 continue; 514 } 515 516 if (ph.p_type == PT_INTERP) { 517 im.loader = elf + ph.p_offset; 518 } 519 520 if (ph.p_type == PT_LOAD) { 521 if ((ph.p_flags & PF_X) != 0 && !foundexec) { 522 im.vaddr = ph.p_vaddr & ~(ph.p_align - 1); 523 foundexec = true; 524 } 525 } 526 } 527 } 528 529 elf_getshdrstrndx(e, &shstrndx); 530 531 start = ~(0x0ULL); 532 end = 0; 533 for (i = 0; i < eh.e_shnum; i++) { 534 GElf_Shdr sh; 535 Elf_Scn *scn; 536 char *scname; 537 538 scn = elf_getscn(e, i); 539 if (scn == NULL) { 540 warnx("WARNING: Could not retrieve section descriptor for \"%s\".", 541 fullpath.c_str()); 542 goto done; 543 } 544 545 if (gelf_getshdr(scn, &sh) != &sh) { 546 warnx("WARNING: Could not retrieve section header for \"%s\".", 547 fullpath.c_str()); 548 goto done; 549 } 550 551 if (sh.sh_flags & SHF_EXECINSTR) { 552 start = std::min(start, sh.sh_addr); 553 end = std::max(end, sh.sh_addr + sh.sh_size); 554 } 555 556 // Check if dwarf is embedded 557 scname = elf_strptr(e, shstrndx, sh.sh_name); 558 if (scname != NULL && strcmp(scname, ".debug_info") == 0) 559 im.dwarf = fullpath; 560 } 561 562 im.start = start; 563 im.end = end; 564 im.binary = path; 565 im.path = fullpath; 566 im.name = basename(fullpath); 567 568 /* 569 * If the dwarf symbols aren't embedded check: 570 * 1. SYSROOT + /path + .debug 571 * 2. SYSROOT + /usr/lib/debug + /path + .debug 572 */ 573 if (im.dwarf == "") { 574 std::string sympath = fullpath + ".debug"; 575 if (access(sympath.c_str(), R_OK) == 0) { 576 im.dwarf = sympath; 577 } 578 } 579 if (im.dwarf == "") { 580 std::string sympath = sysroot + "/usr/lib/debug" + path + ".debug"; 581 if (access(sympath.c_str(), R_OK) == 0) { 582 im.dwarf = sympath; 583 } 584 } 585 586 images[path] = im; 587 588 done: 589 elf_end(e); 590 close(fd); 591 592 return (im); 593 } 594 595 void 596 pmcview::loadsymboltable(image *im, Elf *e, Elf_Scn *scn, GElf_Shdr *sh) 597 { 598 size_t n, nsyms; 599 char *fname; 600 GElf_Sym sym; 601 Elf_Data *data; 602 syminfo si; 603 604 si = syminfo(); 605 606 if ((data = elf_getdata(scn, nullptr)) == nullptr) 607 return; 608 609 nsyms = sh->sh_size / sh->sh_entsize; 610 611 for (n = 0; n < nsyms; n++) { 612 if (gelf_getsym(data, (int) n, &sym) != &sym) 613 return; 614 615 // XXX: We should load globals as well 616 if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) 617 continue; 618 619 if (sym.st_shndx == STN_UNDEF) 620 continue; 621 622 if ((fname = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL) 623 continue; 624 625 // XXX: Extra checks to make sure we don't get corrupted 626 for (int i = 0; fname[i] != 0 && i < 32; i++) { 627 if (!isascii(fname[i])) { 628 printf("EEEK SYMBOL\n"); 629 printf("%s\n", fname); 630 assert(false); 631 } 632 } 633 634 si.offset = sym.st_value; 635 si.length = sym.st_size; 636 si.binary = im->name; 637 si.name = fname; 638 639 im->symbols[si.offset] = si; 640 } 641 } 642 643 void 644 pmcview::loadsymbols(image *im) 645 { 646 int i; 647 int fd; 648 Elf *e; 649 Elf_Scn *scn; 650 GElf_Ehdr eh; 651 GElf_Shdr sh; 652 653 if (access(im->path.c_str(), R_OK) != 0) 654 return; 655 656 fd = open(im->path.c_str(), O_RDONLY, 0); 657 if (fd < 0) { 658 warnx("WARNING: Cannot open \"%s\".", 659 im->path.c_str()); 660 return; 661 } 662 663 e = elf_begin(fd, ELF_C_READ, NULL); 664 if (e == NULL) { 665 warnx("WARNING: Cannot read \"%s\".", 666 im->path.c_str()); 667 close(fd); 668 return; 669 } 670 671 if (gelf_getehdr(e, &eh) != &eh) { 672 warnx("WARNING: Cannot read the ELF header for \"%s\": %s.", 673 im->path.c_str(), elf_errmsg(-1)); 674 goto done; 675 } 676 677 for (i = 0; i < eh.e_shnum; i++) { 678 scn = elf_getscn(e, i); 679 if (scn == NULL) { 680 warnx("WARNING: Could not retrieve section descriptor for \"%s\".", 681 im->path.c_str()); 682 goto done; 683 } 684 685 if (gelf_getshdr(scn, &sh) != &sh) { 686 warnx("WARNING: Could not retrieve section header for \"%s\".", 687 im->path.c_str()); 688 goto done; 689 } 690 691 if (sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM) { 692 loadsymboltable(im, e, scn, &sh); 693 } 694 } 695 696 done: 697 elf_end(e); 698 close(fd); 699 } 700 701 void 702 pmcview::mapimage(int pid, const image &im, uint64_t start) 703 { 704 uint64_t offset; 705 vmmap map; 706 707 // Ignore empty images 708 if (im.start == 0 && im.end == 0) 709 return; 710 711 /* 712 * XXX: Need to adjust the address for the PowerPC kernel that is 713 * dynamic. Wonder if we can fix this elsewhere, because we should need 714 * a way to deal with this for KASLR?. 715 */ 716 717 offset = start - im.vaddr; 718 map.lowpc = im.start + offset; 719 map.highpc = im.end + offset; 720 map.image = im.binary; 721 722 procs[pid].map[start] = map; 723 } 724 725 void 726 pmcview::process(struct pmclog_ev_map_in &p) 727 { 728 // Kernel map-in events should be mapped to pid 0 729 pid_t pid = (p.pl_pid == -1) ? 0 : p.pl_pid; 730 731 image im = loadimage(p.pl_pathname); 732 733 mapimage(pid, im, p.pl_start); 734 } 735 736 void 737 pmcview::process(struct pmclog_ev_map_out &p) 738 { 739 // Kernel map-in events should be mapped to pid 0 740 pid_t pid = (p.pl_pid == -1) ? 0 : p.pl_pid; 741 procinfo &proc = procs[pid]; 742 743 /* 744 * XXX: We should handle all the mmap/munmap cases but only executable 745 * file mappings are included. 746 */ 747 proc.map.erase(p.pl_start); 748 } 749 750 void 751 pmcview::process(struct pmclog_ev_callchain &p) 752 { 753 int i; 754 uint8_t *hdr = (uint8_t *)&p.pl_pc[0]; 755 uint8_t type, len; 756 uintfptr_t *cc = &p.pl_pc[1]; 757 ibsfetchinfo ibsf; 758 ibsopinfo ibso; 759 760 /* 761 * Callchain events are always attributed to one of the kernel 762 * processes. Make sure nobody breaks our assumption. 763 */ 764 assert(p.pl_pid != ~(uint32_t)0); 765 766 ibsf.len = 0; 767 ibso.len = 0; 768 769 if (p.pl_cpuflags & PMC_CC_F_MULTIPART) { 770 for (i = 0; i < 4; i++) { 771 type = hdr[2 * i]; 772 len = hdr[2 * i + 1]; 773 774 switch (type) { 775 case PMC_CC_MULTIPART_IBS_FETCH: 776 ibsf.len = len; 777 ibsf.ctl = cc[PMC_MPIDX_FETCH_CTL]; 778 ibsf.extctl = cc[PMC_MPIDX_FETCH_EXTCTL]; 779 ibsf.linaddr = cc[PMC_MPIDX_FETCH_LINADDR]; 780 ibsf.physaddr = cc[PMC_MPIDX_FETCH_PHYSADDR]; 781 break; 782 case PMC_CC_MULTIPART_IBS_OP: 783 ibso.len = len; 784 ibso.ctl = cc[PMC_MPIDX_OP_CTL]; 785 ibso.rip = cc[PMC_MPIDX_OP_RIP]; 786 ibso.data = cc[PMC_MPIDX_OP_DATA]; 787 ibso.data2 = cc[PMC_MPIDX_OP_DATA2]; 788 ibso.data3 = cc[PMC_MPIDX_OP_DATA3]; 789 ibso.linaddr = cc[PMC_MPIDX_OP_DC_LINADDR]; 790 ibso.physaddr = cc[PMC_MPIDX_OP_DC_PHYSADDR]; 791 ibso.tgtrip = cc[PMC_MPIDX_OP_TGT_RIP]; 792 ibso.data4 = cc[PMC_MPIDX_OP_DATA4]; 793 break; 794 } 795 796 cc += len; 797 } 798 } 799 800 /* 801 * Discard delayed events so we do not get unattributed samples. 802 */ 803 auto pinfo = procs.find(p.pl_pid); 804 if (pinfo == procs.end()) 805 return; 806 807 // Filter on pid, tid, program, thread and events 808 if (filter.filterpid && filter.pids.count(p.pl_pid) == 0) 809 return; 810 if (filter.filtertid && filter.tids.count(p.pl_tid) == 0) 811 return; 812 if (filter.filterprogram && filter.programs.count(pinfo->second.name) == 0) 813 return; 814 if (filter.filterthread) { 815 auto tinfo = pinfo->second.threads.find(p.pl_tid); 816 if (tinfo == pinfo->second.threads.end()) 817 return; 818 if (filter.threads.count(tinfo->second.name) == 0) 819 return; 820 } 821 if (filter.filterevent) { 822 auto pmc = pmcid.find(p.pl_pmcid); 823 if (pmc == pmcid.end()) 824 return; 825 if (filter.events.count(pmcinfo[pmc->second].name) == 0) 826 return; 827 } 828 829 // Filter on cpuset 830 int cpunum = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(p.pl_cpuflags); 831 if (filter.filtercpu && !CPU_ISSET(cpunum, &filter.cpus)) { 832 return; 833 } 834 835 // Filter on user/kernel mode 836 int usermode = PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(p.pl_cpuflags); 837 if (filter.useronly && usermode == 0) { 838 return; 839 } 840 if (filter.kernelonly && usermode != 0) { 841 return; 842 } 843 844 // Advanced filters for AMD IBS 845 if (ibsf.len) { 846 if (filter.ibs_ldlat > IBS_FETCH_CTL_TO_LAT(ibsf.ctl)) 847 return; 848 callchain(p, ibsf, cc, len); 849 } else if (ibso.len) { 850 if (filter.ibs_oplat > IBS_OP_DATA_TO_COMPTORET(ibso.data)) 851 return; 852 if (filter.ibs_ldlat > IBS_OP_DATA3_TO_DCLAT(ibso.data3)) 853 return; 854 if (filter.ibs_mmio) { 855 if (((ibso.data3 & IBS_OP_DATA3_STORE) == 0) && 856 ((ibso.data3 & IBS_OP_DATA3_LOAD) == 0)) 857 return; 858 if (((ibso.data3 & IBS_OP_DATA3_UCMEMACCESS) == 0) && 859 ((ibso.data3 & IBS_OP_DATA3_WCMEMACCESS) == 0)) 860 return; 861 } 862 callchain(p, ibso, cc, len); 863 } else { 864 callchain(p, cc, len); 865 } 866 } 867 868 uint32_t 869 pmcview::pmcidtoeventid(uint32_t id) 870 { 871 return pmcid[id]; 872 } 873 874 std::string 875 pmcview::pidtoname(pid_t pid) 876 { 877 auto p = procs.find(pid); 878 if (p == procs.end()) 879 return (""); 880 else 881 return (p->second.name); 882 } 883 884 syminfo 885 pmcview::addrtosymbol(pid_t pid, uint64_t addr) 886 { 887 syminfo si; 888 procinfo &proc = procs[pid]; 889 vmmap *vm; 890 image *im; 891 892 im = nullptr; 893 auto v = proc.map.upper_bound(addr); 894 if (v != proc.map.begin()) { 895 v--; 896 if (v->second.lowpc <= addr && v->second.highpc >= addr) { 897 vm = &v->second; 898 im = &images[v->second.image]; 899 } 900 } 901 902 if (im == nullptr) { 903 #ifdef DEBUG_VIEW 904 printf("Symbol not found for 0x%lx in %s\n", addr, proc.name.c_str()); 905 printvm(pid); 906 #endif 907 return syminfo(); 908 } 909 910 // Adjust address into the ELF's virtual address space 911 addr -= vm->lowpc - im->start; 912 913 // Load symbols if they haven't been loaded 914 if (im->symbols.size() == 0) { 915 loadsymbols(im); 916 } 917 918 // Look for the first symbol 919 auto s = im->symbols.upper_bound(addr); 920 if (s != im->symbols.begin()) 921 s--; 922 if (s->second.offset <= addr && 923 (s->second.offset + s->second.length + 1) >= addr) { 924 si = s->second; 925 si.funcoff = addr - si.offset; 926 return si; 927 } 928 929 // Special case for assembly functions in the kernel 930 if (s->second.offset <= addr && s->second.length == 0 && pid == 0) { 931 si = s->second; 932 si.funcoff = addr - si.offset; 933 return si; 934 } 935 936 #ifdef DEBUG_VIEW 937 printf("Symbol not found in image %lx\n", addr); 938 printf("%lx %lx %s\n", s->second.offset, s->second.s_length, s->second.s_name.c_str()); 939 printf("%lx\n", addr); 940 #endif 941 942 si = syminfo(); 943 si.binary = im->name; 944 std::stringstream str = std::stringstream(); 945 str << "0x" << std::hex << addr; 946 si.name = str.str(); 947 return si; 948 } 949 950 void 951 pmcview::printvm(pid_t pid) 952 { 953 procinfo &proc = procs[pid]; 954 955 printf("%-18s %-18s %s\n", "Low PC", "High PC", "Image"); 956 for (auto &i : proc.map) { 957 printf("0x%08" PRIx64 " 0x%08" PRIx64 " %s\n", i.second.lowpc, 958 i.second.highpc, i.second.image.c_str()); 959 } 960 } 961 962