git: 93da997ef759 - main - pmc: new pmc log processing framework

Warner Losh <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a690313.43239.6961872a__27907.0435486332$1785266991$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=93da997ef759061670c1575eec92f922a645a5fd

commit 93da997ef759061670c1575eec92f922a645a5fd
Author:     Ali Mashtizadeh <[email protected]>
AuthorDate: 2026-07-28 17:46:22 +0000
Commit:     Warner Losh <[email protected]>
CommitDate: 2026-07-28 19:27:44 +0000

    pmc: new pmc log processing framework
    
    View is a class for building PMC log processing tools it is designed to
    work with the new PMC record command that adds a header with additional
    CPU information.  The new framework processes PMC logs about 2.5 times
    faster and in about half the code as libpmcstat.
    
    Sponsored by: Netflix
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D57776
---
 usr.sbin/pmc/headers.hh |  82 +++++
 usr.sbin/pmc/util.cc    |  82 +++++
 usr.sbin/pmc/util.hh    |  39 ++
 usr.sbin/pmc/view.cc    | 960 ++++++++++++++++++++++++++++++++++++++++++++++++
 usr.sbin/pmc/view.hh    | 411 +++++++++++++++++++++
 5 files changed, 1574 insertions(+)

diff --git a/usr.sbin/pmc/headers.hh b/usr.sbin/pmc/headers.hh
new file mode 100644
index 000000000000..29f76cd7cc2c
--- /dev/null
+++ b/usr.sbin/pmc/headers.hh
@@ -0,0 +1,82 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026, Netflix, Inc.
+ *
+ * This software was developed by Ali Mashtizadeh under the sponsorship from
+ * Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef __HEADERS_HH__
+#define __HEADERS_HH__
+
+#define PMC_HEADER_MAGIC	0x504D434C
+#define PMC_HEADER_VERSION	0x00
+
+#define PMC_ARCH_AMD64		0x01
+#define PMC_ARCH_ARM64		0x02
+#define PMC_ARCH_PPC64		0x03
+#define PMC_ARCH_RISCV64	0x04
+
+struct pmchdr_header
+{
+	uint32_t	magic;
+	uint16_t	version;
+	uint16_t	arch;
+};
+
+#define INFOHDR_TYPE_DONE	0x00
+#define INFOHDR_TYPE_SYSINFO	0x01
+#define INFOHDR_TYPE_PMCINFO	0x02
+#define INFOHDR_TYPE_CPUID	0x03
+
+struct pmchdr_infohdr
+{
+	uint8_t		type;
+	uint8_t		reserved0;
+	uint16_t	length;
+	uint32_t	_reserved1;
+};
+
+struct pmchdr_sysinfo
+{
+	char		cpumodel[64];
+	char		osrelease[64];
+	char		buildid[64];
+};
+
+struct pmchdr_pmcinfo
+{
+	uint64_t	rate;
+	char		pmc[];
+};
+
+struct pmchdr_cpuidinfo
+{
+	uint32_t	cpuid[];
+};
+
+#endif
+
diff --git a/usr.sbin/pmc/util.cc b/usr.sbin/pmc/util.cc
new file mode 100644
index 000000000000..b6e43b9dd130
--- /dev/null
+++ b/usr.sbin/pmc/util.cc
@@ -0,0 +1,82 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026, Netflix, Inc.
+ *
+ * This software was developed by Ali Mashtizadeh under the sponsorship from
+ * Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <string>
+#include <unordered_set>
+
+#include "util.hh"
+
+std::string
+basename(const std::string &path)
+{
+	size_t s;
+
+	s = path.rfind("/");
+	if (s == std::string::npos)
+		return (path);
+	else
+		return (path.substr(s + 1));
+}
+
+void
+split_and_insert(std::unordered_set<int> *set, const std::string &str)
+{
+	size_t pos = 0;
+
+	while (pos < str.length()) {
+		size_t end = str.find(",", pos);
+		if (end == str.npos) {
+			set->insert(std::stoi(str.substr(pos)));
+			break;
+		}
+
+		set->insert(std::stoi(str.substr(pos, end - pos)));
+		pos = end + 1;
+	}
+}
+
+void
+split_and_insert(std::unordered_set<std::string> *set, const std::string &str)
+{
+	size_t pos = 0;
+
+	while (pos < str.length()) {
+		size_t end = str.find(",", pos);
+		if (end == str.npos) {
+			set->insert(str.substr(pos));
+			break;
+		}
+
+		set->insert(str.substr(pos, end - pos));
+		pos = end + 1;
+	}
+}
+
diff --git a/usr.sbin/pmc/util.hh b/usr.sbin/pmc/util.hh
new file mode 100644
index 000000000000..03c350721b2b
--- /dev/null
+++ b/usr.sbin/pmc/util.hh
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026, Netflix, Inc.
+ *
+ * This software was developed by Ali Mashtizadeh under the sponsorship from
+ * Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+std::string basename(const std::string &path);
+void split_and_insert(std::unordered_set<int> *set, const std::string &input);
+void split_and_insert(std::unordered_set<std::string> *set, const std::string &input);
+
+#endif /* __UTIL_HH__ */
+
diff --git a/usr.sbin/pmc/view.cc b/usr.sbin/pmc/view.cc
new file mode 100644
index 000000000000..352856939a66
--- /dev/null
+++ b/usr.sbin/pmc/view.cc
@@ -0,0 +1,960 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026, Netflix, Inc.
+ *
+ * This software was developed by Ali Mashtizadeh under the sponsorship from
+ * Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <pmclog.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <cxxabi.h>
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <sstream>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <dev/hwpmc/hwpmc_ibs.h>
+#include "util.hh"
+#include "view.hh"
+
+std::string
+syminfo::to_string(bool show_line)
+{
+	int status;
+	char *demangled;
+	std::stringstream ss;
+
+	if (name == "") {
+		ss << "0x" << std::hex << offset;
+	} else if (name.length() >= 2 && name[0] == '_' && name[1] == 'Z') {
+		demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
+		if (status == 0) {
+			ss << demangled;
+			free(demangled);
+		} else {
+			ss << name;
+		}
+	} else {
+		ss << name;
+	}
+
+	if (!show_line)
+		return ss.str();
+
+	if (line) {
+		ss << ":" << std::dec << line;
+	} else if (funcoff) {
+		ss << "+0x" << std::hex << funcoff; 
+	}
+
+	return ss.str();
+}
+
+pmcview::pmcview() : tscfreq(0), pmcid(), pmcinfo(), procs(), tidtopid(),
+    images(), sysroot(""), filter()
+{
+	char *root;
+
+	root = getenv("SYSROOT");
+	if (root) {
+		sysroot = root;
+	}
+}
+
+pmcview::~pmcview()
+{
+}
+
+void
+pmcview::setfilter(const pmcfilter &pmcfilter)
+{
+	filter = pmcfilter;
+}
+
+static int
+readlog(int logfd, void *buf, size_t len)
+{
+	int status;
+	char *cur = (char *)buf;
+	size_t left = len;
+
+	while (left != 0) {
+		status = read(logfd, cur, left);
+		if (status < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			else
+				return status;
+		}
+		if (status == 0)
+			return status;
+
+		cur += status;
+		left -= status;
+	}
+
+	return len;
+}
+
+int
+pmcview::process_sysinfo(int logfd, const pmchdr_infohdr &infohdr)
+{
+	int status;
+	pmchdr_sysinfo sysinfo;
+
+	if (infohdr.length != sizeof(sysinfo))
+		errx(EX_IOERR, "PMC log headers have an unexpected size");
+
+	status = readlog(logfd, &sysinfo, sizeof(sysinfo));
+	if (status < 0 || status != infohdr.length)
+		errx(EX_IOERR, "readlog");
+
+	cpumodel = sysinfo.cpumodel;
+	osrelease = sysinfo.osrelease;
+	buildid = sysinfo.buildid;
+
+	return 0;
+}
+
+int
+pmcview::process_pmcinfo(int logfd, const pmchdr_infohdr &infohdr)
+{
+	int status;
+	union {
+		pmchdr_pmcinfo pmcinfo;
+		__unused char reserve_max_size[256];
+	};
+
+	status = readlog(logfd, &pmcinfo, infohdr.length);
+	if (status < 0 || status != infohdr.length)
+		errx(EX_IOERR, "readlog");
+
+	extpmcinfo.emplace_back(pmcinfo.rate, std::string(pmcinfo.pmc));
+
+	return 0;
+}
+
+int
+pmcview::process_cpuidinfo(int logfd, const pmchdr_infohdr &infohdr)
+{
+	int status;
+	pmchdr_cpuidinfo *cpuidinfo;
+	int offset, len;
+	uint32_t root, maxleaf, count;
+
+	len = infohdr.length / 4;
+	cpuidinfo = (pmchdr_cpuidinfo *)new uint32_t[len];
+
+	status = readlog(logfd, cpuidinfo, infohdr.length);
+	if (status < 0 || status != infohdr.length)
+		errx(EX_IOERR, "readlog");
+
+	offset = 0;
+
+	while (offset < len) {
+		maxleaf = cpuidinfo->cpuid[offset];
+
+		/*
+		 * In x86 the roots contains the maximum leaf number present.
+		 */
+		root = maxleaf & 0xFFFF0000;
+		count = maxleaf & 0x0000FFFF;
+
+		for (uint32_t i = 0; i <= count; i++) {
+			cpuid[root + i] = { cpuidinfo->cpuid[4 * i + offset],
+			    cpuidinfo->cpuid[4 * i + 1 + offset],
+			    cpuidinfo->cpuid[4 * i + 2 + offset],
+			    cpuidinfo->cpuid[4 * i + 3 + offset] };
+		}
+
+		offset += 4 * (count + 1);
+	}
+
+	delete[] cpuidinfo;
+
+	return 0;
+}
+
+void
+pmcview::process(int logfd)
+{
+	int status;
+	pmchdr_header hdr;
+	struct pmclog_parse_state *ps;
+
+	// Read header
+	status = readlog(logfd, &hdr, sizeof(hdr));
+	if (status < 0 || status != sizeof(hdr))
+		err(EX_IOERR, "readlog");
+	if (hdr.magic != PMC_HEADER_MAGIC)
+		errx(EX_DATAERR, "PMC log magic mismatch!");
+	if (hdr.version > PMC_HEADER_VERSION)
+		errx(EX_DATAERR, "PMC log version newer than supported!");
+
+	// Save architecture
+	arch = hdr.arch;
+
+	while (1) {
+		pmchdr_infohdr infohdr;
+
+		status = readlog(logfd, &infohdr, sizeof(infohdr));
+		if (status < 0 || status != sizeof(infohdr))
+			errx(EX_IOERR, "readlog");
+
+		if (infohdr.type == INFOHDR_TYPE_DONE) {
+			break;
+		} else if (infohdr.type == INFOHDR_TYPE_SYSINFO) {
+			process_sysinfo(logfd, infohdr);
+		} else if (infohdr.type == INFOHDR_TYPE_PMCINFO) {
+			process_pmcinfo(logfd, infohdr);
+		} else if (infohdr.type == INFOHDR_TYPE_CPUID) {
+			process_cpuidinfo(logfd, infohdr);
+		}
+	}
+
+	ps = static_cast<struct pmclog_parse_state*>(pmclog_open(logfd));
+	if (ps == NULL) {
+		errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n",
+		    strerror(errno));
+	}
+
+	process(ps);
+
+	pmclog_close(ps);
+}
+
+void
+pmcview::process(struct pmclog_ev &p)
+{
+	switch (p.pl_type) {
+	case PMCLOG_TYPE_INITIALIZE:
+		process(p.pl_u.pl_i);
+		return;
+	case PMCLOG_TYPE_CLOSELOG:
+		process(p.pl_u.pl_cl);
+		return;
+	case PMCLOG_TYPE_PMCALLOCATE:
+		process(p.pl_u.pl_a);
+		return;
+	case PMCLOG_TYPE_PMCALLOCATEDYN:
+		process(p.pl_u.pl_ad);
+		return;
+	case PMCLOG_TYPE_PROC_CREATE:
+		process(p.pl_u.pl_pc);
+		return;
+	case PMCLOG_TYPE_PROCEXIT:
+		process(p.pl_u.pl_e);
+		return;
+	case PMCLOG_TYPE_PROCEXEC:
+		process(p.pl_u.pl_x);
+		return;
+	case PMCLOG_TYPE_PROCFORK:
+		process(p.pl_u.pl_f);
+		return;
+	case PMCLOG_TYPE_SYSEXIT:
+		process(p.pl_u.pl_se);
+		return;
+	case PMCLOG_TYPE_MAP_IN:
+		process(p.pl_u.pl_mi);
+		return;
+	case PMCLOG_TYPE_MAP_OUT:
+		process(p.pl_u.pl_mo);
+		return;
+	case PMCLOG_TYPE_THR_CREATE:
+		process(p.pl_u.pl_tc);
+		return;
+	case PMCLOG_TYPE_THR_EXIT:
+		process(p.pl_u.pl_te);
+		return;
+	case PMCLOG_TYPE_CALLCHAIN:
+		process(p.pl_u.pl_cc);
+		return;
+	case PMCLOG_TYPE_PMCATTACH: [[fallthrough]];
+	case PMCLOG_TYPE_PMCDETACH:
+	case PMCLOG_TYPE_USERDATA:
+	case PMCLOG_TYPE_PROCCSW:
+	case PMCLOG_TYPE_DROPNOTIFY:
+		return;
+	};
+}
+
+void
+pmcview::process(struct pmclog_parse_state *p)
+{
+	struct pmclog_ev ev;
+
+	while (pmclog_read(p, &ev) == 0) {
+		process(ev);
+	}
+}
+
+void
+pmcview::process(struct pmclog_ev_initialize &p)
+{
+	tscfreq = p.pl_tsc_freq;
+}
+
+void
+pmcview::process(__unused struct pmclog_ev_closelog &p)
+{
+}
+
+void
+pmcview::process(struct pmclog_ev_pmcallocate &p)
+{
+	pmcid[p.pl_pmcid] = p.pl_event;
+	if (pmcinfo.find(p.pl_event) == pmcinfo.end()) {
+		pmcinfo.emplace(p.pl_event, p);
+	}
+}
+
+void
+pmcview::process(struct pmclog_ev_pmcallocatedyn &p)
+{
+	pmcid[p.pl_pmcid] = p.pl_event;
+	if (pmcinfo.find(p.pl_event) == pmcinfo.end()) {
+		pmcinfo.emplace(p.pl_event, p);
+	}
+}
+
+void
+pmcview::process(struct pmclog_ev_proccreate &p)
+{
+	procs[p.pl_pid] = procinfo(p);
+
+	proccreate(p.pl_pid);
+}
+
+void
+pmcview::process(struct pmclog_ev_procexit &p)
+{
+	/*
+	 * XXX: Seems we can recieve samples after the process exits we need 
+	 * some way to delay cleanup, then after the delayed cleanup discard 
+	 * delayed events.
+	 */
+	procs.erase(p.pl_pid);
+
+	procexit(p.pl_pid);
+}
+
+void
+pmcview::process(struct pmclog_ev_procexec &p)
+{
+	procs[p.pl_pid].map.clear();
+	procs[p.pl_pid].name = basename(p.pl_pathname);
+	procs[p.pl_pid].fullpath = p.pl_pathname;
+	procs[p.pl_pid].baseaddr = p.pl_baseaddr;
+	procs[p.pl_pid].dynaddr = p.pl_dynaddr;
+
+	image im = loadimage(p.pl_pathname);
+
+	mapimage(p.pl_pid, im, im.vaddr + p.pl_dynaddr);
+
+	/*
+	 * Map the dynamic runtime loader
+	 */
+	if (im.isdynamic) {
+		image rtldim = loadimage(im.loader);
+
+		mapimage(p.pl_pid, rtldim, p.pl_baseaddr);
+	}
+
+	procexec(p.pl_pid);
+}
+
+void
+pmcview::process(struct pmclog_ev_procfork &p)
+{
+	procs[p.pl_newpid] = procs[p.pl_oldpid];
+
+	proccreate(p.pl_newpid);
+}
+
+void
+pmcview::process(struct pmclog_ev_sysexit &p)
+{
+	procs.erase(p.pl_pid);
+}
+
+void
+pmcview::process(struct pmclog_ev_threadcreate &p)
+{
+	procs[p.pl_pid].threads[p.pl_tid] = threadinfo(p.pl_tdname);
+	tidtopid[p.pl_tid] = p.pl_pid;
+}
+
+void
+pmcview::process(struct pmclog_ev_threadexit &p)
+{
+	pid_t pid = tidtopid[p.pl_tid];
+	procs[pid].threads.erase(p.pl_tid);
+	tidtopid.erase(p.pl_tid);
+}
+
+/*
+ * Load the ELF file to compute the values needed for the memory map and check 
+ * if the dwarf symbols are available.
+ */
+image
+pmcview::loadimage(const std::string &path)
+{
+	std::string fullpath;
+	image im;
+	GElf_Ehdr eh;
+	Elf *e;
+	const char *elf;
+	uint64_t start;
+	uint64_t end;
+	size_t shstrndx;
+	int fd, i;
+	bool foundexec;
+
+	if (images.find(path) != images.end())
+		return images[path];
+
+	im = image();
+
+	if (path == "unknown") {
+		return (im);
+	}
+
+	fullpath = sysroot + path;
+
+	if (access(fullpath.c_str(), R_OK) != 0)
+		return (im);
+
+	fd = open(fullpath.c_str(), O_RDONLY, 0);
+	if (fd < 0) {
+		warnx("WARNING: Cannot open \"%s\".",
+		    fullpath.c_str());
+		return (im);
+	}
+
+	e = elf_begin(fd, ELF_C_READ, NULL);
+	if (e == NULL) {
+		warnx("WARNING: Cannot read \"%s\".",
+		    fullpath.c_str());
+		close(fd);
+		return (im);
+	}
+
+	if (gelf_getehdr(e, &eh) != &eh) {
+		warnx("WARNING: Cannot read the ELF header for \"%s\": %s.",
+		    fullpath.c_str(), elf_errmsg(-1));
+		goto done;
+	}
+
+	if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN && eh.e_type != ET_REL) {
+		warnx("WARNING: ELF file type is unsupported for \"%s\".",
+		    fullpath.c_str());;
+		goto done;
+	}
+
+	elf = elf_rawfile(e, NULL);
+	if (elf == NULL) {
+		warnx("WARNING: Cannot read the ELF file for \"%s\": %s.",
+		    fullpath.c_str(), elf_errmsg(-1));
+		goto done;
+	}
+
+	if (eh.e_type != ET_REL) {
+		foundexec = false;
+		for (i = 0; i < eh.e_phnum; i++) {
+			GElf_Phdr ph;
+
+			if (gelf_getphdr(e, i, &ph) != &ph) {
+				warnx("WARNING: Cannot read program header for \"%s\": %s.",
+				    fullpath.c_str(), elf_errmsg(-1));
+				goto done;
+			}
+
+			if (ph.p_type == PT_DYNAMIC) {
+				im.isdynamic = 1;
+				continue;
+			}
+
+			if (ph.p_type == PT_INTERP) {
+				im.loader = elf + ph.p_offset;
+			}
+
+			if (ph.p_type == PT_LOAD) {
+				if ((ph.p_flags & PF_X) != 0 && !foundexec) {
+					im.vaddr = ph.p_vaddr & ~(ph.p_align - 1);
+					foundexec = true;
+				}
+			}
+		}
+	}
+
+	elf_getshdrstrndx(e, &shstrndx);
+
+	start = ~(0x0ULL);
+	end = 0;
+	for (i = 0; i < eh.e_shnum; i++) {
+		GElf_Shdr sh;
+		Elf_Scn *scn;
+		char *scname;
+
+		scn = elf_getscn(e, i);
+		if (scn == NULL) {
+			warnx("WARNING: Could not retrieve section descriptor for \"%s\".",
+			    fullpath.c_str());
+			goto done;
+		}
+
+		if (gelf_getshdr(scn, &sh) != &sh) {
+			warnx("WARNING: Could not retrieve section header for \"%s\".",
+			    fullpath.c_str());
+			goto done;
+		}
+
+		if (sh.sh_flags & SHF_EXECINSTR) {
+			start = std::min(start, sh.sh_addr);
+			end = std::max(end, sh.sh_addr + sh.sh_size);
+		}
+
+		// Check if dwarf is embedded
+		scname = elf_strptr(e, shstrndx, sh.sh_name);
+		if (scname != NULL && strcmp(scname, ".debug_info") == 0)
+			im.dwarf = fullpath;
+	}
+
+	im.start = start;
+	im.end = end;
+	im.binary = path;
+	im.path = fullpath;
+	im.name = basename(fullpath);
+
+	/*
+	 * If the dwarf symbols aren't embedded check:
+	 *  1. SYSROOT + /path + .debug
+	 *  2. SYSROOT + /usr/lib/debug + /path + .debug
+	 */
+	if (im.dwarf == "") {
+		std::string sympath = fullpath + ".debug";
+		if (access(sympath.c_str(), R_OK) == 0) {
+			im.dwarf = sympath;
+		}
+	}
+	if (im.dwarf == "") {
+		std::string sympath = sysroot + "/usr/lib/debug" + path + ".debug";
+		if (access(sympath.c_str(), R_OK) == 0) {
+			im.dwarf = sympath;
+		}
+	}
+
+	images[path] = im;
+
+done:
+	elf_end(e);
+	close(fd);
+
+	return (im);
+}
+
+void
+pmcview::loadsymboltable(image *im, Elf *e, Elf_Scn *scn, GElf_Shdr *sh)
+{
+	size_t n, nsyms;
+	char *fname;
+	GElf_Sym sym;
+	Elf_Data *data;
+	syminfo si;
+
+	si = syminfo();
+
+	if ((data = elf_getdata(scn, nullptr)) == nullptr)
+		return;
+
+	nsyms = sh->sh_size / sh->sh_entsize;
+
+	for (n = 0; n < nsyms; n++) {
+		if (gelf_getsym(data, (int) n, &sym) != &sym)
+			return;
+
+		// XXX: We should load globals as well
+		if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
+			continue;
+
+		if (sym.st_shndx == STN_UNDEF)
+			continue;
+
+		if ((fname = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL)
+			continue;
+
+		// XXX: Extra checks to make sure we don't get corrupted
+		for (int i = 0; fname[i] != 0 && i < 32; i++) {
+			if (fname[i] < 0) {
+				printf("EEEK SYMBOL\n");
+				printf("%s\n", fname);
+				assert(false);
+			}
+		}
+
+		si.offset = sym.st_value;
+		si.length = sym.st_size;
+		si.binary = im->name;
+		si.name = fname;
+
+		im->symbols[si.offset] = si;
+        }
+}
+
+void
+pmcview::loadsymbols(image *im)
+{
+	int i;
+	int fd;
+	Elf *e;
+	Elf_Scn *scn;
+	GElf_Ehdr eh;
+	GElf_Shdr sh;
+
+	if (access(im->path.c_str(), R_OK) != 0)
+		return;
+
+	fd = open(im->path.c_str(), O_RDONLY, 0);
+	if (fd < 0) {
+		warnx("WARNING: Cannot open \"%s\".",
+		    im->path.c_str());
+		return;
+	}
+
+	e = elf_begin(fd, ELF_C_READ, NULL);
+	if (e == NULL) {
+		warnx("WARNING: Cannot read \"%s\".",
+		    im->path.c_str());
+		close(fd);
+		return;
+	}
+
+	if (gelf_getehdr(e, &eh) != &eh) {
+		warnx("WARNING: Cannot read the ELF header for \"%s\": %s.",
+		    im->path.c_str(), elf_errmsg(-1));
+		goto done;
+	}
+
+	for (i = 0; i < eh.e_shnum; i++) {
+		scn = elf_getscn(e, i);
+		if (scn == NULL) {
+			warnx("WARNING: Could not retrieve section descriptor for \"%s\".",
+			    im->path.c_str());
+			goto done;
+		}
+
+		if (gelf_getshdr(scn, &sh) != &sh) {
+			warnx("WARNING: Could not retrieve section header for \"%s\".",
+			    im->path.c_str());
+			goto done;
+		}
+
+		if (sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM) {
+			loadsymboltable(im, e, scn, &sh);
+		}
+	}
+
+done:
+	elf_end(e);
+	close(fd);
+}
+
+void
+pmcview::mapimage(int pid, const image &im, uint64_t start)
+{
+	uint64_t offset;
+	vmmap map;
+
+	// Ignore empty images
+	if (im.start == 0 && im.end == 0)
+		return;
+
+	/*
+	 * XXX: Need to adjust the address for the PowerPC kernel that is 
+	 * dynamic. Wonder if we can fix this elsewhere, because we should need 
+	 * a way to deal with this for KASLR?.
+	 */
+
+	offset = start - im.vaddr;
+	map.lowpc = im.start + offset;
+	map.highpc = im.end + offset;
+	map.image = im.binary;
+
+	procs[pid].map[start] = map;
+}
+
+void
+pmcview::process(struct pmclog_ev_map_in &p)
+{
+	// Kernel map-in events should be mapped to pid 0
+	pid_t pid = (p.pl_pid == -1) ? 0 : p.pl_pid;
+
+	image im = loadimage(p.pl_pathname);
+
*** 646 LINES SKIPPED ***
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.