aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJessica Clarke <jrtc27@FreeBSD.org>2023-06-07 14:21:18 +0000
committerJessica Clarke <jrtc27@FreeBSD.org>2023-06-07 14:24:29 +0000
commit21f7397a61f7bff61a1221cc6340cd980a922540 (patch)
tree1b2960e78f22a93c849fe84ff6449dde4e355ec4
parent296a0987be590e73784e4c313d28a61ff310f1a3 (diff)
downloadsrc-21f7397a61f7bff61a1221cc6340cd980a922540.tar.gz
src-21f7397a61f7bff61a1221cc6340cd980a922540.zip
libpmc: Handle PMCALLOCATE log with PMC code on PMU event system
On an arm64 system that reports as a Cortex A72 r0p3, running pmcstat -P CPU_CYCLES command works, but pmcstat -P cpu-cycles command does not. This is because the former uses the PMU event from the JSON source, resulting in pl_event in the log event being a small index (here, 5) into the generated events table, whilst the latter does not match any of the JSON events and falls back on PMC's own tables, mapping it to the PMC event 0x14111, i.e. PMC_EV_ARMV8_EVENT_11H. Then, when libpmc gets the PMCALLOCATE event, it tries to use the event as an index into the JSON-derived table, but doing so only makes sense for the former, whilst for the latter it will go way out of bounds and either read junk (which may trigger the != NULL assertion) or segfault. As far as I can tell we don't have anything lying around to tell us which of the two cases we're in, but we can exploit the fact that the first 0x1000 PMC event codes are reserved, and that none of our PMU events tables reach that number of entries yet. PR: 268857 Reviewed by: mhorne MFC after: 1 month Differential Revision: https://reviews.freebsd.org/D39592
-rw-r--r--lib/libpmc/libpmc.c9
-rw-r--r--lib/libpmc/pmclog.c27
2 files changed, 29 insertions, 7 deletions
diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c
index 57b9c90de256..003d9615a44a 100644
--- a/lib/libpmc/libpmc.c
+++ b/lib/libpmc/libpmc.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/pmc.h>
#include <sys/syscall.h>
+#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <err.h>
@@ -1084,8 +1085,14 @@ pmc_allocate(const char *ctrspec, enum pmc_mode mode,
r = spec_copy = strdup(ctrspec);
ctrname = strsep(&r, ",");
if (pmc_pmu_enabled()) {
- if (pmc_pmu_pmcallocate(ctrname, &pmc_config) == 0)
+ if (pmc_pmu_pmcallocate(ctrname, &pmc_config) == 0) {
+ /*
+ * XXX: pmclog_get_event exploits this to disambiguate
+ * PMU from PMC event codes in PMCALLOCATE events.
+ */
+ assert(pmc_config.pm_ev < PMC_EVENT_FIRST);
goto found;
+ }
/* Otherwise, reset any changes */
pmc_config.pm_ev = 0;
diff --git a/lib/libpmc/pmclog.c b/lib/libpmc/pmclog.c
index 0db91cf51bc2..92bcb1c2b161 100644
--- a/lib/libpmc/pmclog.c
+++ b/lib/libpmc/pmclog.c
@@ -356,12 +356,27 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_flags);
PMCLOG_READ32(le,noop);
PMCLOG_READ64(le,ev->pl_u.pl_a.pl_rate);
- ev->pl_u.pl_a.pl_evname = pmc_pmu_event_get_by_idx(ps->ps_cpuid, ev->pl_u.pl_a.pl_event);
- if (ev->pl_u.pl_a.pl_evname != NULL)
- break;
- else if ((ev->pl_u.pl_a.pl_evname =
- _pmc_name_of_event(ev->pl_u.pl_a.pl_event, ps->ps_arch))
- == NULL) {
+
+ /*
+ * Could be either a PMC event code or a PMU event index;
+ * assume that their encodings don't overlap (i.e. no PMU event
+ * table is more than 0x1000 entries) to distinguish them here.
+ * Otherwise pmc_pmu_event_get_by_idx will go out of bounds if
+ * given a PMC event code when it knows about that CPU.
+ *
+ * XXX: Ideally we'd have user flags to give us that context.
+ */
+ if (ev->pl_u.pl_a.pl_event < PMC_EVENT_FIRST)
+ ev->pl_u.pl_a.pl_evname =
+ pmc_pmu_event_get_by_idx(ps->ps_cpuid,
+ ev->pl_u.pl_a.pl_event);
+ else if (ev->pl_u.pl_a.pl_event <= PMC_EVENT_LAST)
+ ev->pl_u.pl_a.pl_evname =
+ _pmc_name_of_event(ev->pl_u.pl_a.pl_event,
+ ps->ps_arch);
+ else
+ ev->pl_u.pl_a.pl_evname = NULL;
+ if (ev->pl_u.pl_a.pl_evname == NULL) {
printf("unknown event\n");
goto error;
}