1 /* $OpenBSD: pctr.c,v 1.22 2015/02/08 23:40:34 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) - 1; 169 bzero(arch, sizeof(arch)); 170 if (sysctl(mib, 2, arch, &len, NULL, 0) == -1) 171 err(1, "HW_MACHINE"); 172 arch[len] = '\0'; 173 174 if (strcmp(arch, "i386") == 0) 175 atype = ARCH_I386; 176 else if (strcmp(arch, "amd64") == 0) 177 atype = ARCH_AMD64; 178 else 179 errx(1, "architecture %s is not supported", arch); 180 181 /* Get the CPU id */ 182 mib[0] = CTL_MACHDEP; 183 mib[1] = CPU_CPUID; 184 len = sizeof(cpu_id); 185 if (sysctl(mib, 2, &cpu_id, &len, NULL, 0) == -1) 186 err(1, "CPU_CPUID"); 187 188 /* Get the CPU features */ 189 mib[1] = CPU_CPUFEATURE; 190 len = sizeof(cpu_feature); 191 if (sysctl(mib, 2, &cpu_feature, &len, NULL, 0) == -1) 192 err(1, "CPU_CPUFEATURE"); 193 194 /* Get the processor vendor */ 195 mib[0] = CTL_MACHDEP; 196 mib[1] = CPU_CPUVENDOR; 197 len = sizeof(vendor) - 1; 198 bzero(vendor, sizeof(vendor)); 199 if (sysctl(mib, 2, vendor, &len, NULL, 0) == -1) 200 err(1, "CPU_CPUVENDOR"); 201 vendor[len] = '\0'; 202 203 switch (atype) { 204 case ARCH_I386: 205 if (strcmp(vendor, "AuthenticAMD") == 0) { 206 if (((cpu_id >> 8) & 15) >= 6) 207 cpu_type = CPU_AMD; 208 else 209 cpu_type = CPU_UNDEF; /* old AMD cpu */ 210 211 } else if (strcmp(vendor, "GenuineIntel") == 0) { 212 if (((cpu_id >> 8) & 15) == 6 && 213 ((cpu_id >> 4) & 15) > 14) 214 cpu_type = CPU_CORE; 215 else if (((cpu_id >> 8) & 15) >= 6) 216 cpu_type = CPU_P6; 217 else if (((cpu_id >> 4) & 15) > 0) 218 cpu_type = CPU_P5; 219 else 220 cpu_type = CPU_UNDEF; /* old Intel cpu */ 221 } 222 if (cpu_feature & CPUID_TSC) 223 tsc_avail = 1; 224 break; 225 case ARCH_AMD64: 226 if (strcmp(vendor, "AuthenticAMD") == 0) 227 cpu_type = CPU_AMD; 228 else if (strcmp(vendor, "GenuineIntel") == 0) 229 cpu_type = CPU_CORE; 230 if (cpu_feature & CPUID_TSC) 231 tsc_avail = 1; 232 break; 233 } 234 } 235 236 static __inline int 237 pctr_ctrfn_index(struct ctrfn *cfnp, u_int32_t func) 238 { 239 int i; 240 241 for (i = 0; cfnp[i].name != NULL; i++) 242 if (cfnp[i].fn == func) 243 return (i); 244 return (-1); 245 } 246 247 static char * 248 pctr_fn2str(u_int32_t sel) 249 { 250 static char buf[128]; 251 struct ctrfn *cfnp = NULL; 252 char th[6], um[5], *msg; 253 u_int32_t fn; 254 int ind; 255 256 bzero(buf, sizeof(buf)); 257 bzero(th, sizeof(th)); 258 bzero(um, sizeof(um)); 259 switch (cpu_type) { 260 case CPU_P5: 261 fn = sel & 0x3f; 262 if ((ind = pctr_ctrfn_index(p5fn, fn)) < 0) 263 msg = "unknown function"; 264 else 265 msg = p5fn[ind].name; 266 snprintf(buf, sizeof(buf), "%c%c%c %02x %s", 267 sel & P5CTR_C ? 'c' : '-', 268 sel & P5CTR_U ? 'u' : '-', 269 sel & P5CTR_K ? 'k' : '-', 270 fn, msg); 271 break; 272 case CPU_P6: 273 cfnp = p6fn; 274 case CPU_CORE: 275 if (cpu_type == CPU_CORE) 276 cfnp = corefn; 277 fn = sel & 0xff; 278 if ((ind = pctr_ctrfn_index(cfnp, fn)) < 0) 279 msg = "unknown function"; 280 else 281 msg = cfnp[ind].name; 282 if (cfnp[ind].name && cfnp[ind].flags & CFL_MESI) 283 snprintf(um, sizeof (um), "%c%c%c%c", 284 sel & PCTR_UM_M ? 'M' : '-', 285 sel & PCTR_UM_E ? 'E' : '-', 286 sel & PCTR_UM_S ? 'S' : '-', 287 sel & PCTR_UM_I ? 'I' : '-'); 288 else if (cfnp[ind].name && cfnp[ind].flags & CFL_SA) 289 snprintf(um, sizeof(um), "%c", 290 sel & PCTR_UM_A ? 'A' : '-'); 291 if (sel >> PCTR_CM_SHIFT) 292 snprintf(th, sizeof(th), "+%d", 293 sel >> PCTR_CM_SHIFT); 294 snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s %s %s", 295 sel & PCTR_I ? 'i' : '-', 296 sel & PCTR_E ? 'e' : '-', 297 sel & PCTR_K ? 'k' : '-', 298 sel & PCTR_U ? 'u' : '-', 299 fn, (sel >> PCTR_UM_SHIFT) & 0xff, th, um, msg); 300 break; 301 case CPU_AMD: 302 fn = sel & 0xff; 303 if (sel >> PCTR_CM_SHIFT) 304 snprintf(th, sizeof(th), "+%d", 305 sel >> PCTR_CM_SHIFT); 306 snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s", 307 sel & PCTR_I ? 'i' : '-', 308 sel & PCTR_E ? 'e' : '-', 309 sel & PCTR_K ? 'k' : '-', 310 sel & PCTR_U ? 'u' : '-', 311 fn, (sel >> PCTR_UM_SHIFT) & 0xff, th); 312 break; 313 } 314 return (buf); 315 } 316 317 static void 318 pctr_printvals(struct pctrst *st) 319 { 320 int i, n; 321 322 switch (cpu_type) { 323 case CPU_P5: 324 case CPU_P6: 325 case CPU_CORE: 326 n = PCTR_INTEL_NUM; 327 case CPU_AMD: 328 if (cpu_type == CPU_AMD) 329 n = PCTR_AMD_NUM; 330 for (i = 0; i < n; i++) 331 printf(" ctr%d = %16llu [%s]\n", i, st->pctr_hwc[i], 332 pctr_fn2str(st->pctr_fn[i])); 333 if (tsc_avail) 334 printf(" tsc = %16llu\n", st->pctr_tsc); 335 break; 336 } 337 } 338 339 static int 340 pctr_read(struct pctrst *st) 341 { 342 int fd, se; 343 344 fd = open(_PATH_PCTR, O_RDONLY); 345 if (fd < 0) 346 return (-1); 347 if (ioctl(fd, PCIOCRD, st) < 0) { 348 se = errno; 349 close(fd); 350 errno = se; 351 return (-1); 352 } 353 return (close(fd)); 354 } 355 356 static int 357 pctr_write(int ctr, u_int32_t val) 358 { 359 int fd, se; 360 361 fd = open(_PATH_PCTR, O_WRONLY); 362 if (fd < 0) 363 return (-1); 364 if (ioctl(fd, PCIOCS0 + ctr, &val) < 0) { 365 se = errno; 366 close(fd); 367 errno = se; 368 return (-1); 369 } 370 return (close(fd)); 371 } 372 373 static __inline void 374 pctr_printdesc(char *desc) 375 { 376 char *p; 377 378 for (;;) { 379 while (*desc == ' ') 380 desc++; 381 if (strlen(desc) < 70) { 382 if (*desc) 383 printf(" %s\n", desc); 384 return; 385 } 386 p = desc + 72; 387 while (*--p != ' ') 388 ; 389 while (*--p == ' ') 390 ; 391 p++; 392 printf(" %.*s\n", (int)(p-desc), desc); 393 desc = p; 394 } 395 } 396 397 static void 398 pctr_list_fnct(void) 399 { 400 struct ctrfn *cfnp = NULL; 401 402 if (cpu_type == CPU_P5) 403 cfnp = p5fn; 404 else if (cpu_type == CPU_P6) 405 cfnp = p6fn; 406 else if (cpu_type == CPU_CORE) 407 cfnp = corefn; 408 else if (cpu_type == CPU_AMD) 409 cfnp = amdfn; 410 else 411 return; 412 413 for (; cfnp->name; cfnp++) { 414 printf("%02x %s", cfnp->fn, cfnp->name); 415 if (cfnp->flags & CFL_MESI) 416 printf(" (MESI)"); 417 else if (cfnp->flags & CFL_SA) 418 printf(" (A)"); 419 if (cfnp->flags & CFL_C0) 420 printf(" (ctr0 only)"); 421 else if (cfnp->flags & CFL_C1) 422 printf(" (ctr1 only)"); 423 if (cfnp->flags & CFL_UM) 424 printf(" (needs unit mask)"); 425 printf("\n"); 426 if (cfnp->desc) 427 pctr_printdesc(cfnp->desc); 428 } 429 } 430 431 static int 432 pctr_set_cntr(void) 433 { 434 struct ctrfn *cfnp = NULL; 435 u_int32_t val = func; 436 int ind = 0; 437 438 switch (cpu_type) { 439 case CPU_P5: 440 if (ctr >= PCTR_INTEL_NUM) 441 errx(1, "only %d counters are supported", 442 PCTR_INTEL_NUM); 443 if (cflag) 444 val |= P5CTR_C; 445 if (kflag) 446 val |= P5CTR_K; 447 if (uflag) 448 val |= P5CTR_U; 449 if (func && (!kflag && !uflag)) 450 val |= P5CTR_K | P5CTR_U; 451 break; 452 case CPU_P6: 453 cfnp = p6fn; 454 case CPU_CORE: 455 if (cpu_type == CPU_CORE) 456 cfnp = corefn; 457 if (ctr >= PCTR_INTEL_NUM) 458 errx(1, "only %d counters are supported", 459 PCTR_INTEL_NUM); 460 if (func && (ind = pctr_ctrfn_index(cfnp, func)) < 0) 461 errx(1, "function %02x is not supported", func); 462 if (func && (cfnp[ind].flags & CFL_SA)) 463 val |= PCTR_UM_A; 464 if (func && (cfnp[ind].flags & CFL_MESI)) { 465 if (Mflag) 466 val |= PCTR_UM_M; 467 if (Eflag) 468 val |= PCTR_UM_E; 469 if (Sflag) 470 val |= PCTR_UM_S; 471 if (Iflag) 472 val |= PCTR_UM_I; 473 if (!Mflag || !Eflag || !Sflag || !Iflag) 474 val |= PCTR_UM_MESI; 475 } 476 if (func && (cfnp[ind].flags & CFL_ED)) 477 val |= PCTR_E; 478 if (func && (cfnp[ind].flags & CFL_UM) && !masku) 479 errx(1, "function %02x needs unit mask specification", 480 func); 481 case CPU_AMD: 482 if (cpu_type == CPU_AMD && func && 483 ((ind = pctr_ctrfn_index(amdfn, func)) < 0)) 484 errx(1, "function %02x is not supported", func); 485 if (ctr >= PCTR_AMD_NUM) 486 errx(1, "only %d counters are supported", 487 PCTR_AMD_NUM); 488 if (eflag) 489 val |= PCTR_E; 490 if (iflag) 491 val |= PCTR_I; 492 if (kflag) 493 val |= PCTR_K; 494 if (uflag) 495 val |= PCTR_U; 496 if (func && (!kflag && !uflag)) 497 val |= PCTR_K | PCTR_U; 498 val |= masku << PCTR_UM_SHIFT; 499 val |= thold << PCTR_CM_SHIFT; 500 if (func) 501 val |= PCTR_EN; 502 break; 503 } 504 505 return (pctr_write(ctr, val)); 506 } 507 508 static void 509 usage(void) 510 { 511 extern char *__progname; 512 char *usg = NULL; 513 514 switch (cpu_type) { 515 case CPU_P5: 516 usg = "[-cklu] [-f funct] [-s ctr]"; 517 break; 518 case CPU_P6: 519 case CPU_CORE: 520 usg = "[-AEeIiklMSu] [-f funct] [-m umask] [-s ctr] " 521 "[-t thold]"; 522 break; 523 case CPU_AMD: 524 usg = "[-eilku] [-f funct] [-m umask] [-s ctr] " 525 "[-t thold]"; 526 break; 527 } 528 529 fprintf(stderr, "usage: %s %s\n", __progname, usg); 530 exit(1); 531 } 532