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