xref: /openbsd/usr.bin/pctr/pctr.c (revision 3aaa63eb)
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