diff options
author | jkoshy <jkoshy@FreeBSD.org> | 2005-04-19 04:01:25 +0000 |
---|---|---|
committer | jkoshy <jkoshy@FreeBSD.org> | 2005-04-19 04:01:25 +0000 |
commit | dc3444cd91762fa913e417f7f7a7a0484872f54e (patch) | |
tree | 3175e06cfbec643ca7426d756f2362160f9309d4 /usr.sbin/pmccontrol/pmccontrol.c | |
parent | 8c509864f2dd0cdcc6116de38bf9137583c4ab2f (diff) | |
download | FreeBSD-src-dc3444cd91762fa913e417f7f7a7a0484872f54e.zip FreeBSD-src-dc3444cd91762fa913e417f7f7a7a0484872f54e.tar.gz |
Bring a working snapshot of hwpmc(4), its associated libraries, userland utilities
and documentation into -CURRENT.
Bump FreeBSD_version.
Reviewed by: alc, jhb (kernel changes)
Diffstat (limited to 'usr.sbin/pmccontrol/pmccontrol.c')
-rw-r--r-- | usr.sbin/pmccontrol/pmccontrol.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/usr.sbin/pmccontrol/pmccontrol.c b/usr.sbin/pmccontrol/pmccontrol.c new file mode 100644 index 0000000..a1ed2d5 --- /dev/null +++ b/usr.sbin/pmccontrol/pmccontrol.c @@ -0,0 +1,476 @@ +/*- + * Copyright (c) 2003,2004 Joseph Koshy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/sysctl.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <pmc.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +/* Compile time defaults */ + +#define PMCC_PRINT_USAGE 0 +#define PMCC_PRINT_EVENTS 1 +#define PMCC_LIST_STATE 2 +#define PMCC_ENABLE_DISABLE 3 +#define PMCC_SHOW_STATISTICS 4 + +#define PMCC_CPU_ALL -1 +#define PMCC_CPU_WILDCARD '*' + +#define PMCC_PMC_ALL -1 +#define PMCC_PMC_WILDCARD '*' + +#define PMCC_OP_IGNORE 0 +#define PMCC_OP_DISABLE 1 +#define PMCC_OP_ENABLE 2 + +#define PMCC_PROGRAM_NAME "pmccontrol" + +STAILQ_HEAD(pmcc_op_list, pmcc_op) head = STAILQ_HEAD_INITIALIZER(head); + +struct pmcc_op { + char op_cpu; + char op_pmc; + char op_op; + STAILQ_ENTRY(pmcc_op) op_next; +}; + +/* Function Prototypes */ +#if DEBUG +static void pmcc_init_debug(void); +#endif + +static int pmcc_do_list_state(void); +static int pmcc_do_enable_disable(struct pmcc_op_list *); +static int pmcc_do_list_events(void); + +/* Globals */ + +static char usage_message[] = + "Usage:\n" + " " PMCC_PROGRAM_NAME " -l\n" + " " PMCC_PROGRAM_NAME " -s\n" + " " PMCC_PROGRAM_NAME " [-e pmc | -d pmc | -c cpu] ..."; + +#if DEBUG +FILE *debug_stream = NULL; +#endif + +#if DEBUG +#define DEBUG_MSG(...) \ + (void) fprintf(debug_stream, "[pmccontrol] " __VA_ARGS__); +#else +#define DEBUG_MSG(m) /* */ +#endif /* !DEBUG */ + +int pmc_syscall = -1; + +#define PMC_CALL(cmd, params) \ +if ((error = syscall(pmc_syscall, PMC_OP_##cmd, (params))) != 0) \ +{ \ + DEBUG_MSG("ERROR: syscall [" #cmd "]"); \ + exit(EX_OSERR); \ +} + +#if DEBUG +/* log debug messages to a separate file */ +static void +pmcc_init_debug(void) +{ + char *fn; + + fn = getenv("PMCCONTROL_DEBUG"); + if (fn != NULL) + { + debug_stream = fopen(fn, "w"); + if (debug_stream == NULL) + debug_stream = stderr; + } else + debug_stream = stderr; +} +#endif + +static int +pmcc_do_enable_disable(struct pmcc_op_list *op_list) +{ + unsigned char op; + int c, error, i, j, ncpu, npmc, t; + int cpu, pmc; + struct pmcc_op *np; + unsigned char *map; + + if ((ncpu = pmc_ncpu()) < 0) + err(EX_OSERR, "Unable to determine the number of cpus"); + + /* determine the maximum number of PMCs in any CPU */ + npmc = 0; + for (c = 0; c < ncpu; c++) { + if ((t = pmc_npmc(c)) < 0) + err(EX_OSERR, "Unable to determine the number of PMCs in " + "CPU %d", c); + npmc = t > npmc ? t : npmc; + } + + if (npmc == 0) + errx(EX_CONFIG, "No PMCs found"); + + if ((map = malloc(npmc * ncpu)) == NULL) + err(EX_SOFTWARE, "Out of memory"); + + (void) memset(map, PMCC_OP_IGNORE, npmc*ncpu); + + error = 0; + STAILQ_FOREACH(np, op_list, op_next) { + + cpu = np->op_cpu; + pmc = np->op_pmc; + op = np->op_op; + + if (cpu >= ncpu) + errx(EX_DATAERR, "CPU id too large: \"%d\"", cpu); + + if (pmc >= npmc) + errx(EX_DATAERR, "PMC id too large: \"%d\"", pmc); + +#define MARKMAP(M,C,P,V) do { \ + *((M) + (C)*npmc + (P)) = (V); \ +} while (0) + +#define SET_PMCS(C,P,V) do { \ + if ((P) == PMCC_PMC_ALL) { \ + for (j = 0; j < npmc; j++) \ + MARKMAP(map, (C), j, (V)); \ + } else \ + MARKMAP(map, (C), (P), (V)); \ +} while (0) + +#define MAP(M,C,P) (*((M) + (C)*npmc + (P))) + + if (cpu == PMCC_CPU_ALL) + for (i = 0; i < ncpu; i++) + SET_PMCS(i, pmc, op); + else + SET_PMCS(cpu, pmc, op); + } + + /* Configure PMCS */ + for (i = 0; i < ncpu; i++) + for (j = 0; j < npmc; j++) { + unsigned char b; + + b = MAP(map, i, j); + + error = 0; + + if (b == PMCC_OP_ENABLE) + error = pmc_enable(i, j); + else if (b == PMCC_OP_DISABLE) + error = pmc_disable(i, j); + + if (error < 0) + err(EX_OSERR, "%s of PMC %d on CPU %d failed", + b == PMCC_OP_ENABLE ? "Enable" : + "Disable", j, i); + } + + return error; +} + +static int +pmcc_do_list_state(void) +{ + size_t dummy; + int c, cpu, n, npmc, ncpu; + unsigned int logical_cpus_mask; + struct pmc_info *pd; + struct pmc_op_getpmcinfo *pi; + const struct pmc_op_getcpuinfo *pc; + + if (pmc_cpuinfo(&pc) != 0) + err(EX_OSERR, "Unable to determine CPU information"); + + dummy = sizeof(logical_cpus_mask); + if (sysctlbyname("machdep.logical_cpus_mask", &logical_cpus_mask, + &dummy, NULL, 0) < 0) + logical_cpus_mask = 0; + + ncpu = pc->pm_ncpu; + + for (c = cpu = 0; cpu < ncpu; cpu++) { +#if i386 + if (pc->pm_cputype == PMC_CPU_INTEL_PIV && + (logical_cpus_mask & (1 << cpu))) + continue; /* skip P4-style 'logical' cpus */ +#endif + if (pmc_pmcinfo(cpu, &pi) < 0) + err(EX_OSERR, "Unable to get PMC status for CPU %d", + cpu); + + printf("#CPU %d:\n", c++); + npmc = pmc_npmc(cpu); + printf("#N NAME CLASS STATE ROW-DISP\n"); + + for (n = 0; n < npmc; n++) { + pd = &pi->pm_pmcs[n]; + + printf(" %-2d %-16s %-6s %-8s %-10s", + n, + pd->pm_name, + pmc_name_of_class(pd->pm_class), + pd->pm_enabled ? "ENABLED" : "DISABLED", + pmc_name_of_disposition(pd->pm_rowdisp)); + + if (pd->pm_ownerpid != -1) { + printf(" (pid %d)", pd->pm_ownerpid); + printf(" %-32s", + pmc_name_of_event(pd->pm_event)); + if (PMC_IS_SAMPLING_MODE(pd->pm_mode)) + printf(" (reload count %jd)", + pd->pm_reloadcount); + } + printf("\n"); + } + free(pi); + } + return 0; +} + +static int +pmcc_do_list_events(void) +{ + enum pmc_class c; + unsigned int i, j, nevents; + const char **eventnamelist; + const struct pmc_op_getcpuinfo *ci; + + if (pmc_cpuinfo(&ci) != 0) + err(EX_OSERR, "Unable to determine CPU information"); + + eventnamelist = NULL; + + for (i = 0; i < ci->pm_nclass; i++) { + c = ci->pm_classes[i]; + + printf("%s\n", pmc_name_of_class(c)); + if (pmc_event_names_of_class(c, &eventnamelist, &nevents) < 0) + err(EX_OSERR, "ERROR: Cannot find information for " + "event class \"%s\"", pmc_name_of_class(c)); + + for (j = 0; j < nevents; j++) + printf("\t%s\n", eventnamelist[j]); + + free(eventnamelist); + } + return 0; +} + +static int +pmcc_show_statistics(void) +{ + + struct pmc_op_getdriverstats gms; + + if (pmc_get_driver_stats(&gms) < 0) + err(EX_OSERR, "ERROR: cannot retrieve driver statistics"); + + /* + * Print statistics. + */ + +#define PRINT(N,V) (void) printf("%20s %d\n", (N), gms.pm_##V) + + PRINT("interrupts-processed", intr_processed); + PRINT("interrupts-ignored", intr_ignored); + PRINT("system-calls", syscalls); + PRINT("system-calls-with-errors", syscall_errors); + + return 0; +} + +/* + * Main + */ + +int +main(int argc, char **argv) +{ + int error, command, currentcpu, option, pmc; + char *dummy; + struct pmcc_op *p; + +#if DEBUG + pmcc_init_debug(); +#endif + + /* parse args */ + + currentcpu = PMCC_CPU_ALL; + command = PMCC_PRINT_USAGE; + error = 0; + + STAILQ_INIT(&head); + + while ((option = getopt(argc, argv, ":c:d:e:lLs")) != -1) + switch (option) { + case 'L': + if (command != PMCC_PRINT_USAGE) { + error = 1; + break; + } + command = PMCC_PRINT_EVENTS; + break; + + case 'c': + if (command != PMCC_PRINT_USAGE && + command != PMCC_ENABLE_DISABLE) { + error = 1; + break; + } + command = PMCC_ENABLE_DISABLE; + + if (*optarg == PMCC_CPU_WILDCARD) + currentcpu = PMCC_CPU_ALL; + else { + currentcpu = strtoul(optarg, &dummy, 0); + if (*dummy != '\0' || currentcpu < 0) + errx(EX_DATAERR, + "\"%s\" is not a valid CPU id", + optarg); + } + break; + + case 'd': + case 'e': + if (command != PMCC_PRINT_USAGE && + command != PMCC_ENABLE_DISABLE) { + error = 1; + break; + } + command = PMCC_ENABLE_DISABLE; + + if (*optarg == PMCC_PMC_WILDCARD) + pmc = PMCC_PMC_ALL; + else { + pmc = strtoul(optarg, &dummy, 0); + if (*dummy != '\0' || pmc < 0) + errx(EX_DATAERR, + "\"%s\" is not a valid PMC id", + optarg); + } + + if ((p = malloc(sizeof(*p))) == NULL) + err(EX_SOFTWARE, "Out of memory"); + + p->op_cpu = currentcpu; + p->op_pmc = pmc; + p->op_op = option == 'd' ? PMCC_OP_DISABLE : + PMCC_OP_ENABLE; + + STAILQ_INSERT_TAIL(&head, p, op_next); + break; + + case 'l': + if (command != PMCC_PRINT_USAGE) { + error = 1; + break; + } + command = PMCC_LIST_STATE; + break; + + case 's': + if (command != PMCC_PRINT_USAGE) { + error = 1; + break; + } + command = PMCC_SHOW_STATISTICS; + break; + + case ':': + errx(EX_USAGE, + "Missing argument to option '-%c'", optopt); + break; + + case '?': + warnx("Unrecognized option \"-%c\"", optopt); + errx(EX_USAGE, usage_message); + break; + + default: + error = 1; + break; + + } + + if (command == PMCC_PRINT_USAGE) + (void) errx(EX_USAGE, usage_message); + + if (error) + exit(EX_USAGE); + + if (pmc_init() < 0) + err(EX_UNAVAILABLE, + "Initialization of the pmc(3) library failed"); + + switch (command) { + case PMCC_LIST_STATE: + error = pmcc_do_list_state(); + break; + case PMCC_PRINT_EVENTS: + error = pmcc_do_list_events(); + break; + case PMCC_SHOW_STATISTICS: + error = pmcc_show_statistics(); + break; + case PMCC_ENABLE_DISABLE: + if (STAILQ_EMPTY(&head)) + errx(EX_USAGE, "No PMCs specified to enable or disable"); + error = pmcc_do_enable_disable(&head); + break; + default: + assert(0); + + } + + if (error != 0) + err(EX_OSERR, "Command failed"); + exit(0); +} |