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
main(int argc,char ** argv)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
pctr_cpu_creds(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
pctr_ctrfn_index(struct ctrfn * cfnp,u_int32_t func)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 *
pctr_fn2str(u_int32_t sel)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
pctr_printvals(struct pctrst * st)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
pctr_read(struct pctrst * st)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
pctr_write(int ctr,u_int32_t val)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
pctr_printdesc(char * desc)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
pctr_list_fnct(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
pctr_set_cntr(void)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
usage(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