Annotation of src/usr.bin/pctr/pctr.c, Revision 1.18
1.18 ! mikeb 1: /* $OpenBSD: pctr.c,v 1.17 2007/10/25 16:38:06 mikeb Exp $ */
1.13 deraadt 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 <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:
1.18 ! mikeb 55: static void pctr_cpu_creds(void);
1.13 deraadt 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:
1.18 ! mikeb 72: pctr_cpu_creds();
1.13 deraadt 73:
1.18 ! mikeb 74: while ((ch = getopt(argc, argv, "AcEef:IiklMm:Ss:t:u")) != -1)
1.13 deraadt 75: switch (ch) {
1.17 mikeb 76: case 'A':
77: Aflag++;
78: break;
79: case 'c':
80: cflag++;
1.13 deraadt 81: break;
1.17 mikeb 82: case 'E':
1.18 ! mikeb 83: Eflag++;
! 84: break;
1.17 mikeb 85: case 'e':
86: eflag++;
1.13 deraadt 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;
1.18 ! mikeb 93: case 'I':
! 94: Iflag++;
! 95: break;
1.17 mikeb 96: case 'i':
97: iflag++;
98: break;
99: case 'k':
100: kflag++;
101: break;
102: case 'l':
103: list_mode++;
104: break;
1.18 ! mikeb 105: case 'M':
! 106: Mflag++;
! 107: break;
1.13 deraadt 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;
1.18 ! mikeb 113: case 'S':
! 114: Sflag++;
! 115: break;
1.17 mikeb 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;
1.13 deraadt 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:
1.18 ! mikeb 138: if (argc)
! 139: usage();
! 140:
! 141: if (Aflag && (Mflag || Eflag || Sflag || Iflag))
! 142: usage();
! 143:
1.13 deraadt 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:
1.18 ! mikeb 158: static void
1.13 deraadt 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
1.18 ! mikeb 180: errx(1, "architecture %s is not supported", arch);
1.13 deraadt 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",
1.15 deraadt 268: sel & P5CTR_C ? 'c' : '-',
269: sel & P5CTR_U ? 'u' : '-',
270: sel & P5CTR_K ? 'k' : '-',
1.13 deraadt 271: fn, msg);
272: break;
273: case CPU_P6:
274: cfnp = p6fn;
275: case CPU_CORE:
1.14 deraadt 276: if (cpu_type == CPU_CORE)
277: cfnp = corefn;
1.13 deraadt 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",
1.15 deraadt 285: sel & PCTR_UM_M ? 'M' : '-',
286: sel & PCTR_UM_E ? 'E' : '-',
287: sel & PCTR_UM_S ? 'S' : '-',
288: sel & PCTR_UM_I ? 'I' : '-');
1.13 deraadt 289: else if (cfnp[ind].name && cfnp[ind].flags & CFL_SA)
290: snprintf(um, sizeof(um), "%c",
1.15 deraadt 291: sel & PCTR_UM_A ? 'A' : '-');
292: if (sel >> PCTR_CM_SHIFT)
1.13 deraadt 293: snprintf(th, sizeof(th), "+%d",
1.15 deraadt 294: sel >> PCTR_CM_SHIFT);
1.13 deraadt 295: snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s %s %s",
1.15 deraadt 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);
1.13 deraadt 301: break;
302: case CPU_AMD:
303: fn = sel & 0xff;
1.15 deraadt 304: if (sel >> PCTR_CM_SHIFT)
1.13 deraadt 305: snprintf(th, sizeof(th), "+%d",
1.15 deraadt 306: sel >> PCTR_CM_SHIFT);
1.13 deraadt 307: snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s",
1.15 deraadt 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);
1.13 deraadt 313: break;
314: }
315: return (buf);
316: }
1.3 downsj 317:
1.1 dm 318: static void
1.13 deraadt 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)
1.1 dm 376: {
1.7 mickey 377: char *p;
1.1 dm 378:
1.7 mickey 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++;
1.13 deraadt 393: printf(" %.*s\n", (int)(p-desc), desc);
1.7 mickey 394: desc = p;
395: }
1.1 dm 396: }
397:
398: static void
1.13 deraadt 399: pctr_list_fnct(void)
1.1 dm 400: {
1.13 deraadt 401: struct ctrfn *cfnp = NULL;
1.1 dm 402:
1.13 deraadt 403: if (cpu_type == CPU_P5)
1.7 mickey 404: cfnp = p5fn;
1.13 deraadt 405: else if (cpu_type == CPU_P6)
1.7 mickey 406: cfnp = p6fn;
1.13 deraadt 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:
1.7 mickey 414: for (; cfnp->name; cfnp++) {
415: printf("%02x %s", cfnp->fn, cfnp->name);
416: if (cfnp->flags & CFL_MESI)
1.13 deraadt 417: printf(" (MESI)");
1.7 mickey 418: else if (cfnp->flags & CFL_SA)
1.13 deraadt 419: printf(" (A)");
1.7 mickey 420: if (cfnp->flags & CFL_C0)
421: printf(" (ctr0 only)");
1.13 deraadt 422: else if (cfnp->flags & CFL_C1)
1.7 mickey 423: printf(" (ctr1 only)");
1.18 ! mikeb 424: if (cfnp->flags & CFL_UM)
! 425: printf(" (needs unit mask)");
1.7 mickey 426: printf("\n");
427: if (cfnp->desc)
1.13 deraadt 428: pctr_printdesc(cfnp->desc);
1.7 mickey 429: }
1.1 dm 430: }
431:
1.13 deraadt 432: static int
433: pctr_set_cntr(void)
1.1 dm 434: {
1.13 deraadt 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)
1.18 ! mikeb 442: errx(1, "only %d counters are supported",
! 443: PCTR_INTEL_NUM);
1.13 deraadt 444: if (cflag)
1.15 deraadt 445: val |= P5CTR_C;
1.13 deraadt 446: if (kflag)
1.15 deraadt 447: val |= P5CTR_K;
1.13 deraadt 448: if (uflag)
1.15 deraadt 449: val |= P5CTR_U;
1.13 deraadt 450: if (func && (!kflag && !uflag))
1.15 deraadt 451: val |= P5CTR_K | P5CTR_U;
1.13 deraadt 452: break;
453: case CPU_P6:
1.7 mickey 454: cfnp = p6fn;
1.13 deraadt 455: case CPU_CORE:
1.14 deraadt 456: if (cpu_type == CPU_CORE)
457: cfnp = corefn;
1.13 deraadt 458: if (ctr >= PCTR_INTEL_NUM)
1.18 ! mikeb 459: errx(1, "only %d counters are supported",
! 460: PCTR_INTEL_NUM);
1.13 deraadt 461: if (func && (ind = pctr_ctrfn_index(cfnp, func)) < 0)
1.18 ! mikeb 462: errx(1, "function %02x is not supported", func);
! 463: if (func && (cfnp[ind].flags & CFL_SA))
1.15 deraadt 464: val |= PCTR_UM_A;
1.18 ! mikeb 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: }
1.13 deraadt 477: if (func && (cfnp[ind].flags & CFL_ED))
1.15 deraadt 478: val |= PCTR_E;
1.18 ! mikeb 479: if (func && (cfnp[ind].flags & CFL_UM) && !masku)
! 480: errx(1, "function %02x needs unit mask specification",
! 481: func);
1.13 deraadt 482: case CPU_AMD:
483: if (cpu_type == CPU_AMD && func &&
484: ((ind = pctr_ctrfn_index(amdfn, func)) < 0))
1.18 ! mikeb 485: errx(1, "function %02x is not supported", func);
1.13 deraadt 486: if (ctr >= PCTR_AMD_NUM)
1.18 ! mikeb 487: errx(1, "only %d counters are supported",
! 488: PCTR_AMD_NUM);
1.13 deraadt 489: if (eflag)
1.15 deraadt 490: val |= PCTR_E;
1.13 deraadt 491: if (iflag)
1.15 deraadt 492: val |= PCTR_I;
1.13 deraadt 493: if (kflag)
1.15 deraadt 494: val |= PCTR_K;
1.13 deraadt 495: if (uflag)
1.15 deraadt 496: val |= PCTR_U;
1.13 deraadt 497: if (func && (!kflag && !uflag))
1.15 deraadt 498: val |= PCTR_K | PCTR_U;
499: val |= masku << PCTR_UM_SHIFT;
500: val |= thold << PCTR_CM_SHIFT;
1.13 deraadt 501: if (func)
1.15 deraadt 502: val |= PCTR_EN;
1.13 deraadt 503: break;
1.7 mickey 504: }
505:
1.13 deraadt 506: return (pctr_write(ctr, val));
1.1 dm 507: }
508:
509: static void
1.13 deraadt 510: usage(void)
1.1 dm 511: {
1.13 deraadt 512: extern char *__progname;
513: char *usg = NULL;
1.1 dm 514:
1.13 deraadt 515: switch (cpu_type) {
516: case CPU_P5:
1.17 mikeb 517: usg = "[-cklu] [-f funct] [-s ctr]";
1.13 deraadt 518: break;
519: case CPU_P6:
520: case CPU_CORE:
1.17 mikeb 521: usg = "[-AEeIiklMSu] [-f funct] [-m umask] [-s ctr] "
1.13 deraadt 522: "[-t thold]";
523: break;
524: case CPU_AMD:
1.17 mikeb 525: usg = "[-eilku] [-f funct] [-m umask] [-s ctr] "
1.13 deraadt 526: "[-t thold]";
527: break;
1.7 mickey 528: }
1.1 dm 529:
1.13 deraadt 530: fprintf(stderr, "%s: %s\n", __progname, usg);
1.18 ! mikeb 531: exit(1);
1.1 dm 532: }