mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
259 lines
5.7 KiB
C
259 lines
5.7 KiB
C
/*
|
|
* cpacfstats - display and maintain CPACF perf counters
|
|
*
|
|
* low level perf functions
|
|
*
|
|
* Copyright IBM Corp. 2015, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#define __STDC_FORMAT_MACROS
|
|
#include <inttypes.h>
|
|
#include <perfmon/perf_event.h>
|
|
#include <perfmon/pfmlib.h>
|
|
#include <perfmon/pfmlib_perf_event.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "cpacfstats.h"
|
|
|
|
/* correlation between counter and perf counter string */
|
|
static const struct {
|
|
char pfm_name[80];
|
|
enum ctr_e ctr;
|
|
} pmf_counter_name[ALL_COUNTER] = {
|
|
{"cpum_cf::DEA_FUNCTIONS", DES_FUNCTIONS},
|
|
{"cpum_cf::AES_FUNCTIONS", AES_FUNCTIONS},
|
|
{"cpum_cf::SHA_FUNCTIONS", SHA_FUNCTIONS},
|
|
{"cpum_cf::PRNG_FUNCTIONS", PRNG_FUNCTIONS}
|
|
};
|
|
|
|
/*
|
|
* We need one filedescriptor per CPU per counter.
|
|
* So perf_init builds this:
|
|
*
|
|
* ctr_fds - is an array of pointers to file descriptor arrays.
|
|
* Each file descriptor array has space for number of logical CPUs + 1
|
|
* filedescriptors (int values). The last element of each file descriptor
|
|
* array is always 0, assuming there will never appear a filedescriptor
|
|
* with value 0:
|
|
*
|
|
* ctr_fds:
|
|
* ctr_fds[0] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
|
* ctr_fds[1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
|
* ...
|
|
* ctr_fds[ALL_COUNTER-1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
|
*/
|
|
static int *ctr_fds[ALL_COUNTER];
|
|
|
|
|
|
int perf_init(void)
|
|
{
|
|
int i, cpus, ctr, cpu, ec, *fds;
|
|
|
|
memset(ctr_fds, 0, sizeof(ctr_fds));
|
|
|
|
/* initialize performance monitoring library */
|
|
ec = pfm_initialize();
|
|
if (ec != PFM_SUCCESS) {
|
|
eprint("Pfm_initialize() returned with failure (%d:%s)\n",
|
|
ec, pfm_strerror(ec));
|
|
return -1;
|
|
}
|
|
|
|
/* get number of logical processors */
|
|
cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
/* for each counter */
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
|
|
/*
|
|
* allocate an array of ints to store for each CPU
|
|
* one filedescriptor + a terminating 0
|
|
*/
|
|
fds = (int *) calloc(sizeof(int), cpus+1);
|
|
if (!fds) {
|
|
eprint("Malloc() of %d byte failed, errno=%d [%s]\n",
|
|
(int)(sizeof(int) * (cpus+1)),
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
ctr_fds[ctr] = fds;
|
|
|
|
for (cpu = 0; cpu < cpus; cpu++) {
|
|
pfm_perf_encode_arg_t pfm_arg;
|
|
struct perf_event_attr pfm_event;
|
|
int fd;
|
|
|
|
memset(&pfm_arg, 0, sizeof(pfm_arg));
|
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
|
pfm_arg.attr = &pfm_event;
|
|
pfm_arg.size = sizeof(pfm_arg);
|
|
|
|
/* search for the counter's corresponding pfm name */
|
|
for (i = ALL_COUNTER-1; i >= 0; i--)
|
|
if ((int) pmf_counter_name[i].ctr == ctr)
|
|
break;
|
|
if (i < 0) {
|
|
eprint("Pfm ctr name not found for counter %d, please adjust pmf_counter_name[] in %s\n",
|
|
ctr, __FILE__);
|
|
return -1;
|
|
}
|
|
|
|
/* encode the counters perf event into pfm_arg.attr */
|
|
ec = pfm_get_os_event_encoding(
|
|
pmf_counter_name[i].pfm_name,
|
|
PFM_PLM0,
|
|
PFM_OS_PERF_EVENT,
|
|
&pfm_arg);
|
|
if (ec != PFM_SUCCESS) {
|
|
eprint("Pfm_initialize() for %s failed (%d:%s)\n",
|
|
pmf_counter_name[i].pfm_name,
|
|
ec, pfm_strerror(ec));
|
|
return -1;
|
|
}
|
|
|
|
/* fetch file descriptor for this perf event
|
|
* the counter event should start disabled
|
|
*/
|
|
pfm_event.disabled = 1;
|
|
fd = perf_event_open(
|
|
&pfm_event,
|
|
-1, /* pid -1 means all processes */
|
|
cpu,
|
|
-1, /* group filedescriptor */
|
|
0); /* flags */
|
|
if (fd < 0) {
|
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
fds[cpu] = fd;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void perf_close(void)
|
|
{
|
|
int ctr, *fds;
|
|
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
|
close(*fds);
|
|
*fds = 0;
|
|
}
|
|
free(ctr_fds[ctr]);
|
|
ctr_fds[ctr] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
int perf_enable_ctr(enum ctr_e ctr)
|
|
{
|
|
int *fds, ec, rc = 0;
|
|
|
|
if (ctr == ALL_COUNTER) {
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
rc = perf_enable_ctr(ctr);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
} else {
|
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
|
ec = ioctl(*fds, PERF_EVENT_IOC_ENABLE, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
int perf_disable_ctr(enum ctr_e ctr)
|
|
{
|
|
int *fds, ec, rc = 0;
|
|
|
|
if (ctr == ALL_COUNTER) {
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
rc = perf_disable_ctr(ctr);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
} else {
|
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
|
ec = ioctl(*fds, PERF_EVENT_IOC_DISABLE, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
int perf_reset_ctr(enum ctr_e ctr)
|
|
{
|
|
int *fds, ec, rc = 0;
|
|
|
|
if (ctr == ALL_COUNTER) {
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
rc = perf_reset_ctr(ctr);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
} else {
|
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
|
ec = ioctl(*fds, PERF_EVENT_IOC_RESET, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
|
|
{
|
|
int *fds, ec, rc = -1;
|
|
uint64_t val;
|
|
|
|
if (!value)
|
|
return -1;
|
|
*value = 0;
|
|
|
|
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
|
ec = read(*fds, &val, sizeof(val));
|
|
if (ec != sizeof(val)) {
|
|
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
} else {
|
|
*value += val;
|
|
rc = 0;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|