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