From 3a96e8826f166f5592165a2ae31058b0fa444179 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 10 Aug 2023 15:15:52 +0200 Subject: [PATCH] cpumf: Add lspai program and man page to display PAI counter sets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add lspai program and man page to display Processor Activity Information (PAI) facility counter sets in the same way as lscpumf for the CPU Measurement Facility counter sets. Signed-off-by: Thomas Richter Reviewed-by: Jan Höppner Signed-off-by: Steffen Eiden --- cpumf/Makefile | 5 +- cpumf/lspai.c | 352 ++++++++++++++++++++++++++++++++++++++++++++++ cpumf/man/lspai.8 | 80 +++++++++++ 3 files changed, 435 insertions(+), 2 deletions(-) create mode 100644 cpumf/lspai.c create mode 100644 cpumf/man/lspai.8 diff --git a/cpumf/Makefile b/cpumf/Makefile index 1f37055b..0f9b5b36 100644 --- a/cpumf/Makefile +++ b/cpumf/Makefile @@ -1,7 +1,7 @@ include ../common.mak -BIN_FILES = lscpumf chcpumf lshwc pai -MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8 +BIN_FILES = lscpumf chcpumf lshwc pai lspai +MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8 lspai.8 all: $(BIN_FILES) @@ -11,6 +11,7 @@ lscpumf: lscpumf.o $(libs) chcpumf: chcpumf.o $(libs) lshwc: lshwc.o $(libs) pai: pai.o $(libs) +lspai: lspai.o $(libs) install: all install-man $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 diff --git a/cpumf/lspai.c b/cpumf/lspai.c new file mode 100644 index 00000000..434601a3 --- /dev/null +++ b/cpumf/lspai.c @@ -0,0 +1,352 @@ +/* Copyright IBM Corp. 2023 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +/* List available Processor Assist Instrumentation (PAI) counters. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/util_opt.h" +#include "lib/util_prg.h" +#include "lib/util_base.h" +#include "lib/util_path.h" +#include "lib/util_scandir.h" +#include "lib/util_libc.h" +#include "lib/util_file.h" +#include "lib/util_list.h" +#include "lib/libcpumf.h" + +static struct util_opt opt_vec[] = { + UTIL_OPT_SECTION("OPTIONS"), + { + .option = { "numeric", no_argument, NULL, 'n' }, + .desc = "Sort PAI counters by counter number" + }, + { + .option = { "type", required_argument, NULL, 't' }, + .argument = "TYPE", + .desc = "Type of PAI counters to show: crypto, nnpa" + }, + UTIL_OPT_HELP, + UTIL_OPT_VERSION, + UTIL_OPT_END +}; + +static const struct util_prg prg = { + .desc = "List Processor Assist Information counter sets", + .copyright_vec = { + { + .owner = "IBM Corp.", + .pub_first = 2023, + .pub_last = 2023, + }, + UTIL_PRG_COPYRIGHT_END + } +}; + +static bool numsort; /* If true sort counter numerically */ + +#define PAI_PATH "/bus/event_source/devices/%s" + +enum pai_types { /* Bit mask for supported PAI counters */ + pai_type_crypto = 0, /* PAI Crypto Counters */ + pai_type_nnpa = 1, /* PAI NNPA Counters */ + pai_type_max = 2, /* PAI maximum value, must be last */ +}; + +static int pai_types_show; + +struct pai_ctrname { /* List of defined counters */ + char *name; /* Counter name */ + unsigned long nr; /* Counter number */ +}; + +struct pai_node { /* Head for PAI counter sets */ + struct util_list_node node; /* Successor in PAI counter set list */ + enum pai_types type; /* PAI type */ + int pmu; /* Assigned PMU type number */ + const char *name; /* Counter set name */ + char *name_uc; /* Counter set name upper case */ + const char *sysfs_name; /* Counter set name in /sysfs tree */ + const char *filter_name; /* Counter set name for scandir filter */ + struct pai_ctrname *ctrlist; /* List of counter names & numbers */ + size_t ctrsize; /* Total size in bytes of ctrlist */ + int ctridx; /* Index of last entry used in ctrlist */ + unsigned long base; /* Base number for counter set */ +}; + +static struct util_list pai_list; + +/* Return base of counter set, this is the first counter of this set. */ +static unsigned long pai_type_base(enum pai_types t) +{ + switch (t) { + case pai_type_crypto: + return 0x1000; + case pai_type_nnpa: + return 0x1800; + case pai_type_max: + break; + } + return 0; +} + +/* Test PAI counter name from command line option. */ +static const char *pai_type_name(enum pai_types t) +{ + switch (t) { + case pai_type_crypto: + return "crypto"; + case pai_type_nnpa: + return "nnpa"; + case pai_type_max: + break; + } + return "unknown"; +} + +/* Convert PAI counter type to sysfs directory name. Only validated + * input at this time. + */ +static const char *pai_type_sysfs(enum pai_types t) +{ + if (t == pai_type_crypto) + return "pai_crypto"; + return "pai_ext"; +} + +/* Convert PAI counter type to sysfs directory name filter for scandir(). */ +static const char *pai_type_filter(enum pai_types t) +{ + if (t == pai_type_nnpa) + return "^NNPA"; + return "[^.]"; /* Matches anything but . and .. in sysfs */ +} + +/* Sort PAI counter names by assigned counter number. */ +static int pai_ctrcmp(const void *p1, const void *p2) +{ + struct pai_ctrname *l = (struct pai_ctrname *)p1; + struct pai_ctrname *r = (struct pai_ctrname *)p2; + + return l->nr > r->nr ? 1 : -1; +} + +/* Convert string to upper case. */ +static char *str2uc(const char *s) +{ + char *uc = util_strdup(s), *old_uc = uc; + + for (; *uc; ++uc) + *uc = toupper(*uc); + return old_uc; +} + +/* Read counter names and assigned event number from sysfs file tree. + * Exit when sysfs directory can not be scanned. + */ +static void read_counternames(struct pai_node *node) +{ + int i, more = 0, ctr = 0, count = 0; + struct dirent **namelist = NULL; + char *path, *ctrpath; + + /* Read counter names and assigned event number. */ + path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name); + count = util_scandir(&namelist, alphasort, path, node->filter_name); + if (count <= 0) + errx(EXIT_FAILURE, "Cannot open %s", path); + + node->ctrsize = count * sizeof(*node->ctrlist); + node->ctrlist = util_malloc(node->ctrsize); + for (i = 0; i < count && ctr >= 0; i++) { + util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name); + if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) { + node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name); + node->ctrlist[node->ctridx++].nr = ctr; + more++; + } else { + warnx("Cannot parse %s", ctrpath); + } + free(ctrpath); + } + util_scandir_free(namelist, count); + free(path); + + if (numsort && more > 1) + qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp); +} + +static void show_painode(void) +{ + struct pai_node *node; + int indent = 0; + int offset = 0; + + util_list_iterate(&pai_list, node) { + for (int i = 0; i < node->ctridx; ++i) + indent = MAX((size_t)indent, strlen(node->ctrlist[i].name)); + } + + printf("RAW %*s NAME %*s DESCRIPTION\n", 3, "", indent - 5, ""); + util_list_iterate(&pai_list, node) { + for (int i = 0; i < node->ctridx; ++i) { + printf("%d:%ld %s", node->pmu, + node->ctrlist[i].nr, node->ctrlist[i].name); + + offset = indent - strlen(node->ctrlist[i].name) + 1; + printf("%*s", offset, ""); + + printf("Counter %ld / PAI %s counter set\n", + node->ctrlist[i].nr - node->base, node->name_uc); + } + } +} + +/* Release all memory allocated at make_painode(). */ +static void free_painode(void) +{ + struct pai_node *next, *node; + + util_list_iterate_safe(&pai_list, node, next) { + free(node->name_uc); + for (int i = 0; i < node->ctridx; ++i) + free(node->ctrlist[i].name); + free(node->ctrlist); + free(node); + } +} + +static void make_painode(enum pai_types t) +{ + struct pai_node *node = util_zalloc(sizeof(*node)); + char *path; + + node->type = t; + node->sysfs_name = pai_type_sysfs(t); + node->name = pai_type_name(t); + node->name_uc = str2uc(node->name); + node->filter_name = pai_type_filter(t); + node->base = pai_type_base(t); + + /* Read PMU type number. */ + path = util_path_sysfs(PAI_PATH, node->sysfs_name); + node->pmu = libcpumf_pmutype(path); + if (node->pmu < 0) + errx(EXIT_FAILURE, "Cannot open %s", path); + free(path); + + read_counternames(node); + + util_list_add_tail(&pai_list, node); +} + +static int painode_cmp(void *a, void *b, void *UNUSED(data)) +{ + struct pai_node *n1 = (struct pai_node *)a; + struct pai_node *n2 = (struct pai_node *)b; + + return n1->pmu < n2->pmu ? -1 : 1; +} + +static void sort_painode(void) +{ + util_list_sort(&pai_list, painode_cmp, NULL); +} + +/* Check for hardware support and return false if not available. */ +static bool have_support(enum pai_types t) +{ + const char *sysfn = pai_type_sysfs(t); + char *path = util_path_sysfs(PAI_PATH, sysfn); + bool rc = true; + + if (!util_path_is_dir(path)) { + warnx("No support for PAI %s facility", pai_type_name(t)); + rc = false; + } + free(path); + return rc; +} + +/* + * Check the argument for option -t. It must be a valid PAI counter set. + * Exit when an invalid PAI counter set name has been specified. + */ +static void check_type_name(const char *type) +{ + bool no_match = true; + enum pai_types i; + const char *fn; + + for (i = pai_type_crypto; i < pai_type_max; ++i) { + fn = pai_type_name(i); + if (!strcasecmp(fn, type)) { + pai_types_show |= (1 << i); + no_match = false; + } + } + if (no_match) + errx(EXIT_FAILURE, "Invalid argument for -t %s", type); +} + +int main(int argc, char **argv) +{ + int ch; + + util_list_init(&pai_list, struct pai_node, node); + util_prg_init(&prg); + util_opt_init(opt_vec, NULL); + + while ((ch = util_opt_getopt_long(argc, argv)) != -1) { + switch (ch) { + default: + util_opt_print_parse_error(ch, argv); + return EXIT_FAILURE; + case 'h': + util_prg_print_help(); + util_opt_print_help(); + return EXIT_SUCCESS; + case 'v': + util_prg_print_version(); + return EXIT_SUCCESS; + case 'n': + numsort = true; + break; + case 't': + check_type_name(optarg); + break; + } + } + + /* Nothing specified, show all PAI counters */ + if (!pai_types_show) + pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa); + + /* Check for hardware support */ + for (enum pai_types i = pai_type_crypto; i < pai_type_max; ++i) { + if ((pai_types_show & (1 << i))) { + if (!have_support(i)) + pai_types_show &= ~(1 << i); + else + make_painode(i); + } + } + sort_painode(); + show_painode(); + free_painode(); + return ch; +} diff --git a/cpumf/man/lspai.8 b/cpumf/man/lspai.8 new file mode 100644 index 00000000..9ef07aad --- /dev/null +++ b/cpumf/man/lspai.8 @@ -0,0 +1,80 @@ +.\" lspai.8 +.\" +.\" +.\" Copyright IBM Corp. 2021 +.\" s390-tools is free software; you can redistribute it and/or modify +.\" it under the terms of the MIT license. See LICENSE for details. +.\" ---------------------------------------------------------------------- +.ds c \fBlspai\fP +. +.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs" +. +.SH NAME +\*c \- list Processor Activity Instrumentation (PAI) counters +. +.SH SYNOPSIS +\*c +.RB [ \-n ] +.RB [ \-t +.IR "\ TYPE" ] +.br +\*c +.BR \-h | \-\-help +.br +\*c +.BR \-v | \-\-version +. +. +.SH DESCRIPTION +\*c displays the Processor Activity Instrumentation (PAI) counters +for Linux on IBM Z. +The output is a human-readable list of available PAI counter +names and numbers. +.SH OPTIONS +.TP +.BR \-h ", " \-\-help +Displays help information, then exits. +. +.TP +.BR \-v ", " \-\-version +Displays version information, then exits. +. +.TP +.BR \-t ", " \-\-type "\ TYPE" +Specifies the PAI counter set to list. +Valid counter set values are +.I crypto +and +.IR nnpa . +By default, the command lists all available PAI counter sets. +NNPA refers to the Neural Network Processing Assist facility counter set. +Crypto refers to the Cryptografic Processing Assist facility counter set. +. +.TP +.BR \-n ", " \-\-numeric +Shows the PAI counter sets sorted by counter number. +Default sort order is PAI counter name. +. +.SH "EXAMPLE" +The \*c invocation lists all PAI Neural Network Processing Assist Facility +(NNPA) counters in numeric order: +.nf +# lspai -t nnpa -n +RAW NAME DESCRIPTION +13:6144 NNPA_ALL Counter 0 / PAI NNPA counter set +13:6145 NNPA_ADD Counter 1 / PAI NNPA counter set +13:6146 NNPA_SUB Counter 2 / PAI NNPA counter set +13:6147 NNPA_MUL Counter 3 / PAI NNPA counter set +\&... +.fi +The first column shows the raw event number suitable for +.IR perf "(8)" +raw event specification. +The second column shows the PAI NNPA counter name, +suitable for +.IR perf "(8)" +event specification by name. +The third gives a short explanation, if available. +.SH "SEE ALSO" +.BR pai (8) +.BR lscpumf (8)