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;