Module Name:    src
Committed By:   ryo
Date:           Thu Dec  1 00:43:27 UTC 2022

Modified Files:
        src/usr.sbin/tprof: Makefile ksyms.c ksyms.h tprof.8 tprof.c tprof.h
            tprof_analyze.c
Added Files:
        src/usr.sbin/tprof: tprof_top.c

Log Message:
add "top" subcommand to tprof(8)


To generate a diff of this commit:
cvs rdiff -u -r1.11 -r1.12 src/usr.sbin/tprof/Makefile
cvs rdiff -u -r1.1 -r1.2 src/usr.sbin/tprof/ksyms.c \
    src/usr.sbin/tprof/ksyms.h
cvs rdiff -u -r1.18 -r1.19 src/usr.sbin/tprof/tprof.8
cvs rdiff -u -r1.15 -r1.16 src/usr.sbin/tprof/tprof.c
cvs rdiff -u -r1.2 -r1.3 src/usr.sbin/tprof/tprof.h
cvs rdiff -u -r1.7 -r1.8 src/usr.sbin/tprof/tprof_analyze.c
cvs rdiff -u -r0 -r1.1 src/usr.sbin/tprof/tprof_top.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/Makefile
diff -u src/usr.sbin/tprof/Makefile:1.11 src/usr.sbin/tprof/Makefile:1.12
--- src/usr.sbin/tprof/Makefile:1.11	Thu Dec  1 00:41:10 2022
+++ src/usr.sbin/tprof/Makefile	Thu Dec  1 00:43:27 2022
@@ -1,10 +1,10 @@
-#	$NetBSD: Makefile,v 1.11 2022/12/01 00:41:10 ryo Exp $
+#	$NetBSD: Makefile,v 1.12 2022/12/01 00:43:27 ryo Exp $
 
 .PATH:	${.CURDIR}/arch
 
 PROG=	tprof
 MAN=	tprof.8
-SRCS=	tprof.c tprof_analyze.c ksyms.c
+SRCS=	tprof.c tprof_analyze.c tprof_top.c ksyms.c
 
 .if	${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "x86_64"
 SRCS+=	tprof_x86.c
@@ -19,9 +19,11 @@ SRCS+=	tprof_noarch.c
 CPPFLAGS+= -I${NETBSDSRCDIR}/sys/
 
 LDADD+= -lpthread
+LDADD+= -lm
 LDADD+= -lelf
 LDADD+= -lutil
 DPADD+= ${LIBPTHREAD}
+DPADD+= ${LIBM}
 DPADD+= ${LIBELF}
 DPADD+= ${LIBUTIL}
 

Index: src/usr.sbin/tprof/ksyms.c
diff -u src/usr.sbin/tprof/ksyms.c:1.1 src/usr.sbin/tprof/ksyms.c:1.2
--- src/usr.sbin/tprof/ksyms.c:1.1	Thu Dec  1 00:41:10 2022
+++ src/usr.sbin/tprof/ksyms.c	Thu Dec  1 00:43:27 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: ksyms.c,v 1.1 2022/12/01 00:41:10 ryo Exp $	*/
+/*	$NetBSD: ksyms.c,v 1.2 2022/12/01 00:43:27 ryo Exp $	*/
 
 /*
  * Copyright (c) 2010,2011,2012 YAMAMOTO Takashi,
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: ksyms.c,v 1.1 2022/12/01 00:41:10 ryo Exp $");
+__RCSID("$NetBSD: ksyms.c,v 1.2 2022/12/01 00:43:27 ryo Exp $");
 #endif /* not lint */
 
 #include <assert.h>
@@ -43,8 +43,8 @@ __RCSID("$NetBSD: ksyms.c,v 1.1 2022/12/
 #include <util.h>
 #include "ksyms.h"
 
-struct sym **syms = NULL;
-size_t nsyms = 0;
+static struct sym **syms = NULL;
+static size_t nsyms = 0;
 
 static int
 compare_value(const void *p1, const void *p2)
@@ -69,8 +69,8 @@ compare_value(const void *p1, const void
 	return strcmp(s1->name, s2->name);
 }
 
-void
-ksymload(void)
+struct sym **
+ksymload(size_t *nsymp)
 {
 	Elf *e;
 	Elf_Scn *s;
@@ -132,13 +132,15 @@ ksymload(void)
 	elf_end(e);
 	close(fd);
 	qsort(syms, nsyms, sizeof(*syms), compare_value);
-	return;
+	if (nsymp != NULL)
+		*nsymp = nsyms;
+	return syms;
 elffail:
 	errx(EXIT_FAILURE, "libelf: %s", elf_errmsg(elf_errno()));
 }
 
 const char *
-ksymlookup(uint64_t value, uint64_t *offset)
+ksymlookup(uint64_t value, uint64_t *offset, size_t *n)
 {
 	size_t hi;
 	size_t lo;
@@ -171,6 +173,8 @@ ksymlookup(uint64_t value, uint64_t *off
 		if (sym->value <= value &&
 		    (sym->size == 0 || value - sym->value <= sym->size )) {
 			*offset = value - sym->value;
+			if (n != NULL)
+				*n = i;
 			return sym->name;
 		}
 		if (sym->size != 0 && sym->value + sym->size < value) {
Index: src/usr.sbin/tprof/ksyms.h
diff -u src/usr.sbin/tprof/ksyms.h:1.1 src/usr.sbin/tprof/ksyms.h:1.2
--- src/usr.sbin/tprof/ksyms.h:1.1	Thu Dec  1 00:41:10 2022
+++ src/usr.sbin/tprof/ksyms.h	Thu Dec  1 00:43:27 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: ksyms.h,v 1.1 2022/12/01 00:41:10 ryo Exp $	*/
+/*	$NetBSD: ksyms.h,v 1.2 2022/12/01 00:43:27 ryo Exp $	*/
 
 /*
  * Copyright (c) 2010,2011,2012 YAMAMOTO Takashi,
@@ -35,10 +35,7 @@ struct sym {
 	uint64_t size;
 };
 
-extern struct sym **syms;
-extern size_t nsyms;
-
-void ksymload(void);
-const char *ksymlookup(uint64_t, uint64_t *);
+struct sym **ksymload(size_t *);
+const char *ksymlookup(uint64_t, uint64_t *, size_t *);
 
 #endif /* _KSYMS_H_ */

Index: src/usr.sbin/tprof/tprof.8
diff -u src/usr.sbin/tprof/tprof.8:1.18 src/usr.sbin/tprof/tprof.8:1.19
--- src/usr.sbin/tprof/tprof.8:1.18	Thu Dec  1 00:40:05 2022
+++ src/usr.sbin/tprof/tprof.8	Thu Dec  1 00:43:27 2022
@@ -1,4 +1,4 @@
-.\"	$NetBSD: tprof.8,v 1.18 2022/12/01 00:40:05 ryo Exp $
+.\"	$NetBSD: tprof.8,v 1.19 2022/12/01 00:43:27 ryo Exp $
 .\"
 .\" Copyright (c)2011 YAMAMOTO Takashi,
 .\" All rights reserved.
@@ -130,6 +130,39 @@ and ignore the rest.
 .It Fl s
 Per symbol.
 .El
+.It top Xo
+.Oo
+.Fl e
+.Ar name[,value]
+.Op Fl e Ar ...
+.Oc
+.Op Fl i Ar interval
+.Op Fl c
+.Op Fl u
+.Xc
+Displays profiling results in real-time.
+.Ar name
+specifies the name of the event to count.
+.Ar value
+specifies the ratio of the speed to the cycle counter, or the counter until
+overflow.
+The counter reset value on overflow used for profiling is calculated from the
+speed of the cycle counter by default, but for some events this value may be
+too large (counter increasing too slowly) to be sufficient for profiling.
+For example, to specify an event that increases about 1000 times slower than
+the cycle counter, specify
+.Dq Pa -e event,1000 .
+Also, if 
+.Dq Pa -e event,=200
+is specified, profiling is performed every time the counter is increased by 200.
+.Bl -tag -width XXintervalX -offset indent
+.It Fl i Ar interval
+set the update interval in seconds. The default value is 1.
+.It Fl c
+show the delta of the event counters.
+.It Fl u
+Userland processes are also included in the profiling.
+.El
 .El
 .Sh EXAMPLES
 The following command profiles the system during 20 seconds and writes the

Index: src/usr.sbin/tprof/tprof.c
diff -u src/usr.sbin/tprof/tprof.c:1.15 src/usr.sbin/tprof/tprof.c:1.16
--- src/usr.sbin/tprof/tprof.c:1.15	Thu Dec  1 00:40:05 2022
+++ src/usr.sbin/tprof/tprof.c	Thu Dec  1 00:43:27 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: tprof.c,v 1.15 2022/12/01 00:40:05 ryo Exp $	*/
+/*	$NetBSD: tprof.c,v 1.16 2022/12/01 00:43:27 ryo Exp $	*/
 
 /*
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: tprof.c,v 1.15 2022/12/01 00:40:05 ryo Exp $");
+__RCSID("$NetBSD: tprof.c,v 1.16 2022/12/01 00:43:27 ryo Exp $");
 #endif /* not lint */
 
 #include <sys/atomic.h>
@@ -111,6 +111,7 @@ static struct cmdtab {
 	{ "monitor",	true,  false, tprof_monitor },
 	{ "count",	true,  false, tprof_count },
 	{ "analyze",	true,  true,  tprof_analyze },
+	{ "top",	true,  true,  tprof_top },
 	{ NULL,		false, false, NULL },
 };
 
@@ -122,7 +123,8 @@ 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]"
@@ -131,7 +133,8 @@ usage(void)
 	    " only outputs a counter.\n");
 	fprintf(stderr, "\tanalyze [-CkLPs] [-p pid] file\n");
 	fprintf(stderr, "\t\tAnalyze the samples of the file 'file'.\n");
-
+	fprintf(stderr, "\ttop [-e name [-e ...]] [-i interval] [-u]\n");
+	fprintf(stderr, "\t\tDisplay profiling results in real-time.\n");
 	exit(EXIT_FAILURE);
 }
 
@@ -257,6 +260,7 @@ process_stat(void *arg)
 static void
 tprof_list(int argc, char **argv)
 {
+	printf("%u events can be counted at the same time\n", ncounters);
 	tprof_event_list();
 }
 
@@ -309,7 +313,9 @@ tprof_monitor_common(bool do_profile, in
 			nevent++;
 			if (nevent > __arraycount(params) ||
 			    nevent > ncounters)
-				errx(EXIT_FAILURE, "Too many events");
+				errx(EXIT_FAILURE, "Too many events. Only a"
+				    " maximum of %d counters can be used.",
+				    ncounters);
 			break;
 		default:
 			usage();
@@ -333,8 +339,10 @@ tprof_monitor_common(bool do_profile, in
 		if (do_profile)
 			params[i].p_flags |= TPROF_PARAM_PROFILE;
 		ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, &params[i]);
-		if (ret == -1)
-			err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT");
+		if (ret == -1) {
+			err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT: %s",
+			    eventname[i]);
+		}
 	}
 
 	ret = ioctl(devfd, TPROF_IOC_START, &mask);
@@ -404,7 +412,8 @@ tprof_monitor_common(bool do_profile, in
 		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, "\tdropbuf_sample %" PRIu64 "\n",
+		    ts.ts_dropbuf_sample);
 
 		fprintf(stderr, "\n");
 	}

Index: src/usr.sbin/tprof/tprof.h
diff -u src/usr.sbin/tprof/tprof.h:1.2 src/usr.sbin/tprof/tprof.h:1.3
--- src/usr.sbin/tprof/tprof.h:1.2	Fri Jul 13 11:03:36 2018
+++ src/usr.sbin/tprof/tprof.h	Thu Dec  1 00:43:27 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: tprof.h,v 1.2 2018/07/13 11:03:36 maxv Exp $	*/
+/*	$NetBSD: tprof.h,v 1.3 2022/12/01 00:43:27 ryo Exp $	*/
 
 /*
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -29,8 +29,14 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+extern struct tprof_info tprof_info;
+extern int ncpu;
+extern int devfd;
+extern u_int ncounters;
+
 int tprof_event_init(uint32_t);
 void tprof_event_list(void);
 void tprof_event_lookup(const char *, struct tprof_param *);
 
 void tprof_analyze(int, char **);
+void tprof_top(int, char **);

Index: src/usr.sbin/tprof/tprof_analyze.c
diff -u src/usr.sbin/tprof/tprof_analyze.c:1.7 src/usr.sbin/tprof/tprof_analyze.c:1.8
--- src/usr.sbin/tprof/tprof_analyze.c:1.7	Thu Dec  1 00:41:10 2022
+++ src/usr.sbin/tprof/tprof_analyze.c	Thu Dec  1 00:43:27 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: tprof_analyze.c,v 1.7 2022/12/01 00:41:10 ryo Exp $	*/
+/*	$NetBSD: tprof_analyze.c,v 1.8 2022/12/01 00:43:27 ryo Exp $	*/
 
 /*
  * Copyright (c) 2010,2011,2012 YAMAMOTO Takashi,
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: tprof_analyze.c,v 1.7 2022/12/01 00:41:10 ryo Exp $");
+__RCSID("$NetBSD: tprof_analyze.c,v 1.8 2022/12/01 00:43:27 ryo Exp $");
 #endif /* not lint */
 
 #include <assert.h>
@@ -192,7 +192,7 @@ tprof_analyze(int argc, char **argv)
 		errx(EXIT_FAILURE, "fopen");
 	}
 
-	ksymload();
+	ksymload(NULL);
 	rb_tree_init(&addrtree, &addrtree_ops);
 
 	/*
@@ -245,7 +245,7 @@ tprof_analyze(int argc, char **argv)
 			const char *name;
 			uint64_t offset;
 
-			name = ksymlookup(a->addr, &offset);
+			name = ksymlookup(a->addr, &offset, NULL);
 			if (name != NULL) {
 				a->addr -= offset;
 			}
@@ -310,7 +310,7 @@ tprof_analyze(int argc, char **argv)
 
 		a = l[i];
 		if (a->in_kernel) {
-			name = ksymlookup(a->addr, &offset);
+			name = ksymlookup(a->addr, &offset, NULL);
 		} else {
 			name = NULL;
 		}

Added files:

Index: src/usr.sbin/tprof/tprof_top.c
diff -u /dev/null src/usr.sbin/tprof/tprof_top.c:1.1
--- /dev/null	Thu Dec  1 00:43:27 2022
+++ src/usr.sbin/tprof/tprof_top.c	Thu Dec  1 00:43:27 2022
@@ -0,0 +1,739 @@
+/*	$NetBSD: tprof_top.c,v 1.1 2022/12/01 00:43:27 ryo Exp $	*/
+
+/*-
+ * Copyright (c) 2022 Ryo Shimizu <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: tprof_top.c,v 1.1 2022/12/01 00:43:27 ryo Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/rbtree.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include <dev/tprof/tprof_ioctl.h>
+#include "tprof.h"
+#include "ksyms.h"
+
+static struct sym **ksyms;
+static size_t nksyms;
+static sig_atomic_t sigalrm;
+static struct winsize win;
+static long top_interval = 1;
+static u_int nevent;
+static const char *eventname[TPROF_MAXCOUNTERS];
+static int nontty;
+
+/* XXX: use terminfo or curses */
+static void
+cursor_address(u_int x, u_int y)
+{
+	if (nontty)
+		return;
+	printf("\e[%u;%uH", y - 1, x - 1);
+}
+
+static void
+cursor_home(void)
+{
+	if (nontty)
+		return;
+	printf("\e[H");
+}
+
+static void
+cls_eol(void)
+{
+	if (nontty)
+		return;
+	printf("\e[K");
+}
+
+static void
+cls_eos(void)
+{
+	if (nontty)
+		return;
+	printf("\e[J");
+}
+
+static void
+sigwinch_handler(int signo)
+{
+	nontty = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
+}
+
+static void
+sigalrm_handler(int signo)
+{
+	sigalrm = 1;
+}
+
+struct sample_elm {
+	struct rb_node node;
+	uint64_t addr;
+	const char *name;
+	uint32_t flags;
+#define SAMPLE_ELM_FLAGS_USER	0x00000001
+	uint32_t num;
+	uint32_t numcpu[];
+};
+
+static size_t sizeof_sample_elm;
+static rb_tree_t rb_tree_sample;
+static char *samplebuf;
+static u_int sample_nused;
+static u_int sample_kern_nsample;
+static u_int sample_user_nsample;
+static u_int sample_max = 1024 * 512;	/* XXX */
+static uint32_t *sample_nsample_kern_per_cpu;
+static uint32_t *sample_nsample_user_per_cpu;
+static uint64_t *sample_nsample_per_event;
+static uint64_t *sample_nsample_per_event_cpu;
+static uint64_t collect_overflow;
+
+static int opt_userland = 0;
+static int opt_showcounter = 0;
+
+static int
+sample_compare_key(void *ctx, const void *n1, const void *keyp)
+{
+	const struct sample_elm *a1 = n1;
+	const struct sample_elm *a2 = (const struct sample_elm *)keyp;
+	return a1->addr - a2->addr;
+}
+
+static signed int
+sample_compare_nodes(void *ctx, const void *n1, const void *n2)
+{
+	const struct addr *a2 = n2;
+	return sample_compare_key(ctx, n1, a2);
+}
+
+static const rb_tree_ops_t sample_ops = {
+	.rbto_compare_nodes = sample_compare_nodes,
+	.rbto_compare_key = sample_compare_key
+};
+
+static void
+sample_init(void)
+{
+	sizeof_sample_elm = sizeof(struct sample_elm) + sizeof(uint32_t) * ncpu;
+}
+
+static void
+sample_reset(void)
+{
+	if (samplebuf == NULL) {
+		samplebuf = emalloc(sizeof_sample_elm * sample_max);
+		sample_nsample_kern_per_cpu = emalloc(sizeof(uint32_t) * ncpu);
+		sample_nsample_user_per_cpu = emalloc(sizeof(uint32_t) * ncpu);
+		sample_nsample_per_event = emalloc(sizeof(uint64_t) * nevent);
+		sample_nsample_per_event_cpu =
+		    emalloc(sizeof(uint64_t) * nevent * ncpu);
+	}
+	sample_nused = 0;
+	sample_kern_nsample = 0;
+	sample_user_nsample = 0;
+	memset(sample_nsample_kern_per_cpu, 0, sizeof(uint32_t) * ncpu);
+	memset(sample_nsample_user_per_cpu, 0, sizeof(uint32_t) * ncpu);
+	memset(sample_nsample_per_event, 0, sizeof(uint64_t) * nevent);
+	memset(sample_nsample_per_event_cpu, 0,
+	    sizeof(uint64_t) * nevent * ncpu);
+
+	rb_tree_init(&rb_tree_sample, &sample_ops);
+}
+
+static struct sample_elm *
+sample_alloc(void)
+{
+	struct sample_elm *e;
+
+	if (sample_nused >= sample_max) {
+		errx(EXIT_FAILURE, "sample buffer overflow");
+		return NULL;
+	}
+
+	e = (struct sample_elm *)(samplebuf + sizeof_sample_elm *
+	    sample_nused++);
+	memset(e, 0, sizeof_sample_elm);
+	return e;
+}
+
+static void
+sample_takeback(struct sample_elm *e)
+{
+	assert(sample_nused > 0);
+	sample_nused--;
+}
+
+static int
+sample_sortfunc(const void *a, const void *b)
+{
+	const struct sample_elm *ea = a;
+	const struct sample_elm *eb = b;
+	return eb->num - ea->num;
+}
+
+static void
+sample_sort(void)
+{
+	qsort(samplebuf, sample_nused, sizeof_sample_elm, sample_sortfunc);
+}
+
+static void
+sample_collect(tprof_sample_t *s)
+{
+	struct sample_elm *e, *o;
+	const char *name;
+	size_t symid;
+	uint64_t addr, offset;
+	uint32_t flags = 0;
+	uint32_t eventid, cpuid;
+
+	eventid = __SHIFTOUT(s->s_flags, TPROF_SAMPLE_COUNTER_MASK);
+	cpuid = s->s_cpuid;
+
+	sample_nsample_per_event[eventid]++;
+	sample_nsample_per_event_cpu[nevent * cpuid + eventid]++;
+
+	if ((s->s_flags & TPROF_SAMPLE_INKERNEL) == 0) {
+		sample_user_nsample++;
+		sample_nsample_user_per_cpu[cpuid]++;
+
+		name = NULL;
+		addr = s->s_pid;	/* XXX */
+		flags |= SAMPLE_ELM_FLAGS_USER;
+
+		if (!opt_userland)
+			return;
+	} else {
+		sample_kern_nsample++;
+		sample_nsample_kern_per_cpu[cpuid]++;
+
+		name = ksymlookup(s->s_pc, &offset, &symid);
+		if (name != NULL) {
+			addr = ksyms[symid]->value;
+		} else {
+			addr = s->s_pc;
+		}
+	}
+
+	e = sample_alloc();
+	if (e == NULL) {
+		fprintf(stderr, "cannot allocate sample\n");
+		collect_overflow++;
+		return;
+	}
+
+	e->addr = addr;
+	e->name = name;
+	e->flags = flags;
+	e->num = 1;
+	e->numcpu[cpuid] = 1;
+	o = rb_tree_insert_node(&rb_tree_sample, e);
+	if (o != e) {
+		/* already exists */
+		sample_takeback(e);
+		o->num++;
+		o->numcpu[cpuid]++;
+	}
+}
+
+static void
+show_tprof_stat(void)
+{
+	static struct tprof_stat tsbuf[2], *ts0, *ts;
+	static u_int ts_i = 0;
+	int ret;
+
+	ts0 = &tsbuf[ts_i++ & 1];
+	ts = &tsbuf[ts_i & 1];
+	ret = ioctl(devfd, TPROF_IOC_GETSTAT, ts);
+	if (ret == -1)
+		err(EXIT_FAILURE, "TPROF_IOC_GETSTAT");
+
+#define TS_PRINT(label, _m)				\
+	do {						\
+		printf(label "%" PRIu64, ts->_m);	\
+		if (ts->_m != ts0->_m)			\
+			printf("(+%"PRIu64")",		\
+			    ts->_m - ts0->_m);		\
+		printf(" ");				\
+	} while (0)
+	TS_PRINT("tprof sample:", ts_sample);
+	TS_PRINT(" overflow:", ts_overflow);
+	TS_PRINT(" buf:", ts_buf);
+	TS_PRINT(" emptybuf:", ts_emptybuf);
+	TS_PRINT(" dropbuf:", ts_dropbuf);
+	TS_PRINT(" dropbuf_sample:", ts_dropbuf_sample);
+}
+
+static void
+show_timestamp(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	cursor_address(win.ws_col - 7, 0);
+	printf("%-8.8s", &(ctime((time_t *)&tv.tv_sec)[11]));
+}
+
+
+static uint64_t *counters;	/* counters[2][ncpu][nevent] */
+static u_int counters_i;
+
+static void
+show_counters_alloc(void)
+{
+	size_t sz = 2 * ncpu * nevent * sizeof(uint64_t);
+	counters = emalloc(sz);
+	memset(counters, 0, sz);
+}
+
+static void
+show_counters(void)
+{
+	tprof_counts_t countsbuf;
+	uint64_t *cn[2], *c0, *c;
+	u_int i;
+	int n, ret;
+
+	cn[0] = counters;
+	cn[1] = counters + ncpu * nevent;
+	c0 = cn[counters_i++ & 1];
+	c = cn[counters_i & 1];
+
+	for (n = 0; n < ncpu; n++) {
+		countsbuf.c_cpu = n;
+		ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &countsbuf);
+		if (ret == -1)
+			err(EXIT_FAILURE, "TPROF_IOC_GETCOUNTS");
+
+		for (i = 0; i < nevent; i++)
+			c[n * nevent + i] = countsbuf.c_count[i];
+	}
+
+	printf("%-22s", "Event counter (delta)");
+	for (n = 0; n < ncpu; n++) {
+		char cpuname[16];
+		snprintf(cpuname, sizeof(cpuname), "CPU%u", n);
+		printf("%11s", cpuname);
+	}
+	printf("\n");
+
+	for (i = 0; i < nevent; i++) {
+		printf("%-22.22s", eventname[i]);
+		for (n = 0; n < ncpu; n++) {
+			printf("%11"PRIu64,
+			    c[n * nevent + i] - c0[n * nevent + i]);
+		}
+		printf("\n");
+	}
+	printf("\n");
+}
+
+static void
+show_count_per_event(void)
+{
+	u_int i, nsample_total;
+	int n;
+
+	nsample_total = sample_kern_nsample + sample_user_nsample;
+
+	for (i = 0; i < nevent; i++) {
+		if (sample_nsample_per_event[i] >= nsample_total) {
+			printf("%5.1f%%", sample_nsample_per_event[i] *
+			    100.00 / nsample_total);
+		} else {
+			printf("%5.2f%%", sample_nsample_per_event[i] *
+			    100.00 / nsample_total);
+		}
+		printf("%8lu ", sample_nsample_per_event[i]);
+
+		printf("%-32.32s", eventname[i]);
+		for (n = 0; n < ncpu; n++) {
+			printf("%6"PRIu64,
+			    sample_nsample_per_event_cpu[nevent * n + i]);
+		}
+		printf("\n");
+	}
+}
+
+static void
+sample_show(void)
+{
+	static u_int nshow;
+
+	struct sample_elm *e;
+	u_int nsample_total;
+	int i, n, ndisp;
+	char namebuf[32];
+	const char *name;
+
+	int margin_lines = 7;
+
+	margin_lines += 3 + nevent;	/* show_counter_per_event() */
+
+	if (opt_showcounter)
+		margin_lines += 2 + nevent;
+	if (opt_userland)
+		margin_lines += 1;
+
+	ndisp = sample_nused;
+	if (!nontty && ndisp > (win.ws_row - margin_lines))
+		ndisp = win.ws_row - margin_lines;
+
+	cursor_home();
+	if (nshow++ == 0)
+		cls_eos();
+
+
+	show_tprof_stat();
+	cls_eol();
+
+	show_timestamp();
+	printf("\n");
+	printf("\n");
+
+	if (opt_showcounter)
+		show_counters();
+
+	printf("  Rate Sample# Eventname                       ");
+	for (n = 0; n < ncpu; n++) {
+		if (n >= 1000) {
+			snprintf(namebuf, sizeof(namebuf), "%d", n);
+		} else if (n >= 100) {
+			snprintf(namebuf, sizeof(namebuf), "#%d", n);
+		} else {
+			snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
+		}
+		printf(" %5s", namebuf);
+	}
+	printf("\n");
+	printf("------ ------- --------------------------------");
+	for (n = 0; n < ncpu; n++) {
+		printf(" -----");
+	}
+	printf("\n");
+
+	show_count_per_event();
+	printf("\n");
+
+	printf("  Rate Sample# Symbol                          ");
+	for (n = 0; n < ncpu; n++) {
+		if (n >= 1000) {
+			snprintf(namebuf, sizeof(namebuf), "%d", n);
+		} else if (n >= 100) {
+			snprintf(namebuf, sizeof(namebuf), "#%d", n);
+		} else {
+			snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
+		}
+		printf(" %5s", namebuf);
+	}
+	printf("\n");
+	printf("------ ------- --------------------------------");
+	for (n = 0; n < ncpu; n++) {
+		printf(" -----");
+	}
+	printf("\n");
+
+	for (i = 0; i < ndisp; i++) {
+		e = (struct sample_elm *)(samplebuf + sizeof_sample_elm * i);
+		name = e->name;
+		if (name == NULL) {
+			if (e->flags & SAMPLE_ELM_FLAGS_USER) {
+				snprintf(namebuf, sizeof(namebuf), "<PID:%lu>",
+				    e->addr);
+			} else {
+				snprintf(namebuf, sizeof(namebuf), "0x%016lx",
+				    e->addr);
+			}
+			name = namebuf;
+		}
+
+		nsample_total = sample_kern_nsample;
+		if (opt_userland)
+			nsample_total += sample_user_nsample;
+		/*
+		 * even when only kernel mode events are configured,
+		 * interrupts may still occur in the user mode state.
+		 */
+		if (nsample_total == 0)
+			nsample_total = 1;
+
+		if (e->num >= nsample_total) {
+			printf("%5.1f%%", e->num * 100.00 / nsample_total);
+		} else {
+			printf("%5.2f%%", e->num * 100.00 / nsample_total);
+		}
+		printf("%8u %-32.32s", e->num, name);
+
+		for (n = 0; n < ncpu; n++) {
+			if (e->numcpu[n] == 0)
+				printf("     .");
+			else
+				printf("%6u", e->numcpu[n]);
+		}
+		printf("\n");
+	}
+
+	if ((u_int)ndisp != sample_nused) {
+		printf("     :       : (more %u symbols omitted)\n",
+		    sample_nused - ndisp);
+	} else {
+		for (i = ndisp; i <= win.ws_row - margin_lines; i++) {
+			printf("~");
+			cls_eol();
+			printf("\n");
+		}
+	}
+
+
+	printf("------ ------- --------------------------------");
+	for (n = 0; n < ncpu; n++) {
+		printf(" -----");
+	}
+	printf("\n");
+
+	printf("Total %8u %-32.32s", sample_kern_nsample, "in-kernel");
+	for (n = 0; n < ncpu; n++) {
+		printf("%6u", sample_nsample_kern_per_cpu[n]);
+	}
+
+	if (opt_userland) {
+		printf("\n");
+		printf("      %8u %-32.32s", sample_user_nsample, "userland");
+		for (n = 0; n < ncpu; n++) {
+			printf("%6u", sample_nsample_user_per_cpu[n]);
+		}
+	}
+
+	cls_eos();
+}
+
+__dead static void
+tprof_top_usage(void)
+{
+	fprintf(stderr, "%s top [-e name[,scale] [-e ...]]"
+	    " [-i interval] [-c] [-u]\n", getprogname());
+	exit(EXIT_FAILURE);
+}
+
+static int
+parse_event_scale(tprof_param_t *param, const char *str)
+{
+	double d;
+	uint64_t n;
+	char *p;
+
+	if (str[0] == '=') {
+		str++;
+		n = strtoull(str, &p, 0);
+		if (*p != '\0')
+			return -1;
+		param->p_value2 = n;
+		param->p_flags |= TPROF_PARAM_VALUE2_TRIGGERCOUNT;
+	} else {
+		if (strncasecmp("0x", str, 2) == 0)
+			d = strtol(str, &p, 0);
+		else
+			d = strtod(str, &p);
+		if (*p != '\0')
+			return -1;
+		param->p_value2 = 0x100000000ULL / d;
+		param->p_flags |= TPROF_PARAM_VALUE2_SCALE;
+	}
+	return 0;
+}
+
+void
+tprof_top(int argc, char **argv)
+{
+	tprof_param_t params[TPROF_MAXCOUNTERS];
+	struct sigaction sa;
+	struct itimerval it;
+	ssize_t bufsize;
+	u_int i;
+	int ch, ret;
+	char *buf, *tokens[2], *p;
+
+	memset(params, 0, sizeof(params));
+	nevent = 0;
+
+	while ((ch = getopt(argc, argv, "ce:i:u")) != -1) {
+		switch (ch) {
+		case 'c':
+			opt_showcounter = 1;
+			break;
+		case 'e':
+			p = estrdup(optarg);
+			tokens[0] = strtok(p, ",");
+			tokens[1] = strtok(NULL, ",");
+			tprof_event_lookup(tokens[0], &params[nevent]);
+			if (tokens[1] != NULL) {
+				if (parse_event_scale(&params[nevent], tokens[1]) != 0)
+					errx(EXIT_FAILURE, "invalid scale: %s", tokens[1]);
+			}
+			eventname[nevent] = tokens[0];
+			nevent++;
+			if (nevent > __arraycount(params) ||
+			    nevent > ncounters)
+				errx(EXIT_FAILURE, "Too many events. Only a"
+				    " maximum of %d counters can be used.",
+				    ncounters);
+			break;
+		case 'i':
+			top_interval = strtol(optarg, &p, 10);
+			if (*p != '\0' || top_interval <= 0)
+				errx(EXIT_FAILURE, "Bad/invalid interval: %s",
+				    optarg);
+			break;
+		case 'u':
+			opt_userland = 1;
+			break;
+		default:
+			tprof_top_usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 0)
+		tprof_top_usage();
+
+	bufsize = sizeof(tprof_sample_t) * 8192;
+	buf = emalloc(bufsize);
+
+	sample_init();
+
+	if (nevent == 0) {
+		const char *defaultevent;
+
+		switch (tprof_info.ti_ident) {
+		case TPROF_IDENT_INTEL_GENERIC:
+			defaultevent = "unhalted-core-cycles";
+			break;
+		case TPROF_IDENT_AMD_GENERIC:
+			defaultevent = "LsNotHaltedCyc";
+			break;
+		case TPROF_IDENT_ARMV8_GENERIC:
+		case TPROF_IDENT_ARMV7_GENERIC:
+			defaultevent = "CPU_CYCLES";
+			break;
+		default:
+			defaultevent = NULL;
+			break;
+		}
+		if (defaultevent == NULL)
+			errx(EXIT_FAILURE, "cpu not supported");
+
+		tprof_event_lookup(defaultevent, &params[nevent]);
+		eventname[nevent] = defaultevent;
+		nevent++;
+	}
+
+	show_counters_alloc();
+
+	for (i = 0; i < nevent; i++) {
+		params[i].p_counter = i;
+		params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE;
+		if (opt_userland)
+			params[i].p_flags |= TPROF_PARAM_USER;
+		ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, &params[i]);
+		if (ret == -1)
+			err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT: %s",
+			    eventname[i]);
+	}
+
+	tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
+	ret = ioctl(devfd, TPROF_IOC_START, &mask);
+	if (ret == -1)
+		err(EXIT_FAILURE, "TPROF_IOC_START");
+
+
+	sigwinch_handler(0);
+	ksyms = ksymload(&nksyms);
+
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sa.sa_handler = sigwinch_handler;
+	sigaction(SIGWINCH, &sa, NULL);
+
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = 0;
+	sa.sa_handler = sigalrm_handler;
+	sigaction(SIGALRM, &sa, NULL);
+
+	it.it_interval.tv_sec = it.it_value.tv_sec = top_interval;
+	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
+	setitimer(ITIMER_REAL, &it, NULL);
+
+	sample_reset();
+	printf("collecting samples...");
+	fflush(stdout);
+
+	do {
+		/* continue to accumulate tprof_sample until alarm arrives */
+		while (sigalrm == 0) {
+			ssize_t len = read(devfd, buf, bufsize);
+			if (len == -1 && errno != EINTR)
+				err(EXIT_FAILURE, "read");
+			if (len > 0) {
+				tprof_sample_t *s = (tprof_sample_t *)buf;
+				while (s < (tprof_sample_t *)(buf + len)) {
+					sample_collect(s);
+					s++;
+				}
+			}
+		}
+		sigalrm = 0;
+
+		/* update screen */
+		sample_sort();
+		sample_show();
+		fflush(stdout);
+
+		sample_reset();
+	} while (!nontty);
+
+	printf("\n");
+}

Reply via email to