]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pmccontrol/pmccontrol.c
pmap: move the smp_targeted_tlb_shutdown pointer stuff to amd64 pmap.h
[FreeBSD/FreeBSD.git] / usr.sbin / pmccontrol / pmccontrol.c
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 }