pmccontrol.c revision 145774
1/*- 2 * Copyright (c) 2003,2004 Joseph Koshy 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/usr.sbin/pmccontrol/pmccontrol.c 145774 2005-05-01 14:11:49Z jkoshy $"); 30 31#include <sys/types.h> 32#include <sys/queue.h> 33#include <sys/sysctl.h> 34 35#include <assert.h> 36#include <err.h> 37#include <errno.h> 38#include <fcntl.h> 39#include <limits.h> 40#include <pmc.h> 41#include <stdarg.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <sysexits.h> 46#include <unistd.h> 47 48/* Compile time defaults */ 49 50#define PMCC_PRINT_USAGE 0 51#define PMCC_PRINT_EVENTS 1 52#define PMCC_LIST_STATE 2 53#define PMCC_ENABLE_DISABLE 3 54#define PMCC_SHOW_STATISTICS 4 55 56#define PMCC_CPU_ALL -1 57#define PMCC_CPU_WILDCARD '*' 58 59#define PMCC_PMC_ALL -1 60#define PMCC_PMC_WILDCARD '*' 61 62#define PMCC_OP_IGNORE 0 63#define PMCC_OP_DISABLE 1 64#define PMCC_OP_ENABLE 2 65 66#define PMCC_PROGRAM_NAME "pmccontrol" 67 68STAILQ_HEAD(pmcc_op_list, pmcc_op) head = STAILQ_HEAD_INITIALIZER(head); 69 70struct pmcc_op { 71 char op_cpu; 72 char op_pmc; 73 char op_op; 74 STAILQ_ENTRY(pmcc_op) op_next; 75}; 76 77/* Function Prototypes */ 78#if DEBUG 79static void pmcc_init_debug(void); 80#endif 81 82static int pmcc_do_list_state(void); 83static int pmcc_do_enable_disable(struct pmcc_op_list *); 84static int pmcc_do_list_events(void); 85 86/* Globals */ 87 88static char usage_message[] = 89 "Usage:\n" 90 " " PMCC_PROGRAM_NAME " -l\n" 91 " " PMCC_PROGRAM_NAME " -s\n" 92 " " PMCC_PROGRAM_NAME " [-e pmc | -d pmc | -c cpu] ..."; 93 94#if DEBUG 95FILE *debug_stream = NULL; 96#endif 97 98#if DEBUG 99#define DEBUG_MSG(...) \ 100 (void) fprintf(debug_stream, "[pmccontrol] " __VA_ARGS__); 101#else 102#define DEBUG_MSG(m) /* */ 103#endif /* !DEBUG */ 104 105int pmc_syscall = -1; 106 107#define PMC_CALL(cmd, params) \ 108if ((error = syscall(pmc_syscall, PMC_OP_##cmd, (params))) != 0) \ 109{ \ 110 DEBUG_MSG("ERROR: syscall [" #cmd "]"); \ 111 exit(EX_OSERR); \ 112} 113 114#if DEBUG 115/* log debug messages to a separate file */ 116static void 117pmcc_init_debug(void) 118{ 119 char *fn; 120 121 fn = getenv("PMCCONTROL_DEBUG"); 122 if (fn != NULL) 123 { 124 debug_stream = fopen(fn, "w"); 125 if (debug_stream == NULL) 126 debug_stream = stderr; 127 } else 128 debug_stream = stderr; 129} 130#endif 131 132static int 133pmcc_do_enable_disable(struct pmcc_op_list *op_list) 134{ 135 unsigned char op; 136 int c, error, i, j, ncpu, npmc, t; 137 int cpu, pmc; 138 struct pmcc_op *np; 139 unsigned char *map; 140 141 if ((ncpu = pmc_ncpu()) < 0) 142 err(EX_OSERR, "Unable to determine the number of cpus"); 143 144 /* determine the maximum number of PMCs in any CPU */ 145 npmc = 0; 146 for (c = 0; c < ncpu; c++) { 147 if ((t = pmc_npmc(c)) < 0) 148 err(EX_OSERR, "Unable to determine the number of PMCs in " 149 "CPU %d", c); 150 npmc = t > npmc ? t : npmc; 151 } 152 153 if (npmc == 0) 154 errx(EX_CONFIG, "No PMCs found"); 155 156 if ((map = malloc(npmc * ncpu)) == NULL) 157 err(EX_SOFTWARE, "Out of memory"); 158 159 (void) memset(map, PMCC_OP_IGNORE, npmc*ncpu); 160 161 error = 0; 162 STAILQ_FOREACH(np, op_list, op_next) { 163 164 cpu = np->op_cpu; 165 pmc = np->op_pmc; 166 op = np->op_op; 167 168 if (cpu >= ncpu) 169 errx(EX_DATAERR, "CPU id too large: \"%d\"", cpu); 170 171 if (pmc >= npmc) 172 errx(EX_DATAERR, "PMC id too large: \"%d\"", pmc); 173 174#define MARKMAP(M,C,P,V) do { \ 175 *((M) + (C)*npmc + (P)) = (V); \ 176} while (0) 177 178#define SET_PMCS(C,P,V) do { \ 179 if ((P) == PMCC_PMC_ALL) { \ 180 for (j = 0; j < npmc; j++) \ 181 MARKMAP(map, (C), j, (V)); \ 182 } else \ 183 MARKMAP(map, (C), (P), (V)); \ 184} while (0) 185 186#define MAP(M,C,P) (*((M) + (C)*npmc + (P))) 187 188 if (cpu == PMCC_CPU_ALL) 189 for (i = 0; i < ncpu; i++) 190 SET_PMCS(i, pmc, op); 191 else 192 SET_PMCS(cpu, pmc, op); 193 } 194 195 /* Configure PMCS */ 196 for (i = 0; i < ncpu; i++) 197 for (j = 0; j < npmc; j++) { 198 unsigned char b; 199 200 b = MAP(map, i, j); 201 202 error = 0; 203 204 if (b == PMCC_OP_ENABLE) 205 error = pmc_enable(i, j); 206 else if (b == PMCC_OP_DISABLE) 207 error = pmc_disable(i, j); 208 209 if (error < 0) 210 err(EX_OSERR, "%s of PMC %d on CPU %d failed", 211 b == PMCC_OP_ENABLE ? "Enable" : 212 "Disable", j, i); 213 } 214 215 return error; 216} 217 218static int 219pmcc_do_list_state(void) 220{ 221 size_t dummy; 222 int c, cpu, n, npmc, ncpu; 223 unsigned int logical_cpus_mask; 224 struct pmc_info *pd; 225 struct pmc_op_getpmcinfo *pi; 226 const struct pmc_op_getcpuinfo *pc; 227 228 if (pmc_cpuinfo(&pc) != 0) 229 err(EX_OSERR, "Unable to determine CPU information"); 230 231 dummy = sizeof(logical_cpus_mask); 232 if (sysctlbyname("machdep.logical_cpus_mask", &logical_cpus_mask, 233 &dummy, NULL, 0) < 0) 234 logical_cpus_mask = 0; 235 236 ncpu = pc->pm_ncpu; 237 238 for (c = cpu = 0; cpu < ncpu; cpu++) { 239#if i386 240 if (pc->pm_cputype == PMC_CPU_INTEL_PIV && 241 (logical_cpus_mask & (1 << cpu))) 242 continue; /* skip P4-style 'logical' cpus */ 243#endif 244 if (pmc_pmcinfo(cpu, &pi) < 0) 245 err(EX_OSERR, "Unable to get PMC status for CPU %d", 246 cpu); 247 248 printf("#CPU %d:\n", c++); 249 npmc = pmc_npmc(cpu); 250 printf("#N NAME CLASS STATE ROW-DISP\n"); 251 252 for (n = 0; n < npmc; n++) { 253 pd = &pi->pm_pmcs[n]; 254 255 printf(" %-2d %-16s %-6s %-8s %-10s", 256 n, 257 pd->pm_name, 258 pmc_name_of_class(pd->pm_class), 259 pd->pm_enabled ? "ENABLED" : "DISABLED", 260 pmc_name_of_disposition(pd->pm_rowdisp)); 261 262 if (pd->pm_ownerpid != -1) { 263 printf(" (pid %d)", pd->pm_ownerpid); 264 printf(" %-32s", 265 pmc_name_of_event(pd->pm_event)); 266 if (PMC_IS_SAMPLING_MODE(pd->pm_mode)) 267 printf(" (reload count %jd)", 268 pd->pm_reloadcount); 269 } 270 printf("\n"); 271 } 272 free(pi); 273 } 274 return 0; 275} 276 277static int 278pmcc_do_list_events(void) 279{ 280 enum pmc_class c; 281 unsigned int i, j, nevents; 282 const char **eventnamelist; 283 const struct pmc_op_getcpuinfo *ci; 284 285 if (pmc_cpuinfo(&ci) != 0) 286 err(EX_OSERR, "Unable to determine CPU information"); 287 288 eventnamelist = NULL; 289 290 for (i = 0; i < ci->pm_nclass; i++) { 291 c = ci->pm_classes[i].pm_class; 292 293 printf("%s\n", pmc_name_of_class(c)); 294 if (pmc_event_names_of_class(c, &eventnamelist, &nevents) < 0) 295 err(EX_OSERR, "ERROR: Cannot find information for " 296 "event class \"%s\"", pmc_name_of_class(c)); 297 298 for (j = 0; j < nevents; j++) 299 printf("\t%s\n", eventnamelist[j]); 300 301 free(eventnamelist); 302 } 303 return 0; 304} 305 306static int 307pmcc_show_statistics(void) 308{ 309 310 struct pmc_op_getdriverstats gms; 311 312 if (pmc_get_driver_stats(&gms) < 0) 313 err(EX_OSERR, "ERROR: cannot retrieve driver statistics"); 314 315 /* 316 * Print statistics. 317 */ 318 319#define PRINT(N,V) (void) printf("%20s %d\n", (N), gms.pm_##V) 320 321 PRINT("interrupts-processed", intr_processed); 322 PRINT("interrupts-ignored", intr_ignored); 323 PRINT("system-calls", syscalls); 324 PRINT("system-calls-with-errors", syscall_errors); 325 326 return 0; 327} 328 329/* 330 * Main 331 */ 332 333int 334main(int argc, char **argv) 335{ 336 int error, command, currentcpu, option, pmc; 337 char *dummy; 338 struct pmcc_op *p; 339 340#if DEBUG 341 pmcc_init_debug(); 342#endif 343 344 /* parse args */ 345 346 currentcpu = PMCC_CPU_ALL; 347 command = PMCC_PRINT_USAGE; 348 error = 0; 349 350 STAILQ_INIT(&head); 351 352 while ((option = getopt(argc, argv, ":c:d:e:lLs")) != -1) 353 switch (option) { 354 case 'L': 355 if (command != PMCC_PRINT_USAGE) { 356 error = 1; 357 break; 358 } 359 command = PMCC_PRINT_EVENTS; 360 break; 361 362 case 'c': 363 if (command != PMCC_PRINT_USAGE && 364 command != PMCC_ENABLE_DISABLE) { 365 error = 1; 366 break; 367 } 368 command = PMCC_ENABLE_DISABLE; 369 370 if (*optarg == PMCC_CPU_WILDCARD) 371 currentcpu = PMCC_CPU_ALL; 372 else { 373 currentcpu = strtoul(optarg, &dummy, 0); 374 if (*dummy != '\0' || currentcpu < 0) 375 errx(EX_DATAERR, 376 "\"%s\" is not a valid CPU id", 377 optarg); 378 } 379 break; 380 381 case 'd': 382 case 'e': 383 if (command != PMCC_PRINT_USAGE && 384 command != PMCC_ENABLE_DISABLE) { 385 error = 1; 386 break; 387 } 388 command = PMCC_ENABLE_DISABLE; 389 390 if (*optarg == PMCC_PMC_WILDCARD) 391 pmc = PMCC_PMC_ALL; 392 else { 393 pmc = strtoul(optarg, &dummy, 0); 394 if (*dummy != '\0' || pmc < 0) 395 errx(EX_DATAERR, 396 "\"%s\" is not a valid PMC id", 397 optarg); 398 } 399 400 if ((p = malloc(sizeof(*p))) == NULL) 401 err(EX_SOFTWARE, "Out of memory"); 402 403 p->op_cpu = currentcpu; 404 p->op_pmc = pmc; 405 p->op_op = option == 'd' ? PMCC_OP_DISABLE : 406 PMCC_OP_ENABLE; 407 408 STAILQ_INSERT_TAIL(&head, p, op_next); 409 break; 410 411 case 'l': 412 if (command != PMCC_PRINT_USAGE) { 413 error = 1; 414 break; 415 } 416 command = PMCC_LIST_STATE; 417 break; 418 419 case 's': 420 if (command != PMCC_PRINT_USAGE) { 421 error = 1; 422 break; 423 } 424 command = PMCC_SHOW_STATISTICS; 425 break; 426 427 case ':': 428 errx(EX_USAGE, 429 "Missing argument to option '-%c'", optopt); 430 break; 431 432 case '?': 433 warnx("Unrecognized option \"-%c\"", optopt); 434 errx(EX_USAGE, usage_message); 435 break; 436 437 default: 438 error = 1; 439 break; 440 441 } 442 443 if (command == PMCC_PRINT_USAGE) 444 (void) errx(EX_USAGE, usage_message); 445 446 if (error) 447 exit(EX_USAGE); 448 449 if (pmc_init() < 0) 450 err(EX_UNAVAILABLE, 451 "Initialization of the pmc(3) library failed"); 452 453 switch (command) { 454 case PMCC_LIST_STATE: 455 error = pmcc_do_list_state(); 456 break; 457 case PMCC_PRINT_EVENTS: 458 error = pmcc_do_list_events(); 459 break; 460 case PMCC_SHOW_STATISTICS: 461 error = pmcc_show_statistics(); 462 break; 463 case PMCC_ENABLE_DISABLE: 464 if (STAILQ_EMPTY(&head)) 465 errx(EX_USAGE, "No PMCs specified to enable or disable"); 466 error = pmcc_do_enable_disable(&head); 467 break; 468 default: 469 assert(0); 470 471 } 472 473 if (error != 0) 474 err(EX_OSERR, "Command failed"); 475 exit(0); 476} 477