]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/usr.sbin/pmcstat/pmcstat.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / usr.sbin / pmcstat / pmcstat.c
1 /*-
2  * Copyright (c) 2003-2005, Joseph Koshy
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/types.h>
31 #include <sys/event.h>
32 #include <sys/queue.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 #include <sys/ttycom.h>
37 #include <sys/wait.h>
38
39 #include <assert.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <math.h>
45 #include <pmc.h>
46 #include <pmclog.h>
47 #include <signal.h>
48 #include <stdarg.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sysexits.h>
54 #include <unistd.h>
55
56 #include "pmcstat.h"
57
58 /*
59  * A given invocation of pmcstat(8) can manage multiple PMCs of both
60  * the system-wide and per-process variety.  Each of these could be in
61  * 'counting mode' or in 'sampling mode'.
62  *
63  * For 'counting mode' PMCs, pmcstat(8) will periodically issue a
64  * pmc_read() at the configured time interval and print out the value
65  * of the requested PMCs.
66  *
67  * For 'sampling mode' PMCs it can log to a file for offline analysis,
68  * or can analyse sampling data "on the fly", either by converting
69  * samples to printed textual form or by creating gprof(1) compatible
70  * profiles, one per program executed.  When creating gprof(1)
71  * profiles it can optionally merge entries from multiple processes
72  * for a given executable into a single profile file.
73  */
74
75 /* Globals */
76
77 int     pmcstat_interrupt = 0;
78 int     pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT;
79 int     pmcstat_pipefd[NPIPEFD];
80 int     pmcstat_kq;
81
82 /*
83  * cleanup
84  */
85
86 void
87 pmcstat_cleanup(struct pmcstat_args *a)
88 {
89         struct pmcstat_ev *ev, *tmp;
90
91         /* release allocated PMCs. */
92         STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp)
93             if (ev->ev_pmcid != PMC_ID_INVALID) {
94                 if (pmc_release(ev->ev_pmcid) < 0)
95                         err(EX_OSERR, "ERROR: cannot release pmc "
96                             "0x%x \"%s\"", ev->ev_pmcid, ev->ev_name);
97                 free(ev->ev_name);
98                 free(ev->ev_spec);
99                 STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next);
100                 free(ev);
101             }
102
103         /* de-configure the log file if present. */
104         if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
105                 (void) pmc_configure_logfile(-1);
106
107         if (a->pa_logparser) {
108                 pmclog_close(a->pa_logparser);
109                 a->pa_logparser = NULL;
110         }
111
112         if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
113                 pmcstat_shutdown_logging();
114 }
115
116 void
117 pmcstat_start_pmcs(struct pmcstat_args *a)
118 {
119         struct pmcstat_ev *ev;
120
121         STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
122
123             assert(ev->ev_pmcid != PMC_ID_INVALID);
124
125             if (pmc_start(ev->ev_pmcid) < 0) {
126                 warn("ERROR: Cannot start pmc 0x%x \"%s\"",
127                     ev->ev_pmcid, ev->ev_name);
128                 pmcstat_cleanup(a);
129                 exit(EX_OSERR);
130             }
131         }
132
133 }
134
135 void
136 pmcstat_print_headers(struct pmcstat_args *a)
137 {
138         struct pmcstat_ev *ev;
139         int c;
140
141         (void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX);
142
143         STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
144                 if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
145                         continue;
146
147                 c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p';
148
149                 if (ev->ev_fieldskip != 0) {
150                         (void) fprintf(a->pa_printfile, "%*s%c/%*s ",
151                             ev->ev_fieldskip, "", c,
152                             ev->ev_fieldwidth - ev->ev_fieldskip - 2,
153                             ev->ev_name);
154                 } else
155                         (void) fprintf(a->pa_printfile, "%c/%*s ",
156                             c, ev->ev_fieldwidth - 2, ev->ev_name);
157         }
158
159         (void) fflush(a->pa_printfile);
160 }
161
162 void
163 pmcstat_print_counters(struct pmcstat_args *a)
164 {
165         int extra_width;
166         struct pmcstat_ev *ev;
167         pmc_value_t value;
168
169         extra_width = sizeof(PRINT_HEADER_PREFIX) - 1;
170
171         STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
172
173                 /* skip sampling mode counters */
174                 if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
175                         continue;
176
177                 if (pmc_read(ev->ev_pmcid, &value) < 0)
178                         err(EX_OSERR, "ERROR: Cannot read pmc "
179                             "\"%s\"", ev->ev_name);
180
181                 (void) fprintf(a->pa_printfile, "%*ju ",
182                     ev->ev_fieldwidth + extra_width,
183                     (uintmax_t) ev->ev_cumulative ? value :
184                     (value - ev->ev_saved));
185
186                 if (ev->ev_cumulative == 0)
187                         ev->ev_saved = value;
188                 extra_width = 0;
189         }
190
191         (void) fflush(a->pa_printfile);
192 }
193
194 /*
195  * Print output
196  */
197
198 void
199 pmcstat_print_pmcs(struct pmcstat_args *a)
200 {
201         static int linecount = 0;
202
203         /* check if we need to print a header line */
204         if (++linecount > pmcstat_displayheight) {
205                 (void) fprintf(a->pa_printfile, "\n");
206                 linecount = 1;
207         }
208         if (linecount == 1)
209                 pmcstat_print_headers(a);
210         (void) fprintf(a->pa_printfile, "\n");
211
212         pmcstat_print_counters(a);
213
214         return;
215 }
216
217 /*
218  * Do process profiling
219  *
220  * If a pid was specified, attach each allocated PMC to the target
221  * process.  Otherwise, fork a child and attach the PMCs to the child,
222  * and have the child exec() the target program.
223  */
224
225 void
226 pmcstat_setup_process(struct pmcstat_args *a)
227 {
228         char token;
229         struct pmcstat_ev *ev;
230         struct kevent kev;
231
232         if (a->pa_flags & FLAG_HAS_PID) {
233                 STAILQ_FOREACH(ev, &a->pa_head, ev_next)
234                     if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0)
235                             err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to "
236                                 "process %d", ev->ev_name, (int) a->pa_pid);
237         } else {
238
239                 /*
240                  * We need to fork a new process and startup the child
241                  * using execvp().  Before doing the exec() the child
242                  * process reads its pipe for a token so that the parent
243                  * can finish doing its pmc_attach() calls.
244                  */
245                 if (pipe(pmcstat_pipefd) < 0)
246                         err(EX_OSERR, "ERROR: cannot create pipe");
247
248                 switch (a->pa_pid = fork()) {
249                 case -1:
250                         err(EX_OSERR, "ERROR: cannot fork");
251                         /*NOTREACHED*/
252
253                 case 0:         /* child */
254
255                         /* wait for our parent to signal us */
256                         (void) close(pmcstat_pipefd[WRITEPIPEFD]);
257                         if (read(pmcstat_pipefd[READPIPEFD], &token, 1) < 0)
258                                 err(EX_OSERR, "ERROR (child): cannot read "
259                                     "token");
260                         (void) close(pmcstat_pipefd[READPIPEFD]);
261
262                         /* exec() the program requested */
263                         execvp(*a->pa_argv, a->pa_argv);
264                         /* and if that fails, notify the parent */
265                         kill(getppid(), SIGCHLD);
266                         err(EX_OSERR, "ERROR: execvp \"%s\" failed",
267                             *a->pa_argv);
268                         /*NOTREACHED*/
269
270                 default:        /* parent */
271
272                         (void) close(pmcstat_pipefd[READPIPEFD]);
273
274                         /* attach all our PMCs to the child */
275                         STAILQ_FOREACH(ev, &args.pa_head, ev_next)
276                             if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) &&
277                                 pmc_attach(ev->ev_pmcid, a->pa_pid) != 0)
278                                     err(EX_OSERR, "ERROR: cannot attach pmc "
279                                         "\"%s\" to process %d", ev->ev_name,
280                                         (int) a->pa_pid);
281
282                 }
283         }
284
285         /* Ask to be notified via a kevent when the target process exits */
286         EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0,
287             NULL);
288         if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
289                 err(EX_OSERR, "ERROR: cannot monitor child process %d",
290                     a->pa_pid);
291         return;
292 }
293
294 void
295 pmcstat_start_process(struct pmcstat_args *a)
296 {
297
298         /* nothing to do: target is already running */
299         if (a->pa_flags & FLAG_HAS_PID)
300                 return;
301
302         /* write token to child to state that we are ready */
303         if (write(pmcstat_pipefd[WRITEPIPEFD], "+", 1) != 1)
304                 err(EX_OSERR, "ERROR: write failed");
305
306         (void) close(pmcstat_pipefd[WRITEPIPEFD]);
307 }
308
309 void
310 pmcstat_show_usage(void)
311 {
312         errx(EX_USAGE,
313             "[options] [commandline]\n"
314             "\t Measure process and/or system performance using hardware\n"
315             "\t performance monitoring counters.\n"
316             "\t Options include:\n"
317             "\t -C\t\t (toggle) show cumulative counts\n"
318             "\t -D path\t create profiles in directory \"path\"\n"
319             "\t -E\t\t (toggle) show counts at process exit\n"
320             "\t -O file\t send log output to \"file\"\n"
321             "\t -P spec\t allocate a process-private sampling PMC\n"
322             "\t -R file\t read events from \"file\"\n"
323             "\t -S spec\t allocate a system-wide sampling PMC\n"
324             "\t -W\t\t (toggle) show counts per context switch\n"
325             "\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n"
326             "\t -d\t\t (toggle) track descendants\n"
327             "\t -g\t\t produce gprof(1) compatible profiles\n"
328             "\t -k file\t set the path to the kernel\n"
329             "\t -n rate\t set sampling rate\n"
330             "\t -o file\t send print output to \"file\"\n"
331             "\t -p spec\t allocate a process-private counting PMC\n"
332             "\t -s spec\t allocate a system-wide counting PMC\n"
333             "\t -t pid\t\t attach to running process with pid \"pid\"\n"
334             "\t -w secs\t set printing time interval"
335         );
336 }
337
338 /*
339  * Main
340  */
341
342 int
343 main(int argc, char **argv)
344 {
345         double interval;
346         int option, npmc, ncpu;
347         int c, current_cpu, current_sampling_count;
348         int do_print, do_descendants;
349         int do_logproccsw, do_logprocexit;
350         int pipefd[2];
351         int use_cumulative_counts;
352         pid_t pid;
353         char *end;
354         const char *errmsg;
355         enum pmcstat_state runstate;
356         struct pmcstat_ev *ev;
357         struct sigaction sa;
358         struct kevent kev;
359         struct winsize ws;
360         struct stat sb;
361
362         current_cpu             = 0;
363         current_sampling_count  = DEFAULT_SAMPLE_COUNT;
364         do_descendants          = 0;
365         do_logproccsw           = 0;
366         do_logprocexit          = 0;
367         use_cumulative_counts   = 0;
368         args.pa_required        = 0;
369         args.pa_flags           = 0;
370         args.pa_pid             = (pid_t) -1;
371         args.pa_logfd           = -1;
372         args.pa_samplesdir      = ".";
373         args.pa_kernel          = "/boot/kernel/kernel";
374         args.pa_printfile       = stderr;
375         args.pa_interval        = DEFAULT_WAIT_INTERVAL;
376         STAILQ_INIT(&args.pa_head);
377
378         ev = NULL;
379
380         while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgk:n:o:p:s:t:w:"))
381             != -1)
382                 switch (option) {
383                 case 'C':       /* cumulative values */
384                         use_cumulative_counts = !use_cumulative_counts;
385                         args.pa_required |= FLAG_HAS_COUNTING_PMCS;
386                         break;
387
388                 case 'c':       /* CPU */
389                         current_cpu = strtol(optarg, &end, 0);
390                         if (*end != '\0' || current_cpu < 0)
391                                 errx(EX_USAGE,
392                                     "ERROR: Illegal CPU number \"%s\".",
393                                     optarg);
394                         args.pa_required |= FLAG_HAS_SYSTEM_PMCS;
395                         break;
396
397                 case 'D':
398                         if (stat(optarg, &sb) < 0)
399                                 err(EX_OSERR, "ERROR: Cannot stat \"%s\"",
400                                     optarg);
401                         if (!S_ISDIR(sb.st_mode))
402                                 errx(EX_USAGE, "ERROR: \"%s\" is not a "
403                                     "directory", optarg);
404                         args.pa_samplesdir = optarg;
405                         args.pa_flags     |= FLAG_HAS_SAMPLESDIR;
406                         args.pa_required  |= FLAG_DO_GPROF;
407                         break;
408
409                 case 'd':       /* toggle descendents */
410                         do_descendants = !do_descendants;
411                         args.pa_required |= FLAG_HAS_PROCESS_PMCS;
412                         break;
413
414                 case 'g':       /* produce gprof compatible profiles */
415                         args.pa_flags |= FLAG_DO_GPROF;
416                         break;
417
418                 case 'k':       /* pathname to the kernel */
419                         args.pa_kernel = optarg;
420                         args.pa_required |= FLAG_DO_GPROF;
421                         args.pa_flags    |= FLAG_HAS_KERNELPATH;
422                         break;
423
424                 case 'E':       /* log process exit */
425                         do_logprocexit = !do_logprocexit;
426                         args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
427                             FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
428                         break;
429
430                 case 'p':       /* process virtual counting PMC */
431                 case 's':       /* system-wide counting PMC */
432                 case 'P':       /* process virtual sampling PMC */
433                 case 'S':       /* system-wide sampling PMC */
434                         if ((ev = malloc(sizeof(*ev))) == NULL)
435                                 errx(EX_SOFTWARE, "ERROR: Out of memory.");
436
437                         switch (option) {
438                         case 'p': ev->ev_mode = PMC_MODE_TC; break;
439                         case 's': ev->ev_mode = PMC_MODE_SC; break;
440                         case 'P': ev->ev_mode = PMC_MODE_TS; break;
441                         case 'S': ev->ev_mode = PMC_MODE_SS; break;
442                         }
443
444                         if (option == 'P' || option == 'p') {
445                                 args.pa_flags |= FLAG_HAS_PROCESS_PMCS;
446                                 args.pa_required |= (FLAG_HAS_COMMANDLINE |
447                                     FLAG_HAS_PID);
448                         }
449
450                         if (option == 'P' || option == 'S') {
451                                 args.pa_flags |= FLAG_HAS_SAMPLING_PMCS;
452                                 args.pa_required |= (FLAG_HAS_PIPE |
453                                     FLAG_HAS_OUTPUT_LOGFILE);
454                         }
455
456                         if (option == 'p' || option == 's')
457                                 args.pa_flags |= FLAG_HAS_COUNTING_PMCS;
458
459                         if (option == 's' || option == 'S')
460                                 args.pa_flags |= FLAG_HAS_SYSTEM_PMCS;
461
462                         ev->ev_spec  = strdup(optarg);
463
464                         if (option == 'S' || option == 'P')
465                                 ev->ev_count = current_sampling_count;
466                         else
467                                 ev->ev_count = -1;
468
469                         if (option == 'S' || option == 's')
470                                 ev->ev_cpu = current_cpu;
471                         else
472                                 ev->ev_cpu = PMC_CPU_ANY;
473
474                         ev->ev_flags = 0;
475                         if (do_descendants)
476                                 ev->ev_flags |= PMC_F_DESCENDANTS;
477                         if (do_logprocexit)
478                                 ev->ev_flags |= PMC_F_LOG_PROCEXIT;
479                         if (do_logproccsw)
480                                 ev->ev_flags |= PMC_F_LOG_PROCCSW;
481
482                         ev->ev_cumulative  = use_cumulative_counts;
483
484                         ev->ev_saved = 0LL;
485                         ev->ev_pmcid = PMC_ID_INVALID;
486
487                         /* extract event name */
488                         c = strcspn(optarg, ", \t");
489                         ev->ev_name = malloc(c + 1);
490                         (void) strncpy(ev->ev_name, optarg, c);
491                         *(ev->ev_name + c) = '\0';
492
493                         STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next);
494
495                         break;
496
497                 case 'n':       /* sampling count */
498                         current_sampling_count = strtol(optarg, &end, 0);
499                         if (*end != '\0' || current_sampling_count <= 0)
500                                 errx(EX_USAGE,
501                                     "ERROR: Illegal count value \"%s\".",
502                                     optarg);
503                         args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
504                         break;
505
506                 case 'o':       /* outputfile */
507                         if (args.pa_printfile != NULL)
508                                 (void) fclose(args.pa_printfile);
509                         if ((args.pa_printfile = fopen(optarg, "w")) == NULL)
510                                 errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
511                                     "writing.", optarg);
512                         args.pa_flags |= FLAG_DO_PRINT;
513                         break;
514
515                 case 'O':       /* sampling output */
516                         if (args.pa_outputpath)
517                                 errx(EX_USAGE, "ERROR: option -O may only be "
518                                     "specified once.");
519                         args.pa_outputpath = optarg;
520                         args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE;
521                         break;
522
523                 case 'R':       /* read an existing log file */
524                         if (args.pa_logparser != NULL)
525                                 errx(EX_USAGE, "ERROR: option -R may only be "
526                                     "specified once.");
527                         args.pa_inputpath = optarg;
528                         if (args.pa_printfile == stderr)
529                                 args.pa_printfile = stdout;
530                         args.pa_flags |= FLAG_READ_LOGFILE;
531                         break;
532
533                 case 't':       /* target pid */
534                         pid = strtol(optarg, &end, 0);
535                         if (*end != '\0' || pid <= 0)
536                                 errx(EX_USAGE, "ERROR: Illegal pid value "
537                                     "\"%s\".", optarg);
538
539                         args.pa_flags |= FLAG_HAS_PID;
540                         args.pa_required |= FLAG_HAS_PROCESS_PMCS;
541                         args.pa_pid = pid;
542                         break;
543
544                 case 'w':       /* wait interval */
545                         interval = strtod(optarg, &end);
546                         if (*end != '\0' || interval <= 0)
547                                 errx(EX_USAGE, "ERROR: Illegal wait interval "
548                                     "value \"%s\".", optarg);
549                         args.pa_flags |= FLAG_HAS_WAIT_INTERVAL;
550                         args.pa_required |= FLAG_HAS_COUNTING_PMCS;
551                         args.pa_interval = interval;
552                         break;
553
554                 case 'W':       /* toggle LOG_CSW */
555                         do_logproccsw = !do_logproccsw;
556                         args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
557                             FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
558                         break;
559
560                 case '?':
561                 default:
562                         pmcstat_show_usage();
563                         break;
564
565                 }
566
567         args.pa_argc = (argc -= optind);
568         args.pa_argv = (argv += optind);
569
570         if (argc)       /* command line present */
571                 args.pa_flags |= FLAG_HAS_COMMANDLINE;
572
573         /*
574          * Check invocation syntax.
575          */
576
577         /* disallow -O and -R together */
578         if (args.pa_outputpath && args.pa_inputpath)
579                 errx(EX_USAGE, "ERROR: options -O and -R are mutually "
580                     "exclusive.");
581
582         if (args.pa_flags & FLAG_READ_LOGFILE) {
583                 errmsg = NULL;
584                 if (args.pa_flags & FLAG_HAS_COMMANDLINE)
585                         errmsg = "a command line specification";
586                 else if (args.pa_flags & FLAG_HAS_PID)
587                         errmsg = "option -t";
588                 else if (!STAILQ_EMPTY(&args.pa_head))
589                         errmsg = "a PMC event specification";
590                 if (errmsg)
591                         errx(EX_USAGE, "ERROR: option -R may not be used with "
592                             "%s.", errmsg);
593         } else if (STAILQ_EMPTY(&args.pa_head))
594                 /* All other uses require a PMC spec. */
595                 pmcstat_show_usage();
596
597         /* check for -t pid without a process PMC spec */
598         if ((args.pa_required & FLAG_HAS_PID) &&
599             (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
600                 errx(EX_USAGE, "ERROR: option -t requires a process mode PMC "
601                     "to be specified.");
602
603         /* check for process-mode options without a command or -t pid */
604         if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
605             (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
606                 errx(EX_USAGE, "ERROR: options -d, -E, -p, -P, and -W require "
607                     "a command line or target process.");
608
609         /* check for -p | -P without a target process of some sort */
610         if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) &&
611             (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
612                 errx(EX_USAGE, "ERROR: options -P and -p require a "
613                     "target process or a command line.");
614
615         /* check for process-mode options without a process-mode PMC */
616         if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
617             (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
618                 errx(EX_USAGE, "ERROR: options -d, -E, and -W require a "
619                     "process mode PMC to be specified.");
620
621         /* check for -c cpu and not system mode PMCs */
622         if ((args.pa_required & FLAG_HAS_SYSTEM_PMCS) &&
623             (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) == 0)
624                 errx(EX_USAGE, "ERROR: option -c requires at least one "
625                     "system mode PMC to be specified.");
626
627         /* check for counting mode options without a counting PMC */
628         if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) &&
629             (args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0)
630                 errx(EX_USAGE, "ERROR: options -C, -W, -o and -w require at "
631                     "least one counting mode PMC to be specified.");
632
633         /* check for sampling mode options without a sampling PMC spec */
634         if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) &&
635             (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0)
636                 errx(EX_USAGE, "ERROR: options -n and -O require at least "
637                     "one sampling mode PMC to be specified.");
638
639         if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) ==
640             (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE))
641                 errx(EX_USAGE,
642                     "ERROR: option -t cannot be specified with a command "
643                     "line.");
644
645         /* check if -g is being used correctly */
646         if ((args.pa_flags & FLAG_DO_GPROF) &&
647             !(args.pa_flags & (FLAG_HAS_SAMPLING_PMCS|FLAG_READ_LOGFILE)))
648                 errx(EX_USAGE, "ERROR: option -g requires sampling PMCs or -R "
649                     "to be specified.");
650
651         /* check if -O was spuriously specified */
652         if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) &&
653             (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0)
654                 errx(EX_USAGE,
655                     "ERROR: option -O is used only with options "
656                     "-E, -P, -S and -W.");
657
658         /* -D dir and -k kernel path require -g */
659         if ((args.pa_flags & FLAG_HAS_KERNELPATH) &&
660             ((args.pa_flags & FLAG_DO_GPROF) == 0))
661             errx(EX_USAGE, "ERROR: option -k is only used with -g.");
662
663         if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) &&
664             ((args.pa_flags & FLAG_DO_GPROF) == 0))
665             errx(EX_USAGE, "ERROR: option -D is only used with -g.");
666
667         /*
668          * Disallow textual output of sampling PMCs if counting PMCs
669          * have also been asked for, mostly because the combined output
670          * is difficult to make sense of.
671          */
672         if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) &&
673             (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) &&
674             ((args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0))
675                 errx(EX_USAGE, "ERROR: option -O is required if counting and "
676                     "sampling PMCs are specified together.");
677
678         /* if we've been asked to process a log file, do that and exit */
679         if (args.pa_flags & FLAG_READ_LOGFILE) {
680                 /*
681                  * Print the log in textual form if we haven't been
682                  * asked to generate gmon.out files.
683                  */
684                 if ((args.pa_flags & FLAG_DO_GPROF) == 0)
685                         args.pa_flags |= FLAG_DO_PRINT;
686
687                 pmcstat_initialize_logging(&args);
688                 if ((args.pa_logfd = pmcstat_open(args.pa_inputpath,
689                     PMCSTAT_OPEN_FOR_READ)) < 0)
690                         err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
691                             "reading", args.pa_inputpath);
692                 if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL)
693                         err(EX_OSERR, "ERROR: Cannot create parser");
694                 pmcstat_process_log(&args);
695                 exit(EX_OK);
696         }
697
698         /* otherwise, we've been asked to collect data */
699         if (pmc_init() < 0)
700                 err(EX_UNAVAILABLE,
701                     "ERROR: Initialization of the pmc(3) library failed");
702
703         if ((ncpu = pmc_ncpu()) < 0)
704                 err(EX_OSERR, "ERROR: Cannot determine the number CPUs "
705                     "on the system");
706
707         if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
708                 err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
709                     "on CPU %d", 0);
710
711         /* Allocate a kqueue */
712         if ((pmcstat_kq = kqueue()) < 0)
713                 err(EX_OSERR, "ERROR: Cannot allocate kqueue");
714
715         /*
716          * Configure the specified log file or setup a default log
717          * consumer via a pipe.
718          */
719         if (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) {
720                 if (args.pa_outputpath) {
721                         if ((args.pa_logfd = pmcstat_open(args.pa_outputpath,
722                             PMCSTAT_OPEN_FOR_WRITE)) < 0)
723                                 err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
724                                     "writing", args.pa_outputpath);
725                 } else {
726                         /*
727                          * process the log on the fly by reading it in
728                          * through a pipe.
729                          */
730                         if (pipe(pipefd) < 0)
731                                 err(EX_OSERR, "ERROR: pipe(2) failed");
732
733                         if (fcntl(pipefd[READPIPEFD], F_SETFL, O_NONBLOCK) < 0)
734                                 err(EX_OSERR, "ERROR: fcntl(2) failed");
735
736                         EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD,
737                             0, 0, NULL);
738
739                         if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
740                                 err(EX_OSERR, "ERROR: Cannot register kevent");
741
742                         args.pa_logfd = pipefd[WRITEPIPEFD];
743
744                         args.pa_flags |= (FLAG_HAS_PIPE | FLAG_DO_PRINT);
745                         args.pa_logparser = pmclog_open(pipefd[READPIPEFD]);
746                 }
747
748                 if (pmc_configure_logfile(args.pa_logfd) < 0)
749                         err(EX_OSERR, "ERROR: Cannot configure log file");
750         }
751
752         /*
753          * Allocate PMCs.
754          */
755
756         STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
757             if (pmc_allocate(ev->ev_spec, ev->ev_mode,
758                     ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0)
759                     err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with "
760                         "specification \"%s\"",
761                         PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process",
762                         ev->ev_spec);
763
764             if (PMC_IS_SAMPLING_MODE(ev->ev_mode) &&
765                 pmc_set(ev->ev_pmcid, ev->ev_count) < 0)
766                     err(EX_OSERR, "ERROR: Cannot set sampling count "
767                         "for PMC \"%s\"", ev->ev_name);
768         }
769
770         /* compute printout widths */
771         STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
772                 int counter_width;
773                 int display_width;
774                 int header_width;
775
776                 (void) pmc_width(ev->ev_pmcid, &counter_width);
777                 header_width = strlen(ev->ev_name) + 2; /* prefix '%c|' */
778                 display_width = (int) floor(counter_width / 3.32193) + 1;
779
780                 if (header_width > display_width) {
781                         ev->ev_fieldskip = 0;
782                         ev->ev_fieldwidth = header_width;
783                 } else {
784                         ev->ev_fieldskip = display_width -
785                             header_width;
786                         ev->ev_fieldwidth = display_width;
787                 }
788         }
789
790         /*
791          * If our output is being set to a terminal, register a handler
792          * for window size changes.
793          */
794
795         if (isatty(fileno(args.pa_printfile))) {
796
797                 if (ioctl(fileno(args.pa_printfile), TIOCGWINSZ, &ws) < 0)
798                         err(EX_OSERR, "ERROR: Cannot determine window size");
799
800                 pmcstat_displayheight = ws.ws_row - 1;
801
802                 EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
803
804                 if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
805                         err(EX_OSERR, "ERROR: Cannot register kevent for "
806                             "SIGWINCH");
807         }
808
809         EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
810         if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
811                 err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT");
812
813         EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
814         if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
815                 err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO");
816
817         /*
818          * An exec() failure of a forked child is signalled by the
819          * child sending the parent a SIGCHLD.  We don't register an
820          * actual signal handler for SIGCHLD, but instead use our
821          * kqueue to pick up the signal.
822          */
823         EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
824         if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
825                 err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD");
826
827         /* setup a timer if we have counting mode PMCs needing to be printed */
828         if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) &&
829             (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) {
830                 EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0,
831                     args.pa_interval * 1000, NULL);
832
833                 if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
834                         err(EX_OSERR, "ERROR: Cannot register kevent for "
835                             "timer");
836         }
837
838         /* attach PMCs to the target process, starting it if specified */
839         if (args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE))
840                 pmcstat_setup_process(&args);
841
842         /* start the pmcs */
843         pmcstat_start_pmcs(&args);
844
845         /* start the (commandline) process if needed */
846         if (args.pa_flags & FLAG_HAS_COMMANDLINE)
847                 pmcstat_start_process(&args);
848
849         /* initialize logging if printing the configured log */
850         if ((args.pa_flags & FLAG_DO_PRINT) &&
851             (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)))
852                 pmcstat_initialize_logging(&args);
853
854         /* Handle SIGINT using the kqueue loop */
855         sa.sa_handler = SIG_IGN;
856         sa.sa_flags   = 0;
857         (void) sigemptyset(&sa.sa_mask);
858
859         if (sigaction(SIGINT, &sa, NULL) < 0)
860                 err(EX_OSERR, "ERROR: Cannot install signal handler");
861
862         /*
863          * loop till either the target process (if any) exits, or we
864          * are killed by a SIGINT.
865          */
866         runstate = PMCSTAT_RUNNING;
867         do_print = 0;
868         do {
869                 if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) {
870                         if (errno != EINTR)
871                                 err(EX_OSERR, "ERROR: kevent failed");
872                         else
873                                 continue;
874                 }
875
876                 if (kev.flags & EV_ERROR)
877                         errc(EX_OSERR, kev.data, "ERROR: kevent failed");
878
879                 switch (kev.filter) {
880                 case EVFILT_PROC:  /* target has exited */
881                         if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
882                                 FLAG_HAS_PIPE))
883                                 runstate = pmcstat_close_log(&args);
884                         else
885                                 runstate = PMCSTAT_FINISHED;
886                         do_print = 1;
887                         break;
888
889                 case EVFILT_READ:  /* log file data is present */
890                         runstate = pmcstat_process_log(&args);
891                         break;
892
893                 case EVFILT_SIGNAL:
894                         if (kev.ident == SIGCHLD) {
895                                 /*
896                                  * The child process sends us a
897                                  * SIGCHLD if its exec() failed.  We
898                                  * wait for it to exit and then exit
899                                  * ourselves.
900                                  */
901                                 (void) wait(&c);
902                                 runstate = PMCSTAT_FINISHED;
903                         } else if (kev.ident == SIGIO) {
904                                 /*
905                                  * We get a SIGIO if a PMC loses all
906                                  * of its targets, or if logfile
907                                  * writes encounter an error.
908                                  */
909                                 if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
910                                     FLAG_HAS_PIPE)) {
911                                         runstate = pmcstat_close_log(&args);
912                                         if (args.pa_flags &
913                                             (FLAG_DO_PRINT|FLAG_DO_GPROF))
914                                                 pmcstat_process_log(&args);
915                                 }
916                                 do_print = 1; /* print PMCs at exit */
917                                 runstate = PMCSTAT_FINISHED;
918                         } else if (kev.ident == SIGINT) {
919                                 /* Kill the child process if we started it */
920                                 if (args.pa_flags & FLAG_HAS_COMMANDLINE)
921                                         if (kill(args.pa_pid, SIGINT) != 0)
922                                                 err(EX_OSERR, "ERROR: cannot "
923                                                     "signal child process");
924                                 runstate = PMCSTAT_FINISHED;
925                         } else if (kev.ident == SIGWINCH) {
926                                 if (ioctl(fileno(args.pa_printfile),
927                                         TIOCGWINSZ, &ws) < 0)
928                                     err(EX_OSERR, "ERROR: Cannot determine "
929                                         "window size");
930                                 pmcstat_displayheight = ws.ws_row - 1;
931                         } else
932                                 assert(0);
933
934                         break;
935
936                 case EVFILT_TIMER: /* print out counting PMCs */
937                         do_print = 1;
938                         break;
939
940                 }
941
942                 if (do_print &&
943                     (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) {
944                         pmcstat_print_pmcs(&args);
945                         if (runstate == PMCSTAT_FINISHED && /* final newline */
946                             (args.pa_flags & FLAG_DO_PRINT) == 0)
947                                 (void) fprintf(args.pa_printfile, "\n");
948                         do_print = 0;
949                 }
950
951         } while (runstate != PMCSTAT_FINISHED);
952
953         /* flush any pending log entries */
954         if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE))
955                 pmc_flush_logfile();
956
957         pmcstat_cleanup(&args);
958
959         return 0;
960 }