1 /* $OpenBSD: pctr.c,v 1.24 2019/06/28 13:35:02 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Mike Belopuhov, Aleksey Lomovtsev 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Pentium performance counter control program for OpenBSD. 21 * Copyright 1996 David Mazieres <dm@lcs.mit.edu>. 22 * 23 * Modification and redistribution in source and binary forms is 24 * permitted provided that due credit is given to the author and the 25 * OpenBSD project by leaving this copyright notice intact. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <sys/sysctl.h> 31 #include <sys/ioctl.h> 32 33 #include <machine/cpu.h> 34 #include <machine/pctr.h> 35 #include <machine/specialreg.h> 36 37 #include <errno.h> 38 #include <err.h> 39 #include <fcntl.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #include "pctrvar.h" 46 47 static int cpu_type; 48 static int tsc_avail; 49 50 static int ctr, func, masku, thold; 51 static int cflag, eflag, iflag, kflag, uflag; 52 static int Mflag, Eflag, Sflag, Iflag, Aflag; 53 54 static void pctr_cpu_creds(void); 55 static char *pctr_fn2str(u_int32_t); 56 static void pctr_printvals(struct pctrst *); 57 static int pctr_read(struct pctrst *); 58 static int pctr_write(int, u_int32_t); 59 static void pctr_list_fnct(void); 60 static int pctr_set_cntr(void); 61 static void usage(void); 62 63 int 64 main(int argc, char **argv) 65 { 66 const char *errstr; 67 struct pctrst st; 68 int ch = -1; 69 int list_mode = 0, set_mode = 0; 70 71 pctr_cpu_creds(); 72 73 while ((ch = getopt(argc, argv, "AcEef:IiklMm:Ss:t:u")) != -1) 74 switch (ch) { 75 case 'A': 76 Aflag = 1; 77 break; 78 case 'c': 79 cflag = 1; 80 break; 81 case 'E': 82 Eflag = 1; 83 break; 84 case 'e': 85 eflag = 1; 86 break; 87 case 'f': 88 if (sscanf(optarg, "%x", &func) <= 0 || func < 0 || 89 func > PCTR_MAX_FUNCT) 90 errx(1, "invalid function number"); 91 break; 92 case 'I': 93 Iflag = 1; 94 break; 95 case 'i': 96 iflag = 1; 97 break; 98 case 'k': 99 kflag = 1; 100 break; 101 case 'l': 102 list_mode = 1; 103 break; 104 case 'M': 105 Mflag = 1; 106 break; 107 case 'm': 108 if (sscanf(optarg, "%x", &masku) <= 0 || masku < 0 || 109 masku > PCTR_MAX_UMASK) 110 errx(1, "invalid unit mask number"); 111 break; 112 case 'S': 113 Sflag = 1; 114 break; 115 case 's': 116 set_mode = 1; 117 ctr = strtonum(optarg, 0, PCTR_NUM-1, &errstr); 118 if (errstr) 119 errx(1, "counter number is %s: %s", errstr, 120 optarg); 121 break; 122 case 't': 123 thold = strtonum(optarg, 0, 0xff, &errstr); 124 if (errstr) 125 errx(1, "threshold is %s: %s", errstr, optarg); 126 break; 127 case 'u': 128 uflag = 1; 129 break; 130 default: 131 usage(); 132 /* NOTREACHED */ 133 } 134 argc -= optind; 135 argv += optind; 136 137 if (argc) 138 usage(); 139 140 if (Aflag && (Mflag || Eflag || Sflag || Iflag)) 141 usage(); 142 143 if (list_mode) 144 pctr_list_fnct(); 145 else if (set_mode) { 146 if (pctr_set_cntr() < 0) 147 err(1, "pctr_set_cntr"); 148 } else { 149 bzero(&st, sizeof(st)); 150 if (pctr_read(&st) < 0) 151 err(1, "pctr_read"); 152 pctr_printvals(&st); 153 } 154 return (0); 155 } 156 157 static void 158 pctr_cpu_creds(void) 159 { 160 int atype; 161 char arch[16], vendor[64]; 162 int mib[2], cpu_id, cpu_feature; 163 size_t len; 164 165 /* Get the architecture */ 166 mib[0] = CTL_HW; 167 mib[1] = HW_MACHINE; 168 len = sizeof(arch); 169 if (sysctl(mib, 2, arch, &len, NULL, 0) == -1) 170 err(1, "HW_MACHINE"); 171 172 if (strcmp(arch, "i386") == 0) 173 atype = ARCH_I386; 174 else if (strcmp(arch, "amd64") == 0) 175 atype = ARCH_AMD64; 176 else 177 errx(1, "architecture %s is not supported", arch); 178 179 /* Get the CPU id */ 180 mib[0] = CTL_MACHDEP; 181 mib[1] = CPU_CPUID; 182 len = sizeof(cpu_id); 183 if (sysctl(mib, 2, &cpu_id, &len, NULL, 0) == -1) 184 err(1, "CPU_CPUID"); 185 186 /* Get the CPU features */ 187 mib[1] = CPU_CPUFEATURE; 188 len = sizeof(cpu_feature); 189 if (sysctl(mib, 2, &cpu_feature, &len, NULL, 0) == -1) 190 err(1, "CPU_CPUFEATURE"); 191 192 /* Get the processor vendor */ 193 mib[0] = CTL_MACHDEP; 194 mib[1] = CPU_CPUVENDOR; 195 len = sizeof(vendor); 196 if (sysctl(mib, 2, vendor, &len, NULL, 0) == -1) 197 err(1, "CPU_CPUVENDOR"); 198 199 switch (atype) { 200 case ARCH_I386: 201 if (strcmp(vendor, "AuthenticAMD") == 0) { 202 if (((cpu_id >> 8) & 15) >= 6) 203 cpu_type = CPU_AMD; 204 else 205 cpu_type = CPU_UNDEF; /* old AMD cpu */ 206 207 } else if (strcmp(vendor, "GenuineIntel") == 0) { 208 if (((cpu_id >> 8) & 15) == 6 && 209 ((cpu_id >> 4) & 15) > 14) 210 cpu_type = CPU_CORE; 211 else if (((cpu_id >> 8) & 15) >= 6) 212 cpu_type = CPU_P6; 213 else if (((cpu_id >> 4) & 15) > 0) 214 cpu_type = CPU_P5; 215 else 216 cpu_type = CPU_UNDEF; /* old Intel cpu */ 217 } 218 if (cpu_feature & CPUID_TSC) 219 tsc_avail = 1; 220 break; 221 case ARCH_AMD64: 222 if (strcmp(vendor, "AuthenticAMD") == 0) 223 cpu_type = CPU_AMD; 224 else if (strcmp(vendor, "GenuineIntel") == 0) 225 cpu_type = CPU_CORE; 226 if (cpu_feature & CPUID_TSC) 227 tsc_avail = 1; 228 break; 229 } 230 } 231 232 static __inline int 233 pctr_ctrfn_index(struct ctrfn *cfnp, u_int32_t func) 234 { 235 int i; 236 237 for (i = 0; cfnp[i].name != NULL; i++) 238 if (cfnp[i].fn == func) 239 return (i); 240 return (-1); 241 } 242 243 static char * 244 pctr_fn2str(u_int32_t sel) 245 { 246 static char buf[128]; 247 struct ctrfn *cfnp = NULL; 248 char th[6], um[5], *msg; 249 u_int32_t fn; 250 int ind; 251 252 bzero(buf, sizeof(buf)); 253 bzero(th, sizeof(th)); 254 bzero(um, sizeof(um)); 255 switch (cpu_type) { 256 case CPU_P5: 257 fn = sel & 0x3f; 258 if ((ind = pctr_ctrfn_index(p5fn, fn)) < 0) 259 msg = "unknown function"; 260 else 261 msg = p5fn[ind].name; 262 snprintf(buf, sizeof(buf), "%c%c%c %02x %s", 263 sel & P5CTR_C ? 'c' : '-', 264 sel & P5CTR_U ? 'u' : '-', 265 sel & P5CTR_K ? 'k' : '-', 266 fn, msg); 267 break; 268 case CPU_P6: 269 cfnp = p6fn; 270 case CPU_CORE: 271 if (cpu_type == CPU_CORE) 272 cfnp = corefn; 273 fn = sel & 0xff; 274 if ((ind = pctr_ctrfn_index(cfnp, fn)) < 0) 275 msg = "unknown function"; 276 else 277 msg = cfnp[ind].name; 278 if (cfnp[ind].name && cfnp[ind].flags & CFL_MESI) 279 snprintf(um, sizeof (um), "%c%c%c%c", 280 sel & PCTR_UM_M ? 'M' : '-', 281 sel & PCTR_UM_E ? 'E' : '-', 282 sel & PCTR_UM_S ? 'S' : '-', 283 sel & PCTR_UM_I ? 'I' : '-'); 284 else if (cfnp[ind].name && cfnp[ind].flags & CFL_SA) 285 snprintf(um, sizeof(um), "%c", 286 sel & PCTR_UM_A ? 'A' : '-'); 287 if (sel >> PCTR_CM_SHIFT) 288 snprintf(th, sizeof(th), "+%d", 289 sel >> PCTR_CM_SHIFT); 290 snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s %s %s", 291 sel & PCTR_I ? 'i' : '-', 292 sel & PCTR_E ? 'e' : '-', 293 sel & PCTR_K ? 'k' : '-', 294 sel & PCTR_U ? 'u' : '-', 295 fn, (sel >> PCTR_UM_SHIFT) & 0xff, th, um, msg); 296 break; 297 case CPU_AMD: 298 fn = sel & 0xff; 299 if (sel >> PCTR_CM_SHIFT) 300 snprintf(th, sizeof(th), "+%d", 301 sel >> PCTR_CM_SHIFT); 302 snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s", 303 sel & PCTR_I ? 'i' : '-', 304 sel & PCTR_E ? 'e' : '-', 305 sel & PCTR_K ? 'k' : '-', 306 sel & PCTR_U ? 'u' : '-', 307 fn, (sel >> PCTR_UM_SHIFT) & 0xff, th); 308 break; 309 } 310 return (buf); 311 } 312 313 static void 314 pctr_printvals(struct pctrst *st) 315 { 316 int i, n; 317 318 switch (cpu_type) { 319 case CPU_P5: 320 case CPU_P6: 321 case CPU_CORE: 322 n = PCTR_INTEL_NUM; 323 case CPU_AMD: 324 if (cpu_type == CPU_AMD) 325 n = PCTR_AMD_NUM; 326 for (i = 0; i < n; i++) 327 printf(" ctr%d = %16llu [%s]\n", i, st->pctr_hwc[i], 328 pctr_fn2str(st->pctr_fn[i])); 329 if (tsc_avail) 330 printf(" tsc = %16llu\n", st->pctr_tsc); 331 break; 332 } 333 } 334 335 static int 336 pctr_read(struct pctrst *st) 337 { 338 int fd, se; 339 340 fd = open(_PATH_PCTR, O_RDONLY); 341 if (fd == -1) 342 return (-1); 343 if (ioctl(fd, PCIOCRD, st) == -1) { 344 se = errno; 345 close(fd); 346 errno = se; 347 return (-1); 348 } 349 return (close(fd)); 350 } 351 352 static int 353 pctr_write(int ctr, u_int32_t val) 354 { 355 int fd, se; 356 357 fd = open(_PATH_PCTR, O_WRONLY); 358 if (fd == -1) 359 return (-1); 360 if (ioctl(fd, PCIOCS0 + ctr, &val) == -1) { 361 se = errno; 362 close(fd); 363 errno = se; 364 return (-1); 365 } 366 return (close(fd)); 367 } 368 369 static __inline void 370 pctr_printdesc(char *desc) 371 { 372 char *p; 373 374 for (;;) { 375 while (*desc == ' ') 376 desc++; 377 if (strlen(desc) < 70) { 378 if (*desc) 379 printf(" %s\n", desc); 380 return; 381 } 382 p = desc + 72; 383 while (*--p != ' ') 384 ; 385 while (*--p == ' ') 386 ; 387 p++; 388 printf(" %.*s\n", (int)(p-desc), desc); 389 desc = p; 390 } 391 } 392 393 static void 394 pctr_list_fnct(void) 395 { 396 struct ctrfn *cfnp = NULL; 397 398 if (cpu_type == CPU_P5) 399 cfnp = p5fn; 400 else if (cpu_type == CPU_P6) 401 cfnp = p6fn; 402 else if (cpu_type == CPU_CORE) 403 cfnp = corefn; 404 else if (cpu_type == CPU_AMD) 405 cfnp = amdfn; 406 else 407 return; 408 409 for (; cfnp->name; cfnp++) { 410 printf("%02x %s", cfnp->fn, cfnp->name); 411 if (cfnp->flags & CFL_MESI) 412 printf(" (MESI)"); 413 else if (cfnp->flags & CFL_SA) 414 printf(" (A)"); 415 if (cfnp->flags & CFL_C0) 416 printf(" (ctr0 only)"); 417 else if (cfnp->flags & CFL_C1) 418 printf(" (ctr1 only)"); 419 if (cfnp->flags & CFL_UM) 420 printf(" (needs unit mask)"); 421 printf("\n"); 422 if (cfnp->desc) 423 pctr_printdesc(cfnp->desc); 424 } 425 } 426 427 static int 428 pctr_set_cntr(void) 429 { 430 struct ctrfn *cfnp = NULL; 431 u_int32_t val = func; 432 int ind = 0; 433 434 switch (cpu_type) { 435 case CPU_P5: 436 if (ctr >= PCTR_INTEL_NUM) 437 errx(1, "only %d counters are supported", 438 PCTR_INTEL_NUM); 439 if (cflag) 440 val |= P5CTR_C; 441 if (kflag) 442 val |= P5CTR_K; 443 if (uflag) 444 val |= P5CTR_U; 445 if (func && (!kflag && !uflag)) 446 val |= P5CTR_K | P5CTR_U; 447 break; 448 case CPU_P6: 449 cfnp = p6fn; 450 case CPU_CORE: 451 if (cpu_type == CPU_CORE) 452 cfnp = corefn; 453 if (ctr >= PCTR_INTEL_NUM) 454 errx(1, "only %d counters are supported", 455 PCTR_INTEL_NUM); 456 if (func && (ind = pctr_ctrfn_index(cfnp, func)) < 0) 457 errx(1, "function %02x is not supported", func); 458 if (func && (cfnp[ind].flags & CFL_SA)) 459 val |= PCTR_UM_A; 460 if (func && (cfnp[ind].flags & CFL_MESI)) { 461 if (Mflag) 462 val |= PCTR_UM_M; 463 if (Eflag) 464 val |= PCTR_UM_E; 465 if (Sflag) 466 val |= PCTR_UM_S; 467 if (Iflag) 468 val |= PCTR_UM_I; 469 if (!Mflag || !Eflag || !Sflag || !Iflag) 470 val |= PCTR_UM_MESI; 471 } 472 if (func && (cfnp[ind].flags & CFL_ED)) 473 val |= PCTR_E; 474 if (func && (cfnp[ind].flags & CFL_UM) && !masku) 475 errx(1, "function %02x needs unit mask specification", 476 func); 477 case CPU_AMD: 478 if (cpu_type == CPU_AMD && func && 479 ((ind = pctr_ctrfn_index(amdfn, func)) < 0)) 480 errx(1, "function %02x is not supported", func); 481 if (ctr >= PCTR_AMD_NUM) 482 errx(1, "only %d counters are supported", 483 PCTR_AMD_NUM); 484 if (eflag) 485 val |= PCTR_E; 486 if (iflag) 487 val |= PCTR_I; 488 if (kflag) 489 val |= PCTR_K; 490 if (uflag) 491 val |= PCTR_U; 492 if (func && (!kflag && !uflag)) 493 val |= PCTR_K | PCTR_U; 494 val |= masku << PCTR_UM_SHIFT; 495 val |= thold << PCTR_CM_SHIFT; 496 if (func) 497 val |= PCTR_EN; 498 break; 499 } 500 501 return (pctr_write(ctr, val)); 502 } 503 504 static void 505 usage(void) 506 { 507 extern char *__progname; 508 char *usg = NULL; 509 510 switch (cpu_type) { 511 case CPU_P5: 512 usg = "[-cklu] [-f funct] [-s ctr]"; 513 break; 514 case CPU_P6: 515 case CPU_CORE: 516 usg = "[-AEeIiklMSu] [-f funct] [-m umask] [-s ctr] " 517 "[-t thold]"; 518 break; 519 case CPU_AMD: 520 usg = "[-eilku] [-f funct] [-m umask] [-s ctr] " 521 "[-t thold]"; 522 break; 523 } 524 525 fprintf(stderr, "usage: %s %s\n", __progname, usg); 526 exit(1); 527 } 528