Commit b6e28991 authored by Aleksandr Rybalko's avatar Aleksandr Rybalko Committed by Toomas Soome
Browse files

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
parent 33a0803f
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd August 10, 2021 .Dd May 28, 2022
.Dt PMC 3 .Dt PMC 3
.Os .Os
.Sh NAME .Sh NAME
...@@ -200,6 +200,8 @@ Supported capabilities include: ...@@ -200,6 +200,8 @@ Supported capabilities include:
.Bl -tag -width "Li PMC_CAP_INTERRUPT" -compact .Bl -tag -width "Li PMC_CAP_INTERRUPT" -compact
.It Li PMC_CAP_CASCADE .It Li PMC_CAP_CASCADE
The ability to cascade counters. The ability to cascade counters.
.It Li PMC_CAP_DOMWIDE
Separate counters tied to each NUMA domain.
.It Li PMC_CAP_EDGE .It Li PMC_CAP_EDGE
The ability to count negated to asserted transitions of the hardware The ability to count negated to asserted transitions of the hardware
conditions being probed for. conditions being probed for.
...@@ -218,6 +220,8 @@ The ability to read from performance counters. ...@@ -218,6 +220,8 @@ The ability to read from performance counters.
.It Li PMC_CAP_SYSTEM .It Li PMC_CAP_SYSTEM
The ability to restrict counting of hardware events to when the CPU is The ability to restrict counting of hardware events to when the CPU is
running privileged code. running privileged code.
.It Li PMC_CAP_SYSWIDE
A single counter aggregating events for the whole system.
.It Li PMC_CAP_THRESHOLD .It Li PMC_CAP_THRESHOLD
The ability to ignore simultaneous hardware events below a The ability to ignore simultaneous hardware events below a
programmable threshold. programmable threshold.
......
...@@ -171,7 +171,10 @@ enum pmc_cputype { ...@@ -171,7 +171,10 @@ enum pmc_cputype {
__PMC_CLASS(MIPS74K, 0x12, "MIPS 74K") \ __PMC_CLASS(MIPS74K, 0x12, "MIPS 74K") \
__PMC_CLASS(E500, 0x13, "Freescale e500 class") \ __PMC_CLASS(E500, 0x13, "Freescale e500 class") \
__PMC_CLASS(BERI, 0x14, "MIPS BERI") \ __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 { enum pmc_class {
#undef __PMC_CLASS #undef __PMC_CLASS
...@@ -180,7 +183,7 @@ enum pmc_class { ...@@ -180,7 +183,7 @@ enum pmc_class {
}; };
#define PMC_CLASS_FIRST PMC_CLASS_TSC #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: * A PMC can be in the following states:
...@@ -308,7 +311,9 @@ enum pmc_disp { ...@@ -308,7 +311,9 @@ enum pmc_disp {
__PMC_CAP(QUALIFIER, 8, "further qualify monitored events") \ __PMC_CAP(QUALIFIER, 8, "further qualify monitored events") \
__PMC_CAP(PRECISE, 9, "perform precise sampling") \ __PMC_CAP(PRECISE, 9, "perform precise sampling") \
__PMC_CAP(TAGGING, 10, "tag upstream events") \ __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 enum pmc_caps
{ {
...@@ -318,7 +323,7 @@ enum pmc_caps ...@@ -318,7 +323,7 @@ enum pmc_caps
}; };
#define PMC_CAP_FIRST PMC_CAP_INTERRUPT #define PMC_CAP_FIRST PMC_CAP_INTERRUPT
#define PMC_CAP_LAST PMC_CAP_CASCADE #define PMC_CAP_LAST PMC_CAP_DOMWIDE
/* /*
* PMC Event Numbers * PMC Event Numbers
......
...@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); ...@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
#include <regex.h> #include <regex.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -116,6 +117,7 @@ static int pmcstat_kq; ...@@ -116,6 +117,7 @@ static int pmcstat_kq;
static kvm_t *pmcstat_kvm; static kvm_t *pmcstat_kvm;
static struct kinfo_proc *pmcstat_plist; static struct kinfo_proc *pmcstat_plist;
struct pmcstat_args args; struct pmcstat_args args;
static bool libpmc_initialized = false;
static void static void
pmcstat_get_cpumask(const char *cpuspec, cpuset_t *cpumask) pmcstat_get_cpumask(const char *cpuspec, cpuset_t *cpumask)
...@@ -419,6 +421,22 @@ pmcstat_topexit(void) ...@@ -419,6 +421,22 @@ pmcstat_topexit(void)
endwin(); 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 * Main
*/ */
...@@ -426,14 +444,14 @@ pmcstat_topexit(void) ...@@ -426,14 +444,14 @@ pmcstat_topexit(void)
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
cpuset_t cpumask, rootmask; cpuset_t cpumask, dommask, rootmask;
double interval; double interval;
double duration; double duration;
int option, npmc; int option, npmc;
int c, check_driver_stats; int c, check_driver_stats;
int do_callchain, do_descendants, do_logproccsw, do_logprocexit; int do_callchain, do_descendants, do_logproccsw, do_logprocexit;
int do_print, do_read, do_listcounters, do_descr; int do_print, do_read, do_listcounters, do_descr, domains;
int do_userspace; int do_userspace, i;
size_t len; size_t len;
int graphdepth; int graphdepth;
int pipefd[2], rfd; int pipefd[2], rfd;
...@@ -450,6 +468,7 @@ main(int argc, char **argv) ...@@ -450,6 +468,7 @@ main(int argc, char **argv)
struct winsize ws; struct winsize ws;
struct stat sb; struct stat sb;
char buffer[PATH_MAX]; char buffer[PATH_MAX];
uint32_t caps;
check_driver_stats = 0; check_driver_stats = 0;
current_sampling_count = 0; current_sampling_count = 0;
...@@ -460,6 +479,7 @@ main(int argc, char **argv) ...@@ -460,6 +479,7 @@ main(int argc, char **argv)
do_logproccsw = 0; do_logproccsw = 0;
do_logprocexit = 0; do_logprocexit = 0;
do_listcounters = 0; do_listcounters = 0;
domains = 0;
use_cumulative_counts = 0; use_cumulative_counts = 0;
graphfilename = "-"; graphfilename = "-";
args.pa_required = 0; args.pa_required = 0;
...@@ -489,8 +509,10 @@ main(int argc, char **argv) ...@@ -489,8 +509,10 @@ main(int argc, char **argv)
bzero(&ds_end, sizeof(ds_end)); bzero(&ds_end, sizeof(ds_end));
ev = NULL; ev = NULL;
event = NULL; event = NULL;
caps = 0;
CPU_ZERO(&cpumask); CPU_ZERO(&cpumask);
/* Default to using the running system kernel. */ /* Default to using the running system kernel. */
len = 0; len = 0;
if (sysctlbyname("kern.bootfile", NULL, &len, NULL, 0) == -1) if (sysctlbyname("kern.bootfile", NULL, &len, NULL, 0) == -1)
...@@ -500,6 +522,9 @@ main(int argc, char **argv) ...@@ -500,6 +522,9 @@ main(int argc, char **argv)
errx(EX_SOFTWARE, "ERROR: Out of memory."); errx(EX_SOFTWARE, "ERROR: Out of memory.");
if (sysctlbyname("kern.bootfile", args.pa_kernel, &len, NULL, 0) == -1) if (sysctlbyname("kern.bootfile", args.pa_kernel, &len, NULL, 0) == -1)
err(EX_OSERR, "ERROR: Cannot determine path of running kernel"); 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 * The initial CPU mask specifies the root mask of this process
...@@ -640,6 +665,7 @@ main(int argc, char **argv) ...@@ -640,6 +665,7 @@ main(int argc, char **argv)
case 's': /* system-wide counting PMC */ case 's': /* system-wide counting PMC */
case 'P': /* process virtual sampling PMC */ case 'P': /* process virtual sampling PMC */
case 'S': /* system-wide sampling PMC */ case 'S': /* system-wide sampling PMC */
caps = 0;
if ((ev = malloc(sizeof(*ev))) == NULL) if ((ev = malloc(sizeof(*ev))) == NULL)
errx(EX_SOFTWARE, "ERROR: Out of memory."); errx(EX_SOFTWARE, "ERROR: Out of memory.");
...@@ -707,12 +733,48 @@ main(int argc, char **argv) ...@@ -707,12 +733,48 @@ main(int argc, char **argv)
errx(EX_SOFTWARE, "ERROR: Out of memory."); errx(EX_SOFTWARE, "ERROR: Out of memory.");
(void) strncpy(ev->ev_name, optarg, c); (void) strncpy(ev->ev_name, optarg, c);
*(ev->ev_name + c) = '\0'; *(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); 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') { if (option == 's' || option == 'S') {
CPU_CLR(ev->ev_cpu, &cpumask); 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); pmcstat_clone_event_descriptor(ev, &cpumask, &args);
ev->ev_pmcid = saved_pmcid;
CPU_SET(ev->ev_cpu, &cpumask); CPU_SET(ev->ev_cpu, &cpumask);
} }
...@@ -1050,17 +1112,8 @@ main(int argc, char **argv) ...@@ -1050,17 +1112,8 @@ main(int argc, char **argv)
} }
/* if we've been asked to process a log file, skip init */ /* if we've been asked to process a log file, skip init */
if ((args.pa_flags & FLAG_READ_LOGFILE) == 0) { if ((args.pa_flags & FLAG_READ_LOGFILE) == 0)
if (pmc_init() < 0) libpmc_initialize(&npmc);
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);
}
/* Allocate a kqueue */ /* Allocate a kqueue */
if ((pmcstat_kq = kqueue()) < 0) if ((pmcstat_kq = kqueue()) < 0)
...@@ -1134,7 +1187,8 @@ main(int argc, char **argv) ...@@ -1134,7 +1187,8 @@ main(int argc, char **argv)
*/ */
STAILQ_FOREACH(ev, &args.pa_events, ev_next) { 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_flags, ev->ev_cpu, &ev->ev_pmcid,
ev->ev_count) < 0) ev->ev_count) < 0)
err(EX_OSERR, err(EX_OSERR,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment