1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2003,2004 Joseph Koshy 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/param.h> 31 #include <sys/queue.h> 32 #include <sys/cpuset.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 static 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 static 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 #if DEBUG 107 /* log debug messages to a separate file */ 108 static void 109 pmcc_init_debug(void) 110 { 111 char *fn; 112 113 fn = getenv("PMCCONTROL_DEBUG"); 114 if (fn != NULL) 115 { 116 debug_stream = fopen(fn, "w"); 117 if (debug_stream == NULL) 118 debug_stream = stderr; 119 } else 120 debug_stream = stderr; 121 } 122 #endif 123 124 static int 125 pmcc_do_enable_disable(struct pmcc_op_list *op_list) 126 { 127 int c, error, i, j, ncpu, npmc, t; 128 struct pmcc_op *np; 129 unsigned char *map; 130 unsigned char op; 131 int cpu, pmc; 132 133 if ((ncpu = pmc_ncpu()) < 0) 134 err(EX_OSERR, "Unable to determine the number of cpus"); 135 136 /* Determine the maximum number of PMCs in any CPU. */ 137 npmc = 0; 138 for (c = 0; c < ncpu; c++) { 139 if ((t = pmc_npmc(c)) < 0) 140 err(EX_OSERR, 141 "Unable to determine the number of PMCs in CPU %d", 142 c); 143 npmc = MAX(t, npmc); 144 } 145 146 if (npmc == 0) 147 errx(EX_CONFIG, "No PMCs found"); 148 149 if ((map = calloc(npmc, ncpu)) == NULL) 150 err(EX_SOFTWARE, "Out of memory"); 151 152 error = 0; 153 STAILQ_FOREACH(np, op_list, op_next) { 154 155 cpu = np->op_cpu; 156 pmc = np->op_pmc; 157 op = np->op_op; 158 159 if (cpu >= ncpu) 160 errx(EX_DATAERR, "CPU id too large: \"%d\"", cpu); 161 162 if (pmc >= npmc) 163 errx(EX_DATAERR, "PMC id too large: \"%d\"", pmc); 164 165 #define MARKMAP(M,C,P,V) do { \ 166 *((M) + (C)*npmc + (P)) = (V); \ 167 } while (0) 168 169 #define SET_PMCS(C,P,V) do { \ 170 if ((P) == PMCC_PMC_ALL) { \ 171 for (j = 0; j < npmc; j++) \ 172 MARKMAP(map, (C), j, (V)); \ 173 } else \ 174 MARKMAP(map, (C), (P), (V)); \ 175 } while (0) 176 177 #define MAP(M,C,P) (*((M) + (C)*npmc + (P))) 178 179 if (cpu == PMCC_CPU_ALL) 180 for (i = 0; i < ncpu; i++) { 181 SET_PMCS(i, pmc, op); 182 } 183 else 184 SET_PMCS(cpu, pmc, op); 185 } 186 187 /* Configure PMCS */ 188 for (i = 0; i < ncpu; i++) 189 for (j = 0; j < npmc; j++) { 190 unsigned char b; 191 192 b = MAP(map, i, j); 193 194 error = 0; 195 196 if (b == PMCC_OP_ENABLE) 197 error = pmc_enable(i, j); 198 else if (b == PMCC_OP_DISABLE) 199 error = pmc_disable(i, j); 200 201 if (error < 0) 202 err(EX_OSERR, "%s of PMC %d on CPU %d failed", 203 b == PMCC_OP_ENABLE ? "Enable" : "Disable", 204 j, i); 205 } 206 207 return error; 208 } 209 210 static int 211 pmcc_do_list_state(void) 212 { 213 cpuset_t logical_cpus_mask; 214 long cpusetsize; 215 size_t setsize; 216 int c, cpu, n, npmc, ncpu; 217 struct pmc_info *pd; 218 struct pmc_pmcinfo *pi; 219 const struct pmc_cpuinfo *pc; 220 221 if (pmc_cpuinfo(&pc) != 0) 222 err(EX_OSERR, "Unable to determine CPU information"); 223 224 printf("%d %s CPUs present, with %d PMCs per CPU\n", pc->pm_ncpu, 225 pmc_name_of_cputype(pc->pm_cputype), 226 pc->pm_npmc); 227 228 /* Determine the set of logical CPUs. */ 229 cpusetsize = sysconf(_SC_CPUSET_SIZE); 230 if (cpusetsize == -1 || (u_long)cpusetsize > sizeof(cpuset_t)) 231 err(EX_OSERR, "Cannot determine which CPUs are logical"); 232 CPU_ZERO(&logical_cpus_mask); 233 setsize = (size_t)cpusetsize; 234 if (sysctlbyname("machdep.logical_cpus_mask", &logical_cpus_mask, 235 &setsize, NULL, 0) < 0) 236 CPU_ZERO(&logical_cpus_mask); 237 238 ncpu = pc->pm_ncpu; 239 240 for (c = cpu = 0; cpu < ncpu; cpu++) { 241 if (pmc_pmcinfo(cpu, &pi) < 0) { 242 if (errno == ENXIO) 243 continue; 244 err(EX_OSERR, "Unable to get PMC status for CPU %d", 245 cpu); 246 } 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 277 static int 278 pmcc_do_list_events(void) 279 { 280 enum pmc_class c; 281 unsigned int i, j, nevents; 282 const char **eventnamelist; 283 const struct pmc_cpuinfo *ci; 284 285 /* First, try pmu events. */ 286 if (pmc_pmu_enabled()) { 287 pmc_pmu_print_counters(NULL); 288 return (0); 289 } 290 291 /* Otherwise, use the legacy pmc(3) interfaces. */ 292 if (pmc_cpuinfo(&ci) != 0) 293 err(EX_OSERR, "Unable to determine CPU information"); 294 295 eventnamelist = NULL; 296 297 for (i = 0; i < ci->pm_nclass; i++) { 298 c = ci->pm_classes[i].pm_class; 299 300 printf("%s\n", pmc_name_of_class(c)); 301 if (pmc_event_names_of_class(c, &eventnamelist, &nevents) < 0) 302 err(EX_OSERR, 303 "ERROR: Cannot find information for event class \"%s\"", 304 pmc_name_of_class(c)); 305 306 for (j = 0; j < nevents; j++) 307 printf("\t%s\n", eventnamelist[j]); 308 309 free(eventnamelist); 310 } 311 return 0; 312 } 313 314 static int 315 pmcc_show_statistics(void) 316 { 317 318 struct pmc_driverstats gms; 319 320 if (pmc_get_driver_stats(&gms) < 0) 321 err(EX_OSERR, "ERROR: cannot retrieve driver statistics"); 322 323 /* 324 * Print statistics. 325 */ 326 327 #define PRINT(N,V) (void) printf("%-40s %d\n", (N), gms.pm_##V) 328 PRINT("interrupts processed:", intr_processed); 329 PRINT("non-PMC interrupts:", intr_ignored); 330 PRINT("sampling stalls due to space shortages:", intr_bufferfull); 331 PRINT("system calls:", syscalls); 332 PRINT("system calls with errors:", syscall_errors); 333 PRINT("buffer requests:", buffer_requests); 334 PRINT("buffer requests failed:", buffer_requests_failed); 335 PRINT("sampling log sweeps:", log_sweeps); 336 337 return 0; 338 } 339 340 /* 341 * Main 342 */ 343 344 int 345 main(int argc, char **argv) 346 { 347 int error, command, currentcpu, option, pmc; 348 char *dummy; 349 struct pmcc_op *p; 350 351 #if DEBUG 352 pmcc_init_debug(); 353 #endif 354 355 /* parse args */ 356 357 currentcpu = PMCC_CPU_ALL; 358 command = PMCC_PRINT_USAGE; 359 error = 0; 360 361 STAILQ_INIT(&head); 362 363 while ((option = getopt(argc, argv, ":c:d:e:lLs")) != -1) 364 switch (option) { 365 case 'L': 366 if (command != PMCC_PRINT_USAGE) { 367 error = 1; 368 break; 369 } 370 command = PMCC_PRINT_EVENTS; 371 break; 372 373 case 'c': 374 if (command != PMCC_PRINT_USAGE && 375 command != PMCC_ENABLE_DISABLE) { 376 error = 1; 377 break; 378 } 379 command = PMCC_ENABLE_DISABLE; 380 381 if (*optarg == PMCC_CPU_WILDCARD) 382 currentcpu = PMCC_CPU_ALL; 383 else { 384 currentcpu = strtoul(optarg, &dummy, 0); 385 if (*dummy != '\0' || currentcpu < 0) 386 errx(EX_DATAERR, 387 "\"%s\" is not a valid CPU id", 388 optarg); 389 } 390 break; 391 392 case 'd': 393 case 'e': 394 if (command != PMCC_PRINT_USAGE && 395 command != PMCC_ENABLE_DISABLE) { 396 error = 1; 397 break; 398 } 399 command = PMCC_ENABLE_DISABLE; 400 401 if (*optarg == PMCC_PMC_WILDCARD) 402 pmc = PMCC_PMC_ALL; 403 else { 404 pmc = strtoul(optarg, &dummy, 0); 405 if (*dummy != '\0' || pmc < 0) 406 errx(EX_DATAERR, 407 "\"%s\" is not a valid PMC id", 408 optarg); 409 } 410 411 if ((p = malloc(sizeof(*p))) == NULL) 412 err(EX_SOFTWARE, "Out of memory"); 413 414 p->op_cpu = currentcpu; 415 p->op_pmc = pmc; 416 p->op_op = option == 'd' ? PMCC_OP_DISABLE : 417 PMCC_OP_ENABLE; 418 419 STAILQ_INSERT_TAIL(&head, p, op_next); 420 break; 421 422 case 'l': 423 if (command != PMCC_PRINT_USAGE) { 424 error = 1; 425 break; 426 } 427 command = PMCC_LIST_STATE; 428 break; 429 430 case 's': 431 if (command != PMCC_PRINT_USAGE) { 432 error = 1; 433 break; 434 } 435 command = PMCC_SHOW_STATISTICS; 436 break; 437 438 case ':': 439 errx(EX_USAGE, 440 "Missing argument to option '-%c'", optopt); 441 break; 442 443 case '?': 444 warnx("Unrecognized option \"-%c\"", optopt); 445 errx(EX_USAGE, "%s", usage_message); 446 break; 447 448 default: 449 error = 1; 450 break; 451 452 } 453 454 if (command == PMCC_PRINT_USAGE) 455 (void) errx(EX_USAGE, "%s", usage_message); 456 457 if (error) 458 exit(EX_USAGE); 459 460 if (pmc_init() < 0) 461 err(EX_UNAVAILABLE, 462 "Initialization of the pmc(3) library failed"); 463 464 switch (command) { 465 case PMCC_LIST_STATE: 466 error = pmcc_do_list_state(); 467 break; 468 case PMCC_PRINT_EVENTS: 469 error = pmcc_do_list_events(); 470 break; 471 case PMCC_SHOW_STATISTICS: 472 error = pmcc_show_statistics(); 473 break; 474 case PMCC_ENABLE_DISABLE: 475 if (STAILQ_EMPTY(&head)) 476 errx(EX_USAGE, 477 "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