pmcstat.c revision 145256
168583Smarcel/*-
268583Smarcel * Copyright (c) 2003,2004 Joseph Koshy
368583Smarcel * All rights reserved.
468583Smarcel *
568583Smarcel * Redistribution and use in source and binary forms, with or without
6293593Sdchagin * modification, are permitted provided that the following conditions
768583Smarcel * are met:
868583Smarcel * 1. Redistributions of source code must retain the above copyright
968583Smarcel *    notice, this list of conditions and the following disclaimer.
1068583Smarcel * 2. Redistributions in binary form must reproduce the above copyright
1168583Smarcel *    notice, this list of conditions and the following disclaimer in the
1268583Smarcel *    documentation and/or other materials provided with the distribution.
1368583Smarcel *
14177999Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15227776Slstewart * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16164184Strhodes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17122256Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18255673Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19122256Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20161330Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21161330Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2268583Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2368583Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2483366Sjulian * SUCH DAMAGE.
2583366Sjulian *
2668583Smarcel */
2768583Smarcel
2868583Smarcel#include <sys/cdefs.h>
2983221Smarcel__FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcstat.c 145256 2005-04-19 04:01:25Z jkoshy $");
3083221Smarcel
3183221Smarcel#include <sys/types.h>
3283221Smarcel#include <sys/event.h>
3383221Smarcel#include <sys/queue.h>
3483221Smarcel#include <sys/time.h>
3583221Smarcel#include <sys/ttycom.h>
3683221Smarcel#include <sys/wait.h>
37143198Ssobomax
38293479Sdchagin#include <assert.h>
39293479Sdchagin#include <err.h>
40293479Sdchagin#include <errno.h>
4183221Smarcel#include <fcntl.h>
4268583Smarcel#include <limits.h>
4368583Smarcel#include <math.h>
4483221Smarcel#include <pmc.h>
4583221Smarcel#include <signal.h>
4683221Smarcel#include <stdarg.h>
4783221Smarcel#include <stdio.h>
4868583Smarcel#include <stdint.h>
4983221Smarcel#include <stdlib.h>
5083221Smarcel#include <string.h>
5183221Smarcel#include <sysexits.h>
5283221Smarcel#include <unistd.h>
5368583Smarcel
5483221Smarcel/* Operation modes */
5583221Smarcel
5683221Smarcel#define	FLAG_HAS_PID			0x00000001
5768583Smarcel#define	FLAG_HAS_WAIT_INTERVAL		0x00000002
5883221Smarcel#define	FLAG_HAS_LOG_FILE		0x00000004
5983221Smarcel#define	FLAG_HAS_PROCESS		0x00000008
6083221Smarcel#define	FLAG_USING_SAMPLING		0x00000010
6168583Smarcel#define	FLAG_USING_COUNTING		0x00000020
6283221Smarcel#define	FLAG_USING_PROCESS_PMC		0x00000040
6383221Smarcel
6468583Smarcel#define	DEFAULT_SAMPLE_COUNT		65536
6583221Smarcel#define	DEFAULT_WAIT_INTERVAL		5.0
6683221Smarcel#define	DEFAULT_DISPLAY_HEIGHT		23
6783221Smarcel#define	DEFAULT_LOGFILE_NAME		"pmcstat.out"
6883221Smarcel
6968583Smarcel#define	PRINT_HEADER_PREFIX		"# "
7083221Smarcel#define	READPIPEFD			0
7183221Smarcel#define	WRITEPIPEFD			1
7268583Smarcel#define	NPIPEFD				2
7383221Smarcel
7483221Smarcelstruct pmcstat_ev {
7568583Smarcel	STAILQ_ENTRY(pmcstat_ev) ev_next;
7683221Smarcel	char	       *ev_spec;  /* event specification */
7783221Smarcel	char	       *ev_name;  /* (derived) event name */
7883221Smarcel	enum pmc_mode	ev_mode;  /* desired mode */
7983221Smarcel	int		ev_count; /* associated count if in sampling mode */
8068583Smarcel	int		ev_cpu;	  /* specific cpu if requested */
8183221Smarcel	int		ev_descendants; /* attach to descendants */
8283221Smarcel	int		ev_cumulative;  /* show cumulative counts */
8383221Smarcel	int		ev_fieldwidth;  /* print width */
8468583Smarcel	int		ev_fieldskip;   /* #leading spaces */
8583221Smarcel	pmc_value_t	ev_saved; /* saved value for incremental counts */
8683221Smarcel	pmc_id_t	ev_pmcid; /* allocated ID */
8783221Smarcel};
8883221Smarcel
8968583Smarcelstruct pmcstat_args {
9083221Smarcel	int	pa_flags;
9183221Smarcel	pid_t	pa_pid;
92156843Snetchild	FILE   *pa_outputfile;
9368583Smarcel	FILE   *pa_logfile;
9483221Smarcel	double  pa_interval;
9583221Smarcel	int	pa_argc;
9683221Smarcel	char  **pa_argv;
9783221Smarcel	STAILQ_HEAD(, pmcstat_ev) pa_head;
9868583Smarcel} args;
9983221Smarcel
10083221Smarcelint	pmcstat_interrupt = 0;
10168583Smarcelint	pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT;
10283221Smarcelint	pmcstat_pipefd[NPIPEFD];
10383221Smarcelint	pmcstat_kq;
10483221Smarcel
10583221Smarcel/* Function prototypes */
10683221Smarcelvoid pmcstat_cleanup(struct pmcstat_args *_a);
10783221Smarcelvoid pmcstat_print_counters(struct pmcstat_args *_a);
10868583Smarcelvoid pmcstat_print_headers(struct pmcstat_args *_a);
10983221Smarcelvoid pmcstat_print_pmcs(struct pmcstat_args *_a);
11083221Smarcelvoid pmcstat_setup_process(struct pmcstat_args *_a);
11168583Smarcelvoid pmcstat_show_usage(void);
11283221Smarcelvoid pmcstat_start_pmcs(struct pmcstat_args *_a);
11383221Smarcelvoid pmcstat_start_process(struct pmcstat_args *_a);
11468583Smarcel
11583221Smarcel
11668583Smarcel/*
11768583Smarcel * cleanup
11883221Smarcel */
11968583Smarcel
12068583Smarcelvoid
12183221Smarcelpmcstat_cleanup(struct pmcstat_args *a)
12296889Smarcel{
12396889Smarcel	struct pmcstat_ev *ev, *tmp;
12496889Smarcel
12596889Smarcel	/* de-configure the log file if present. */
12668583Smarcel	if (a->pa_flags & FLAG_USING_SAMPLING) {
12783221Smarcel		(void) pmc_configure_logfile(-1);
12883221Smarcel		(void) fclose(a->pa_logfile);
12968583Smarcel	}
13083221Smarcel
13183221Smarcel	/* release allocated PMCs. */
132156843Snetchild	STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp)
13368583Smarcel	    if (ev->ev_pmcid != PMC_ID_INVALID) {
13483221Smarcel		if (pmc_release(ev->ev_pmcid) < 0)
13568583Smarcel			err(EX_OSERR, "ERROR: cannot release pmc "
13668583Smarcel			    "%d \"%s\"", ev->ev_pmcid, ev->ev_name);
13783221Smarcel		free(ev->ev_name);
13883221Smarcel		free(ev->ev_spec);
13983221Smarcel		STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next);
14068583Smarcel		free(ev);
14183221Smarcel	    }
14283221Smarcel}
143227692Sed
14468583Smarcelvoid
14583221Smarcelpmcstat_start_pmcs(struct pmcstat_args *a)
14683221Smarcel{
14768583Smarcel	struct pmcstat_ev *ev;
14883221Smarcel
14983221Smarcel	STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
15083221Smarcel
15168583Smarcel	    assert(ev->ev_pmcid != PMC_ID_INVALID);
15283221Smarcel
15383221Smarcel	    if (pmc_start(ev->ev_pmcid) < 0) {
15483221Smarcel	        warn("ERROR: Cannot start pmc %d \"%s\"",
15568583Smarcel		    ev->ev_pmcid, ev->ev_name);
15683221Smarcel		pmcstat_cleanup(a);
15783221Smarcel	    }
15883221Smarcel	}
15968583Smarcel
16083221Smarcel}
16183221Smarcel
16268583Smarcelvoid
16383221Smarcelpmcstat_print_headers(struct pmcstat_args *a)
164234354Sjkim{
16568583Smarcel	struct pmcstat_ev *ev;
16683221Smarcel	int c;
16783221Smarcel
16868583Smarcel	(void) fprintf(a->pa_outputfile, PRINT_HEADER_PREFIX);
16983221Smarcel
17083221Smarcel	STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
17168583Smarcel		if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
17283221Smarcel			continue;
17383221Smarcel
17468583Smarcel		c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p';
17583221Smarcel
17683221Smarcel		if (ev->ev_fieldskip != 0) {
17768583Smarcel			(void) fprintf(a->pa_outputfile, "%*s%c/%*s ",
17883221Smarcel			    ev->ev_fieldskip, "", c,
17983221Smarcel			    ev->ev_fieldwidth - ev->ev_fieldskip - 2,
180219560Savg			    ev->ev_name);
18168583Smarcel		} else
18283221Smarcel			(void) fprintf(a->pa_outputfile, "%c/%*s ",
18368583Smarcel			    c, ev->ev_fieldwidth - 2, ev->ev_name);
18468583Smarcel	}
18583221Smarcel
18683221Smarcel	(void) fflush(a->pa_outputfile);
18768583Smarcel}
18883221Smarcel
18983221Smarcelvoid
19083221Smarcelpmcstat_print_counters(struct pmcstat_args *a)
19168583Smarcel{
19283221Smarcel	int extra_width;
19383221Smarcel	struct pmcstat_ev *ev;
19483221Smarcel	pmc_value_t value;
19583221Smarcel
19668583Smarcel	extra_width = sizeof(PRINT_HEADER_PREFIX) - 1;
19783221Smarcel
19883221Smarcel	STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
19983221Smarcel
20083221Smarcel		/* skip sampling mode counters */
20168583Smarcel		if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
20283221Smarcel			continue;
20368583Smarcel
20468583Smarcel		if (pmc_read(ev->ev_pmcid, &value) < 0)
20583221Smarcel			err(EX_OSERR, "ERROR: Cannot read pmc "
20683221Smarcel			    "\"%s\"", ev->ev_name);
20783221Smarcel
20868583Smarcel		(void) fprintf(a->pa_outputfile, "%*ju ",
209161309Snetchild		    ev->ev_fieldwidth + extra_width, (uintmax_t)
210161309Snetchild		    ev->ev_cumulative ? value : (value - ev->ev_saved));
211161309Snetchild		if (ev->ev_cumulative == 0)
21283221Smarcel			ev->ev_saved = value;
21383221Smarcel		extra_width = 0;
21483221Smarcel	}
21583221Smarcel
21668583Smarcel	(void) fflush(a->pa_outputfile);
21783221Smarcel}
21868583Smarcel
21968583Smarcel/*
22083221Smarcel * Print output
22183221Smarcel */
22268583Smarcel
22383221Smarcelvoid
22483221Smarcelpmcstat_print_pmcs(struct pmcstat_args *a)
22583221Smarcel{
22668583Smarcel	static int linecount = 0;
22783221Smarcel
22883221Smarcel	if (++linecount > pmcstat_displayheight) {
22983221Smarcel		(void) fprintf(a->pa_outputfile, "\n");
23068583Smarcel		linecount = 1;
23183221Smarcel	}
23283221Smarcel
23383221Smarcel	if (linecount == 1)
23483221Smarcel		pmcstat_print_headers(a);
23568583Smarcel
23683221Smarcel	(void) fprintf(a->pa_outputfile, "\n");
23783221Smarcel	pmcstat_print_counters(a);
23868583Smarcel
239156851Snetchild	return;
240156851Snetchild}
241156851Snetchild
242156851Snetchild/*
24383221Smarcel * Do process profiling
24483221Smarcel *
24583221Smarcel * If a pid was specified, attach each allocated PMC to the target
24668583Smarcel * process.  Otherwise, fork a child and attach the PMCs to the child,
24783221Smarcel * and have the child exec() the target program.
24883221Smarcel */
24983221Smarcel
25068583Smarcelvoid
25183221Smarcelpmcstat_setup_process(struct pmcstat_args *a)
25283221Smarcel{
25383221Smarcel	char token;
25468583Smarcel	struct pmcstat_ev *ev;
25583221Smarcel	struct kevent kev;
25683221Smarcel
25783221Smarcel	if (a->pa_flags & FLAG_HAS_PID) {
25868583Smarcel
25983221Smarcel		STAILQ_FOREACH(ev, &args.pa_head, ev_next)
26083221Smarcel		    if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0)
26168583Smarcel			    err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to "
26283221Smarcel				"process %d", ev->ev_name, (int) a->pa_pid);
26383221Smarcel
26483221Smarcel	} else {
26568583Smarcel
266156851Snetchild		/*
267156851Snetchild		 * We need to fork a new process and startup the child
268238918Sjhb		 * using execvp().  Before doing the exec() the child
269156851Snetchild		 * process reads its pipe for a token so that the parent
27083221Smarcel		 * can finish doing its pmc_attach() calls.
27183221Smarcel		 */
27283221Smarcel
27383221Smarcel		if (pipe(pmcstat_pipefd) < 0)
27472540Sjlemon			err(EX_OSERR, "ERROR: cannot create pipe");
27583221Smarcel
27683221Smarcel		switch (a->pa_pid = fork()) {
27768583Smarcel		case -1:
27883221Smarcel			err(EX_OSERR, "ERROR: cannot fork");
27983221Smarcel			/*NOTREACHED*/
28083221Smarcel
28183221Smarcel		case 0:		/* child */
28283221Smarcel
28368583Smarcel			/* wait for our parent to signal us */
28483221Smarcel			(void) close(pmcstat_pipefd[WRITEPIPEFD]);
28583221Smarcel			if (read(pmcstat_pipefd[READPIPEFD], &token, 1) < 0)
28683221Smarcel				err(EX_OSERR, "ERROR (child): cannot read "
28783221Smarcel				    "token");
28868583Smarcel			(void) close(pmcstat_pipefd[READPIPEFD]);
28983221Smarcel
29083221Smarcel			/* exec() the program requested */
29168583Smarcel			execvp(*args.pa_argv, args.pa_argv);
29283221Smarcel			err(EX_OSERR, "ERROR (child): execvp failed");
29383221Smarcel			/*NOTREACHED*/
29483221Smarcel
29568583Smarcel		default:	/* parent */
296156851Snetchild
297156851Snetchild			(void) close(pmcstat_pipefd[READPIPEFD]);
298156851Snetchild
299156851Snetchild			/* attach all our PMCs to the child */
300147142Ssobomax			STAILQ_FOREACH(ev, &args.pa_head, ev_next)
301147142Ssobomax			    if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) &&
302147142Ssobomax				pmc_attach(ev->ev_pmcid, a->pa_pid) != 0)
303147142Ssobomax				    err(EX_OSERR, "ERROR: cannot attach pmc "
30483221Smarcel					"\"%s\" to process %d", ev->ev_name,
30583221Smarcel					(int) a->pa_pid);
30683221Smarcel
30768583Smarcel		}
30883221Smarcel	}
30983221Smarcel
31083221Smarcel	/* Ask to be notified via a kevent when the child exits */
31168583Smarcel	EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0);
31283221Smarcel
31383221Smarcel	if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
31483221Smarcel		err(EX_OSERR, "ERROR: cannot monitor process %d",
31583221Smarcel		    a->pa_pid);
31668583Smarcel
31783221Smarcel	return;
31883221Smarcel}
31983221Smarcel
32068583Smarcelvoid
32183221Smarcelpmcstat_start_process(struct pmcstat_args *a)
32283221Smarcel{
32383221Smarcel
32483221Smarcel	/* nothing to do: target is already running */
32568583Smarcel	if (a->pa_flags & FLAG_HAS_PID)
32683221Smarcel		return;
32783221Smarcel
32883221Smarcel	/* write token to child to state that we are ready */
32983221Smarcel	if (write(pmcstat_pipefd[WRITEPIPEFD], "+", 1) != 1)
33068583Smarcel		err(EX_OSERR, "ERROR: write failed");
33183221Smarcel
33283221Smarcel	(void) close(pmcstat_pipefd[WRITEPIPEFD]);
33383221Smarcel}
33468583Smarcel
33583221Smarcelvoid
33683221Smarcelpmcstat_show_usage(void)
33783221Smarcel{
33868583Smarcel	errx(EX_USAGE,
33983221Smarcel	    "[options] [commandline]\n"
34083221Smarcel	    "\t Measure process and/or system performance using hardware\n"
34183221Smarcel	    "\t performance monitoring counters.\n"
34268583Smarcel	    "\t Options include:\n"
34383221Smarcel	    "\t -C\t\t toggle showing cumulative counts\n"
34483221Smarcel	    "\t -O file\t set sampling log file to \"file\"\n"
34583221Smarcel	    "\t -P spec\t allocate process-private sampling PMC\n"
34668583Smarcel	    "\t -S spec\t allocate system-wide sampling PMC\n"
34783221Smarcel	    "\t -c cpu\t\t set default cpu\n"
34868583Smarcel	    "\t -d\t\t toggle tracking descendants\n"
34968583Smarcel	    "\t -n rate\t set sampling rate\n"
35083221Smarcel	    "\t -o file\t send print output to \"file\"\n"
351234360Sjkim	    "\t -p spec\t allocate process-private counting PMC\n"
35283221Smarcel	    "\t -s spec\t allocate system-wide counting PMC\n"
35383221Smarcel	    "\t -t pid\t attach to running process with pid \"pid\"\n"
35468583Smarcel	    "\t -w secs\t set printing time interval"
35568583Smarcel	);
35683221Smarcel}
35768583Smarcel
35868583Smarcel/*
35983221Smarcel * Main
36083221Smarcel */
361276811Sdchagin
36283221Smarcelint
363293502Sdchaginmain(int argc, char **argv)
36468583Smarcel{
36583221Smarcel	double interval;
36668583Smarcel	int option, npmc, ncpu;
36768583Smarcel	int c, current_cpu, current_sampling_count;
36883221Smarcel	int running;
36983221Smarcel	int do_descendants, use_cumulative_counts;
37068583Smarcel	pid_t pid;
37183221Smarcel	char *end;
37283221Smarcel	struct pmcstat_ev *ev;
37383221Smarcel	struct pmc_op_getpmcinfo *ppmci;
37483221Smarcel	struct sigaction sa;
37583221Smarcel	struct kevent kev;
37683221Smarcel	struct winsize ws;
37783221Smarcel
37868583Smarcel	current_cpu 		= 0;
37983221Smarcel	current_sampling_count  = DEFAULT_SAMPLE_COUNT;
38083221Smarcel	do_descendants          = 0;
38168583Smarcel	use_cumulative_counts   = 0;
38283221Smarcel	args.pa_flags		= 0;
38383221Smarcel	args.pa_pid		= (pid_t) -1;
38483221Smarcel	args.pa_logfile		= NULL;
385161309Snetchild	args.pa_outputfile	= stderr;
386218611Sdchagin	args.pa_interval	= DEFAULT_WAIT_INTERVAL;
387161309Snetchild	STAILQ_INIT(&args.pa_head);
38868583Smarcel
389184790Sed	ev = NULL;
390184790Sed
391184790Sed	while ((option = getopt(argc, argv, "CO:P:S:c:dn:o:p:s:t:w:")) != -1)
392184790Sed		switch (option) {
39383221Smarcel		case 'C':	/* cumulative values */
39483221Smarcel			use_cumulative_counts = !use_cumulative_counts;
39568583Smarcel			break;
39683221Smarcel
39783221Smarcel		case 'c':	/* CPU */
39883221Smarcel			current_cpu = strtol(optarg, &end, 0);
39983221Smarcel			if (*end != '\0' || current_cpu < 0)
40068583Smarcel				errx(EX_USAGE,
40183221Smarcel				    "ERROR: Illegal CPU number \"%s\"",
40268583Smarcel				    optarg);
40368583Smarcel
404166728Sjkim			break;
405166728Sjkim
406166728Sjkim		case 'd':	/* toggle descendents */
407166728Sjkim			do_descendants = !do_descendants;
408166728Sjkim			break;
40983221Smarcel
41083221Smarcel		case 'p':	/* process virtual counting PMC */
41183221Smarcel		case 's':	/* system-wide counting PMC */
41283221Smarcel		case 'P':	/* process virtual sampling PMC */
41368583Smarcel		case 'S':	/* system-wide sampling PMC */
41483221Smarcel			if ((ev = malloc(sizeof(*ev))) == NULL)
41568583Smarcel				errx(EX_SOFTWARE, "ERROR: Out of memory");
41668583Smarcel
41783221Smarcel			switch (option) {
41868583Smarcel			case 'p': ev->ev_mode = PMC_MODE_TC; break;
41968583Smarcel			case 's': ev->ev_mode = PMC_MODE_SC; break;
42083221Smarcel			case 'P': ev->ev_mode = PMC_MODE_TS; break;
42168583Smarcel			case 'S': ev->ev_mode = PMC_MODE_SS; break;
42268583Smarcel			}
42383221Smarcel
42468583Smarcel			if (option == 'P' || option == 'p')
42568583Smarcel				args.pa_flags |= FLAG_USING_PROCESS_PMC;
42683221Smarcel
42768583Smarcel			if (option == 'P' || option == 'S')
42868583Smarcel				args.pa_flags |= FLAG_USING_SAMPLING;
42983221Smarcel
43068583Smarcel			if (option == 'p' || option == 's')
43168583Smarcel				args.pa_flags |= FLAG_USING_COUNTING;
43283221Smarcel
43383221Smarcel			ev->ev_spec  = strdup(optarg);
43483221Smarcel
43583221Smarcel			if (option == 'S' || option == 'P')
43668583Smarcel				ev->ev_count = current_sampling_count;
43783221Smarcel			else
43883221Smarcel				ev->ev_count = -1;
43968583Smarcel
44083221Smarcel			if (option == 'S' || option == 's')
44183221Smarcel				ev->ev_cpu = current_cpu;
44268583Smarcel			else
44383221Smarcel				ev->ev_cpu = PMC_CPU_ANY;
44483221Smarcel
44568583Smarcel			ev->ev_descendants = do_descendants;
44683221Smarcel			ev->ev_cumulative  = use_cumulative_counts;
44783221Smarcel
44883221Smarcel			ev->ev_saved = 0LL;
44983221Smarcel			ev->ev_pmcid = PMC_ID_INVALID;
45083221Smarcel
45183221Smarcel			/* extract event name */
45268583Smarcel			c = strcspn(optarg, ", \t");
45383221Smarcel			ev->ev_name = malloc(c + 1);
45483221Smarcel			(void) strncpy(ev->ev_name, optarg, c);
45583221Smarcel			*(ev->ev_name + c) = '\0';
45683221Smarcel
45768583Smarcel			STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next);
45883221Smarcel
45983221Smarcel			break;
46083221Smarcel
46183221Smarcel		case 'n':	/* sampling count */
46283221Smarcel			current_sampling_count = strtol(optarg, &end, 0);
46383221Smarcel			if (*end != '\0' || current_sampling_count <= 0)
46468583Smarcel				errx(EX_USAGE,
46583221Smarcel				    "ERROR: Illegal count value \"%s\"",
46683221Smarcel				    optarg);
46783221Smarcel			break;
46883221Smarcel
46968583Smarcel		case 'o':	/* outputfile */
47083221Smarcel			if (args.pa_outputfile != NULL)
47183221Smarcel				(void) fclose(args.pa_outputfile);
47268583Smarcel
47383221Smarcel			if ((args.pa_outputfile = fopen(optarg, "w")) == NULL)
47483221Smarcel				errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
47568583Smarcel				    "writing", optarg);
47683221Smarcel
47783221Smarcel		case 'O':	/* sampling output */
47868583Smarcel			if (args.pa_logfile != NULL)
479293488Sdchagin				(void) fclose(args.pa_logfile);
480293488Sdchagin
481293488Sdchagin			if ((args.pa_logfile = fopen(optarg, "w")) == NULL)
482293488Sdchagin				errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
483293488Sdchagin				    "writing", optarg);
484293488Sdchagin			break;
485293488Sdchagin
486293488Sdchagin		case 't':	/* target pid */
48783221Smarcel			pid = strtol(optarg, &end, 0);
48883221Smarcel			if (*end != '\0' || pid <= 0)
48983221Smarcel				errx(EX_USAGE, "ERROR: Illegal pid value "
49083221Smarcel				    "\"%s\"", optarg);
49168583Smarcel
49283221Smarcel			args.pa_flags |= FLAG_HAS_PID;
49383221Smarcel			args.pa_pid = pid;
49468583Smarcel
49583221Smarcel			break;
49683221Smarcel
49768583Smarcel		case 'w':	/* wait interval */
49883221Smarcel			interval = strtod(optarg, &end);
49983221Smarcel			if (*end != '\0' || interval <= 0)
50075057Salc				errx(EX_USAGE, "ERROR: Illegal wait interval "
501293484Sdchagin				    "value \"%s\"", optarg);
502293484Sdchagin			args.pa_flags |= FLAG_HAS_WAIT_INTERVAL;
503293484Sdchagin			args.pa_interval = interval;
504293484Sdchagin
505165410Sjkim			break;
506165410Sjkim
507165410Sjkim		case '?':
508165410Sjkim		default:
50983221Smarcel			pmcstat_show_usage();
51083221Smarcel			break;
51183221Smarcel
51283221Smarcel		}
51383221Smarcel
51483221Smarcel	args.pa_argc = (argc -= optind);
51575057Salc	args.pa_argv = (argv += optind);
51683221Smarcel
51783221Smarcel	if (argc)
51883221Smarcel		args.pa_flags |= FLAG_HAS_PROCESS;
51983221Smarcel
52068583Smarcel	/*
52183221Smarcel	 * Check invocation syntax.
52283221Smarcel	 */
52383221Smarcel
52483221Smarcel	if (STAILQ_EMPTY(&args.pa_head)) {
52568583Smarcel		warnx("ERROR: At least one PMC event must be specified");
52683221Smarcel		pmcstat_show_usage();
52768583Smarcel	}
52868583Smarcel
52983221Smarcel	if (argc == 0) {
53068583Smarcel		if (args.pa_pid == -1) {
53168583Smarcel			if (args.pa_flags & FLAG_USING_PROCESS_PMC)
53283221Smarcel				errx(EX_USAGE, "ERROR: the -P or -p options "
53368583Smarcel				    "require a target process");
53468583Smarcel		} else if ((args.pa_flags & FLAG_USING_PROCESS_PMC) == 0)
53583221Smarcel			errx(EX_USAGE,
53683221Smarcel			    "ERROR: option -t requires a process-mode pmc "
53783221Smarcel			    "specification");
53883221Smarcel	} else if (args.pa_pid != -1)
53968583Smarcel		errx(EX_USAGE,
54083221Smarcel		    "ERROR: option -t cannot be specified with a command "
54183221Smarcel		    "name");
54283221Smarcel
54383221Smarcel	if (pmc_init() < 0)
54483221Smarcel		err(EX_UNAVAILABLE,
54583221Smarcel		    "ERROR: Initialization of the pmc(3) library failed");
546163736Snetchild
547163736Snetchild	if ((ncpu = pmc_ncpu()) < 0)
548163736Snetchild		err(EX_OSERR, "ERROR: Cannot determine the number CPUs "
549163736Snetchild		    "on the system");
550163736Snetchild
55168583Smarcel	if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
55283221Smarcel		err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
55383221Smarcel		    "on CPU %d", 0);
55468583Smarcel
55583221Smarcel	/*
55683221Smarcel	 * Allocate PMCs.
55783221Smarcel	 */
55883221Smarcel
55983221Smarcel	if (pmc_pmcinfo(0, &ppmci) < 0)
56068583Smarcel		err(EX_OSERR, "ERROR: cannot retrieve pmc information");
56183221Smarcel
56283221Smarcel	assert(ppmci != NULL);
56383221Smarcel
56483221Smarcel	STAILQ_FOREACH(ev, &args.pa_head, ev_next)
56583221Smarcel	    if (pmc_allocate(ev->ev_spec, ev->ev_mode,
56668583Smarcel		    (ev->ev_descendants ? PMC_F_DESCENDANTS : 0),
56783221Smarcel		    ev->ev_cpu, &ev->ev_pmcid) < 0)
568158407Snetchild		    err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with "
569158407Snetchild			"specification \"%s\"",
57068583Smarcel			PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process",
57183221Smarcel			ev->ev_spec);
572165690Snetchild
573165690Snetchild	/* compute printout widths */
574165690Snetchild	STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
575165690Snetchild		int pmc_width;
57668583Smarcel		int pmc_display_width;
57783221Smarcel		int pmc_header_width;
578293506Sdchagin
579293506Sdchagin		pmc_width = ppmci->pm_pmcs[ev->ev_pmcid].pm_width;
580293506Sdchagin		pmc_header_width = strlen(ev->ev_name) + 2; /* prefix '%c|' */
58168583Smarcel		pmc_display_width = (int) floor(pmc_width / 3.32193) + 1;
58283221Smarcel
58383221Smarcel		if (pmc_header_width > pmc_display_width) {
58483221Smarcel			ev->ev_fieldskip = 0;
58568583Smarcel			ev->ev_fieldwidth = pmc_header_width;
58683221Smarcel		} else {
58783221Smarcel			ev->ev_fieldskip = pmc_display_width -
58883221Smarcel			    pmc_header_width;
58983221Smarcel			ev->ev_fieldwidth = pmc_display_width;
59083221Smarcel		}
59168583Smarcel	}
59283221Smarcel
59383221Smarcel	/* Allocate a kqueue */
59483221Smarcel	if ((pmcstat_kq = kqueue()) < 0)
59583221Smarcel		err(EX_OSERR, "ERROR: Cannot allocate kqueue");
59683221Smarcel
59768583Smarcel	/*
59883221Smarcel	 * If our output is being set to a terminal, register a handler
59983221Smarcel	 * for window size changes.
60083221Smarcel	 */
60183221Smarcel
60268583Smarcel	if (isatty(fileno(args.pa_outputfile))) {
60383221Smarcel
60483221Smarcel		if (ioctl(fileno(args.pa_outputfile), TIOCGWINSZ, &ws) < 0)
60583221Smarcel			err(EX_OSERR, "ERROR: Cannot determine window size");
60668583Smarcel
60783221Smarcel		pmcstat_displayheight = ws.ws_row - 1;
608220030Savg
609220030Savg		EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
61068583Smarcel
61183221Smarcel		if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
612220030Savg			err(EX_OSERR, "ERROR: Cannot register kevent for "
613220030Savg			    "SIGWINCH");
61468583Smarcel	}
61583221Smarcel
61683221Smarcel	EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
61783221Smarcel
61868583Smarcel	if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
61983221Smarcel		err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT");
62068583Smarcel
62168583Smarcel	if (args.pa_flags & FLAG_USING_SAMPLING) {
62283221Smarcel
62368583Smarcel		/*
62468583Smarcel		 * configure log file
62583221Smarcel		 */
62683221Smarcel
62783221Smarcel		if (args.pa_logfile == NULL)
62868583Smarcel			if ((args.pa_logfile =
62983221Smarcel				fopen(DEFAULT_LOGFILE_NAME, "w")) == NULL)
63083221Smarcel				err(EX_CANTCREAT, "ERROR: Cannot open sampling "
63183221Smarcel				    "log file \"%s\"", DEFAULT_LOGFILE_NAME);
63283221Smarcel
63383221Smarcel		if (pmc_configure_logfile(fileno(args.pa_logfile)) < 0)
63483221Smarcel			err(EX_OSERR, "ERROR: Cannot configure sampling "
63583221Smarcel			    "log");
63668583Smarcel
63783221Smarcel		STAILQ_FOREACH(ev, &args.pa_head, ev_next)
63883221Smarcel		    if (PMC_IS_SAMPLING_MODE(ev->ev_mode) &&
63983221Smarcel			pmc_set(ev->ev_pmcid, ev->ev_count) < 0)
64068583Smarcel			    err(EX_OSERR, "ERROR: Cannot set sampling count "
64183221Smarcel				"for PMC \"%s\"", ev->ev_name);
64283221Smarcel	}
64383221Smarcel
64468583Smarcel	/* setup a timer for any counting mode PMCs */
64583221Smarcel	if (args.pa_flags & FLAG_USING_COUNTING) {
646234358Sjkim		EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0,
64783221Smarcel		    args.pa_interval * 1000, NULL);
64868583Smarcel
64983221Smarcel		if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
650234358Sjkim			err(EX_OSERR, "ERROR: Cannot register kevent for "
65183221Smarcel			    "timer");
65268583Smarcel	}
65383221Smarcel
654234358Sjkim	/* attach PMCs to the target process, starting it if specified */
65583221Smarcel	if (args.pa_flags & FLAG_HAS_PROCESS)
65683221Smarcel		pmcstat_setup_process(&args);
65783221Smarcel
65885022Smarcel	/* start the pmcs */
65985022Smarcel	pmcstat_start_pmcs(&args);
66085022Smarcel
66183221Smarcel	/* start the (commandline) process if needed */
66283221Smarcel	if (args.pa_flags & FLAG_HAS_PROCESS)
66368583Smarcel		pmcstat_start_process(&args);
66468583Smarcel
66583221Smarcel	/* Handle SIGINT using the kqueue loop */
66668583Smarcel	sa.sa_handler = SIG_IGN;
66768583Smarcel	sa.sa_flags   = 0;
66883221Smarcel	(void) sigemptyset(&sa.sa_mask);
66983221Smarcel
67083221Smarcel	if (sigaction(SIGINT, &sa, NULL) < 0)
67183221Smarcel		err(EX_OSERR, "ERROR: Cannot install signal handler");
67283221Smarcel
67383221Smarcel	/*
67483221Smarcel	 * loop till either the target process (if any) exits, or we
67583221Smarcel	 * are killed by a SIGINT.
67683221Smarcel	 */
67785022Smarcel
67885022Smarcel	running = 1;
67985022Smarcel	do {
68083221Smarcel		if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) {
68183221Smarcel			if (errno != EINTR)
68283221Smarcel				err(EX_OSERR, "ERROR: kevent failed");
68383221Smarcel			else
68483221Smarcel				continue;
68583221Smarcel		}
68683221Smarcel
68783221Smarcel		if (kev.flags & EV_ERROR)
68883221Smarcel			errc(EX_OSERR, kev.data, "ERROR: kevent failed");
68983221Smarcel
69083221Smarcel		switch (kev.filter) {
69183221Smarcel		case EVFILT_PROC: /* target process exited */
69283221Smarcel			running = 0;
69383221Smarcel			/* FALLTHROUGH */
69483221Smarcel
69583221Smarcel		case EVFILT_TIMER: /* print out counting PMCs */
69683221Smarcel			pmcstat_print_pmcs(&args);
69783221Smarcel
69883221Smarcel			if (running == 0) /* final newline */
69983221Smarcel				(void) fprintf(args.pa_outputfile, "\n");
70083221Smarcel			break;
70183221Smarcel
70283221Smarcel		case EVFILT_SIGNAL:
70383221Smarcel			if (kev.ident == SIGINT) {
70483221Smarcel				/* pass the signal on to the child process */
70583221Smarcel				if ((args.pa_flags & FLAG_HAS_PROCESS) &&
706134839Sdfr				    (args.pa_flags & FLAG_HAS_PID) == 0)
707134839Sdfr					if (kill(args.pa_pid, SIGINT) != 0)
708134839Sdfr						err(EX_OSERR, "cannot kill "
709122802Ssobomax						    "child");
710122802Ssobomax				running = 0;
711122802Ssobomax			} else if (kev.ident == SIGWINCH) {
712122802Ssobomax				if (ioctl(fileno(args.pa_outputfile),
713122802Ssobomax					TIOCGWINSZ, &ws) < 0)
714122802Ssobomax				    err(EX_OSERR, "ERROR: Cannot determine "
715122802Ssobomax					"window size");
716122802Ssobomax				pmcstat_displayheight = ws.ws_row - 1;
717122802Ssobomax			} else
718122802Ssobomax				assert(0);
719122802Ssobomax
720122802Ssobomax			break;
721122802Ssobomax		}
722122802Ssobomax
723122802Ssobomax	} while (running);
724122802Ssobomax
725122802Ssobomax	pmcstat_cleanup(&args);
726122802Ssobomax
727122802Ssobomax	return 0;
728122802Ssobomax}
729122802Ssobomax