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(¶ms[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, ¶ms[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, ¶ms[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