Module Name: src Committed By: ryo Date: Fri Dec 9 01:56:40 UTC 2022
Modified Files: src/usr.sbin/tprof: Makefile tprof.8 tprof_top.c Log Message: mainly fixes around display. - use terminfo - dynamically adjust column widths - mode can be changed while running To generate a diff of this commit: cvs rdiff -u -r1.12 -r1.13 src/usr.sbin/tprof/Makefile cvs rdiff -u -r1.20 -r1.21 src/usr.sbin/tprof/tprof.8 cvs rdiff -u -r1.3 -r1.4 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.12 src/usr.sbin/tprof/Makefile:1.13 --- src/usr.sbin/tprof/Makefile:1.12 Thu Dec 1 00:43:27 2022 +++ src/usr.sbin/tprof/Makefile Fri Dec 9 01:56:40 2022 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.12 2022/12/01 00:43:27 ryo Exp $ +# $NetBSD: Makefile,v 1.13 2022/12/09 01:56:40 ryo Exp $ .PATH: ${.CURDIR}/arch @@ -21,11 +21,15 @@ CPPFLAGS+= -I${NETBSDSRCDIR}/sys/ LDADD+= -lpthread LDADD+= -lm LDADD+= -lelf +LDADD+= -lterminfo LDADD+= -lutil DPADD+= ${LIBPTHREAD} DPADD+= ${LIBM} DPADD+= ${LIBELF} +DPADD+= ${LIBTERMINFO} DPADD+= ${LIBUTIL} +COPTS.tprof_top.c = -Wno-format-nonliteral + .include <bsd.own.mk> .include <bsd.prog.mk> Index: src/usr.sbin/tprof/tprof.8 diff -u src/usr.sbin/tprof/tprof.8:1.20 src/usr.sbin/tprof/tprof.8:1.21 --- src/usr.sbin/tprof/tprof.8:1.20 Fri Dec 9 01:55:46 2022 +++ src/usr.sbin/tprof/tprof.8 Fri Dec 9 01:56:40 2022 @@ -1,4 +1,4 @@ -.\" $NetBSD: tprof.8,v 1.20 2022/12/09 01:55:46 ryo Exp $ +.\" $NetBSD: tprof.8,v 1.21 2022/12/09 01:56:40 ryo Exp $ .\" .\" Copyright (c)2011 YAMAMOTO Takashi, .\" All rights reserved. @@ -166,6 +166,21 @@ show the delta of the event counters. .It Fl u Userland processes are also included in the profiling. .El +.Pp +While +.Nm +.Ar top +is running, it accepts commands from the terminal. +These commands are currently recognized: +.Bl -tag -width XXcommandsX -offset indent +.It Ic a +toggle accumurative mode. +.It Ic q +quit +.Nm . +.It Ic z +clear accumulated data. +.El .El .Sh EXAMPLES The following command profiles the system during 20 seconds and writes the Index: src/usr.sbin/tprof/tprof_top.c diff -u src/usr.sbin/tprof/tprof_top.c:1.3 src/usr.sbin/tprof/tprof_top.c:1.4 --- src/usr.sbin/tprof/tprof_top.c:1.3 Fri Dec 9 01:55:46 2022 +++ src/usr.sbin/tprof/tprof_top.c Fri Dec 9 01:56:40 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: tprof_top.c,v 1.3 2022/12/09 01:55:46 ryo Exp $ */ +/* $NetBSD: tprof_top.c,v 1.4 2022/12/09 01:56:40 ryo Exp $ */ /*- * Copyright (c) 2022 Ryo Shimizu <r...@nerv.org> @@ -28,15 +28,17 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: tprof_top.c,v 1.3 2022/12/09 01:55:46 ryo Exp $"); +__RCSID("$NetBSD: tprof_top.c,v 1.4 2022/12/09 01:56:40 ryo Exp $"); #endif /* not lint */ #include <sys/param.h> #include <sys/types.h> -#include <sys/rbtree.h> #include <sys/ioctl.h> +#include <sys/rbtree.h> +#include <sys/select.h> #include <sys/time.h> +#include <assert.h> #include <err.h> #include <errno.h> #include <fcntl.h> @@ -46,6 +48,8 @@ __RCSID("$NetBSD: tprof_top.c,v 1.3 2022 #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <term.h> +#include <termios.h> #include <unistd.h> #include <util.h> @@ -57,6 +61,9 @@ __RCSID("$NetBSD: tprof_top.c,v 1.3 2022 #define SAMPLE_MODE_INSTANTANEOUS 1 #define SAMPLE_MODE_NUM 2 +#define LINESTR "-------------------------------------------------------------" +#define SYMBOL_LEN 32 /* symbol and event name */ + struct sample_elm { struct rb_node node; uint64_t addr; @@ -80,9 +87,14 @@ static int opt_userland = 0; static int opt_showcounter = 0; /* for display */ +static char *term; static struct winsize win; static int nontty; +static struct termios termios_save; +static bool termios_saved; static long top_interval = 1; +static bool do_redraw; +static u_int nshow; /* for profiling and counting samples */ static sig_atomic_t sigalrm; @@ -95,6 +107,8 @@ static rb_tree_t rb_tree_sample; struct ptrarray sample_list[SAMPLE_MODE_NUM]; static u_int sample_n_kern[SAMPLE_MODE_NUM]; static u_int sample_n_user[SAMPLE_MODE_NUM]; +static u_int sample_event_width = 7; +static u_int *sample_cpu_width; /* [ncpu] */ static uint32_t *sample_n_kern_per_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */ static uint32_t *sample_n_user_per_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */ static uint64_t *sample_n_per_event[SAMPLE_MODE_NUM]; /* [nevent] */ @@ -127,43 +141,145 @@ cycle_event_name(void) return cycleevent; } -/* XXX: use terminfo or curses */ static void -cursor_address(u_int x, u_int y) +reset_cursor_pos(void) { - if (nontty) + int i; + char *p; + + if (nontty || term == NULL) return; - printf("\e[%u;%uH", y - 1, x - 1); + + printf("\r"); + + /* cursor_up * n */ + if ((p = tigetstr("cuu")) != NULL) { + printf("%s", tparm(p, win.ws_row - 1, 0, 0, 0, 0, 0, 0, 0, 0)); + } else if ((p = tigetstr("cuu1")) != NULL) { + for (i = win.ws_row - 1; i > 0; i--) + printf("%s", p); + } } static void -cursor_home(void) +clr_to_eol(void) { - if (nontty) + char *p; + + if (nontty || term == NULL) return; - printf("\e[H"); + + if ((p = tigetstr("el")) != NULL) + printf("%s", p); } +/* newline, and clearing to end of line if needed */ static void -cls_eol(void) +lim_newline(int *lim) { - if (nontty) - return; - printf("\e[K"); + if (*lim >= 1) + clr_to_eol(); + + printf("\n"); + *lim = win.ws_col; } -static void -cls_eos(void) +static int +lim_printf(int *lim, const char *fmt, ...) { - if (nontty) - return; - printf("\e[J"); + va_list ap; + size_t written; + char *p; + + if (*lim <= 0) + return 0; + + p = alloca(*lim + 1); + + va_start(ap, fmt); + vsnprintf(p, *lim + 1, fmt, ap); + va_end(ap); + + written = strlen(p); + if (written == 0) { + *lim = 0; + return 0; + } + + fwrite(p, written, 1, stdout); + *lim -= written; + + return written; } static void sigwinch_handler(int signo) { + char *p; + + win.ws_col = tigetnum("lines"); + win.ws_row = tigetnum("cols"); + nontty = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win); + if (nontty != 0) { + nontty = !isatty(STDOUT_FILENO); + win.ws_col = 65535; + win.ws_row = 65535; + } + + if ((p = getenv("LINES")) != NULL) + win.ws_row = strtoul(p, NULL, 0); + if ((p = getenv("COLUMNS")) != NULL) + win.ws_col = strtoul(p, NULL, 0); + + do_redraw = true; +} + +static void +tty_setup(void) +{ + struct termios termios; + + term = getenv("TERM"); + if (term != NULL) + setupterm(term, 0, NULL); + + sigwinch_handler(0); + + if (tcgetattr(STDOUT_FILENO, &termios_save) == 0) { + termios_saved = true; + + /* stty cbreak */ + termios = termios_save; + termios.c_iflag |= BRKINT|IXON|IMAXBEL; + termios.c_oflag |= OPOST; + termios.c_lflag |= ISIG|IEXTEN; + termios.c_lflag &= ~(ICANON|ECHO); + tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios); + } +} + +static void +tty_restore(void) +{ + if (termios_saved) { + tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios_save); + termios_saved = false; + } +} + +static void +sigtstp_handler(int signo) +{ + tty_restore(); + + signal(SIGWINCH, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + kill(0, SIGTSTP); + nshow = 0; } static void @@ -173,6 +289,30 @@ sigalrm_handler(int signo) } static void +die(int signo) +{ + tty_restore(); + printf("\n"); + + exit(EXIT_SUCCESS); +} + +static void __dead +die_errc(int status, int code, const char *fmt, ...) +{ + va_list ap; + + tty_restore(); + + va_start(ap, fmt); + if (code == 0) + verrx(status, fmt, ap); + else + verrc(status, code, fmt, ap); + va_end(ap); +} + +static void ptrarray_push(struct ptrarray *ptrarray, void *ptr) { int error; @@ -183,7 +323,7 @@ ptrarray_push(struct ptrarray *ptrarray, error = reallocarr(&ptrarray->pa_ptrs, ptrarray->pa_allocnum, sizeof(*ptrarray->pa_ptrs)); if (error != 0) - errc(EXIT_FAILURE, error, "rellocarr failed"); + die_errc(EXIT_FAILURE, error, "rellocarr failed"); } ptrarray->pa_ptrs[ptrarray->pa_inuse++] = ptr; } @@ -234,12 +374,22 @@ static void sample_init(void) { const struct sample_elm *e; - int mode; + int l, mode, n; + u_int size; + char buf[16]; - u_int size = sizeof(struct sample_elm) + + size = sizeof(struct sample_elm) + sizeof(e->num_cpu[0]) * SAMPLE_MODE_NUM * ncpu; sizeof_sample_elm = n_align(size, __alignof(struct sample_elm)); + sample_cpu_width = ecalloc(1, sizeof(*sample_cpu_width) * ncpu); + for (n = 0; n < ncpu; n++) { + sample_cpu_width[n] = 5; + l = snprintf(buf, sizeof(buf), "CPU%d", n); + if (sample_cpu_width[n] < (u_int)l) + sample_cpu_width[n] = l; + } + for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) { sample_n_kern_per_cpu[mode] = ecalloc(1, sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu); @@ -408,32 +558,41 @@ sample_collect(tprof_sample_t *s) } static void -show_tprof_stat(void) +show_tprof_stat(int *lim) { static struct tprof_stat tsbuf[2], *ts0, *ts; static u_int ts_i = 0; - int ret; + static int tprofstat_width[6]; + int ret, l; + char tmpbuf[128]; 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"); + die_errc(EXIT_FAILURE, errno, "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(" "); \ +#define TS_PRINT(idx, label, _m) \ + do { \ + __CTASSERT(idx < __arraycount(tprofstat_width)); \ + lim_printf(lim, "%s", label); \ + l = snprintf(tmpbuf, sizeof(tmpbuf), "%"PRIu64, ts->_m);\ + if (ts->_m != ts0->_m) \ + l += snprintf(tmpbuf + l, sizeof(tmpbuf) - l, \ + "(+%"PRIu64")", ts->_m - ts0->_m); \ + assert(l < (int)sizeof(tmpbuf)); \ + if (tprofstat_width[idx] < l) \ + tprofstat_width[idx] = l; \ + lim_printf(lim, "%-*.*s ", tprofstat_width[idx], \ + tprofstat_width[idx], tmpbuf); \ } 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); + lim_printf(lim, "tprof "); + TS_PRINT(0, "sample:", ts_sample); + TS_PRINT(1, "overflow:", ts_overflow); + TS_PRINT(2, "buf:", ts_buf); + TS_PRINT(3, "emptybuf:", ts_emptybuf); + TS_PRINT(4, "dropbuf:", ts_dropbuf); + TS_PRINT(5, "dropbuf_sample:", ts_dropbuf_sample); } static void @@ -441,7 +600,6 @@ 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])); } @@ -453,7 +611,7 @@ show_counters_alloc(void) } static void -show_counters(void) +show_counters(int *lim) { tprof_counts_t countsbuf; uint64_t *cn[2], *c0, *c; @@ -469,70 +627,128 @@ show_counters(void) countsbuf.c_cpu = n; ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &countsbuf); if (ret == -1) - err(EXIT_FAILURE, "TPROF_IOC_GETCOUNTS"); + die_errc(EXIT_FAILURE, errno, "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); + if (do_redraw) { + lim_printf(lim, "%-22s", "Event counter (delta)"); + for (n = 0; n < ncpu; n++) { + char cpuname[16]; + snprintf(cpuname, sizeof(cpuname), "CPU%u", n); + lim_printf(lim, "%11s", cpuname); + } + lim_newline(lim); + } else { + printf("\n"); } - printf("\n"); for (i = 0; i < nevent; i++) { - printf("%-22.22s", eventname[i]); + lim_printf(lim, "%-22.22s", eventname[i]); for (n = 0; n < ncpu; n++) { - printf("%11"PRIu64, + lim_printf(lim, "%11"PRIu64, c[n * nevent + i] - c0[n * nevent + i]); } - printf("\n"); + lim_newline(lim); } - printf("\n"); + lim_newline(lim); } static void -show_count_per_event(void) +show_count_per_event(int *lim) { u_int i, nsample_total; - int n; + int n, l; + char buf[32]; nsample_total = sample_n_kern[opt_mode] + sample_n_user[opt_mode]; + if (nsample_total == 0) + nsample_total = 1; + + /* calc width in advance */ + for (i = 0; i < nevent; i++) { + l = snprintf(buf, sizeof(buf), "%"PRIu64, + sample_n_per_event[opt_mode][i]); + if (sample_event_width < (u_int)l) + sample_event_width = l; + } + for (i = 0; i < nevent; i++) { + for (n = 0; n < ncpu; n++) { + l = snprintf(buf, sizeof(buf), "%"PRIu64, + sample_n_per_event_cpu[opt_mode][nevent * n + i]); + if (sample_cpu_width[n] < (u_int)l) + sample_cpu_width[n] = l; + } + } + + if (do_redraw) { + lim_printf(lim, " Rate %*s Eventname ", + sample_event_width, "Sample#"); + for (n = 0; n < ncpu; n++) { + snprintf(buf, sizeof(buf), "CPU%d", n); + lim_printf(lim, " %*s", sample_cpu_width[n], buf); + } + lim_newline(lim); + + lim_printf(lim, "------ %*.*s %*.*s", + sample_event_width, sample_event_width, LINESTR, + SYMBOL_LEN, SYMBOL_LEN, LINESTR); + for (n = 0; n < ncpu; n++) { + lim_printf(lim, " %*.*s", + sample_cpu_width[n], sample_cpu_width[n], LINESTR); + } + lim_newline(lim); + } else { + printf("\n\n"); + } for (i = 0; i < nevent; i++) { if (sample_n_per_event[opt_mode][i] >= nsample_total) { - printf("%5.1f%%", sample_n_per_event[opt_mode][i] * - 100.00 / nsample_total); + lim_printf(lim, "%5.1f%%", 100.0 * + sample_n_per_event[opt_mode][i] / nsample_total); } else { - printf("%5.2f%%", sample_n_per_event[opt_mode][i] * - 100.00 / nsample_total); + lim_printf(lim, "%5.2f%%", 100.0 * + sample_n_per_event[opt_mode][i] / nsample_total); } - printf("%8"PRIu64" ", sample_n_per_event[opt_mode][i]); + lim_printf(lim, " %*"PRIu64" ", sample_event_width, + sample_n_per_event[opt_mode][i]); - printf("%-32.32s", eventname[i]); + lim_printf(lim, "%-32.32s", eventname[i]); for (n = 0; n < ncpu; n++) { - printf("%6"PRIu64, + lim_printf(lim, " %*"PRIu64, sample_cpu_width[n], sample_n_per_event_cpu[opt_mode][nevent * n + i]); } - printf("\n"); + lim_newline(lim); } } static void sample_show(void) { - static u_int nshow; - struct sample_elm *e; struct ptrarray *samples; u_int nsample_total; - int i, n, ndisp; + int i, l, lim, n, ndisp; char namebuf[32]; const char *name; + if (nshow++ == 0) { + printf("\n"); + if (!nontty) { + signal(SIGWINCH, sigwinch_handler); + signal(SIGINT, die); + signal(SIGQUIT, die); + signal(SIGTERM, die); + signal(SIGTSTP, sigtstp_handler); + + tty_setup(); + } + } else { + reset_cursor_pos(); + } + int margin_lines = 7; margin_lines += 3 + nevent; /* show_counter_per_event() */ @@ -552,62 +768,49 @@ sample_show(void) if (!nontty && ndisp > (win.ws_row - margin_lines)) ndisp = win.ws_row - margin_lines; - cursor_home(); - if (nshow++ == 0) - cls_eos(); - - + lim = win.ws_col; if (opt_mode == SAMPLE_MODE_ACCUMULATIVE) - printf("[Accumulative mode] "); - - show_tprof_stat(); - cls_eol(); + lim_printf(&lim, "[Accumulative mode] "); + show_tprof_stat(&lim); - show_timestamp(); - printf("\n"); - printf("\n"); + if (lim >= 16) { + l = win.ws_col - lim; + if (!nontty) { + clr_to_eol(); + for (; l <= win.ws_col - 17; l = ((l + 8) & -8)) + printf("\t"); + } + show_timestamp(); + } + lim_newline(&lim); + lim_newline(&lim); if (opt_showcounter) - show_counters(); + show_counters(&lim); - 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 { + show_count_per_event(&lim); + lim_newline(&lim); + + if (do_redraw) { + lim_printf(&lim, " Rate %*s Symbol ", + sample_event_width, "Sample#"); + for (n = 0; n < ncpu; n++) { snprintf(namebuf, sizeof(namebuf), "CPU%d", n); + lim_printf(&lim, " %*s", sample_cpu_width[n], namebuf); } - printf(" %5s", namebuf); - } - printf("\n"); - printf("------ ------- --------------------------------"); - for (n = 0; n < ncpu; n++) { - printf(" -----"); - } - printf("\n"); + lim_newline(&lim); - 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); + lim_printf(&lim, "------ %*.*s %*.*s", + sample_event_width, sample_event_width, LINESTR, + SYMBOL_LEN, SYMBOL_LEN, LINESTR); + for (n = 0; n < ncpu; n++) { + lim_printf(&lim, " %*.*s", sample_cpu_width[n], + sample_cpu_width[n], LINESTR); } - printf(" %5s", namebuf); - } - printf("\n"); - printf("------ ------- --------------------------------"); - for (n = 0; n < ncpu; n++) { - printf(" -----"); + lim_newline(&lim); + } else { + printf("\n\n"); } - printf("\n"); for (i = 0; i < ndisp; i++) { e = (struct sample_elm *)samples->pa_ptrs[i]; @@ -634,58 +837,72 @@ sample_show(void) nsample_total = 1; if (e->num[opt_mode] >= nsample_total) { - printf("%5.1f%%", - e->num[opt_mode] * 100.00 / nsample_total); + lim_printf(&lim, "%5.1f%%", 100.0 * + e->num[opt_mode] / nsample_total); } else { - printf("%5.2f%%", - e->num[opt_mode] * 100.00 / nsample_total); + lim_printf(&lim, "%5.2f%%", 100.0 * + e->num[opt_mode] / nsample_total); } - printf("%8u %-32.32s", e->num[opt_mode], name); + lim_printf(&lim, " %*u %-32.32s", sample_event_width, + e->num[opt_mode], name); for (n = 0; n < ncpu; n++) { if (SAMPLE_ELM_NUM_CPU(e, opt_mode)[n] == 0) { - printf(" ."); + lim_printf(&lim, " %*s", sample_cpu_width[n], + "."); } else { - printf("%6u", + lim_printf(&lim, " %*u", sample_cpu_width[n], SAMPLE_ELM_NUM_CPU(e, opt_mode)[n]); } } - printf("\n"); + lim_newline(&lim); } if ((u_int)ndisp != samples->pa_inuse) { - printf(" : : (more %zu symbols omitted)\n", - samples->pa_inuse - ndisp); - } else { + lim_printf(&lim, " : %*s (more %zu symbols omitted)", + sample_event_width, ":", samples->pa_inuse - ndisp); + lim_newline(&lim); + } else if (!nontty) { for (i = ndisp; i <= win.ws_row - margin_lines; i++) { printf("~"); - cls_eol(); - printf("\n"); + lim_newline(&lim); } } - - printf("------ ------- --------------------------------"); - for (n = 0; n < ncpu; n++) { - printf(" -----"); + if (do_redraw) { + lim_printf(&lim, "------ %*.*s %*.*s", + sample_event_width, sample_event_width, LINESTR, + SYMBOL_LEN, SYMBOL_LEN, LINESTR); + for (n = 0; n < ncpu; n++) { + lim_printf(&lim, " %*.*s", + sample_cpu_width[n], sample_cpu_width[n], LINESTR); + } + lim_newline(&lim); + } else { + printf("\n"); } - printf("\n"); - printf("Total %8u %-32.32s", sample_n_kern[opt_mode], "in-kernel"); + lim_printf(&lim, "Total %*u %-32.32s", + sample_event_width, sample_n_kern[opt_mode], "in-kernel"); for (n = 0; n < ncpu; n++) { - printf("%6u", sample_n_kern_per_cpu[opt_mode][n]); + lim_printf(&lim, " %*u", sample_cpu_width[n], + sample_n_kern_per_cpu[opt_mode][n]); } if (opt_userland) { - printf("\n"); - printf(" %8u %-32.32s", - sample_n_user[opt_mode], "userland"); + lim_newline(&lim); + lim_printf(&lim, " %*u %-32.32s", + sample_event_width, sample_n_user[opt_mode], "userland"); for (n = 0; n < ncpu; n++) { - printf("%6u", sample_n_user_per_cpu[opt_mode][n]); + lim_printf(&lim, " %*u", sample_cpu_width[n], + sample_n_user_per_cpu[opt_mode][n]); } } - cls_eos(); + if (nontty) + printf("\n"); + else + clr_to_eol(); } __dead static void @@ -727,17 +944,17 @@ void tprof_top(int argc, char **argv) { tprof_param_t params[TPROF_MAXCOUNTERS]; - struct sigaction sa; struct itimerval it; - ssize_t tprof_bufsize; + ssize_t tprof_bufsize, len; u_int i; int ch, ret; char *tprof_buf, *tokens[2], *p; + bool noinput = false; memset(params, 0, sizeof(params)); nevent = 0; - while ((ch = getopt(argc, argv, "ace:i:u")) != -1) { + while ((ch = getopt(argc, argv, "ace:i:L:u")) != -1) { switch (ch) { case 'a': opt_mode = SAMPLE_MODE_ACCUMULATIVE; @@ -753,23 +970,23 @@ tprof_top(int argc, char **argv) if (tokens[1] != NULL) { if (parse_event_scale(¶ms[nevent], tokens[1]) != 0) { - errx(EXIT_FAILURE, "invalid scale: %s", - tokens[1]); + die_errc(EXIT_FAILURE, 0, + "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); + die_errc(EXIT_FAILURE, 0, + "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); + die_errc(EXIT_FAILURE, 0, + "Bad/invalid interval: %s", optarg); break; case 'u': opt_userland = 1; @@ -787,7 +1004,7 @@ tprof_top(int argc, char **argv) if (nevent == 0) { const char *defaultevent = cycle_event_name(); if (defaultevent == NULL) - errx(EXIT_FAILURE, "cpu not supported"); + die_errc(EXIT_FAILURE, 0, "cpu not supported"); tprof_event_lookup(defaultevent, ¶ms[nevent]); eventname[nevent] = defaultevent; @@ -804,28 +1021,18 @@ tprof_top(int argc, char **argv) params[i].p_flags |= TPROF_PARAM_USER; ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, ¶ms[i]); if (ret == -1) - err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT: %s", - eventname[i]); + die_errc(EXIT_FAILURE, errno, + "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"); - + die_errc(EXIT_FAILURE, errno, "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); + signal(SIGALRM, sigalrm_handler); it.it_interval.tv_sec = it.it_value.tv_sec = top_interval; it.it_interval.tv_usec = it.it_value.tv_usec = 0; @@ -838,15 +1045,65 @@ tprof_top(int argc, char **argv) tprof_bufsize = sizeof(tprof_sample_t) * 8192; tprof_buf = emalloc(tprof_bufsize); do { - /* continue to accumulate tprof_sample until alarm arrives */ - while (sigalrm == 0) { - ssize_t len = read(devfd, tprof_buf, tprof_bufsize); - if (len == -1 && errno != EINTR) - err(EXIT_FAILURE, "read"); - if (len > 0) { - tprof_sample_t *s = (tprof_sample_t *)tprof_buf; - while (s < (tprof_sample_t *)(tprof_buf + len)) - sample_collect(s++); + bool force_update = false; + + for (;;) { + fd_set r; + int nfound; + char c; + + FD_ZERO(&r); + if (!noinput) + FD_SET(STDIN_FILENO, &r); + FD_SET(devfd, &r); + nfound = select(devfd + 1, &r, NULL, NULL, NULL); + if (nfound == -1) { + if (errno == EINTR) + break; + die_errc(EXIT_FAILURE, errno, "select"); + } + + if (FD_ISSET(STDIN_FILENO, &r)) { + len = read(STDIN_FILENO, &c, 1); + if (len <= 0) { + noinput = true; + continue; + } + switch (c) { + case 0x0c: /* ^L */ + do_redraw = true; + break; + case 'a': + /* toggle mode */ + opt_mode = (opt_mode + 1) % + SAMPLE_MODE_NUM; + break; + case 'q': + goto done; + case 'z': + sample_reset(true); + break; + default: + printf("[%02x]", c); + fflush(stdout); + } + force_update = true; + continue; + } + if (sigalrm != 0 || force_update) + break; + + if (FD_ISSET(devfd, &r)) { + len = read(devfd, tprof_buf, tprof_bufsize); + if (len == -1 && errno != EINTR) + die_errc(EXIT_FAILURE, errno, "read"); + if (len > 0) { + tprof_sample_t *s = + (tprof_sample_t *)tprof_buf; + while (s < + (tprof_sample_t *)(tprof_buf + len)) + sample_collect(s++); + } } } sigalrm = 0; @@ -854,9 +1111,14 @@ tprof_top(int argc, char **argv) /* update screen */ sample_show(); fflush(stdout); + do_redraw = false; + if (force_update) + continue; sample_reset(false); + } while (!nontty); - printf("\n"); + done: + die(0); }