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