xref: /openbsd/usr.bin/pctr/pctr.c (revision 8529ddd3)
1 /*	$OpenBSD: pctr.c,v 1.22 2015/02/08 23:40:34 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
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
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) - 1;
169 	bzero(arch, sizeof(arch));
170 	if (sysctl(mib, 2, arch, &len, NULL, 0) == -1)
171 		err(1, "HW_MACHINE");
172 	arch[len] = '\0';
173 
174 	if (strcmp(arch, "i386") == 0)
175 		atype = ARCH_I386;
176 	else if (strcmp(arch, "amd64") == 0)
177 		atype = ARCH_AMD64;
178 	else
179 		errx(1, "architecture %s is not supported", arch);
180 
181 	/* Get the CPU id */
182 	mib[0] = CTL_MACHDEP;
183 	mib[1] = CPU_CPUID;
184 	len = sizeof(cpu_id);
185 	if (sysctl(mib, 2, &cpu_id, &len, NULL, 0) == -1)
186 		err(1, "CPU_CPUID");
187 
188 	/* Get the CPU features */
189 	mib[1] = CPU_CPUFEATURE;
190 	len = sizeof(cpu_feature);
191 	if (sysctl(mib, 2, &cpu_feature, &len, NULL, 0) == -1)
192 		err(1, "CPU_CPUFEATURE");
193 
194 	/* Get the processor vendor */
195 	mib[0] = CTL_MACHDEP;
196 	mib[1] = CPU_CPUVENDOR;
197 	len = sizeof(vendor) - 1;
198 	bzero(vendor, sizeof(vendor));
199 	if (sysctl(mib, 2, vendor, &len, NULL, 0) == -1)
200 		err(1, "CPU_CPUVENDOR");
201 	vendor[len] = '\0';
202 
203 	switch (atype) {
204 	case ARCH_I386:
205 		if (strcmp(vendor, "AuthenticAMD") == 0) {
206 			if (((cpu_id >> 8) & 15) >= 6)
207 				cpu_type = CPU_AMD;
208 			else
209 				cpu_type = CPU_UNDEF;	/* old AMD cpu */
210 
211 		} else if (strcmp(vendor, "GenuineIntel") == 0) {
212 			if (((cpu_id >> 8) & 15) == 6 &&
213 			    ((cpu_id >> 4) & 15) > 14)
214 				cpu_type = CPU_CORE;
215 			else if (((cpu_id >> 8) & 15) >= 6)
216 				cpu_type = CPU_P6;
217 			else if (((cpu_id >> 4) & 15) > 0)
218 				cpu_type = CPU_P5;
219 			else
220 				cpu_type = CPU_UNDEF;	/* old Intel cpu */
221 		}
222 		if (cpu_feature & CPUID_TSC)
223 			tsc_avail = 1;
224 		break;
225 	case ARCH_AMD64:
226 		if (strcmp(vendor, "AuthenticAMD") == 0)
227 			cpu_type = CPU_AMD;
228 		else if (strcmp(vendor, "GenuineIntel") == 0)
229 			cpu_type = CPU_CORE;
230 		if (cpu_feature & CPUID_TSC)
231 			tsc_avail = 1;
232 		break;
233 	}
234 }
235 
236 static __inline int
237 pctr_ctrfn_index(struct ctrfn *cfnp, u_int32_t func)
238 {
239 	int i;
240 
241 	for (i = 0; cfnp[i].name != NULL; i++)
242 		if (cfnp[i].fn == func)
243 			return (i);
244 	return (-1);
245 }
246 
247 static char *
248 pctr_fn2str(u_int32_t sel)
249 {
250 	static char buf[128];
251 	struct ctrfn *cfnp = NULL;
252 	char th[6], um[5], *msg;
253 	u_int32_t fn;
254 	int ind;
255 
256 	bzero(buf, sizeof(buf));
257 	bzero(th, sizeof(th));
258 	bzero(um, sizeof(um));
259 	switch (cpu_type) {
260 	case CPU_P5:
261 		fn = sel & 0x3f;
262 		if ((ind = pctr_ctrfn_index(p5fn, fn)) < 0)
263 			msg = "unknown function";
264 		else
265 			msg = p5fn[ind].name;
266 		snprintf(buf, sizeof(buf), "%c%c%c %02x %s",
267 		    sel & P5CTR_C ? 'c' : '-',
268 		    sel & P5CTR_U ? 'u' : '-',
269 		    sel & P5CTR_K ? 'k' : '-',
270 		    fn, msg);
271 		break;
272 	case CPU_P6:
273 		cfnp = p6fn;
274 	case CPU_CORE:
275 		if (cpu_type == CPU_CORE)
276 			cfnp = corefn;
277 		fn = sel & 0xff;
278 		if ((ind = pctr_ctrfn_index(cfnp, fn)) < 0)
279 			msg = "unknown function";
280 		else
281 			msg = cfnp[ind].name;
282 		if (cfnp[ind].name && cfnp[ind].flags & CFL_MESI)
283 			snprintf(um, sizeof (um), "%c%c%c%c",
284 			    sel & PCTR_UM_M ? 'M' : '-',
285 			    sel & PCTR_UM_E ? 'E' : '-',
286 			    sel & PCTR_UM_S ? 'S' : '-',
287 			    sel & PCTR_UM_I ? 'I' : '-');
288 		else if (cfnp[ind].name && cfnp[ind].flags & CFL_SA)
289 			snprintf(um, sizeof(um), "%c",
290 			    sel & PCTR_UM_A ? 'A' : '-');
291 		if (sel >> PCTR_CM_SHIFT)
292 			snprintf(th, sizeof(th), "+%d",
293 			    sel >> PCTR_CM_SHIFT);
294 		snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s %s %s",
295 		    sel & PCTR_I ? 'i' : '-',
296 		    sel & PCTR_E ? 'e' : '-',
297 		    sel & PCTR_K ? 'k' : '-',
298 		    sel & PCTR_U ? 'u' : '-',
299 		    fn, (sel >> PCTR_UM_SHIFT) & 0xff, th, um, msg);
300 		break;
301 	case CPU_AMD:
302 		fn = sel & 0xff;
303 		if (sel >> PCTR_CM_SHIFT)
304 			snprintf(th, sizeof(th), "+%d",
305 			    sel >> PCTR_CM_SHIFT);
306 		snprintf(buf, sizeof(buf), "%c%c%c%c %02x %02x %s",
307 		    sel & PCTR_I ? 'i' : '-',
308 		    sel & PCTR_E ? 'e' : '-',
309 		    sel & PCTR_K ? 'k' : '-',
310 		    sel & PCTR_U ? 'u' : '-',
311 		    fn, (sel >> PCTR_UM_SHIFT) & 0xff, th);
312 		break;
313 	}
314 	return (buf);
315 }
316 
317 static void
318 pctr_printvals(struct pctrst *st)
319 {
320 	int i, n;
321 
322 	switch (cpu_type) {
323 	case CPU_P5:
324 	case CPU_P6:
325 	case CPU_CORE:
326 		n = PCTR_INTEL_NUM;
327 	case CPU_AMD:
328 		if (cpu_type == CPU_AMD)
329 			n = PCTR_AMD_NUM;
330 		for (i = 0; i < n; i++)
331 			printf(" ctr%d = %16llu  [%s]\n", i, st->pctr_hwc[i],
332 			    pctr_fn2str(st->pctr_fn[i]));
333 		if (tsc_avail)
334 			printf("  tsc = %16llu\n", st->pctr_tsc);
335 		break;
336 	}
337 }
338 
339 static int
340 pctr_read(struct pctrst *st)
341 {
342 	int fd, se;
343 
344 	fd = open(_PATH_PCTR, O_RDONLY);
345 	if (fd < 0)
346 		return (-1);
347 	if (ioctl(fd, PCIOCRD, st) < 0) {
348 		se = errno;
349 		close(fd);
350 		errno = se;
351 		return (-1);
352 	}
353 	return (close(fd));
354 }
355 
356 static int
357 pctr_write(int ctr, u_int32_t val)
358 {
359 	int fd, se;
360 
361 	fd = open(_PATH_PCTR, O_WRONLY);
362 	if (fd < 0)
363 		return (-1);
364 	if (ioctl(fd, PCIOCS0 + ctr, &val) < 0) {
365 		se = errno;
366 		close(fd);
367 		errno = se;
368 		return (-1);
369 	}
370 	return (close(fd));
371 }
372 
373 static __inline void
374 pctr_printdesc(char *desc)
375 {
376 	char *p;
377 
378 	for (;;) {
379 		while (*desc == ' ')
380 			desc++;
381 		if (strlen(desc) < 70) {
382 			if (*desc)
383 				printf("      %s\n", desc);
384 			return;
385 		}
386 		p = desc + 72;
387 		while (*--p != ' ')
388 			;
389 		while (*--p == ' ')
390 			;
391 		p++;
392 		printf("      %.*s\n", (int)(p-desc), desc);
393 		desc = p;
394 	}
395 }
396 
397 static void
398 pctr_list_fnct(void)
399 {
400 	struct ctrfn *cfnp = NULL;
401 
402 	if (cpu_type == CPU_P5)
403 		cfnp = p5fn;
404 	else if (cpu_type == CPU_P6)
405 		cfnp = p6fn;
406 	else if (cpu_type == CPU_CORE)
407 		cfnp = corefn;
408 	else if (cpu_type == CPU_AMD)
409 		cfnp = amdfn;
410 	else
411 		return;
412 
413 	for (; cfnp->name; cfnp++) {
414 		printf("%02x  %s", cfnp->fn, cfnp->name);
415 		if (cfnp->flags & CFL_MESI)
416 			printf("  (MESI)");
417 		else if (cfnp->flags & CFL_SA)
418 			printf("  (A)");
419 		if (cfnp->flags & CFL_C0)
420 			printf("  (ctr0 only)");
421 		else if (cfnp->flags & CFL_C1)
422 			printf("  (ctr1 only)");
423 		if (cfnp->flags & CFL_UM)
424 			printf("  (needs unit mask)");
425 		printf("\n");
426 		if (cfnp->desc)
427 			pctr_printdesc(cfnp->desc);
428 	}
429 }
430 
431 static int
432 pctr_set_cntr(void)
433 {
434 	struct ctrfn *cfnp = NULL;
435 	u_int32_t val = func;
436 	int ind = 0;
437 
438 	switch (cpu_type) {
439 	case CPU_P5:
440 		if (ctr >= PCTR_INTEL_NUM)
441 			errx(1, "only %d counters are supported",
442 			    PCTR_INTEL_NUM);
443 		if (cflag)
444 			val |= P5CTR_C;
445 		if (kflag)
446 			val |= P5CTR_K;
447 		if (uflag)
448 			val |= P5CTR_U;
449 		if (func && (!kflag && !uflag))
450 			val |= P5CTR_K | P5CTR_U;
451 		break;
452 	case CPU_P6:
453 		cfnp = p6fn;
454 	case CPU_CORE:
455 		if (cpu_type == CPU_CORE)
456 			cfnp = corefn;
457 		if (ctr >= PCTR_INTEL_NUM)
458 			errx(1, "only %d counters are supported",
459 			    PCTR_INTEL_NUM);
460 		if (func && (ind = pctr_ctrfn_index(cfnp, func)) < 0)
461 			errx(1, "function %02x is not supported", func);
462 		if (func && (cfnp[ind].flags & CFL_SA))
463 			val |= PCTR_UM_A;
464 		if (func && (cfnp[ind].flags & CFL_MESI)) {
465 			if (Mflag)
466 				val |= PCTR_UM_M;
467 			if (Eflag)
468 				val |= PCTR_UM_E;
469 			if (Sflag)
470 				val |= PCTR_UM_S;
471 			if (Iflag)
472 				val |= PCTR_UM_I;
473 			if (!Mflag || !Eflag || !Sflag || !Iflag)
474 				val |= PCTR_UM_MESI;
475 		}
476 		if (func && (cfnp[ind].flags & CFL_ED))
477 			val |= PCTR_E;
478 		if (func && (cfnp[ind].flags & CFL_UM) && !masku)
479 			errx(1, "function %02x needs unit mask specification",
480 			    func);
481 	case CPU_AMD:
482 		if (cpu_type == CPU_AMD && func &&
483 		    ((ind = pctr_ctrfn_index(amdfn, func)) < 0))
484 			errx(1, "function %02x is not supported", func);
485 		if (ctr >= PCTR_AMD_NUM)
486 			errx(1, "only %d counters are supported",
487 			    PCTR_AMD_NUM);
488 		if (eflag)
489 			val |= PCTR_E;
490 		if (iflag)
491 			val |= PCTR_I;
492 		if (kflag)
493 			val |= PCTR_K;
494 		if (uflag)
495 			val |= PCTR_U;
496 		if (func && (!kflag && !uflag))
497 			val |= PCTR_K | PCTR_U;
498 		val |= masku << PCTR_UM_SHIFT;
499 		val |= thold << PCTR_CM_SHIFT;
500 		if (func)
501 			val |= PCTR_EN;
502 		break;
503 	}
504 
505 	return (pctr_write(ctr, val));
506 }
507 
508 static void
509 usage(void)
510 {
511 	extern char *__progname;
512 	char *usg = NULL;
513 
514 	switch (cpu_type) {
515 	case CPU_P5:
516 		usg = "[-cklu] [-f funct] [-s ctr]";
517 		break;
518 	case CPU_P6:
519 	case CPU_CORE:
520 		usg = "[-AEeIiklMSu] [-f funct] [-m umask] [-s ctr] "
521 		    "[-t thold]";
522 		break;
523 	case CPU_AMD:
524 		usg = "[-eilku] [-f funct] [-m umask] [-s ctr] "
525 		    "[-t thold]";
526 		break;
527 	}
528 
529 	fprintf(stderr, "usage: %s %s\n", __progname, usg);
530 	exit(1);
531 }
532