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