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