GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/pctr/pctr.c Lines: 0 267 0.0 %
Date: 2016-12-06 Branches: 0 300 0.0 %

Line Branch Exec Source
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
}