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
to_string(bool show_line)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
pmcview()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
~pmcview()103 pmcview::~pmcview()
104 {
105 }
106
107 void
setfilter(const pmcfilter & pmcfilter)108 pmcview::setfilter(const pmcfilter &pmcfilter)
109 {
110 filter = pmcfilter;
111 }
112
113 static int
readlog(int logfd,void * buf,size_t len)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
process_sysinfo(int logfd,const pmchdr_infohdr & infohdr)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
process_pmcinfo(int logfd,const pmchdr_infohdr & infohdr)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
process_cpuidinfo(int logfd,const pmchdr_infohdr & infohdr)177 pmcview::process_cpuidinfo(int logfd, const pmchdr_infohdr &infohdr)
178 {
179 int status;
180 pmchdr_cpuidinfo *cpuidinfo;
181 int offset, len;
182 uint32_t root, maxleaf, count;
183
184 len = infohdr.length / 4;
185 cpuidinfo = (pmchdr_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->cpuid[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->cpuid[4 * i + offset],
204 cpuidinfo->cpuid[4 * i + 1 + offset],
205 cpuidinfo->cpuid[4 * i + 2 + offset],
206 cpuidinfo->cpuid[4 * i + 3 + offset] };
207 }
208
209 offset += 4 * (count + 1);
210 }
211
212 delete[] cpuidinfo;
213
214 return 0;
215 }
216
217 void
process(int logfd)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
process(struct pmclog_ev & p)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
process(struct pmclog_parse_state * p)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
process(struct pmclog_ev_initialize & p)331 pmcview::process(struct pmclog_ev_initialize &p)
332 {
333 tscfreq = p.pl_tsc_freq;
334 }
335
336 void
process(__unused struct pmclog_ev_closelog & p)337 pmcview::process(__unused struct pmclog_ev_closelog &p)
338 {
339 }
340
341 void
process(struct pmclog_ev_pmcallocate & p)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
process(struct pmclog_ev_pmcallocatedyn & p)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
process(struct pmclog_ev_proccreate & p)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
process(struct pmclog_ev_procexit & p)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
process(struct pmclog_ev_procexec & p)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
process(struct pmclog_ev_procfork & p)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
process(struct pmclog_ev_sysexit & p)414 pmcview::process(struct pmclog_ev_sysexit &p)
415 {
416 procs.erase(p.pl_pid);
417 }
418
419 void
process(struct pmclog_ev_threadcreate & p)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
process(struct pmclog_ev_threadexit & p)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
loadimage(const std::string & path)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
loadsymboltable(image * im,Elf * e,Elf_Scn * scn,GElf_Shdr * sh)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
loadsymbols(image * im)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
mapimage(int pid,const image & im,uint64_t start)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
process(struct pmclog_ev_map_in & p)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
process(struct pmclog_ev_map_out & p)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
process(struct pmclog_ev_callchain & p)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
pmcidtoeventid(uint32_t id)869 pmcview::pmcidtoeventid(uint32_t id)
870 {
871 return pmcid[id];
872 }
873
874 std::string
pidtoname(pid_t pid)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
addrtosymbol(pid_t pid,uint64_t addr)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
printvm(pid_t pid)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