Module Name: src Committed By: ryo Date: Thu Dec 1 00:40:05 UTC 2022
Modified Files: src/usr.sbin/tprof: tprof.8 tprof.c Log Message: Improve tprof(8) - Added "tprof count" subcommand to perform counts only. - Event options (u,k) are now optional. The default value is both userland and kernel. (:uk) - Event counters can be displayed with SIGINFO during `tprof monitor' or `tprof count'. To generate a diff of this commit: cvs rdiff -u -r1.17 -r1.18 src/usr.sbin/tprof/tprof.8 cvs rdiff -u -r1.14 -r1.15 src/usr.sbin/tprof/tprof.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.sbin/tprof/tprof.8 diff -u src/usr.sbin/tprof/tprof.8:1.17 src/usr.sbin/tprof/tprof.8:1.18 --- src/usr.sbin/tprof/tprof.8:1.17 Thu Dec 1 00:32:52 2022 +++ src/usr.sbin/tprof/tprof.8 Thu Dec 1 00:40:05 2022 @@ -1,4 +1,4 @@ -.\" $NetBSD: tprof.8,v 1.17 2022/12/01 00:32:52 ryo Exp $ +.\" $NetBSD: tprof.8,v 1.18 2022/12/01 00:40:05 ryo Exp $ .\" .\" Copyright (c)2011 YAMAMOTO Takashi, .\" All rights reserved. @@ -66,7 +66,7 @@ Valid actions are: Display a list of performance counter events available on the system. .It monitor Xo .Fl e -.Ar name:option +.Ar name[:option] .Op Fl e Ar ... .Op Fl o Ar outfile .Ar command @@ -81,12 +81,25 @@ specifies the source of the event; it mu .Ar u (userland) and .Ar k -(kernel). +(kernel). If omitted, it is assumed that both are specified. The collected samples are written into the file .Ar outfile if specified. The default is .Dq Pa tprof.out . +.It count Xo +.Fl e +.Ar name[:option] +.Op Fl e Ar ... +.Op Fl i Ar interval +.Ar command +.Xc +Same as +.Ar monitor , +but does not do any profiling, +only outputs counters every +.Ar interval +second. .It analyze Xo .Op Fl CkLPs .Op Fl p Ar pid Index: src/usr.sbin/tprof/tprof.c diff -u src/usr.sbin/tprof/tprof.c:1.14 src/usr.sbin/tprof/tprof.c:1.15 --- src/usr.sbin/tprof/tprof.c:1.14 Thu Dec 1 00:32:52 2022 +++ src/usr.sbin/tprof/tprof.c Thu Dec 1 00:40:05 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: tprof.c,v 1.14 2022/12/01 00:32:52 ryo Exp $ */ +/* $NetBSD: tprof.c,v 1.15 2022/12/01 00:40:05 ryo Exp $ */ /* * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -57,10 +57,12 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: tprof.c,v 1.14 2022/12/01 00:32:52 ryo Exp $"); +__RCSID("$NetBSD: tprof.c,v 1.15 2022/12/01 00:40:05 ryo Exp $"); #endif /* not lint */ +#include <sys/atomic.h> #include <sys/ioctl.h> +#include <sys/sysctl.h> #include <sys/wait.h> #include <dev/tprof/tprof_ioctl.h> @@ -69,13 +71,16 @@ __RCSID("$NetBSD: tprof.c,v 1.14 2022/12 #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <math.h> #include <pthread.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> +#include <util.h> #include "tprof.h" #define _PATH_TPROF "/dev/tprof" @@ -84,10 +89,17 @@ struct tprof_info tprof_info; u_int ncounters; int devfd; int outfd; +int ncpu; u_int nevent; +double interval = 0xffffffff; /* XXX */ +const char *eventname[TPROF_MAXCOUNTERS]; +u_int eventnamewidth[TPROF_MAXCOUNTERS]; +#define COUNTER_COLUMNS_WIDTH 11 static void tprof_list(int, char **); -static void tprof_monitor(int, char **) __dead; +static void tprof_monitor_common(bool, int, char **) __dead; +static void tprof_monitor(int, char **); +static void tprof_count(int, char **); static struct cmdtab { const char *label; @@ -97,6 +109,7 @@ static struct cmdtab { } const tprof_cmdtab[] = { { "list", false, false, tprof_list }, { "monitor", true, false, tprof_monitor }, + { "count", true, false, tprof_count }, { "analyze", true, true, tprof_analyze }, { NULL, false, false, NULL }, }; @@ -109,15 +122,33 @@ usage(void) fprintf(stderr, "\n"); fprintf(stderr, "\tlist\n"); fprintf(stderr, "\t\tList the available events.\n"); - fprintf(stderr, "\tmonitor -e name:option [-e ...] [-o outfile] command\n"); + fprintf(stderr, "\tmonitor -e name[:option] [-e ...] [-o outfile] command\n"); fprintf(stderr, "\t\tMonitor the event 'name' with option 'option'\n" "\t\tcounted during the execution of 'command'.\n"); + fprintf(stderr, "\tcount -e name[:option] [-e ...] [-i interval]" + " command\n"); + fprintf(stderr, "\t\tSame as monitor, but does not profile," + " only outputs a counter.\n"); fprintf(stderr, "\tanalyze [-CkLPs] [-p pid] file\n"); fprintf(stderr, "\t\tAnalyze the samples of the file 'file'.\n"); exit(EXIT_FAILURE); } +static int +getncpu(void) +{ + size_t size; + int mib[2]; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + size = sizeof(ncpu); + if (sysctl(mib, 2, &ncpu, &size, NULL, 0) == -1) + ncpu = 1; + return ncpu; +} + static void * process_samples(void *dummy) { @@ -150,13 +181,87 @@ process_samples(void *dummy) } static void +show_counters(void) +{ + unsigned int i; + int n, ret; + + fprintf(stderr, " "); + for (i = 0; i < nevent; i++) + fprintf(stderr, " %*s", eventnamewidth[i], eventname[i]); + fprintf(stderr, "\n"); + + for (n = 0; n < ncpu; n++) { + tprof_counts_t counts; + + memset(&counts, 0, sizeof(counts)); + counts.c_cpu = n; + ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &counts); + if (ret == -1) + err(EXIT_FAILURE, "TPROF_IOC_GETCOUNTS"); + + fprintf(stderr, "CPU%-3d", n); + for (i = 0; i < nevent; i++) { + fprintf(stderr, " %*"PRIu64, + eventnamewidth[i], counts.c_count[i]); + } + fprintf(stderr, "\n"); + } +} + +/* XXX: avoid mixing with the output of the child process SIGINFO handler... */ +static void +output_delay(void) +{ + struct timespec delay_ts; + + delay_ts.tv_sec = 0; + delay_ts.tv_nsec = 100000000; + nanosleep(&delay_ts, NULL); +} + +static void +siginfo_nothing(int signo) +{ + __nothing; +} + +static void +siginfo_showcount(int signo) +{ + output_delay(); + show_counters(); +} + +static void * +process_stat(void *arg) +{ + unsigned int *done = arg; + double ival, fval; + struct timespec ts; + + ival = floor(interval); + fval = (1000000000 * (interval - ival)); + ts.tv_sec = ival; + ts.tv_nsec = fval; + + while (atomic_add_int_nv(done, 0) == 0) { + show_counters(); + nanosleep(&ts, NULL); + if (errno == EINTR) /* interrupted by SIGINFO? */ + output_delay(); + } + return NULL; +} + +static void tprof_list(int argc, char **argv) { tprof_event_list(); } static void -tprof_monitor(int argc, char **argv) +tprof_monitor_common(bool do_profile, int argc, char **argv) { const char *outfile = "tprof.out"; struct tprof_stat ts; @@ -164,28 +269,43 @@ tprof_monitor(int argc, char **argv) pid_t pid; pthread_t pt; int ret, ch, i; - char *tokens[2]; + char *tokens[2], *p; tprof_countermask_t mask = TPROF_COUNTERMASK_ALL; memset(params, 0, sizeof(params)); - while ((ch = getopt(argc, argv, "o:e:")) != -1) { + while ((ch = getopt(argc, argv, do_profile ? "o:e:" : "e:i:")) != -1) { switch (ch) { case 'o': outfile = optarg; break; + case 'i': + interval = strtod(optarg, &p); + if (*p != '\0' || interval <= 0) + errx(EXIT_FAILURE, "Bad/invalid interval: %s", + optarg); + break; case 'e': - tokens[0] = strtok(optarg, ":"); + p = estrdup(optarg); + tokens[0] = strtok(p, ":"); tokens[1] = strtok(NULL, ":"); - if (tokens[1] == NULL) - usage(); tprof_event_lookup(tokens[0], ¶ms[nevent]); - if (strchr(tokens[1], 'u')) - params[nevent].p_flags |= TPROF_PARAM_USER; - if (strchr(tokens[1], 'k')) - params[nevent].p_flags |= TPROF_PARAM_KERN; - if (params[nevent].p_flags == 0) - usage(); + + if (tokens[1] == NULL) { + params[nevent].p_flags |= + (TPROF_PARAM_USER | TPROF_PARAM_KERN); + } else { + if (strchr(tokens[1], 'u')) + params[nevent].p_flags |= + TPROF_PARAM_USER; + if (strchr(tokens[1], 'k')) + params[nevent].p_flags |= + TPROF_PARAM_KERN; + } + eventname[nevent] = tokens[0]; + eventnamewidth[nevent] = strlen(eventname[nevent]); + if (eventnamewidth[nevent] < COUNTER_COLUMNS_WIDTH) + eventnamewidth[nevent] = COUNTER_COLUMNS_WIDTH; nevent++; if (nevent > __arraycount(params) || nevent > ncounters) @@ -201,14 +321,17 @@ tprof_monitor(int argc, char **argv) usage(); } - outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (outfd == -1) { - err(EXIT_FAILURE, "%s", outfile); + if (do_profile) { + outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (outfd == -1) { + err(EXIT_FAILURE, "%s", outfile); + } } for (i = 0; i < (int)nevent; i++) { params[i].p_counter = i; - params[i].p_flags |= TPROF_PARAM_PROFILE; + if (do_profile) + params[i].p_flags |= TPROF_PARAM_PROFILE; ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, ¶ms[i]); if (ret == -1) err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT"); @@ -230,11 +353,18 @@ tprof_monitor(int argc, char **argv) } signal(SIGINT, SIG_IGN); - - ret = pthread_create(&pt, NULL, process_samples, NULL); - if (ret != 0) { + if (do_profile) + signal(SIGINFO, siginfo_showcount); + else + signal(SIGINFO, siginfo_nothing); + + unsigned int done = 0; + if (do_profile) + ret = pthread_create(&pt, NULL, process_samples, NULL); + else + ret = pthread_create(&pt, NULL, process_stat, &done); + if (ret != 0) errx(1, "pthread_create: %s", strerror(ret)); - } for (;;) { int status; @@ -256,30 +386,52 @@ tprof_monitor(int argc, char **argv) err(EXIT_FAILURE, "TPROF_IOC_STOP"); } + if (!do_profile) { + atomic_add_int(&done, 1); /* terminate thread */ + kill(0, SIGINFO); + } + pthread_join(pt, NULL); - ret = ioctl(devfd, TPROF_IOC_GETSTAT, &ts); - if (ret == -1) { - err(EXIT_FAILURE, "TPROF_IOC_GETSTAT"); - } + if (do_profile) { + ret = ioctl(devfd, TPROF_IOC_GETSTAT, &ts); + if (ret == -1) + err(EXIT_FAILURE, "TPROF_IOC_GETSTAT"); - fprintf(stderr, "\n%s statistics:\n", getprogname()); - fprintf(stderr, "\tsample %" PRIu64 "\n", ts.ts_sample); - fprintf(stderr, "\toverflow %" PRIu64 "\n", ts.ts_overflow); - fprintf(stderr, "\tbuf %" PRIu64 "\n", ts.ts_buf); - fprintf(stderr, "\temptybuf %" PRIu64 "\n", ts.ts_emptybuf); - fprintf(stderr, "\tdropbuf %" PRIu64 "\n", ts.ts_dropbuf); - fprintf(stderr, "\tdropbuf_sample %" PRIu64 "\n", ts.ts_dropbuf_sample); + fprintf(stderr, "\n%s statistics:\n", getprogname()); + fprintf(stderr, "\tsample %" PRIu64 "\n", ts.ts_sample); + fprintf(stderr, "\toverflow %" PRIu64 "\n", ts.ts_overflow); + fprintf(stderr, "\tbuf %" PRIu64 "\n", ts.ts_buf); + fprintf(stderr, "\temptybuf %" PRIu64 "\n", ts.ts_emptybuf); + fprintf(stderr, "\tdropbuf %" PRIu64 "\n", ts.ts_dropbuf); + fprintf(stderr, "\tdropbuf_sample %" PRIu64 "\n", ts.ts_dropbuf_sample); + + fprintf(stderr, "\n"); + } + show_counters(); exit(EXIT_SUCCESS); } +static void +tprof_monitor(int argc, char **argv) +{ + tprof_monitor_common(true, argc, argv); +} + +static void +tprof_count(int argc, char **argv) +{ + tprof_monitor_common(false, argc, argv); +} + int main(int argc, char *argv[]) { const struct cmdtab *ct; int ret; + getncpu(); setprogname(argv[0]); argv += 1, argc -= 1;