Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 28 Jul 2026 19:29:23 +0000
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        Ali Mashtizadeh <ali@mashtizadeh.com>
Subject:   git: 93da997ef759 - main - pmc: new pmc log processing framework
Message-ID:  <6a690313.43239.6961872a@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by imp:

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

commit 93da997ef759061670c1575eec92f922a645a5fd
Author:     Ali Mashtizadeh <ali@mashtizadeh.com>
AuthorDate: 2026-07-28 17:46:22 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
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 ***


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a690313.43239.6961872a>