pmccontrol.c revision 224057
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 224057 2011-07-15 11:30:41Z avg $"); 30 31#include <sys/param.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 " -l\n" 92 " " PMCC_PROGRAM_NAME " -s\n" 93 " " PMCC_PROGRAM_NAME " [-e pmc | -d pmc | -c cpu] ..."; 94 95#if DEBUG 96FILE *debug_stream = NULL; 97#endif 98 99#if DEBUG 100#define DEBUG_MSG(...) \ 101 (void) fprintf(debug_stream, "[pmccontrol] " __VA_ARGS__); 102#else 103#define DEBUG_MSG(m) /* */ 104#endif /* !DEBUG */ 105 106int pmc_syscall = -1; 107 108#define PMC_CALL(cmd, params) \ 109if ((error = syscall(pmc_syscall, PMC_OP_##cmd, (params))) != 0) \ 110{ \ 111 DEBUG_MSG("ERROR: syscall [" #cmd "]"); \ 112 exit(EX_OSERR); \ 113} 114 115#if DEBUG 116/* log debug messages to a separate file */ 117static void 118pmcc_init_debug(void) 119{ 120 char *fn; 121 122 fn = getenv("PMCCONTROL_DEBUG"); 123 if (fn != NULL) 124 { 125 debug_stream = fopen(fn, "w"); 126 if (debug_stream == NULL) 127 debug_stream = stderr; 128 } else 129 debug_stream = stderr; 130} 131#endif 132 133static int 134pmcc_do_enable_disable(struct pmcc_op_list *op_list) 135{ 136 int c, error, i, j, ncpu, npmc, t; 137 struct pmcc_op *np; 138 unsigned char *map; 139 unsigned char op; 140 int cpu, pmc; 141 142 if ((ncpu = pmc_ncpu()) < 0) 143 err(EX_OSERR, "Unable to determine the number of cpus"); 144 145 /* Determine the maximum number of PMCs in any CPU. */ 146 npmc = 0; 147 for (c = 0; c < ncpu; c++) { 148 if ((t = pmc_npmc(c)) < 0) 149 err(EX_OSERR, "Unable to determine the number of " 150 "PMCs in CPU %d", c); 151 npmc = t > npmc ? t : npmc; 152 } 153 154 if (npmc == 0) 155 errx(EX_CONFIG, "No PMCs found"); 156 157 if ((map = malloc(npmc * ncpu)) == NULL) 158 err(EX_SOFTWARE, "Out of memory"); 159 160 (void) memset(map, PMCC_OP_IGNORE, npmc*ncpu); 161 162 error = 0; 163 STAILQ_FOREACH(np, op_list, op_next) { 164 165 cpu = np->op_cpu; 166 pmc = np->op_pmc; 167 op = np->op_op; 168 169 if (cpu >= ncpu) 170 errx(EX_DATAERR, "CPU id too large: \"%d\"", cpu); 171 172 if (pmc >= npmc) 173 errx(EX_DATAERR, "PMC id too large: \"%d\"", pmc); 174 175#define MARKMAP(M,C,P,V) do { \ 176 *((M) + (C)*npmc + (P)) = (V); \ 177} while (0) 178 179#define SET_PMCS(C,P,V) do { \ 180 if ((P) == PMCC_PMC_ALL) { \ 181 for (j = 0; j < npmc; j++) \ 182 MARKMAP(map, (C), j, (V)); \ 183 } else \ 184 MARKMAP(map, (C), (P), (V)); \ 185} while (0) 186 187#define MAP(M,C,P) (*((M) + (C)*npmc + (P))) 188 189 if (cpu == PMCC_CPU_ALL) 190 for (i = 0; i < ncpu; i++) { 191 SET_PMCS(i, pmc, op); 192 } 193 else 194 SET_PMCS(cpu, pmc, op); 195 } 196 197 /* Configure PMCS */ 198 for (i = 0; i < ncpu; i++) 199 for (j = 0; j < npmc; j++) { 200 unsigned char b; 201 202 b = MAP(map, i, j); 203 204 error = 0; 205 206 if (b == PMCC_OP_ENABLE) 207 error = pmc_enable(i, j); 208 else if (b == PMCC_OP_DISABLE) 209 error = pmc_disable(i, j); 210 211 if (error < 0) 212 err(EX_OSERR, "%s of PMC %d on CPU %d failed", 213 b == PMCC_OP_ENABLE ? "Enable" : 214 "Disable", j, i); 215 } 216 217 return error; 218} 219 220static int 221pmcc_do_list_state(void) 222{ 223 size_t dummy; 224 int c, cpu, n, npmc, ncpu; 225 unsigned int logical_cpus_mask; 226 struct pmc_info *pd; 227 struct pmc_pmcinfo *pi; 228 const struct pmc_cpuinfo *pc; 229 230 if (pmc_cpuinfo(&pc) != 0) 231 err(EX_OSERR, "Unable to determine CPU information"); 232 233 printf("%d %s CPUs present, with %d PMCs per CPU\n", pc->pm_ncpu, 234 pmc_name_of_cputype(pc->pm_cputype), 235 pc->pm_npmc); 236 237 dummy = sizeof(logical_cpus_mask); 238 if (sysctlbyname("machdep.logical_cpus_mask", &logical_cpus_mask, 239 &dummy, NULL, 0) < 0) 240 logical_cpus_mask = 0; 241 242 ncpu = pc->pm_ncpu; 243 244 for (c = cpu = 0; cpu < ncpu; cpu++) { 245#if defined(__i386__) || defined(__amd64__) 246 if (pc->pm_cputype == PMC_CPU_INTEL_PIV && 247 (logical_cpus_mask & (1 << cpu))) 248 continue; /* skip P4-style 'logical' cpus */ 249#endif 250 if (pmc_pmcinfo(cpu, &pi) < 0) { 251 if (errno == ENXIO) 252 continue; 253 err(EX_OSERR, "Unable to get PMC status for CPU %d", 254 cpu); 255 } 256 257 printf("#CPU %d:\n", c++); 258 npmc = pmc_npmc(cpu); 259 printf("#N NAME CLASS STATE ROW-DISP\n"); 260 261 for (n = 0; n < npmc; n++) { 262 pd = &pi->pm_pmcs[n]; 263 264 printf(" %-2d %-16s %-6s %-8s %-10s", 265 n, 266 pd->pm_name, 267 pmc_name_of_class(pd->pm_class), 268 pd->pm_enabled ? "ENABLED" : "DISABLED", 269 pmc_name_of_disposition(pd->pm_rowdisp)); 270 271 if (pd->pm_ownerpid != -1) { 272 printf(" (pid %d)", pd->pm_ownerpid); 273 printf(" %-32s", 274 pmc_name_of_event(pd->pm_event)); 275 if (PMC_IS_SAMPLING_MODE(pd->pm_mode)) 276 printf(" (reload count %jd)", 277 pd->pm_reloadcount); 278 } 279 printf("\n"); 280 } 281 free(pi); 282 } 283 return 0; 284} 285 286static int 287pmcc_do_list_events(void) 288{ 289 enum pmc_class c; 290 unsigned int i, j, nevents; 291 const char **eventnamelist; 292 const struct pmc_cpuinfo *ci; 293 294 if (pmc_cpuinfo(&ci) != 0) 295 err(EX_OSERR, "Unable to determine CPU information"); 296 297 eventnamelist = NULL; 298 299 for (i = 0; i < ci->pm_nclass; i++) { 300 c = ci->pm_classes[i].pm_class; 301 302 printf("%s\n", pmc_name_of_class(c)); 303 if (pmc_event_names_of_class(c, &eventnamelist, &nevents) < 0) 304 err(EX_OSERR, "ERROR: Cannot find information for " 305 "event class \"%s\"", pmc_name_of_class(c)); 306 307 for (j = 0; j < nevents; j++) 308 printf("\t%s\n", eventnamelist[j]); 309 310 free(eventnamelist); 311 } 312 return 0; 313} 314 315static int 316pmcc_show_statistics(void) 317{ 318 319 struct pmc_driverstats gms; 320 321 if (pmc_get_driver_stats(&gms) < 0) 322 err(EX_OSERR, "ERROR: cannot retrieve driver statistics"); 323 324 /* 325 * Print statistics. 326 */ 327 328#define PRINT(N,V) (void) printf("%-40s %d\n", (N), gms.pm_##V) 329 PRINT("interrupts processed:", intr_processed); 330 PRINT("non-PMC interrupts:", intr_ignored); 331 PRINT("sampling stalls due to space shortages:", intr_bufferfull); 332 PRINT("system calls:", syscalls); 333 PRINT("system calls with errors:", syscall_errors); 334 PRINT("buffer requests:", buffer_requests); 335 PRINT("buffer requests failed:", buffer_requests_failed); 336 PRINT("sampling log sweeps:", log_sweeps); 337 338 return 0; 339} 340 341/* 342 * Main 343 */ 344 345int 346main(int argc, char **argv) 347{ 348 int error, command, currentcpu, option, pmc; 349 char *dummy; 350 struct pmcc_op *p; 351 352#if DEBUG 353 pmcc_init_debug(); 354#endif 355 356 /* parse args */ 357 358 currentcpu = PMCC_CPU_ALL; 359 command = PMCC_PRINT_USAGE; 360 error = 0; 361 362 STAILQ_INIT(&head); 363 364 while ((option = getopt(argc, argv, ":c:d:e:lLs")) != -1) 365 switch (option) { 366 case 'L': 367 if (command != PMCC_PRINT_USAGE) { 368 error = 1; 369 break; 370 } 371 command = PMCC_PRINT_EVENTS; 372 break; 373 374 case 'c': 375 if (command != PMCC_PRINT_USAGE && 376 command != PMCC_ENABLE_DISABLE) { 377 error = 1; 378 break; 379 } 380 command = PMCC_ENABLE_DISABLE; 381 382 if (*optarg == PMCC_CPU_WILDCARD) 383 currentcpu = PMCC_CPU_ALL; 384 else { 385 currentcpu = strtoul(optarg, &dummy, 0); 386 if (*dummy != '\0' || currentcpu < 0) 387 errx(EX_DATAERR, 388 "\"%s\" is not a valid CPU id", 389 optarg); 390 } 391 break; 392 393 case 'd': 394 case 'e': 395 if (command != PMCC_PRINT_USAGE && 396 command != PMCC_ENABLE_DISABLE) { 397 error = 1; 398 break; 399 } 400 command = PMCC_ENABLE_DISABLE; 401 402 if (*optarg == PMCC_PMC_WILDCARD) 403 pmc = PMCC_PMC_ALL; 404 else { 405 pmc = strtoul(optarg, &dummy, 0); 406 if (*dummy != '\0' || pmc < 0) 407 errx(EX_DATAERR, 408 "\"%s\" is not a valid PMC id", 409 optarg); 410 } 411 412 if ((p = malloc(sizeof(*p))) == NULL) 413 err(EX_SOFTWARE, "Out of memory"); 414 415 p->op_cpu = currentcpu; 416 p->op_pmc = pmc; 417 p->op_op = option == 'd' ? PMCC_OP_DISABLE : 418 PMCC_OP_ENABLE; 419 420 STAILQ_INSERT_TAIL(&head, p, op_next); 421 break; 422 423 case 'l': 424 if (command != PMCC_PRINT_USAGE) { 425 error = 1; 426 break; 427 } 428 command = PMCC_LIST_STATE; 429 break; 430 431 case 's': 432 if (command != PMCC_PRINT_USAGE) { 433 error = 1; 434 break; 435 } 436 command = PMCC_SHOW_STATISTICS; 437 break; 438 439 case ':': 440 errx(EX_USAGE, 441 "Missing argument to option '-%c'", optopt); 442 break; 443 444 case '?': 445 warnx("Unrecognized option \"-%c\"", optopt); 446 errx(EX_USAGE, usage_message); 447 break; 448 449 default: 450 error = 1; 451 break; 452 453 } 454 455 if (command == PMCC_PRINT_USAGE) 456 (void) errx(EX_USAGE, usage_message); 457 458 if (error) 459 exit(EX_USAGE); 460 461 if (pmc_init() < 0) 462 err(EX_UNAVAILABLE, 463 "Initialization of the pmc(3) library failed"); 464 465 switch (command) { 466 case PMCC_LIST_STATE: 467 error = pmcc_do_list_state(); 468 break; 469 case PMCC_PRINT_EVENTS: 470 error = pmcc_do_list_events(); 471 break; 472 case PMCC_SHOW_STATISTICS: 473 error = pmcc_show_statistics(); 474 break; 475 case PMCC_ENABLE_DISABLE: 476 if (STAILQ_EMPTY(&head)) 477 errx(EX_USAGE, "No PMCs specified to enable or disable"); 478 error = pmcc_do_enable_disable(&head); 479 break; 480 default: 481 assert(0); 482 483 } 484 485 if (error != 0) 486 err(EX_OSERR, "Command failed"); 487 exit(0); 488} 489