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