System wide and NUMA domain wide counters support. PMC classes for ARM DMC-620 and CMN-600.

Add support for system wide and NUMA domain wide counters support.
Add 3 new PMC classes for ARM DMC-620 and CMN-600 controllers PMU.

Reviewed by:	mhorne
Sponsored By:	ARM
Sponsored By:	Ampere Computing
Differential Revision: https://reviews.freebsd.org/D35342
This commit is contained in:
Aleksandr Rybalko 2022-02-16 00:17:02 +00:00 committed by Toomas Soome
parent 33a0803f00
commit b6e28991bf
3 changed files with 83 additions and 20 deletions

View file

@ -23,7 +23,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 10, 2021
.Dd May 28, 2022
.Dt PMC 3
.Os
.Sh NAME
@ -200,6 +200,8 @@ Supported capabilities include:
.Bl -tag -width "Li PMC_CAP_INTERRUPT" -compact
.It Li PMC_CAP_CASCADE
The ability to cascade counters.
.It Li PMC_CAP_DOMWIDE
Separate counters tied to each NUMA domain.
.It Li PMC_CAP_EDGE
The ability to count negated to asserted transitions of the hardware
conditions being probed for.
@ -218,6 +220,8 @@ The ability to read from performance counters.
.It Li PMC_CAP_SYSTEM
The ability to restrict counting of hardware events to when the CPU is
running privileged code.
.It Li PMC_CAP_SYSWIDE
A single counter aggregating events for the whole system.
.It Li PMC_CAP_THRESHOLD
The ability to ignore simultaneous hardware events below a
programmable threshold.

View file

@ -171,7 +171,10 @@ enum pmc_cputype {
__PMC_CLASS(MIPS74K, 0x12, "MIPS 74K") \
__PMC_CLASS(E500, 0x13, "Freescale e500 class") \
__PMC_CLASS(BERI, 0x14, "MIPS BERI") \
__PMC_CLASS(POWER8, 0x15, "IBM POWER8 class")
__PMC_CLASS(POWER8, 0x15, "IBM POWER8 class") \
__PMC_CLASS(DMC620_PMU_CD2, 0x16, "ARM DMC620 Memory Controller PMU CLKDIV2") \
__PMC_CLASS(DMC620_PMU_C, 0x17, "ARM DMC620 Memory Controller PMU CLK") \
__PMC_CLASS(CMN600_PMU, 0x18, "Arm CoreLink CMN600 Coherent Mesh Network PMU")
enum pmc_class {
#undef __PMC_CLASS
@ -180,7 +183,7 @@ enum pmc_class {
};
#define PMC_CLASS_FIRST PMC_CLASS_TSC
#define PMC_CLASS_LAST PMC_CLASS_POWER8
#define PMC_CLASS_LAST PMC_CLASS_CMN600_PMU
/*
* A PMC can be in the following states:
@ -308,7 +311,9 @@ enum pmc_disp {
__PMC_CAP(QUALIFIER, 8, "further qualify monitored events") \
__PMC_CAP(PRECISE, 9, "perform precise sampling") \
__PMC_CAP(TAGGING, 10, "tag upstream events") \
__PMC_CAP(CASCADE, 11, "cascade counters")
__PMC_CAP(CASCADE, 11, "cascade counters") \
__PMC_CAP(SYSWIDE, 12, "system wide counter") \
__PMC_CAP(DOMWIDE, 13, "NUMA domain wide counter")
enum pmc_caps
{
@ -318,7 +323,7 @@ enum pmc_caps
};
#define PMC_CAP_FIRST PMC_CAP_INTERRUPT
#define PMC_CAP_LAST PMC_CAP_CASCADE
#define PMC_CAP_LAST PMC_CAP_DOMWIDE
/*
* PMC Event Numbers

View file

@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
#include <regex.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@ -116,6 +117,7 @@ static int pmcstat_kq;
static kvm_t *pmcstat_kvm;
static struct kinfo_proc *pmcstat_plist;
struct pmcstat_args args;
static bool libpmc_initialized = false;
static void
pmcstat_get_cpumask(const char *cpuspec, cpuset_t *cpumask)
@ -419,6 +421,22 @@ pmcstat_topexit(void)
endwin();
}
static inline void
libpmc_initialize(int *npmc)
{
if (libpmc_initialized)
return;
if (pmc_init() < 0)
err(EX_UNAVAILABLE, "ERROR: Initialization of the pmc(3)"
" library failed");
/* assume all CPUs are identical */
if ((*npmc = pmc_npmc(0)) < 0)
err(EX_OSERR, "ERROR: Cannot determine the number of PMCs on "
"CPU %d", 0);
libpmc_initialized = true;
}
/*
* Main
*/
@ -426,14 +444,14 @@ pmcstat_topexit(void)
int
main(int argc, char **argv)
{
cpuset_t cpumask, rootmask;
cpuset_t cpumask, dommask, rootmask;
double interval;
double duration;
int option, npmc;
int c, check_driver_stats;
int do_callchain, do_descendants, do_logproccsw, do_logprocexit;
int do_print, do_read, do_listcounters, do_descr;
int do_userspace;
int do_print, do_read, do_listcounters, do_descr, domains;
int do_userspace, i;
size_t len;
int graphdepth;
int pipefd[2], rfd;
@ -450,6 +468,7 @@ main(int argc, char **argv)
struct winsize ws;
struct stat sb;
char buffer[PATH_MAX];
uint32_t caps;
check_driver_stats = 0;
current_sampling_count = 0;
@ -460,6 +479,7 @@ main(int argc, char **argv)
do_logproccsw = 0;
do_logprocexit = 0;
do_listcounters = 0;
domains = 0;
use_cumulative_counts = 0;
graphfilename = "-";
args.pa_required = 0;
@ -489,8 +509,10 @@ main(int argc, char **argv)
bzero(&ds_end, sizeof(ds_end));
ev = NULL;
event = NULL;
caps = 0;
CPU_ZERO(&cpumask);
/* Default to using the running system kernel. */
len = 0;
if (sysctlbyname("kern.bootfile", NULL, &len, NULL, 0) == -1)
@ -500,6 +522,9 @@ main(int argc, char **argv)
errx(EX_SOFTWARE, "ERROR: Out of memory.");
if (sysctlbyname("kern.bootfile", args.pa_kernel, &len, NULL, 0) == -1)
err(EX_OSERR, "ERROR: Cannot determine path of running kernel");
len = sizeof(domains);
if (sysctlbyname("vm.ndomains", &domains, &len, NULL, 0) == -1)
err(EX_OSERR, "ERROR: Cannot get number of domains");
/*
* The initial CPU mask specifies the root mask of this process
@ -640,6 +665,7 @@ main(int argc, char **argv)
case 's': /* system-wide counting PMC */
case 'P': /* process virtual sampling PMC */
case 'S': /* system-wide sampling PMC */
caps = 0;
if ((ev = malloc(sizeof(*ev))) == NULL)
errx(EX_SOFTWARE, "ERROR: Out of memory.");
@ -707,12 +733,48 @@ main(int argc, char **argv)
errx(EX_SOFTWARE, "ERROR: Out of memory.");
(void) strncpy(ev->ev_name, optarg, c);
*(ev->ev_name + c) = '\0';
libpmc_initialize(&npmc);
if (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) {
if (pmc_allocate(ev->ev_spec, ev->ev_mode,
ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid,
ev->ev_count) < 0)
err(EX_OSERR, "ERROR: Cannot allocate "
"system-mode pmc with specification"
" \"%s\"", ev->ev_spec);
if (pmc_capabilities(ev->ev_pmcid, &caps)) {
pmc_release(ev->ev_pmcid);
err(EX_OSERR, "ERROR: Cannot get pmc "
"capabilities");
}
}
STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next);
if ((caps & PMC_CAP_SYSWIDE) == PMC_CAP_SYSWIDE)
break;
if ((caps & PMC_CAP_DOMWIDE) == PMC_CAP_DOMWIDE) {
CPU_ZERO(&cpumask);
/*
* Get number of domains and allocate one
* counter in each.
* First already allocated.
*/
for (i = 1; i < domains; i++) {
CPU_ZERO(&dommask);
cpuset_getaffinity(CPU_LEVEL_WHICH,
CPU_WHICH_DOMAIN, i, sizeof(dommask),
&dommask);
CPU_SET(CPU_FFS(&dommask) - 1, &cpumask);
}
args.pa_flags |= FLAGS_HAS_CPUMASK;
}
if (option == 's' || option == 'S') {
CPU_CLR(ev->ev_cpu, &cpumask);
pmc_id_t saved_pmcid = ev->ev_pmcid;
ev->ev_pmcid = PMC_ID_INVALID;
pmcstat_clone_event_descriptor(ev, &cpumask, &args);
ev->ev_pmcid = saved_pmcid;
CPU_SET(ev->ev_cpu, &cpumask);
}
@ -1050,17 +1112,8 @@ main(int argc, char **argv)
}
/* if we've been asked to process a log file, skip init */
if ((args.pa_flags & FLAG_READ_LOGFILE) == 0) {
if (pmc_init() < 0)
err(EX_UNAVAILABLE,
"ERROR: Initialization of the pmc(3) library failed"
);
if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
err(EX_OSERR,
"ERROR: Cannot determine the number of PMCs on CPU %d",
0);
}
if ((args.pa_flags & FLAG_READ_LOGFILE) == 0)
libpmc_initialize(&npmc);
/* Allocate a kqueue */
if ((pmcstat_kq = kqueue()) < 0)
@ -1134,7 +1187,8 @@ main(int argc, char **argv)
*/
STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
if (pmc_allocate(ev->ev_spec, ev->ev_mode,
if (ev->ev_pmcid == PMC_ID_INVALID &&
pmc_allocate(ev->ev_spec, ev->ev_mode,
ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid,
ev->ev_count) < 0)
err(EX_OSERR,