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