GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/pctr/pctr.c Lines: 0 273 0.0 %
Date: 2017-11-13 Branches: 0 223 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: pctr.c,v 1.23 2017/09/10 11:30:43 tom 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);
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
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 *
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
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
336
pctr_read(struct pctrst *st)
337
{
338
	int fd, se;
339
340
	fd = open(_PATH_PCTR, O_RDONLY);
341
	if (fd < 0)
342
		return (-1);
343
	if (ioctl(fd, PCIOCRD, st) < 0) {
344
		se = errno;
345
		close(fd);
346
		errno = se;
347
		return (-1);
348
	}
349
	return (close(fd));
350
}
351
352
static int
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 < 0)
359
		return (-1);
360
	if (ioctl(fd, PCIOCS0 + ctr, &val) < 0) {
361
		se = errno;
362
		close(fd);
363
		errno = se;
364
		return (-1);
365
	}
366
	return (close(fd));
367
}
368
369
static __inline void
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
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
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
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
}