summaryrefslogtreecommitdiffstats
path: root/usr.sbin/gstat/gstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/gstat/gstat.c')
-rw-r--r--usr.sbin/gstat/gstat.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/usr.sbin/gstat/gstat.c b/usr.sbin/gstat/gstat.c
new file mode 100644
index 0000000..8be3775
--- /dev/null
+++ b/usr.sbin/gstat/gstat.c
@@ -0,0 +1,455 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * 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.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/devicestat.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <curses.h>
+#include <devstat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <histedit.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static int flag_a, flag_b, flag_c, flag_d, flag_o, flag_p;
+static int flag_I = 1000000;
+
+#define PRINTMSG(...) do { \
+ if (flag_b && !loop) \
+ printf(__VA_ARGS__); \
+ else if (!flag_b) \
+ printw(__VA_ARGS__); \
+ } while(0)
+
+static void usage(void);
+
+static const char*
+el_prompt(void)
+{
+
+ return ("Filter: ");
+}
+
+int
+main(int argc, char **argv)
+{
+ int error, i, quit;
+ int curx, cury, maxx, maxy, line_len, loop, max_flen;
+ struct devstat *gsp, *gsq;
+ void *sp, *sq;
+ double dt;
+ struct timespec tp, tq;
+ struct gmesh gmp;
+ struct gprovider *pp;
+ struct gconsumer *cp;
+ struct gident *gid;
+ regex_t f_re, tmp_f_re;
+ short cf, cb;
+ char *p;
+ char f_s[100], pf_s[100], tmp_f_s[100];
+ const char *line;
+ long double ld[13];
+ uint64_t u64;
+ EditLine *el;
+ History *hist;
+ HistEvent hist_ev;
+
+ hist = NULL;
+ el = NULL;
+ maxx = -1;
+ curx = -1;
+ loop = 1;
+ /* Turn on batch mode if output is not tty. */
+ if (!isatty(fileno(stdout)))
+ flag_b = 1;
+
+ f_s[0] = '\0';
+ while ((i = getopt(argc, argv, "abdcf:I:op")) != -1) {
+ switch (i) {
+ case 'a':
+ flag_a = 1;
+ break;
+ case 'b':
+ flag_b = 1;
+ break;
+ case 'c':
+ flag_c = 1;
+ break;
+ case 'd':
+ flag_d = 1;
+ break;
+ case 'f':
+ if (strlen(optarg) > sizeof(f_s) - 1)
+ errx(EX_USAGE, "Filter string too long");
+ if (regcomp(&f_re, optarg, REG_EXTENDED) != 0)
+ errx(EX_USAGE,
+ "Invalid filter - see re_format(7)");
+ strlcpy(f_s, optarg, sizeof(f_s));
+ break;
+ case 'o':
+ flag_o = 1;
+ break;
+ case 'I':
+ p = NULL;
+ i = strtoul(optarg, &p, 0);
+ if (p == optarg || errno == EINVAL ||
+ errno == ERANGE) {
+ errx(1, "Invalid argument to -I");
+ } else if (!strcmp(p, "s"))
+ i *= 1000000;
+ else if (!strcmp(p, "ms"))
+ i *= 1000;
+ else if (!strcmp(p, "us"))
+ i *= 1;
+ flag_I = i;
+ break;
+ case 'p':
+ flag_p = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0)
+ usage();
+
+ i = geom_gettree(&gmp);
+ if (i != 0)
+ err(1, "geom_gettree = %d", i);
+ error = geom_stats_open();
+ if (error)
+ err(1, "geom_stats_open()");
+ sq = NULL;
+ sq = geom_stats_snapshot_get();
+ if (sq == NULL)
+ err(1, "geom_stats_snapshot()");
+ if (!flag_b) {
+ /* Setup curses */
+ initscr();
+ start_color();
+ use_default_colors();
+ pair_content(0, &cf, &cb);
+ init_pair(1, COLOR_GREEN, cb);
+ init_pair(2, COLOR_MAGENTA, cb);
+ init_pair(3, COLOR_RED, cb);
+ cbreak();
+ noecho();
+ nonl();
+ nodelay(stdscr, 1);
+ intrflush(stdscr, FALSE);
+ keypad(stdscr, TRUE);
+ /* Setup libedit */
+ hist = history_init();
+ if (hist == NULL)
+ err(EX_SOFTWARE, "history_init()");
+ history(hist, &hist_ev, H_SETSIZE, 100);
+ el = el_init("gstat", stdin, stdout, stderr);
+ if (el == NULL)
+ err(EX_SOFTWARE, "el_init");
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_SIGNAL, 1);
+ el_set(el, EL_HIST, history, hist);
+ el_set(el, EL_PROMPT, el_prompt);
+ if (f_s[0] != '\0')
+ history(hist, &hist_ev, H_ENTER, f_s);
+ }
+ geom_stats_snapshot_timestamp(sq, &tq);
+ for (quit = 0; !quit;) {
+ sp = geom_stats_snapshot_get();
+ if (sp == NULL)
+ err(1, "geom_stats_snapshot()");
+ geom_stats_snapshot_timestamp(sp, &tp);
+ dt = tp.tv_sec - tq.tv_sec;
+ dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9;
+ tq = tp;
+
+ geom_stats_snapshot_reset(sp);
+ geom_stats_snapshot_reset(sq);
+ move(0,0);
+ PRINTMSG("dT: %5.3fs w: %.3fs", dt, (float)flag_I / 1000000);
+ if (f_s[0] != '\0') {
+ PRINTMSG(" filter: ");
+ if (!flag_b) {
+ getyx(stdscr, cury, curx);
+ getmaxyx(stdscr, maxy, maxx);
+ }
+ strlcpy(pf_s, f_s, sizeof(pf_s));
+ max_flen = maxx - curx - 1;
+ if ((int)strlen(f_s) > max_flen && max_flen >= 0) {
+ if (max_flen > 3)
+ pf_s[max_flen - 3] = '.';
+ if (max_flen > 2)
+ pf_s[max_flen - 2] = '.';
+ if (max_flen > 1)
+ pf_s[max_flen - 1] = '.';
+ pf_s[max_flen] = '\0';
+ }
+ PRINTMSG("%s", pf_s);
+ }
+ PRINTMSG("\n");
+ PRINTMSG(" L(q) ops/s ");
+ PRINTMSG(" r/s kBps ms/r ");
+ PRINTMSG(" w/s kBps ms/w ");
+ if (flag_d)
+ PRINTMSG(" d/s kBps ms/d ");
+ if (flag_o)
+ PRINTMSG(" o/s ms/o ");
+ PRINTMSG("%%busy Name\n");
+ for (;;) {
+ gsp = geom_stats_snapshot_next(sp);
+ gsq = geom_stats_snapshot_next(sq);
+ if (gsp == NULL || gsq == NULL)
+ break;
+ if (gsp->id == NULL)
+ continue;
+ gid = geom_lookupid(&gmp, gsp->id);
+ if (gid == NULL) {
+ geom_deletetree(&gmp);
+ i = geom_gettree(&gmp);
+ if (i != 0)
+ err(1, "geom_gettree = %d", i);
+ gid = geom_lookupid(&gmp, gsp->id);
+ }
+ if (gid == NULL)
+ continue;
+ if (gid->lg_what == ISCONSUMER && !flag_c)
+ continue;
+ if (flag_p && gid->lg_what == ISPROVIDER &&
+ ((struct gprovider *)(gid->lg_ptr))->lg_geom->lg_rank != 1)
+ continue;
+ /* Do not print past end of window */
+ if (!flag_b) {
+ getyx(stdscr, cury, curx);
+ if (curx > 0)
+ continue;
+ }
+ if ((gid->lg_what == ISPROVIDER
+ || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') {
+ pp = gid->lg_ptr;
+ if ((regexec(&f_re, pp->lg_name, 0, NULL, 0)
+ != 0))
+ continue;
+ }
+ if (gsp->sequence0 != gsp->sequence1) {
+ PRINTMSG("*\n");
+ continue;
+ }
+ devstat_compute_statistics(gsp, gsq, dt,
+ DSM_QUEUE_LENGTH, &u64,
+ DSM_TRANSFERS_PER_SECOND, &ld[0],
+
+ DSM_TRANSFERS_PER_SECOND_READ, &ld[1],
+ DSM_MB_PER_SECOND_READ, &ld[2],
+ DSM_MS_PER_TRANSACTION_READ, &ld[3],
+
+ DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4],
+ DSM_MB_PER_SECOND_WRITE, &ld[5],
+ DSM_MS_PER_TRANSACTION_WRITE, &ld[6],
+
+ DSM_BUSY_PCT, &ld[7],
+
+ DSM_TRANSFERS_PER_SECOND_FREE, &ld[8],
+ DSM_MB_PER_SECOND_FREE, &ld[9],
+ DSM_MS_PER_TRANSACTION_FREE, &ld[10],
+
+ DSM_TRANSFERS_PER_SECOND_OTHER, &ld[11],
+ DSM_MS_PER_TRANSACTION_OTHER, &ld[12],
+
+ DSM_NONE);
+
+ if (flag_a && ld[7] < 0.1) {
+ *gsq = *gsp;
+ continue;
+ }
+
+ PRINTMSG(" %4ju", (uintmax_t)u64);
+ PRINTMSG(" %6.0f", (double)ld[0]);
+ PRINTMSG(" %6.0f", (double)ld[1]);
+ PRINTMSG(" %6.0f", (double)ld[2] * 1024);
+ if (ld[3] > 1e3)
+ PRINTMSG(" %6.0f", (double)ld[3]);
+ else
+ PRINTMSG(" %6.1f", (double)ld[3]);
+ PRINTMSG(" %6.0f", (double)ld[4]);
+ PRINTMSG(" %6.0f", (double)ld[5] * 1024);
+ if (ld[6] > 1e3)
+ PRINTMSG(" %6.0f", (double)ld[6]);
+ else
+ PRINTMSG(" %6.1f", (double)ld[6]);
+
+ if (flag_d) {
+ PRINTMSG(" %6.0f", (double)ld[8]);
+ PRINTMSG(" %6.0f", (double)ld[9] * 1024);
+ if (ld[10] > 1e3)
+ PRINTMSG(" %6.0f", (double)ld[10]);
+ else
+ PRINTMSG(" %6.1f", (double)ld[10]);
+ }
+
+ if (flag_o) {
+ PRINTMSG(" %6.0f", (double)ld[11]);
+ if (ld[12] > 1e3)
+ PRINTMSG(" %6.0f", (double)ld[12]);
+ else
+ PRINTMSG(" %6.1f", (double)ld[12]);
+ }
+
+ if (ld[7] > 80)
+ i = 3;
+ else if (ld[7] > 50)
+ i = 2;
+ else
+ i = 1;
+ if (!flag_b)
+ attron(COLOR_PAIR(i));
+ PRINTMSG(" %6.1lf", (double)ld[7]);
+ if (!flag_b) {
+ attroff(COLOR_PAIR(i));
+ PRINTMSG("|");
+ } else
+ PRINTMSG(" ");
+ if (gid == NULL) {
+ PRINTMSG(" ??");
+ } else if (gid->lg_what == ISPROVIDER) {
+ pp = gid->lg_ptr;
+ PRINTMSG(" %s", pp->lg_name);
+ } else if (gid->lg_what == ISCONSUMER) {
+ cp = gid->lg_ptr;
+ PRINTMSG(" %s/%s/%s",
+ cp->lg_geom->lg_class->lg_name,
+ cp->lg_geom->lg_name,
+ cp->lg_provider->lg_name);
+ }
+ if (!flag_b)
+ clrtoeol();
+ PRINTMSG("\n");
+ *gsq = *gsp;
+ }
+ geom_stats_snapshot_free(sp);
+ if (flag_b) {
+ /* We loop extra to make sure we get the information. */
+ if (!loop)
+ break;
+ loop = 0;
+ usleep(flag_I);
+ continue;
+ }
+ getyx(stdscr, cury, curx);
+ getmaxyx(stdscr, maxy, maxx);
+ clrtobot();
+ if (maxy - 1 <= cury)
+ move(maxy - 1, 0);
+ refresh();
+ usleep(flag_I);
+ while((i = getch()) != ERR) {
+ switch (i) {
+ case '>':
+ flag_I *= 2;
+ break;
+ case '<':
+ flag_I /= 2;
+ if (flag_I < 1000)
+ flag_I = 1000;
+ break;
+ case 'c':
+ flag_c = !flag_c;
+ break;
+ case 'f':
+ move(0,0);
+ clrtoeol();
+ refresh();
+ line = el_gets(el, &line_len);
+ if (line == NULL)
+ err(1, "el_gets");
+ if (line_len > 1)
+ history(hist, &hist_ev, H_ENTER, line);
+ strlcpy(tmp_f_s, line, sizeof(f_s));
+ if ((p = strchr(tmp_f_s, '\n')) != NULL)
+ *p = '\0';
+ /*
+ * We have to clear since we messed up
+ * curses idea of the screen by using
+ * libedit.
+ */
+ clear();
+ refresh();
+ if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED)
+ != 0) {
+ move(0, 0);
+ printw("Invalid filter");
+ refresh();
+ sleep(1);
+ } else {
+ strlcpy(f_s, tmp_f_s, sizeof(f_s));
+ f_re = tmp_f_re;
+ }
+ break;
+ case 'F':
+ f_s[0] = '\0';
+ break;
+ case 'q':
+ quit = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (!flag_b) {
+ endwin();
+ el_end(el);
+ }
+ exit(EX_OK);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: gstat [-abcdp] [-f filter] [-I interval]\n");
+ exit(EX_USAGE);
+ /* NOTREACHED */
+}
OpenPOWER on IntegriCloud