xref: /freebsd/usr.sbin/pmc/view.cc (revision 82a3337952b922c48c55d1359ab0472f1d116c7a)
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