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