xref: /freebsd/usr.sbin/pmccontrol/pmccontrol.c (revision 2f9966ff63d65bd474478888c9088eeae3f9c669)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003,2004 Joseph Koshy
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/param.h>
31 #include <sys/queue.h>
32 #include <sys/cpuset.h>
33 #include <sys/sysctl.h>
34 
35 #include <assert.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <pmc.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sysexits.h>
46 #include <unistd.h>
47 
48 /* Compile time defaults */
49 
50 #define	PMCC_PRINT_USAGE	0
51 #define	PMCC_PRINT_EVENTS	1
52 #define	PMCC_LIST_STATE 	2
53 #define	PMCC_ENABLE_DISABLE	3
54 #define	PMCC_SHOW_STATISTICS	4
55 
56 #define	PMCC_CPU_ALL		-1
57 #define	PMCC_CPU_WILDCARD	'*'
58 
59 #define	PMCC_PMC_ALL		-1
60 #define	PMCC_PMC_WILDCARD	'*'
61 
62 #define	PMCC_OP_IGNORE		0
63 #define	PMCC_OP_DISABLE		1
64 #define	PMCC_OP_ENABLE		2
65 
66 #define	PMCC_PROGRAM_NAME	"pmccontrol"
67 
68 static STAILQ_HEAD(pmcc_op_list, pmcc_op) head = STAILQ_HEAD_INITIALIZER(head);
69 
70 struct pmcc_op {
71 	char	op_cpu;
72 	char	op_pmc;
73 	char	op_op;
74 	STAILQ_ENTRY(pmcc_op) op_next;
75 };
76 
77 /* Function Prototypes */
78 #if	DEBUG
79 static void	pmcc_init_debug(void);
80 #endif
81 
82 static int	pmcc_do_list_state(void);
83 static int	pmcc_do_enable_disable(struct pmcc_op_list *);
84 static int	pmcc_do_list_events(void);
85 
86 /* Globals */
87 
88 static char usage_message[] =
89 	"Usage:\n"
90 	"       " PMCC_PROGRAM_NAME " -L\n"
91 	"       " PMCC_PROGRAM_NAME " -l\n"
92 	"       " PMCC_PROGRAM_NAME " -s\n"
93 	"       " PMCC_PROGRAM_NAME " [-e pmc | -d pmc | -c cpu] ...";
94 
95 #if DEBUG
96 static FILE *debug_stream = NULL;
97 #endif
98 
99 #if DEBUG
100 #define DEBUG_MSG(...)					                \
101 	(void) fprintf(debug_stream, "[pmccontrol] " __VA_ARGS__)
102 #else
103 #define DEBUG_MSG(m)		/*  */
104 #endif /* !DEBUG */
105 
106 #if DEBUG
107 /* log debug messages to a separate file */
108 static void
109 pmcc_init_debug(void)
110 {
111 	char *fn;
112 
113 	fn = getenv("PMCCONTROL_DEBUG");
114 	if (fn != NULL)
115 	{
116 		debug_stream = fopen(fn, "w");
117 		if (debug_stream == NULL)
118 			debug_stream = stderr;
119 	} else
120 		debug_stream = stderr;
121 }
122 #endif
123 
124 static int
125 pmcc_do_enable_disable(struct pmcc_op_list *op_list)
126 {
127 	int c, error, i, j, ncpu, npmc, t;
128 	struct pmcc_op *np;
129 	unsigned char *map;
130 	unsigned char op;
131 	int cpu, pmc;
132 
133 	if ((ncpu = pmc_ncpu()) < 0)
134 		err(EX_OSERR, "Unable to determine the number of cpus");
135 
136 	/* Determine the maximum number of PMCs in any CPU. */
137 	npmc = 0;
138 	for (c = 0; c < ncpu; c++) {
139 		if ((t = pmc_npmc(c)) < 0)
140 			err(EX_OSERR,
141 			    "Unable to determine the number of PMCs in CPU %d",
142 			    c);
143 		npmc = MAX(t, npmc);
144 	}
145 
146 	if (npmc == 0)
147 		errx(EX_CONFIG, "No PMCs found");
148 
149 	if ((map = calloc(npmc, ncpu)) == NULL)
150 		err(EX_SOFTWARE, "Out of memory");
151 
152 	error = 0;
153 	STAILQ_FOREACH(np, op_list, op_next) {
154 
155 		cpu = np->op_cpu;
156 		pmc = np->op_pmc;
157 		op  = np->op_op;
158 
159 		if (cpu >= ncpu)
160 			errx(EX_DATAERR, "CPU id too large: \"%d\"", cpu);
161 
162 		if (pmc >= npmc)
163 			errx(EX_DATAERR, "PMC id too large: \"%d\"", pmc);
164 
165 #define MARKMAP(M,C,P,V)	do {				\
166 		*((M) + (C)*npmc + (P)) = (V);			\
167 } while (0)
168 
169 #define	SET_PMCS(C,P,V)		do {				\
170 		if ((P) == PMCC_PMC_ALL) {			\
171 			for (j = 0; j < npmc; j++)		\
172 				MARKMAP(map, (C), j, (V));	\
173 		} else						\
174 			MARKMAP(map, (C), (P), (V));		\
175 } while (0)
176 
177 #define MAP(M,C,P)	(*((M) + (C)*npmc + (P)))
178 
179 		if (cpu == PMCC_CPU_ALL)
180 			for (i = 0; i < ncpu; i++) {
181 				SET_PMCS(i, pmc, op);
182 			}
183 		else
184 			SET_PMCS(cpu, pmc, op);
185 	}
186 
187 	/* Configure PMCS */
188 	for (i = 0; i < ncpu; i++)
189 		for (j = 0; j < npmc; j++) {
190 			unsigned char b;
191 
192 			b = MAP(map, i, j);
193 
194 			error = 0;
195 
196 			if (b == PMCC_OP_ENABLE)
197 				error = pmc_enable(i, j);
198 			else if (b == PMCC_OP_DISABLE)
199 				error = pmc_disable(i, j);
200 
201 			if (error < 0)
202 				err(EX_OSERR, "%s of PMC %d on CPU %d failed",
203 				    b == PMCC_OP_ENABLE ? "Enable" : "Disable",
204 				    j, i);
205 		}
206 
207 	return error;
208 }
209 
210 static int
211 pmcc_do_list_state(void)
212 {
213 	cpuset_t logical_cpus_mask;
214 	long cpusetsize;
215 	size_t setsize;
216 	int c, cpu, n, npmc, ncpu;
217 	struct pmc_info *pd;
218 	struct pmc_pmcinfo *pi;
219 	const struct pmc_cpuinfo *pc;
220 
221 	if (pmc_cpuinfo(&pc) != 0)
222 		err(EX_OSERR, "Unable to determine CPU information");
223 
224 	printf("%d %s CPUs present, with %d PMCs per CPU\n", pc->pm_ncpu,
225 	       pmc_name_of_cputype(pc->pm_cputype),
226 		pc->pm_npmc);
227 
228 	/* Determine the set of logical CPUs. */
229 	cpusetsize = sysconf(_SC_CPUSET_SIZE);
230 	if (cpusetsize == -1 || (u_long)cpusetsize > sizeof(cpuset_t))
231 		err(EX_OSERR, "Cannot determine which CPUs are logical");
232 	CPU_ZERO(&logical_cpus_mask);
233 	setsize = (size_t)cpusetsize;
234 	if (sysctlbyname("machdep.logical_cpus_mask", &logical_cpus_mask,
235 	    &setsize, NULL, 0) < 0)
236 		CPU_ZERO(&logical_cpus_mask);
237 
238 	ncpu = pc->pm_ncpu;
239 
240 	for (c = cpu = 0; cpu < ncpu; cpu++) {
241 		if (pmc_pmcinfo(cpu, &pi) < 0) {
242 			if (errno == ENXIO)
243 				continue;
244 			err(EX_OSERR, "Unable to get PMC status for CPU %d",
245 			    cpu);
246 		}
247 
248 		printf("#CPU %d:\n", c++);
249 		npmc = pmc_npmc(cpu);
250 		printf("#N  NAME             CLASS  STATE    ROW-DISP\n");
251 
252 		for (n = 0; n < npmc; n++) {
253 			pd = &pi->pm_pmcs[n];
254 
255 			printf(" %-2d %-16s %-6s %-8s %-10s",
256 			    n,
257 			    pd->pm_name,
258 			    pmc_name_of_class(pd->pm_class),
259 			    pd->pm_enabled ? "ENABLED" : "DISABLED",
260 			    pmc_name_of_disposition(pd->pm_rowdisp));
261 
262 			if (pd->pm_ownerpid != -1) {
263 			        printf(" (pid %d)", pd->pm_ownerpid);
264 				printf(" %-32s",
265 				    pmc_name_of_event(pd->pm_event));
266 				if (PMC_IS_SAMPLING_MODE(pd->pm_mode))
267 					printf(" (reload count %jd)",
268 					    pd->pm_reloadcount);
269 			}
270 			printf("\n");
271 		}
272 		free(pi);
273 	}
274 	return 0;
275 }
276 
277 static int
278 pmcc_do_list_events(void)
279 {
280 	enum pmc_class c;
281 	unsigned int i, j, nevents;
282 	const char **eventnamelist;
283 	const struct pmc_cpuinfo *ci;
284 
285 	/* First, try pmu events. */
286 	if (pmc_pmu_enabled()) {
287 		pmc_pmu_print_counters(NULL);
288 		return (0);
289 	}
290 
291 	/* Otherwise, use the legacy pmc(3) interfaces. */
292 	if (pmc_cpuinfo(&ci) != 0)
293 		err(EX_OSERR, "Unable to determine CPU information");
294 
295 	eventnamelist = NULL;
296 
297 	for (i = 0; i < ci->pm_nclass; i++) {
298 		c = ci->pm_classes[i].pm_class;
299 
300 		printf("%s\n", pmc_name_of_class(c));
301 		if (pmc_event_names_of_class(c, &eventnamelist, &nevents) < 0)
302 			err(EX_OSERR,
303 "ERROR: Cannot find information for event class \"%s\"",
304 			    pmc_name_of_class(c));
305 
306 		for (j = 0; j < nevents; j++)
307 			printf("\t%s\n", eventnamelist[j]);
308 
309 		free(eventnamelist);
310 	}
311 	return 0;
312 }
313 
314 static int
315 pmcc_show_statistics(void)
316 {
317 
318 	struct pmc_driverstats gms;
319 
320 	if (pmc_get_driver_stats(&gms) < 0)
321 		err(EX_OSERR, "ERROR: cannot retrieve driver statistics");
322 
323 	/*
324 	 * Print statistics.
325 	 */
326 
327 #define	PRINT(N,V)	(void) printf("%-40s %d\n", (N), gms.pm_##V)
328 	PRINT("interrupts processed:", intr_processed);
329 	PRINT("non-PMC interrupts:", intr_ignored);
330 	PRINT("sampling stalls due to space shortages:", intr_bufferfull);
331 	PRINT("system calls:", syscalls);
332 	PRINT("system calls with errors:", syscall_errors);
333 	PRINT("buffer requests:", buffer_requests);
334 	PRINT("buffer requests failed:", buffer_requests_failed);
335 	PRINT("sampling log sweeps:", log_sweeps);
336 
337 	return 0;
338 }
339 
340 /*
341  * Main
342  */
343 
344 int
345 main(int argc, char **argv)
346 {
347 	int error, command, currentcpu, option, pmc;
348 	char *dummy;
349 	struct pmcc_op *p;
350 
351 #if DEBUG
352 	pmcc_init_debug();
353 #endif
354 
355 	/* parse args */
356 
357 	currentcpu = PMCC_CPU_ALL;
358 	command    = PMCC_PRINT_USAGE;
359 	error      = 0;
360 
361 	STAILQ_INIT(&head);
362 
363 	while ((option = getopt(argc, argv, ":c:d:e:lLs")) != -1)
364 		switch (option) {
365 		case 'L':
366 			if (command != PMCC_PRINT_USAGE) {
367 				error = 1;
368 				break;
369 			}
370 			command = PMCC_PRINT_EVENTS;
371 			break;
372 
373 		case 'c':
374 			if (command != PMCC_PRINT_USAGE &&
375 			    command != PMCC_ENABLE_DISABLE) {
376 				error = 1;
377 				break;
378 			}
379 			command = PMCC_ENABLE_DISABLE;
380 
381 			if (*optarg == PMCC_CPU_WILDCARD)
382 				currentcpu = PMCC_CPU_ALL;
383 			else {
384 				currentcpu = strtoul(optarg, &dummy, 0);
385 				if (*dummy != '\0' || currentcpu < 0)
386 					errx(EX_DATAERR,
387 					    "\"%s\" is not a valid CPU id",
388 					    optarg);
389 			}
390 			break;
391 
392 		case 'd':
393 		case 'e':
394 			if (command != PMCC_PRINT_USAGE &&
395 			    command != PMCC_ENABLE_DISABLE) {
396 				error = 1;
397 				break;
398 			}
399 			command = PMCC_ENABLE_DISABLE;
400 
401 			if (*optarg == PMCC_PMC_WILDCARD)
402 				pmc = PMCC_PMC_ALL;
403 			else {
404 				pmc = strtoul(optarg, &dummy, 0);
405 				if (*dummy != '\0' || pmc < 0)
406 					errx(EX_DATAERR,
407 					    "\"%s\" is not a valid PMC id",
408 					    optarg);
409 			}
410 
411 			if ((p = malloc(sizeof(*p))) == NULL)
412 				err(EX_SOFTWARE, "Out of memory");
413 
414 			p->op_cpu = currentcpu;
415 			p->op_pmc = pmc;
416 			p->op_op  = option == 'd' ? PMCC_OP_DISABLE :
417 			    PMCC_OP_ENABLE;
418 
419 			STAILQ_INSERT_TAIL(&head, p, op_next);
420 			break;
421 
422 		case 'l':
423 			if (command != PMCC_PRINT_USAGE) {
424 				error = 1;
425 				break;
426 			}
427 			command = PMCC_LIST_STATE;
428 			break;
429 
430 		case 's':
431 			if (command != PMCC_PRINT_USAGE) {
432 				error = 1;
433 				break;
434 			}
435 			command = PMCC_SHOW_STATISTICS;
436 			break;
437 
438 		case ':':
439 			errx(EX_USAGE,
440 			    "Missing argument to option '-%c'", optopt);
441 			break;
442 
443 		case '?':
444 			warnx("Unrecognized option \"-%c\"", optopt);
445 			errx(EX_USAGE, "%s", usage_message);
446 			break;
447 
448 		default:
449 			error = 1;
450 			break;
451 
452 		}
453 
454 	if (command == PMCC_PRINT_USAGE)
455 		(void) errx(EX_USAGE, "%s", usage_message);
456 
457 	if (error)
458 		exit(EX_USAGE);
459 
460 	if (pmc_init() < 0)
461 		err(EX_UNAVAILABLE,
462 		    "Initialization of the pmc(3) library failed");
463 
464 	switch (command) {
465 	case PMCC_LIST_STATE:
466 		error = pmcc_do_list_state();
467 		break;
468 	case PMCC_PRINT_EVENTS:
469 		error = pmcc_do_list_events();
470 		break;
471 	case PMCC_SHOW_STATISTICS:
472 		error = pmcc_show_statistics();
473 		break;
474 	case PMCC_ENABLE_DISABLE:
475 		if (STAILQ_EMPTY(&head))
476 			errx(EX_USAGE,
477 			    "No PMCs specified to enable or disable");
478 		error = pmcc_do_enable_disable(&head);
479 		break;
480 	default:
481 		assert(0);
482 
483 	}
484 
485 	if (error != 0)
486 		err(EX_OSERR, "Command failed");
487 	exit(0);
488 }
489