xref: /netbsd-src/usr.sbin/tprof/tprof.c (revision 8dc7413669b73cb84d497cf71e0116c89e6f786c)
1*8dc74136Smsaitoh /*	$NetBSD: tprof.c,v 1.21 2023/04/17 08:37:24 msaitoh Exp $	*/
28415a5d7Syamt 
3a087cb3cSmaxv /*
4a087cb3cSmaxv  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5a087cb3cSmaxv  * All rights reserved.
6a087cb3cSmaxv  *
7a087cb3cSmaxv  * This code is derived from software contributed to The NetBSD Foundation
8a087cb3cSmaxv  * by Maxime Villard.
9a087cb3cSmaxv  *
10a087cb3cSmaxv  * Redistribution and use in source and binary forms, with or without
11a087cb3cSmaxv  * modification, are permitted provided that the following conditions
12a087cb3cSmaxv  * are met:
13a087cb3cSmaxv  * 1. Redistributions of source code must retain the above copyright
14a087cb3cSmaxv  *    notice, this list of conditions and the following disclaimer.
15a087cb3cSmaxv  * 2. Redistributions in binary form must reproduce the above copyright
16a087cb3cSmaxv  *    notice, this list of conditions and the following disclaimer in the
17a087cb3cSmaxv  *    documentation and/or other materials provided with the distribution.
18a087cb3cSmaxv  *
19a087cb3cSmaxv  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20a087cb3cSmaxv  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21a087cb3cSmaxv  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22a087cb3cSmaxv  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23a087cb3cSmaxv  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24a087cb3cSmaxv  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25a087cb3cSmaxv  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26a087cb3cSmaxv  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27a087cb3cSmaxv  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28a087cb3cSmaxv  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29a087cb3cSmaxv  * POSSIBILITY OF SUCH DAMAGE.
30a087cb3cSmaxv  */
31a087cb3cSmaxv 
32a087cb3cSmaxv /*
338415a5d7Syamt  * Copyright (c)2008 YAMAMOTO Takashi,
348415a5d7Syamt  * All rights reserved.
358415a5d7Syamt  *
368415a5d7Syamt  * Redistribution and use in source and binary forms, with or without
378415a5d7Syamt  * modification, are permitted provided that the following conditions
388415a5d7Syamt  * are met:
398415a5d7Syamt  * 1. Redistributions of source code must retain the above copyright
408415a5d7Syamt  *    notice, this list of conditions and the following disclaimer.
418415a5d7Syamt  * 2. Redistributions in binary form must reproduce the above copyright
428415a5d7Syamt  *    notice, this list of conditions and the following disclaimer in the
438415a5d7Syamt  *    documentation and/or other materials provided with the distribution.
448415a5d7Syamt  *
458415a5d7Syamt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
468415a5d7Syamt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
478415a5d7Syamt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
488415a5d7Syamt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
498415a5d7Syamt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
508415a5d7Syamt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
518415a5d7Syamt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
528415a5d7Syamt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
538415a5d7Syamt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
548415a5d7Syamt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
558415a5d7Syamt  * SUCH DAMAGE.
568415a5d7Syamt  */
578415a5d7Syamt 
588415a5d7Syamt #include <sys/cdefs.h>
598415a5d7Syamt #ifndef lint
60*8dc74136Smsaitoh __RCSID("$NetBSD: tprof.c,v 1.21 2023/04/17 08:37:24 msaitoh Exp $");
618415a5d7Syamt #endif /* not lint */
628415a5d7Syamt 
6307a59189Sryo #include <sys/atomic.h>
648415a5d7Syamt #include <sys/ioctl.h>
6507a59189Sryo #include <sys/sysctl.h>
668415a5d7Syamt #include <sys/wait.h>
678415a5d7Syamt 
688415a5d7Syamt #include <dev/tprof/tprof_ioctl.h>
698415a5d7Syamt 
708415a5d7Syamt #include <err.h>
718415a5d7Syamt #include <errno.h>
728415a5d7Syamt #include <fcntl.h>
738415a5d7Syamt #include <inttypes.h>
7407a59189Sryo #include <math.h>
758415a5d7Syamt #include <pthread.h>
768415a5d7Syamt #include <signal.h>
778415a5d7Syamt #include <stdbool.h>
788415a5d7Syamt #include <stdio.h>
798415a5d7Syamt #include <stdlib.h>
808415a5d7Syamt #include <string.h>
8107a59189Sryo #include <time.h>
828415a5d7Syamt #include <unistd.h>
8307a59189Sryo #include <util.h>
84a087cb3cSmaxv #include "tprof.h"
858415a5d7Syamt 
868415a5d7Syamt #define	_PATH_TPROF	"/dev/tprof"
878415a5d7Syamt 
88caba1a6fSryo struct tprof_info tprof_info;
89caba1a6fSryo u_int ncounters;
908415a5d7Syamt int devfd;
918415a5d7Syamt int outfd;
9207a59189Sryo int ncpu;
93caba1a6fSryo u_int nevent;
9407a59189Sryo double interval = 0xffffffff;	/* XXX */
9507a59189Sryo const char *eventname[TPROF_MAXCOUNTERS];
9607a59189Sryo u_int eventnamewidth[TPROF_MAXCOUNTERS];
9707a59189Sryo #define	COUNTER_COLUMNS_WIDTH	11
988415a5d7Syamt 
99ab933b7bSmaxv static void tprof_list(int, char **);
10007a59189Sryo static void tprof_monitor_common(bool, int, char **) __dead;
101f8c97917Sryo static void tprof_monitor(int, char **) __dead;
102f8c97917Sryo static void tprof_count(int, char **) __dead;
103ab933b7bSmaxv 
104ab933b7bSmaxv static struct cmdtab {
105ab933b7bSmaxv 	const char *label;
106ab933b7bSmaxv 	bool takesargs;
107ab933b7bSmaxv 	bool argsoptional;
108ab933b7bSmaxv 	void (*func)(int, char **);
109ab933b7bSmaxv } const tprof_cmdtab[] = {
110ab933b7bSmaxv 	{ "list",	false, false, tprof_list },
111ab933b7bSmaxv 	{ "monitor",	true,  false, tprof_monitor },
11207a59189Sryo 	{ "count",	true,  false, tprof_count },
11396b21aedSmaxv 	{ "analyze",	true,  true,  tprof_analyze },
114e93233dbSryo 	{ "top",	true,  true,  tprof_top },
115ab933b7bSmaxv 	{ NULL,		false, false, NULL },
116ab933b7bSmaxv };
117ab933b7bSmaxv 
1184c70cdf1Sjoerg __dead static void
usage(void)1198415a5d7Syamt usage(void)
1208415a5d7Syamt {
1218415a5d7Syamt 
12296b21aedSmaxv 	fprintf(stderr, "%s op [arguments]\n", getprogname());
1232d6f41e7Syamt 	fprintf(stderr, "\n");
124ab933b7bSmaxv 	fprintf(stderr, "\tlist\n");
125ab933b7bSmaxv 	fprintf(stderr, "\t\tList the available events.\n");
126e93233dbSryo 	fprintf(stderr, "\tmonitor -e name[:option] [-e ...] [-o outfile]"
127e93233dbSryo 	    " command\n");
128ab933b7bSmaxv 	fprintf(stderr, "\t\tMonitor the event 'name' with option 'option'\n"
129ab933b7bSmaxv 	    "\t\tcounted during the execution of 'command'.\n");
13007a59189Sryo 	fprintf(stderr, "\tcount -e name[:option] [-e ...] [-i interval]"
13107a59189Sryo 	    " command\n");
13207a59189Sryo 	fprintf(stderr, "\t\tSame as monitor, but does not profile,"
13307a59189Sryo 	    " only outputs a counter.\n");
134b15bdb80Swiz 	fprintf(stderr, "\tanalyze [-CkLPs] [-p pid] file\n");
135c52e6df8Smaxv 	fprintf(stderr, "\t\tAnalyze the samples of the file 'file'.\n");
136671f47e0Sryoon 	fprintf(stderr, "\ttop [-e name [-e ...]] [-i interval] [-acu]\n");
137e93233dbSryo 	fprintf(stderr, "\t\tDisplay profiling results in real-time.\n");
1388415a5d7Syamt 	exit(EXIT_FAILURE);
1398415a5d7Syamt }
1408415a5d7Syamt 
14107a59189Sryo static int
getncpu(void)14207a59189Sryo getncpu(void)
14307a59189Sryo {
14407a59189Sryo 	size_t size;
14507a59189Sryo 	int mib[2];
14607a59189Sryo 
14707a59189Sryo 	mib[0] = CTL_HW;
14807a59189Sryo 	mib[1] = HW_NCPU;
14907a59189Sryo 	size = sizeof(ncpu);
15007a59189Sryo 	if (sysctl(mib, 2, &ncpu, &size, NULL, 0) == -1)
15107a59189Sryo 		ncpu = 1;
15207a59189Sryo 	return ncpu;
15307a59189Sryo }
15407a59189Sryo 
1558415a5d7Syamt static void *
process_samples(void * dummy)1568415a5d7Syamt process_samples(void *dummy)
1578415a5d7Syamt {
1588415a5d7Syamt 
1598415a5d7Syamt 	for (;;) {
1608415a5d7Syamt 		char buf[4096];
1618415a5d7Syamt 		const char *cp;
1628415a5d7Syamt 		ssize_t ssz;
1638415a5d7Syamt 
1648415a5d7Syamt 		ssz = read(devfd, buf, sizeof(buf));
1658415a5d7Syamt 		if (ssz == -1) {
1668415a5d7Syamt 			err(EXIT_FAILURE, "read");
1678415a5d7Syamt 		}
1688415a5d7Syamt 		if (ssz == 0) {
1698415a5d7Syamt 			break;
1708415a5d7Syamt 		}
1718415a5d7Syamt 		cp = buf;
1728415a5d7Syamt 		while (ssz) {
1738415a5d7Syamt 			ssize_t wsz;
1748415a5d7Syamt 
1758415a5d7Syamt 			wsz = write(outfd, cp, ssz);
1768415a5d7Syamt 			if (wsz == -1) {
1778415a5d7Syamt 				err(EXIT_FAILURE, "write");
1788415a5d7Syamt 			}
1798415a5d7Syamt 			ssz -= wsz;
1808415a5d7Syamt 			cp += wsz;
1818415a5d7Syamt 		}
1828415a5d7Syamt 	}
1838415a5d7Syamt 	return NULL;
1848415a5d7Syamt }
1858415a5d7Syamt 
186ab933b7bSmaxv static void
show_counters(void)18707a59189Sryo show_counters(void)
18807a59189Sryo {
18907a59189Sryo 	unsigned int i;
19007a59189Sryo 	int n, ret;
19107a59189Sryo 
19207a59189Sryo 	fprintf(stderr, "      ");
19307a59189Sryo 	for (i = 0; i < nevent; i++)
19407a59189Sryo 		fprintf(stderr, " %*s", eventnamewidth[i], eventname[i]);
19507a59189Sryo 	fprintf(stderr, "\n");
19607a59189Sryo 
19707a59189Sryo 	for (n = 0; n < ncpu; n++) {
19807a59189Sryo 		tprof_counts_t counts;
19907a59189Sryo 
20007a59189Sryo 		memset(&counts, 0, sizeof(counts));
20107a59189Sryo 		counts.c_cpu = n;
20207a59189Sryo 		ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &counts);
20307a59189Sryo 		if (ret == -1)
20407a59189Sryo 			err(EXIT_FAILURE, "TPROF_IOC_GETCOUNTS");
20507a59189Sryo 
20607a59189Sryo 		fprintf(stderr, "CPU%-3d", n);
20707a59189Sryo 		for (i = 0; i < nevent; i++) {
20807a59189Sryo 			fprintf(stderr, " %*"PRIu64,
20907a59189Sryo 			    eventnamewidth[i], counts.c_count[i]);
21007a59189Sryo 		}
21107a59189Sryo 		fprintf(stderr, "\n");
21207a59189Sryo 	}
21307a59189Sryo }
21407a59189Sryo 
21507a59189Sryo /* XXX: avoid mixing with the output of the child process SIGINFO handler... */
21607a59189Sryo static void
output_delay(void)21707a59189Sryo output_delay(void)
21807a59189Sryo {
21907a59189Sryo 	struct timespec delay_ts;
22007a59189Sryo 
22107a59189Sryo 	delay_ts.tv_sec = 0;
22207a59189Sryo 	delay_ts.tv_nsec = 100000000;
22307a59189Sryo 	nanosleep(&delay_ts, NULL);
22407a59189Sryo }
22507a59189Sryo 
22607a59189Sryo static void
siginfo_nothing(int signo)22707a59189Sryo siginfo_nothing(int signo)
22807a59189Sryo {
22907a59189Sryo 	__nothing;
23007a59189Sryo }
23107a59189Sryo 
23207a59189Sryo static void
siginfo_showcount(int signo)23307a59189Sryo siginfo_showcount(int signo)
23407a59189Sryo {
23507a59189Sryo 	output_delay();
23607a59189Sryo 	show_counters();
23707a59189Sryo }
23807a59189Sryo 
23907a59189Sryo static void *
process_stat(void * arg)24007a59189Sryo process_stat(void *arg)
24107a59189Sryo {
24207a59189Sryo 	unsigned int *done = arg;
24307a59189Sryo 	double ival, fval;
24407a59189Sryo 	struct timespec ts;
24507a59189Sryo 
24607a59189Sryo 	ival = floor(interval);
24707a59189Sryo 	fval = (1000000000 * (interval - ival));
24807a59189Sryo 	ts.tv_sec = ival;
24907a59189Sryo 	ts.tv_nsec = fval;
25007a59189Sryo 
25107a59189Sryo 	while (atomic_add_int_nv(done, 0) == 0) {
25207a59189Sryo 		show_counters();
25307a59189Sryo 		nanosleep(&ts, NULL);
25407a59189Sryo 		if (errno == EINTR)	/* interrupted by SIGINFO? */
25507a59189Sryo 			output_delay();
25607a59189Sryo 	}
25707a59189Sryo 	return NULL;
25807a59189Sryo }
25907a59189Sryo 
26007a59189Sryo static void
tprof_list(int argc,char ** argv)261ab933b7bSmaxv tprof_list(int argc, char **argv)
2628415a5d7Syamt {
263*8dc74136Smsaitoh 	const char *defaultevent = tprof_cycle_event_name();
264*8dc74136Smsaitoh 
265*8dc74136Smsaitoh 	printf("%u events can be counted at the same time.\n", ncounters);
266*8dc74136Smsaitoh 	if (defaultevent != NULL)
267*8dc74136Smsaitoh 		printf("The default counter for monitor and top command is "
268*8dc74136Smsaitoh 		    "\"%s\".\n", defaultevent);
269ab933b7bSmaxv 	tprof_event_list();
270ab933b7bSmaxv }
271ab933b7bSmaxv 
27263a85c5bSryo int
tprof_parse_event(tprof_param_t * param,const char * str,uint32_t flags,const char ** eventnamep,char ** errmsgp)27363a85c5bSryo tprof_parse_event(tprof_param_t *param, const char *str, uint32_t flags,
27463a85c5bSryo     const char **eventnamep, char **errmsgp)
27563a85c5bSryo {
27663a85c5bSryo 	double d;
27763a85c5bSryo 	uint64_t n;
27863a85c5bSryo 	int error = 0;
27963a85c5bSryo 	char *p, *event = NULL, *opt = NULL, *scale = NULL;
28063a85c5bSryo 	bool allow_option, allow_scale;
28163a85c5bSryo 	static char errmsgbuf[128];
28263a85c5bSryo 
28363a85c5bSryo 	allow_option = flags & TPROF_PARSE_EVENT_F_ALLOWOPTION;
28463a85c5bSryo 	allow_scale = flags & TPROF_PARSE_EVENT_F_ALLOWSCALE;
28563a85c5bSryo 
28663a85c5bSryo 	p = estrdup(str);
28763a85c5bSryo 	event = p;
28863a85c5bSryo 	if (allow_option) {
28963a85c5bSryo 		opt = strchr(p, ':');
29063a85c5bSryo 		if (opt != NULL) {
29163a85c5bSryo 			*opt++ = '\0';
29263a85c5bSryo 			p = opt;
29363a85c5bSryo 		}
29463a85c5bSryo 	}
29563a85c5bSryo 	if (allow_scale) {
29663a85c5bSryo 		scale = strchr(p, ',');
29763a85c5bSryo 		if (scale != NULL)
29863a85c5bSryo 			*scale++ = '\0';
29963a85c5bSryo 	}
30063a85c5bSryo 
30163a85c5bSryo 	tprof_event_lookup(event, param);
30263a85c5bSryo 
30363a85c5bSryo 	if (opt != NULL) {
30463a85c5bSryo 		while (*opt != '\0') {
30563a85c5bSryo 			switch (*opt) {
30663a85c5bSryo 			case 'u':
30763a85c5bSryo 				param->p_flags |= TPROF_PARAM_USER;
30863a85c5bSryo 				break;
30963a85c5bSryo 			case 'k':
31063a85c5bSryo 				param->p_flags |= TPROF_PARAM_KERN;
31163a85c5bSryo 				break;
31263a85c5bSryo 			default:
31363a85c5bSryo 				error = -1;
31463a85c5bSryo 				snprintf(errmsgbuf, sizeof(errmsgbuf),
31563a85c5bSryo 				    "invalid option: '%c'", *opt);
31663a85c5bSryo 				goto done;
31763a85c5bSryo 			}
318473c5ef3Sryo 			opt++;
31963a85c5bSryo 		}
32063a85c5bSryo 	} else if (allow_option) {
32163a85c5bSryo 		param->p_flags |= TPROF_PARAM_USER;
32263a85c5bSryo 		param->p_flags |= TPROF_PARAM_KERN;
32363a85c5bSryo 	}
32463a85c5bSryo 
32563a85c5bSryo 	if (scale != NULL) {
32663a85c5bSryo 		if (*scale == '=') {
32763a85c5bSryo 			scale++;
32863a85c5bSryo 			n = strtoull(scale, &p, 0);
32963a85c5bSryo 			if (*p != '\0') {
33063a85c5bSryo 				error = -1;
33163a85c5bSryo 			} else {
33263a85c5bSryo 				param->p_value2 = n;
33363a85c5bSryo 				param->p_flags |=
33463a85c5bSryo 				    TPROF_PARAM_VALUE2_TRIGGERCOUNT;
33563a85c5bSryo 			}
33663a85c5bSryo 		} else {
33763a85c5bSryo 			if (strncasecmp("0x", scale, 2) == 0)
33863a85c5bSryo 				d = strtol(scale, &p, 0);
33963a85c5bSryo 			else
34063a85c5bSryo 				d = strtod(scale, &p);
34163a85c5bSryo 			if (*p != '\0' || d <= 0) {
34263a85c5bSryo 				error = -1;
34363a85c5bSryo 			} else {
34463a85c5bSryo 				param->p_value2 = 0x100000000ULL / d;
34563a85c5bSryo 				param->p_flags |= TPROF_PARAM_VALUE2_SCALE;
34663a85c5bSryo 			}
34763a85c5bSryo 		}
34863a85c5bSryo 
34963a85c5bSryo 		if (error != 0) {
35063a85c5bSryo 			snprintf(errmsgbuf, sizeof(errmsgbuf),
35163a85c5bSryo 			    "invalid scale: %s", scale);
35263a85c5bSryo 			goto done;
35363a85c5bSryo 		}
35463a85c5bSryo 	}
35563a85c5bSryo 
35663a85c5bSryo  done:
35763a85c5bSryo 	if (eventnamep != NULL)
35863a85c5bSryo 		*eventnamep = event;
35963a85c5bSryo 	if (error != 0 && errmsgp != NULL)
36063a85c5bSryo 		*errmsgp = errmsgbuf;
36163a85c5bSryo 	return error;
36263a85c5bSryo }
36363a85c5bSryo 
364*8dc74136Smsaitoh const char *
tprof_cycle_event_name(void)365*8dc74136Smsaitoh tprof_cycle_event_name(void)
366*8dc74136Smsaitoh {
367*8dc74136Smsaitoh 	const char *cycleevent;
368*8dc74136Smsaitoh 
369*8dc74136Smsaitoh 	switch (tprof_info.ti_ident) {
370*8dc74136Smsaitoh 	case TPROF_IDENT_INTEL_GENERIC:
371*8dc74136Smsaitoh 		cycleevent = "unhalted-core-cycles";
372*8dc74136Smsaitoh 		break;
373*8dc74136Smsaitoh 	case TPROF_IDENT_AMD_GENERIC:
374*8dc74136Smsaitoh 		cycleevent = "LsNotHaltedCyc";
375*8dc74136Smsaitoh 		break;
376*8dc74136Smsaitoh 	case TPROF_IDENT_ARMV8_GENERIC:
377*8dc74136Smsaitoh 	case TPROF_IDENT_ARMV7_GENERIC:
378*8dc74136Smsaitoh 		cycleevent = "CPU_CYCLES";
379*8dc74136Smsaitoh 		break;
380*8dc74136Smsaitoh 	default:
381*8dc74136Smsaitoh 		cycleevent = NULL;
382*8dc74136Smsaitoh 		break;
383*8dc74136Smsaitoh 	}
384*8dc74136Smsaitoh 	return cycleevent;
385*8dc74136Smsaitoh }
386*8dc74136Smsaitoh 
387ab933b7bSmaxv static void
tprof_monitor_common(bool do_profile,int argc,char ** argv)38807a59189Sryo tprof_monitor_common(bool do_profile, int argc, char **argv)
389ab933b7bSmaxv {
3908415a5d7Syamt 	const char *outfile = "tprof.out";
391ab933b7bSmaxv 	struct tprof_stat ts;
392caba1a6fSryo 	tprof_param_t params[TPROF_MAXCOUNTERS];
3938415a5d7Syamt 	pid_t pid;
3948415a5d7Syamt 	pthread_t pt;
395caba1a6fSryo 	int ret, ch, i;
39663a85c5bSryo 	char *p, *errmsg;
397caba1a6fSryo 	tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
3988415a5d7Syamt 
399caba1a6fSryo 	memset(params, 0, sizeof(params));
400a087cb3cSmaxv 
40107a59189Sryo 	while ((ch = getopt(argc, argv, do_profile ? "o:e:" : "e:i:")) != -1) {
4028415a5d7Syamt 		switch (ch) {
4038415a5d7Syamt 		case 'o':
4048415a5d7Syamt 			outfile = optarg;
4058415a5d7Syamt 			break;
40607a59189Sryo 		case 'i':
40707a59189Sryo 			interval = strtod(optarg, &p);
40807a59189Sryo 			if (*p != '\0' || interval <= 0)
40907a59189Sryo 				errx(EXIT_FAILURE, "Bad/invalid interval: %s",
41007a59189Sryo 				    optarg);
41107a59189Sryo 			break;
412a087cb3cSmaxv 		case 'e':
41363a85c5bSryo 			if (tprof_parse_event(&params[nevent], optarg,
41463a85c5bSryo 			    TPROF_PARSE_EVENT_F_ALLOWOPTION |
41563a85c5bSryo 			    (do_profile ? TPROF_PARSE_EVENT_F_ALLOWSCALE : 0),
41663a85c5bSryo 			    &eventname[nevent], &errmsg) != 0) {
41763a85c5bSryo 				errx(EXIT_FAILURE, "%s", errmsg);
41807a59189Sryo 			}
41907a59189Sryo 			eventnamewidth[nevent] = strlen(eventname[nevent]);
42007a59189Sryo 			if (eventnamewidth[nevent] < COUNTER_COLUMNS_WIDTH)
42107a59189Sryo 				eventnamewidth[nevent] = COUNTER_COLUMNS_WIDTH;
422caba1a6fSryo 			nevent++;
423caba1a6fSryo 			if (nevent > __arraycount(params) ||
424caba1a6fSryo 			    nevent > ncounters)
425e93233dbSryo 				errx(EXIT_FAILURE, "Too many events. Only a"
426e93233dbSryo 				    " maximum of %d counters can be used.",
427e93233dbSryo 				    ncounters);
428a087cb3cSmaxv 			break;
4298415a5d7Syamt 		default:
4308415a5d7Syamt 			usage();
4318415a5d7Syamt 		}
4328415a5d7Syamt 	}
4338415a5d7Syamt 	argc -= optind;
4348415a5d7Syamt 	argv += optind;
435*8dc74136Smsaitoh 	if (argc == 0)
436a087cb3cSmaxv 		usage();
437*8dc74136Smsaitoh 	if (nevent == 0) {
438*8dc74136Smsaitoh 		const char *defaultevent = tprof_cycle_event_name();
439*8dc74136Smsaitoh 		if (defaultevent == NULL)
440*8dc74136Smsaitoh 			errx(EXIT_FAILURE, "cpu not supported");
441*8dc74136Smsaitoh 
442*8dc74136Smsaitoh 		tprof_event_lookup(defaultevent, &params[nevent]);
443*8dc74136Smsaitoh 		eventname[nevent] = defaultevent;
444*8dc74136Smsaitoh 		params[nevent].p_flags |= TPROF_PARAM_KERN;
445*8dc74136Smsaitoh 		nevent++;
446a087cb3cSmaxv 	}
447a087cb3cSmaxv 
44807a59189Sryo 	if (do_profile) {
4498415a5d7Syamt 		outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
4508415a5d7Syamt 		if (outfd == -1) {
451efeccfb5Syamt 			err(EXIT_FAILURE, "%s", outfile);
4528415a5d7Syamt 		}
45307a59189Sryo 	}
4548415a5d7Syamt 
455caba1a6fSryo 	for (i = 0; i < (int)nevent; i++) {
456caba1a6fSryo 		params[i].p_counter = i;
45707a59189Sryo 		if (do_profile)
458caba1a6fSryo 			params[i].p_flags |= TPROF_PARAM_PROFILE;
459caba1a6fSryo 		ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, &params[i]);
460e93233dbSryo 		if (ret == -1) {
461e93233dbSryo 			err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT: %s",
462e93233dbSryo 			    eventname[i]);
463e93233dbSryo 		}
464caba1a6fSryo 	}
465caba1a6fSryo 
466caba1a6fSryo 	ret = ioctl(devfd, TPROF_IOC_START, &mask);
4678415a5d7Syamt 	if (ret == -1) {
4683c917c1dSyamt 		err(EXIT_FAILURE, "TPROF_IOC_START");
4698415a5d7Syamt 	}
4708415a5d7Syamt 
4718415a5d7Syamt 	pid = fork();
4728415a5d7Syamt 	switch (pid) {
4738415a5d7Syamt 	case -1:
4748415a5d7Syamt 		err(EXIT_FAILURE, "fork");
4758415a5d7Syamt 	case 0:
4768415a5d7Syamt 		close(devfd);
4778415a5d7Syamt 		execvp(argv[0], argv);
4788415a5d7Syamt 		_Exit(EXIT_FAILURE);
4798415a5d7Syamt 	}
4808415a5d7Syamt 
4818415a5d7Syamt 	signal(SIGINT, SIG_IGN);
48207a59189Sryo 	if (do_profile)
48307a59189Sryo 		signal(SIGINFO, siginfo_showcount);
48407a59189Sryo 	else
48507a59189Sryo 		signal(SIGINFO, siginfo_nothing);
4868415a5d7Syamt 
48707a59189Sryo 	unsigned int done = 0;
48807a59189Sryo 	if (do_profile)
489ab933b7bSmaxv 		ret = pthread_create(&pt, NULL, process_samples, NULL);
49007a59189Sryo 	else
49107a59189Sryo 		ret = pthread_create(&pt, NULL, process_stat, &done);
49207a59189Sryo 	if (ret != 0)
493ab933b7bSmaxv 		errx(1, "pthread_create: %s", strerror(ret));
4948415a5d7Syamt 
4958415a5d7Syamt 	for (;;) {
4968415a5d7Syamt 		int status;
4978415a5d7Syamt 
4988415a5d7Syamt 		pid = wait4(-1, &status, 0, NULL);
4998415a5d7Syamt 		if (pid == -1) {
5008415a5d7Syamt 			if (errno == ECHILD) {
5018415a5d7Syamt 				break;
5028415a5d7Syamt 			}
5038415a5d7Syamt 			err(EXIT_FAILURE, "wait4");
5048415a5d7Syamt 		}
5058415a5d7Syamt 		if (pid != 0 && WIFEXITED(status)) {
5068415a5d7Syamt 			break;
5078415a5d7Syamt 		}
5088415a5d7Syamt 	}
5098415a5d7Syamt 
510caba1a6fSryo 	ret = ioctl(devfd, TPROF_IOC_STOP, &mask);
5118415a5d7Syamt 	if (ret == -1) {
5128415a5d7Syamt 		err(EXIT_FAILURE, "TPROF_IOC_STOP");
5138415a5d7Syamt 	}
5148415a5d7Syamt 
51507a59189Sryo 	if (!do_profile) {
51607a59189Sryo 		atomic_add_int(&done, 1);	/* terminate thread */
51707a59189Sryo 		kill(0, SIGINFO);
51807a59189Sryo 	}
51907a59189Sryo 
5208415a5d7Syamt 	pthread_join(pt, NULL);
5218415a5d7Syamt 
52207a59189Sryo 	if (do_profile) {
5238415a5d7Syamt 		ret = ioctl(devfd, TPROF_IOC_GETSTAT, &ts);
52407a59189Sryo 		if (ret == -1)
5258415a5d7Syamt 			err(EXIT_FAILURE, "TPROF_IOC_GETSTAT");
5268415a5d7Syamt 
5278415a5d7Syamt 		fprintf(stderr, "\n%s statistics:\n", getprogname());
5288415a5d7Syamt 		fprintf(stderr, "\tsample %" PRIu64 "\n", ts.ts_sample);
5298415a5d7Syamt 		fprintf(stderr, "\toverflow %" PRIu64 "\n", ts.ts_overflow);
5308415a5d7Syamt 		fprintf(stderr, "\tbuf %" PRIu64 "\n", ts.ts_buf);
5318415a5d7Syamt 		fprintf(stderr, "\temptybuf %" PRIu64 "\n", ts.ts_emptybuf);
5328415a5d7Syamt 		fprintf(stderr, "\tdropbuf %" PRIu64 "\n", ts.ts_dropbuf);
533e93233dbSryo 		fprintf(stderr, "\tdropbuf_sample %" PRIu64 "\n",
534e93233dbSryo 		    ts.ts_dropbuf_sample);
5358415a5d7Syamt 
53607a59189Sryo 		fprintf(stderr, "\n");
53707a59189Sryo 	}
53807a59189Sryo 	show_counters();
53907a59189Sryo 
5408415a5d7Syamt 	exit(EXIT_SUCCESS);
5418415a5d7Syamt }
542ab933b7bSmaxv 
54307a59189Sryo static void
tprof_monitor(int argc,char ** argv)54407a59189Sryo tprof_monitor(int argc, char **argv)
54507a59189Sryo {
54607a59189Sryo 	tprof_monitor_common(true, argc, argv);
54707a59189Sryo }
54807a59189Sryo 
54907a59189Sryo static void
tprof_count(int argc,char ** argv)55007a59189Sryo tprof_count(int argc, char **argv)
55107a59189Sryo {
55207a59189Sryo 	tprof_monitor_common(false, argc, argv);
55307a59189Sryo }
55407a59189Sryo 
555ab933b7bSmaxv int
main(int argc,char * argv[])556ab933b7bSmaxv main(int argc, char *argv[])
557ab933b7bSmaxv {
558ab933b7bSmaxv 	const struct cmdtab *ct;
559ab933b7bSmaxv 	int ret;
560ab933b7bSmaxv 
56107a59189Sryo 	getncpu();
562ab933b7bSmaxv 	setprogname(argv[0]);
563ab933b7bSmaxv 	argv += 1, argc -= 1;
564ab933b7bSmaxv 
565ab933b7bSmaxv 	devfd = open(_PATH_TPROF, O_RDWR);
566ab933b7bSmaxv 	if (devfd == -1) {
567ab933b7bSmaxv 		err(EXIT_FAILURE, "%s", _PATH_TPROF);
568ab933b7bSmaxv 	}
569ab933b7bSmaxv 
570caba1a6fSryo 	ret = ioctl(devfd, TPROF_IOC_GETINFO, &tprof_info);
571ab933b7bSmaxv 	if (ret == -1) {
572ab933b7bSmaxv 		err(EXIT_FAILURE, "TPROF_IOC_GETINFO");
573ab933b7bSmaxv 	}
574caba1a6fSryo 	if (tprof_info.ti_version != TPROF_VERSION) {
575ab933b7bSmaxv 		errx(EXIT_FAILURE, "version mismatch: version=%d, expected=%d",
576caba1a6fSryo 		    tprof_info.ti_version, TPROF_VERSION);
577ab933b7bSmaxv 	}
578caba1a6fSryo 	if (tprof_event_init(tprof_info.ti_ident) == -1) {
5798c9dd8d0Smaxv 		errx(EXIT_FAILURE, "cpu not supported");
580ab933b7bSmaxv 	}
581ab933b7bSmaxv 
582caba1a6fSryo 	ret = ioctl(devfd, TPROF_IOC_GETNCOUNTERS, &ncounters);
583caba1a6fSryo 	if (ret == -1) {
584caba1a6fSryo 		err(EXIT_FAILURE, "TPROF_IOC_GETNCOUNTERS");
585caba1a6fSryo 	}
586caba1a6fSryo 	if (ncounters == 0) {
587caba1a6fSryo 		errx(EXIT_FAILURE, "no available counters");
588caba1a6fSryo 	}
589caba1a6fSryo 
590de53e8aeSjmcneill 	if (argc == 0)
591de53e8aeSjmcneill 		usage();
592de53e8aeSjmcneill 
593ab933b7bSmaxv 	for (ct = tprof_cmdtab; ct->label != NULL; ct++) {
594ab933b7bSmaxv 		if (strcmp(argv[0], ct->label) == 0) {
595ab933b7bSmaxv 			if (!ct->argsoptional &&
596ab933b7bSmaxv 			    ((ct->takesargs == 0) ^ (argv[1] == NULL)))
597ab933b7bSmaxv 			{
598ab933b7bSmaxv 				usage();
599ab933b7bSmaxv 			}
600ab933b7bSmaxv 			(*ct->func)(argc, argv);
601ab933b7bSmaxv 			break;
602ab933b7bSmaxv 		}
603ab933b7bSmaxv 	}
604c52e6df8Smaxv 	if (ct->label == NULL) {
605c52e6df8Smaxv 		usage();
606c52e6df8Smaxv 	}
607ab933b7bSmaxv }
608