pmccontrol.c revision 224058
1279377Simp/*- 2279377Simp * Copyright (c) 2003,2004 Joseph Koshy 3279377Simp * All rights reserved. 4279377Simp * 5279377Simp * Redistribution and use in source and binary forms, with or without 6279377Simp * modification, are permitted provided that the following conditions 7279377Simp * are met: 8279377Simp * 1. Redistributions of source code must retain the above copyright 9279377Simp * notice, this list of conditions and the following disclaimer. 10279377Simp * 2. Redistributions in binary form must reproduce the above copyright 11279377Simp * notice, this list of conditions and the following disclaimer in the 12279377Simp * documentation and/or other materials provided with the distribution. 13279377Simp * 14279377Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15279377Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16279377Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17279377Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18279377Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19279377Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20279377Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21279377Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22279377Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23279377Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24279377Simp * SUCH DAMAGE. 25279377Simp * 26279377Simp */ 27279377Simp 28279377Simp#include <sys/cdefs.h> 29279377Simp__FBSDID("$FreeBSD: head/usr.sbin/pmccontrol/pmccontrol.c 224058 2011-07-15 11:46:54Z attilio $"); 30279377Simp 31279377Simp#include <sys/param.h> 32279377Simp#include <sys/queue.h> 33279377Simp#include <sys/cpuset.h> 34279377Simp#include <sys/sysctl.h> 35279377Simp 36279377Simp#include <assert.h> 37279377Simp#include <err.h> 38279377Simp#include <errno.h> 39279377Simp#include <fcntl.h> 40279377Simp#include <limits.h> 41279377Simp#include <pmc.h> 42279377Simp#include <stdarg.h> 43279377Simp#include <stdio.h> 44279377Simp#include <stdlib.h> 45279377Simp#include <string.h> 46279377Simp#include <sysexits.h> 47279377Simp#include <unistd.h> 48279377Simp 49279377Simp/* Compile time defaults */ 50279377Simp 51279377Simp#define PMCC_PRINT_USAGE 0 52279377Simp#define PMCC_PRINT_EVENTS 1 53279377Simp#define PMCC_LIST_STATE 2 54279377Simp#define PMCC_ENABLE_DISABLE 3 55279377Simp#define PMCC_SHOW_STATISTICS 4 56279377Simp 57279377Simp#define PMCC_CPU_ALL -1 58279377Simp#define PMCC_CPU_WILDCARD '*' 59279377Simp 60279377Simp#define PMCC_PMC_ALL -1 61279377Simp#define PMCC_PMC_WILDCARD '*' 62279377Simp 63279377Simp#define PMCC_OP_IGNORE 0 64279377Simp#define PMCC_OP_DISABLE 1 65279377Simp#define PMCC_OP_ENABLE 2 66279377Simp 67279377Simp#define PMCC_PROGRAM_NAME "pmccontrol" 68279377Simp 69279377SimpSTAILQ_HEAD(pmcc_op_list, pmcc_op) head = STAILQ_HEAD_INITIALIZER(head); 70279377Simp 71279377Simpstruct pmcc_op { 72279377Simp char op_cpu; 73279377Simp char op_pmc; 74279377Simp char op_op; 75279377Simp STAILQ_ENTRY(pmcc_op) op_next; 76279377Simp}; 77279377Simp 78279377Simp/* Function Prototypes */ 79279377Simp#if DEBUG 80279377Simpstatic void pmcc_init_debug(void); 81279377Simp#endif 82279377Simp 83279377Simpstatic int pmcc_do_list_state(void); 84279377Simpstatic int pmcc_do_enable_disable(struct pmcc_op_list *); 85279377Simpstatic int pmcc_do_list_events(void); 86279377Simp 87279377Simp/* Globals */ 88279377Simp 89279377Simpstatic char usage_message[] = 90279377Simp "Usage:\n" 91279377Simp " " PMCC_PROGRAM_NAME " -L\n" 92279377Simp " " PMCC_PROGRAM_NAME " -l\n" 93279377Simp " " PMCC_PROGRAM_NAME " -s\n" 94279377Simp " " PMCC_PROGRAM_NAME " [-e pmc | -d pmc | -c cpu] ..."; 95279377Simp 96279377Simp#if DEBUG 97279377SimpFILE *debug_stream = NULL; 98279377Simp#endif 99279377Simp 100279377Simp#if DEBUG 101279377Simp#define DEBUG_MSG(...) \ 102279377Simp (void) fprintf(debug_stream, "[pmccontrol] " __VA_ARGS__); 103279377Simp#else 104279377Simp#define DEBUG_MSG(m) /* */ 105279377Simp#endif /* !DEBUG */ 106279377Simp 107279377Simpint pmc_syscall = -1; 108279377Simp 109279377Simp#define PMC_CALL(cmd, params) \ 110279377Simpif ((error = syscall(pmc_syscall, PMC_OP_##cmd, (params))) != 0) \ 111279377Simp{ \ 112279377Simp DEBUG_MSG("ERROR: syscall [" #cmd "]"); \ 113279377Simp exit(EX_OSERR); \ 114279377Simp} 115279377Simp 116279377Simp#if DEBUG 117279377Simp/* log debug messages to a separate file */ 118279377Simpstatic void 119279377Simppmcc_init_debug(void) 120279377Simp{ 121279377Simp char *fn; 122279377Simp 123279377Simp fn = getenv("PMCCONTROL_DEBUG"); 124279377Simp if (fn != NULL) 125279377Simp { 126279377Simp debug_stream = fopen(fn, "w"); 127279377Simp if (debug_stream == NULL) 128279377Simp debug_stream = stderr; 129279377Simp } else 130279377Simp debug_stream = stderr; 131279377Simp} 132279377Simp#endif 133279377Simp 134279377Simpstatic int 135279377Simppmcc_do_enable_disable(struct pmcc_op_list *op_list) 136279377Simp{ 137279377Simp int c, error, i, j, ncpu, npmc, t; 138279377Simp struct pmcc_op *np; 139279377Simp unsigned char *map; 140279377Simp unsigned char op; 141279377Simp int cpu, pmc; 142279377Simp 143279377Simp if ((ncpu = pmc_ncpu()) < 0) 144279377Simp err(EX_OSERR, "Unable to determine the number of cpus"); 145279377Simp 146279377Simp /* Determine the maximum number of PMCs in any CPU. */ 147279377Simp npmc = 0; 148279377Simp for (c = 0; c < ncpu; c++) { 149279377Simp if ((t = pmc_npmc(c)) < 0) 150279377Simp err(EX_OSERR, "Unable to determine the number of " 151279377Simp "PMCs in CPU %d", c); 152279377Simp npmc = t > npmc ? t : npmc; 153279377Simp } 154279377Simp 155279377Simp if (npmc == 0) 156279377Simp errx(EX_CONFIG, "No PMCs found"); 157279377Simp 158279377Simp if ((map = malloc(npmc * ncpu)) == NULL) 159279377Simp err(EX_SOFTWARE, "Out of memory"); 160279377Simp 161279377Simp (void) memset(map, PMCC_OP_IGNORE, npmc*ncpu); 162279377Simp 163279377Simp error = 0; 164279377Simp STAILQ_FOREACH(np, op_list, op_next) { 165279377Simp 166279377Simp cpu = np->op_cpu; 167279377Simp pmc = np->op_pmc; 168279377Simp op = np->op_op; 169279377Simp 170279377Simp if (cpu >= ncpu) 171279377Simp errx(EX_DATAERR, "CPU id too large: \"%d\"", cpu); 172279377Simp 173279377Simp if (pmc >= npmc) 174279377Simp errx(EX_DATAERR, "PMC id too large: \"%d\"", pmc); 175279377Simp 176279377Simp#define MARKMAP(M,C,P,V) do { \ 177279377Simp *((M) + (C)*npmc + (P)) = (V); \ 178279377Simp} while (0) 179279377Simp 180279377Simp#define SET_PMCS(C,P,V) do { \ 181279377Simp if ((P) == PMCC_PMC_ALL) { \ 182279377Simp for (j = 0; j < npmc; j++) \ 183279377Simp MARKMAP(map, (C), j, (V)); \ 184279377Simp } else \ 185279377Simp MARKMAP(map, (C), (P), (V)); \ 186279377Simp} while (0) 187279377Simp 188279377Simp#define MAP(M,C,P) (*((M) + (C)*npmc + (P))) 189279377Simp 190279377Simp if (cpu == PMCC_CPU_ALL) 191279377Simp for (i = 0; i < ncpu; i++) { 192279377Simp SET_PMCS(i, pmc, op); 193279377Simp } 194279377Simp else 195279377Simp SET_PMCS(cpu, pmc, op); 196279377Simp } 197279377Simp 198279377Simp /* Configure PMCS */ 199279377Simp for (i = 0; i < ncpu; i++) 200279377Simp for (j = 0; j < npmc; j++) { 201279377Simp unsigned char b; 202279377Simp 203279377Simp b = MAP(map, i, j); 204279377Simp 205279377Simp error = 0; 206279377Simp 207279377Simp if (b == PMCC_OP_ENABLE) 208279377Simp error = pmc_enable(i, j); 209279377Simp else if (b == PMCC_OP_DISABLE) 210279377Simp error = pmc_disable(i, j); 211279377Simp 212279377Simp if (error < 0) 213279377Simp err(EX_OSERR, "%s of PMC %d on CPU %d failed", 214279377Simp b == PMCC_OP_ENABLE ? "Enable" : 215279377Simp "Disable", j, i); 216279377Simp } 217279377Simp 218279377Simp return error; 219279377Simp} 220279377Simp 221279377Simpstatic int 222279377Simppmcc_do_list_state(void) 223279377Simp{ 224279377Simp cpuset_t logical_cpus_mask; 225279377Simp long cpusetsize; 226279377Simp size_t setsize; 227279377Simp int c, cpu, n, npmc, ncpu; 228279377Simp struct pmc_info *pd; 229279377Simp struct pmc_pmcinfo *pi; 230279377Simp const struct pmc_cpuinfo *pc; 231279377Simp 232279377Simp if (pmc_cpuinfo(&pc) != 0) 233279377Simp err(EX_OSERR, "Unable to determine CPU information"); 234279377Simp 235279377Simp printf("%d %s CPUs present, with %d PMCs per CPU\n", pc->pm_ncpu, 236279377Simp pmc_name_of_cputype(pc->pm_cputype), 237279377Simp pc->pm_npmc); 238279377Simp 239279377Simp /* Determine the set of logical CPUs. */ 240279377Simp cpusetsize = sysconf(_SC_CPUSET_SIZE); 241279377Simp if (cpusetsize == -1 || (u_long)cpusetsize > sizeof(cpuset_t)) 242279377Simp err(EX_OSERR, "Cannot determine which CPUs are logical"); 243279377Simp CPU_ZERO(&logical_cpus_mask); 244279377Simp setsize = (size_t)cpusetsize; 245279377Simp if (sysctlbyname("machdep.logical_cpus_mask", &logical_cpus_mask, 246279377Simp &setsize, NULL, 0) < 0) 247279377Simp CPU_ZERO(&logical_cpus_mask); 248279377Simp 249279377Simp ncpu = pc->pm_ncpu; 250279377Simp 251279377Simp for (c = cpu = 0; cpu < ncpu; cpu++) { 252279377Simp#if defined(__i386__) || defined(__amd64__) 253279377Simp if (pc->pm_cputype == PMC_CPU_INTEL_PIV && 254279377Simp CPU_ISSET(cpu, &logical_cpus_mask)) 255279377Simp continue; /* skip P4-style 'logical' cpus */ 256279377Simp#endif 257279377Simp if (pmc_pmcinfo(cpu, &pi) < 0) { 258279377Simp if (errno == ENXIO) 259279377Simp continue; 260279377Simp err(EX_OSERR, "Unable to get PMC status for CPU %d", 261279377Simp cpu); 262279377Simp } 263279377Simp 264279377Simp printf("#CPU %d:\n", c++); 265279377Simp npmc = pmc_npmc(cpu); 266279377Simp printf("#N NAME CLASS STATE ROW-DISP\n"); 267279377Simp 268279377Simp for (n = 0; n < npmc; n++) { 269279377Simp pd = &pi->pm_pmcs[n]; 270279377Simp 271279377Simp printf(" %-2d %-16s %-6s %-8s %-10s", 272279377Simp n, 273279377Simp pd->pm_name, 274279377Simp pmc_name_of_class(pd->pm_class), 275279377Simp pd->pm_enabled ? "ENABLED" : "DISABLED", 276279377Simp pmc_name_of_disposition(pd->pm_rowdisp)); 277279377Simp 278279377Simp if (pd->pm_ownerpid != -1) { 279279377Simp printf(" (pid %d)", pd->pm_ownerpid); 280279377Simp printf(" %-32s", 281279377Simp pmc_name_of_event(pd->pm_event)); 282279377Simp if (PMC_IS_SAMPLING_MODE(pd->pm_mode)) 283279377Simp printf(" (reload count %jd)", 284279377Simp pd->pm_reloadcount); 285279377Simp } 286279377Simp printf("\n"); 287279377Simp } 288279377Simp free(pi); 289279377Simp } 290279377Simp return 0; 291279377Simp} 292279377Simp 293279377Simpstatic int 294279377Simppmcc_do_list_events(void) 295279377Simp{ 296279377Simp enum pmc_class c; 297279377Simp unsigned int i, j, nevents; 298279377Simp const char **eventnamelist; 299279377Simp const struct pmc_cpuinfo *ci; 300279377Simp 301279377Simp if (pmc_cpuinfo(&ci) != 0) 302279377Simp err(EX_OSERR, "Unable to determine CPU information"); 303279377Simp 304279377Simp eventnamelist = NULL; 305279377Simp 306279377Simp for (i = 0; i < ci->pm_nclass; i++) { 307279377Simp c = ci->pm_classes[i].pm_class; 308279377Simp 309279377Simp printf("%s\n", pmc_name_of_class(c)); 310279377Simp if (pmc_event_names_of_class(c, &eventnamelist, &nevents) < 0) 311279377Simp err(EX_OSERR, "ERROR: Cannot find information for " 312279377Simp "event class \"%s\"", pmc_name_of_class(c)); 313279377Simp 314279377Simp for (j = 0; j < nevents; j++) 315279377Simp printf("\t%s\n", eventnamelist[j]); 316279377Simp 317279377Simp free(eventnamelist); 318279377Simp } 319279377Simp return 0; 320279377Simp} 321279377Simp 322279377Simpstatic int 323279377Simppmcc_show_statistics(void) 324279377Simp{ 325279377Simp 326279377Simp struct pmc_driverstats gms; 327279377Simp 328279377Simp if (pmc_get_driver_stats(&gms) < 0) 329279377Simp err(EX_OSERR, "ERROR: cannot retrieve driver statistics"); 330279377Simp 331279377Simp /* 332279377Simp * Print statistics. 333279377Simp */ 334279377Simp 335279377Simp#define PRINT(N,V) (void) printf("%-40s %d\n", (N), gms.pm_##V) 336279377Simp PRINT("interrupts processed:", intr_processed); 337279377Simp PRINT("non-PMC interrupts:", intr_ignored); 338279377Simp PRINT("sampling stalls due to space shortages:", intr_bufferfull); 339279377Simp PRINT("system calls:", syscalls); 340279377Simp PRINT("system calls with errors:", syscall_errors); 341279377Simp PRINT("buffer requests:", buffer_requests); 342279377Simp PRINT("buffer requests failed:", buffer_requests_failed); 343279377Simp PRINT("sampling log sweeps:", log_sweeps); 344279377Simp 345279377Simp return 0; 346279377Simp} 347279377Simp 348279377Simp/* 349279377Simp * Main 350279377Simp */ 351279377Simp 352279377Simpint 353279377Simpmain(int argc, char **argv) 354279377Simp{ 355279377Simp int error, command, currentcpu, option, pmc; 356279377Simp char *dummy; 357279377Simp struct pmcc_op *p; 358279377Simp 359279377Simp#if DEBUG 360279377Simp pmcc_init_debug(); 361279377Simp#endif 362279377Simp 363279377Simp /* parse args */ 364279377Simp 365279377Simp currentcpu = PMCC_CPU_ALL; 366279377Simp command = PMCC_PRINT_USAGE; 367279377Simp error = 0; 368279377Simp 369279377Simp STAILQ_INIT(&head); 370279377Simp 371279377Simp while ((option = getopt(argc, argv, ":c:d:e:lLs")) != -1) 372279377Simp switch (option) { 373279377Simp case 'L': 374279377Simp if (command != PMCC_PRINT_USAGE) { 375279377Simp error = 1; 376279377Simp break; 377279377Simp } 378279377Simp command = PMCC_PRINT_EVENTS; 379279377Simp break; 380279377Simp 381279377Simp case 'c': 382279377Simp if (command != PMCC_PRINT_USAGE && 383279377Simp command != PMCC_ENABLE_DISABLE) { 384279377Simp error = 1; 385279377Simp break; 386279377Simp } 387279377Simp command = PMCC_ENABLE_DISABLE; 388279377Simp 389279377Simp if (*optarg == PMCC_CPU_WILDCARD) 390279377Simp currentcpu = PMCC_CPU_ALL; 391279377Simp else { 392279377Simp currentcpu = strtoul(optarg, &dummy, 0); 393279377Simp if (*dummy != '\0' || currentcpu < 0) 394279377Simp errx(EX_DATAERR, 395279377Simp "\"%s\" is not a valid CPU id", 396279377Simp optarg); 397279377Simp } 398279377Simp break; 399279377Simp 400279377Simp case 'd': 401279377Simp case 'e': 402279377Simp if (command != PMCC_PRINT_USAGE && 403279377Simp command != PMCC_ENABLE_DISABLE) { 404279377Simp error = 1; 405279377Simp break; 406279377Simp } 407279377Simp command = PMCC_ENABLE_DISABLE; 408279377Simp 409279377Simp if (*optarg == PMCC_PMC_WILDCARD) 410279377Simp pmc = PMCC_PMC_ALL; 411279377Simp else { 412279377Simp pmc = strtoul(optarg, &dummy, 0); 413279377Simp if (*dummy != '\0' || pmc < 0) 414279377Simp errx(EX_DATAERR, 415279377Simp "\"%s\" is not a valid PMC id", 416279377Simp optarg); 417279377Simp } 418279377Simp 419279377Simp if ((p = malloc(sizeof(*p))) == NULL) 420279377Simp err(EX_SOFTWARE, "Out of memory"); 421279377Simp 422279377Simp p->op_cpu = currentcpu; 423279377Simp p->op_pmc = pmc; 424279377Simp p->op_op = option == 'd' ? PMCC_OP_DISABLE : 425279377Simp PMCC_OP_ENABLE; 426279377Simp 427279377Simp STAILQ_INSERT_TAIL(&head, p, op_next); 428279377Simp break; 429279377Simp 430279377Simp case 'l': 431279377Simp if (command != PMCC_PRINT_USAGE) { 432279377Simp error = 1; 433279377Simp break; 434279377Simp } 435279377Simp command = PMCC_LIST_STATE; 436279377Simp break; 437279377Simp 438279377Simp case 's': 439279377Simp if (command != PMCC_PRINT_USAGE) { 440279377Simp error = 1; 441279377Simp break; 442279377Simp } 443279377Simp command = PMCC_SHOW_STATISTICS; 444279377Simp break; 445279377Simp 446279377Simp case ':': 447279377Simp errx(EX_USAGE, 448279377Simp "Missing argument to option '-%c'", optopt); 449279377Simp break; 450279377Simp 451279377Simp case '?': 452279377Simp warnx("Unrecognized option \"-%c\"", optopt); 453279377Simp errx(EX_USAGE, usage_message); 454279377Simp break; 455279377Simp 456279377Simp default: 457279377Simp error = 1; 458279377Simp break; 459279377Simp 460279377Simp } 461279377Simp 462279377Simp if (command == PMCC_PRINT_USAGE) 463279377Simp (void) errx(EX_USAGE, usage_message); 464279377Simp 465279377Simp if (error) 466279377Simp exit(EX_USAGE); 467279377Simp 468279377Simp if (pmc_init() < 0) 469279377Simp err(EX_UNAVAILABLE, 470279377Simp "Initialization of the pmc(3) library failed"); 471279377Simp 472279377Simp switch (command) { 473279377Simp case PMCC_LIST_STATE: 474279377Simp error = pmcc_do_list_state(); 475279377Simp break; 476279377Simp case PMCC_PRINT_EVENTS: 477279377Simp error = pmcc_do_list_events(); 478279377Simp break; 479279377Simp case PMCC_SHOW_STATISTICS: 480279377Simp error = pmcc_show_statistics(); 481279377Simp break; 482279377Simp case PMCC_ENABLE_DISABLE: 483279377Simp if (STAILQ_EMPTY(&head)) 484279377Simp errx(EX_USAGE, "No PMCs specified to enable or disable"); 485279377Simp error = pmcc_do_enable_disable(&head); 486279377Simp break; 487279377Simp default: 488279377Simp assert(0); 489279377Simp 490279377Simp } 491279377Simp 492279377Simp if (error != 0) 493279377Simp err(EX_OSERR, "Command failed"); 494279377Simp exit(0); 495279377Simp} 496279377Simp