1112444Sphk/*-
2112444Sphk * Copyright (c) 2003 Poul-Henning Kamp
3112444Sphk * All rights reserved.
4112444Sphk *
5112444Sphk * Redistribution and use in source and binary forms, with or without
6112444Sphk * modification, are permitted provided that the following conditions
7112444Sphk * are met:
8112444Sphk * 1. Redistributions of source code must retain the above copyright
9112444Sphk *    notice, this list of conditions and the following disclaimer.
10112444Sphk * 2. Redistributions in binary form must reproduce the above copyright
11112444Sphk *    notice, this list of conditions and the following disclaimer in the
12112444Sphk *    documentation and/or other materials provided with the distribution.
13112444Sphk * 3. The names of the authors may not be used to endorse or promote
14112444Sphk *    products derived from this software without specific prior written
15112444Sphk *    permission.
16112444Sphk *
17112444Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18112444Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19112444Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20112444Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21112444Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22112444Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23112444Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24112444Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25112444Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26112444Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27112444Sphk * SUCH DAMAGE.
28112444Sphk *
29112444Sphk * $FreeBSD$
30112444Sphk */
31112444Sphk
32112444Sphk
33158771Ssimon#include <sys/devicestat.h>
34158771Ssimon#include <sys/mman.h>
35158771Ssimon#include <sys/resource.h>
36158771Ssimon#include <sys/time.h>
37158771Ssimon
38158771Ssimon#include <curses.h>
39158771Ssimon#include <devstat.h>
40158771Ssimon#include <err.h>
41158771Ssimon#include <errno.h>
42158771Ssimon#include <fcntl.h>
43158771Ssimon#include <histedit.h>
44158771Ssimon#include <libgeom.h>
45158771Ssimon#include <paths.h>
46158771Ssimon#include <regex.h>
47158771Ssimon#include <stdint.h>
48112444Sphk#include <stdio.h>
49112444Sphk#include <stdlib.h>
50112444Sphk#include <string.h>
51158771Ssimon#include <sysexits.h>
52112444Sphk#include <unistd.h>
53112444Sphk
54268791Sdelphijstatic int flag_a, flag_b, flag_c, flag_d, flag_o, flag_p;
55168016Sdesstatic int flag_I = 1000000;
56112444Sphk
57183665Slulf#define PRINTMSG(...) do {						\
58183665Slulf		if (flag_b && !loop)					\
59183665Slulf			printf(__VA_ARGS__);				\
60183665Slulf		else if (!flag_b)					\
61183665Slulf			printw(__VA_ARGS__);				\
62183665Slulf	} while(0)
63183665Slulf
64112488Skeramidastatic void usage(void);
65112488Skeramida
66158771Ssimonstatic const char*
67158771Ssimonel_prompt(void)
68158771Ssimon{
69158771Ssimon
70158771Ssimon	return ("Filter: ");
71158771Ssimon}
72158771Ssimon
73112444Sphkint
74112444Sphkmain(int argc, char **argv)
75112444Sphk{
76112444Sphk	int error, i, quit;
77183665Slulf	int curx, cury, maxx, maxy, line_len, loop, max_flen;
78112444Sphk	struct devstat *gsp, *gsq;
79112444Sphk	void *sp, *sq;
80112444Sphk	double dt;
81112444Sphk	struct timespec tp, tq;
82112444Sphk	struct gmesh gmp;
83112444Sphk	struct gprovider *pp;
84129302Sstefanf	struct gconsumer *cp;
85112444Sphk	struct gident *gid;
86158771Ssimon	regex_t f_re, tmp_f_re;
87112444Sphk	short cf, cb;
88112444Sphk	char *p;
89158771Ssimon	char f_s[100], pf_s[100], tmp_f_s[100];
90158771Ssimon	const char *line;
91266610Smav	long double ld[13];
92112444Sphk	uint64_t u64;
93158771Ssimon	EditLine *el;
94158771Ssimon	History *hist;
95158771Ssimon	HistEvent hist_ev;
96112444Sphk
97183665Slulf	hist = NULL;
98183665Slulf	el = NULL;
99183665Slulf	maxx = -1;
100183665Slulf	curx = -1;
101183665Slulf	loop = 1;
102189739Smaxim	/* Turn on batch mode if output is not tty. */
103189739Smaxim	if (!isatty(fileno(stdout)))
104189739Smaxim		flag_b = 1;
105189739Smaxim
106158771Ssimon	f_s[0] = '\0';
107268791Sdelphij	while ((i = getopt(argc, argv, "abdcf:I:op")) != -1) {
108112444Sphk		switch (i) {
109136354Sle		case 'a':
110136354Sle			flag_a = 1;
111136354Sle			break;
112183665Slulf		case 'b':
113183665Slulf			flag_b = 1;
114183665Slulf			break;
115112444Sphk		case 'c':
116112491Skeramida			flag_c = 1;
117112444Sphk			break;
118125844Sphk		case 'd':
119125844Sphk			flag_d = 1;
120125844Sphk			break;
121158771Ssimon		case 'f':
122158771Ssimon			if (strlen(optarg) > sizeof(f_s) - 1)
123158771Ssimon				errx(EX_USAGE, "Filter string too long");
124158771Ssimon			if (regcomp(&f_re, optarg, REG_EXTENDED) != 0)
125158771Ssimon				errx(EX_USAGE,
126158771Ssimon				    "Invalid filter - see re_format(7)");
127288205Sdelphij			strlcpy(f_s, optarg, sizeof(f_s));
128158771Ssimon			break;
129266610Smav		case 'o':
130266610Smav			flag_o = 1;
131266610Smav			break;
132112444Sphk		case 'I':
133112444Sphk			p = NULL;
134112444Sphk			i = strtoul(optarg, &p, 0);
135112487Skeramida			if (p == optarg || errno == EINVAL ||
136112487Skeramida			    errno == ERANGE) {
137112444Sphk				errx(1, "Invalid argument to -I");
138112444Sphk			} else if (!strcmp(p, "s"))
139112444Sphk				i *= 1000000;
140112444Sphk			else if (!strcmp(p, "ms"))
141112444Sphk				i *= 1000;
142112444Sphk			else if (!strcmp(p, "us"))
143112444Sphk				i *= 1;
144112444Sphk			flag_I = i;
145112444Sphk			break;
146268791Sdelphij		case 'p':
147268791Sdelphij			flag_p = 1;
148268791Sdelphij			break;
149112444Sphk		case '?':
150112444Sphk		default:
151112488Skeramida			usage();
152112444Sphk		}
153112444Sphk	}
154112444Sphk	argc -= optind;
155112444Sphk	argv += optind;
156112444Sphk	if (argc != 0)
157112488Skeramida		usage();
158112444Sphk
159112444Sphk	i = geom_gettree(&gmp);
160112444Sphk	if (i != 0)
161112444Sphk		err(1, "geom_gettree = %d", i);
162112444Sphk	error = geom_stats_open();
163112444Sphk	if (error)
164112444Sphk		err(1, "geom_stats_open()");
165112444Sphk	sq = NULL;
166112444Sphk	sq = geom_stats_snapshot_get();
167112444Sphk	if (sq == NULL)
168112444Sphk		err(1, "geom_stats_snapshot()");
169183665Slulf	if (!flag_b) {
170183665Slulf		/* Setup curses */
171183665Slulf		initscr();
172183665Slulf		start_color();
173183665Slulf		use_default_colors();
174183665Slulf		pair_content(0, &cf, &cb);
175183665Slulf		init_pair(1, COLOR_GREEN, cb);
176183665Slulf		init_pair(2, COLOR_MAGENTA, cb);
177183665Slulf		init_pair(3, COLOR_RED, cb);
178183665Slulf		cbreak();
179183665Slulf		noecho();
180183665Slulf		nonl();
181183665Slulf		nodelay(stdscr, 1);
182183665Slulf		intrflush(stdscr, FALSE);
183183665Slulf		keypad(stdscr, TRUE);
184183665Slulf		/* Setup libedit */
185183665Slulf		hist = history_init();
186183665Slulf		if (hist == NULL)
187183665Slulf			err(EX_SOFTWARE, "history_init()");
188183665Slulf		history(hist, &hist_ev, H_SETSIZE, 100);
189183665Slulf		el = el_init("gstat", stdin, stdout, stderr);
190183665Slulf		if (el == NULL)
191183665Slulf			err(EX_SOFTWARE, "el_init");
192183665Slulf		el_set(el, EL_EDITOR, "emacs");
193183665Slulf		el_set(el, EL_SIGNAL, 1);
194183665Slulf		el_set(el, EL_HIST, history, hist);
195183665Slulf		el_set(el, EL_PROMPT, el_prompt);
196183665Slulf		if (f_s[0] != '\0')
197183665Slulf			history(hist, &hist_ev, H_ENTER, f_s);
198183665Slulf	}
199112444Sphk	geom_stats_snapshot_timestamp(sq, &tq);
200112444Sphk	for (quit = 0; !quit;) {
201112444Sphk		sp = geom_stats_snapshot_get();
202112444Sphk		if (sp == NULL)
203112444Sphk			err(1, "geom_stats_snapshot()");
204112444Sphk		geom_stats_snapshot_timestamp(sp, &tp);
205112444Sphk		dt = tp.tv_sec - tq.tv_sec;
206112444Sphk		dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9;
207112444Sphk		tq = tp;
208112444Sphk
209112444Sphk		geom_stats_snapshot_reset(sp);
210112444Sphk		geom_stats_snapshot_reset(sq);
211112444Sphk		move(0,0);
212183665Slulf		PRINTMSG("dT: %5.3fs  w: %.3fs", dt, (float)flag_I / 1000000);
213158771Ssimon		if (f_s[0] != '\0') {
214183665Slulf			PRINTMSG("  filter: ");
215183665Slulf			if (!flag_b) {
216183665Slulf				getyx(stdscr, cury, curx);
217183665Slulf				getmaxyx(stdscr, maxy, maxx);
218183665Slulf			}
219288205Sdelphij			strlcpy(pf_s, f_s, sizeof(pf_s));
220158771Ssimon			max_flen = maxx - curx - 1;
221158771Ssimon			if ((int)strlen(f_s) > max_flen && max_flen >= 0) {
222158771Ssimon				if (max_flen > 3)
223158771Ssimon					pf_s[max_flen - 3] = '.';
224158771Ssimon				if (max_flen > 2)
225158771Ssimon					pf_s[max_flen - 2] = '.';
226158771Ssimon				if (max_flen > 1)
227158771Ssimon					pf_s[max_flen - 1] = '.';
228158771Ssimon				pf_s[max_flen] = '\0';
229158771Ssimon			}
230183665Slulf			PRINTMSG("%s", pf_s);
231158771Ssimon		}
232183665Slulf		PRINTMSG("\n");
233183665Slulf		PRINTMSG(" L(q)  ops/s   ");
234183665Slulf		PRINTMSG(" r/s   kBps   ms/r   ");
235183665Slulf		PRINTMSG(" w/s   kBps   ms/w   ");
236125844Sphk		if (flag_d)
237183665Slulf			PRINTMSG(" d/s   kBps   ms/d   ");
238266610Smav		if (flag_o)
239266610Smav			PRINTMSG(" o/s   ms/o   ");
240183665Slulf		PRINTMSG("%%busy Name\n");
241112444Sphk		for (;;) {
242112444Sphk			gsp = geom_stats_snapshot_next(sp);
243112444Sphk			gsq = geom_stats_snapshot_next(sq);
244112444Sphk			if (gsp == NULL || gsq == NULL)
245112444Sphk				break;
246112444Sphk			if (gsp->id == NULL)
247112444Sphk				continue;
248112444Sphk			gid = geom_lookupid(&gmp, gsp->id);
249112444Sphk			if (gid == NULL) {
250112444Sphk				geom_deletetree(&gmp);
251112444Sphk				i = geom_gettree(&gmp);
252112444Sphk				if (i != 0)
253112444Sphk					err(1, "geom_gettree = %d", i);
254112444Sphk				gid = geom_lookupid(&gmp, gsp->id);
255112444Sphk			}
256112444Sphk			if (gid == NULL)
257112444Sphk				continue;
258158771Ssimon			if (gid->lg_what == ISCONSUMER && !flag_c)
259112444Sphk				continue;
260268791Sdelphij			if (flag_p && gid->lg_what == ISPROVIDER &&
261268791Sdelphij			   ((struct gprovider *)(gid->lg_ptr))->lg_geom->lg_rank != 1)
262268791Sdelphij				continue;
263158771Ssimon			/* Do not print past end of window */
264183665Slulf			if (!flag_b) {
265183665Slulf				getyx(stdscr, cury, curx);
266183665Slulf				if (curx > 0)
267183665Slulf					continue;
268183665Slulf			}
269158771Ssimon			if ((gid->lg_what == ISPROVIDER
270158771Ssimon			    || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') {
271158771Ssimon				pp = gid->lg_ptr;
272158771Ssimon				if ((regexec(&f_re, pp->lg_name, 0, NULL, 0)
273158771Ssimon				     != 0))
274158771Ssimon				  continue;
275158771Ssimon			}
276112444Sphk			if (gsp->sequence0 != gsp->sequence1) {
277183665Slulf				PRINTMSG("*\n");
278112444Sphk				continue;
279112444Sphk			}
280112444Sphk			devstat_compute_statistics(gsp, gsq, dt,
281112444Sphk			    DSM_QUEUE_LENGTH, &u64,
282112444Sphk			    DSM_TRANSFERS_PER_SECOND, &ld[0],
283125844Sphk
284112444Sphk			    DSM_TRANSFERS_PER_SECOND_READ, &ld[1],
285112444Sphk			    DSM_MB_PER_SECOND_READ, &ld[2],
286112444Sphk			    DSM_MS_PER_TRANSACTION_READ, &ld[3],
287125844Sphk
288112444Sphk			    DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4],
289112444Sphk			    DSM_MB_PER_SECOND_WRITE, &ld[5],
290112444Sphk			    DSM_MS_PER_TRANSACTION_WRITE, &ld[6],
291125844Sphk
292112444Sphk			    DSM_BUSY_PCT, &ld[7],
293266610Smav
294125844Sphk			    DSM_TRANSFERS_PER_SECOND_FREE, &ld[8],
295125844Sphk			    DSM_MB_PER_SECOND_FREE, &ld[9],
296125844Sphk			    DSM_MS_PER_TRANSACTION_FREE, &ld[10],
297266610Smav
298266610Smav			    DSM_TRANSFERS_PER_SECOND_OTHER, &ld[11],
299266610Smav			    DSM_MS_PER_TRANSACTION_OTHER, &ld[12],
300266610Smav
301112444Sphk			    DSM_NONE);
302112444Sphk
303136354Sle			if (flag_a && ld[7] < 0.1) {
304136354Sle				*gsq = *gsp;
305136354Sle				continue;
306136354Sle			}
307136354Sle
308183665Slulf			PRINTMSG(" %4ju", (uintmax_t)u64);
309183665Slulf			PRINTMSG(" %6.0f", (double)ld[0]);
310183665Slulf			PRINTMSG(" %6.0f", (double)ld[1]);
311183665Slulf			PRINTMSG(" %6.0f", (double)ld[2] * 1024);
312175118Sphk			if (ld[3] > 1e3)
313183665Slulf				PRINTMSG(" %6.0f", (double)ld[3]);
314175118Sphk			else
315183665Slulf				PRINTMSG(" %6.1f", (double)ld[3]);
316183665Slulf			PRINTMSG(" %6.0f", (double)ld[4]);
317183665Slulf			PRINTMSG(" %6.0f", (double)ld[5] * 1024);
318175118Sphk			if (ld[6] > 1e3)
319183665Slulf				PRINTMSG(" %6.0f", (double)ld[6]);
320175118Sphk			else
321183665Slulf				PRINTMSG(" %6.1f", (double)ld[6]);
322112444Sphk
323125844Sphk			if (flag_d) {
324183665Slulf				PRINTMSG(" %6.0f", (double)ld[8]);
325183665Slulf				PRINTMSG(" %6.0f", (double)ld[9] * 1024);
326175118Sphk				if (ld[10] > 1e3)
327183665Slulf					PRINTMSG(" %6.0f", (double)ld[10]);
328175118Sphk				else
329183665Slulf					PRINTMSG(" %6.1f", (double)ld[10]);
330125844Sphk			}
331125844Sphk
332266610Smav			if (flag_o) {
333266610Smav				PRINTMSG(" %6.0f", (double)ld[11]);
334266610Smav				if (ld[12] > 1e3)
335266610Smav					PRINTMSG(" %6.0f", (double)ld[12]);
336266610Smav				else
337266610Smav					PRINTMSG(" %6.1f", (double)ld[12]);
338266610Smav			}
339266610Smav
340112444Sphk			if (ld[7] > 80)
341112444Sphk				i = 3;
342112444Sphk			else if (ld[7] > 50)
343112444Sphk				i = 2;
344112444Sphk			else
345112444Sphk				i = 1;
346183665Slulf			if (!flag_b)
347183665Slulf				attron(COLOR_PAIR(i));
348183665Slulf			PRINTMSG(" %6.1lf", (double)ld[7]);
349189739Smaxim			if (!flag_b) {
350183665Slulf				attroff(COLOR_PAIR(i));
351189739Smaxim				PRINTMSG("|");
352189739Smaxim			} else
353189739Smaxim				PRINTMSG(" ");
354112444Sphk			if (gid == NULL) {
355183665Slulf				PRINTMSG(" ??");
356126786Sjhb			} else if (gid->lg_what == ISPROVIDER) {
357126786Sjhb				pp = gid->lg_ptr;
358183665Slulf				PRINTMSG(" %s", pp->lg_name);
359126786Sjhb			} else if (gid->lg_what == ISCONSUMER) {
360126786Sjhb				cp = gid->lg_ptr;
361183665Slulf				PRINTMSG(" %s/%s/%s",
362126786Sjhb				    cp->lg_geom->lg_class->lg_name,
363126786Sjhb				    cp->lg_geom->lg_name,
364126786Sjhb				    cp->lg_provider->lg_name);
365112444Sphk			}
366183665Slulf			if (!flag_b)
367183665Slulf				clrtoeol();
368183665Slulf			PRINTMSG("\n");
369112444Sphk			*gsq = *gsp;
370112444Sphk		}
371112444Sphk		geom_stats_snapshot_free(sp);
372183665Slulf		if (flag_b) {
373183665Slulf			/* We loop extra to make sure we get the information. */
374183665Slulf			if (!loop)
375183665Slulf				break;
376183665Slulf			loop = 0;
377183665Slulf			usleep(flag_I);
378183665Slulf			continue;
379183665Slulf		}
380158771Ssimon		getyx(stdscr, cury, curx);
381158771Ssimon		getmaxyx(stdscr, maxy, maxx);
382112444Sphk		clrtobot();
383158771Ssimon		if (maxy - 1 <= cury)
384158771Ssimon			move(maxy - 1, 0);
385112444Sphk		refresh();
386112444Sphk		usleep(flag_I);
387158771Ssimon		while((i = getch()) != ERR) {
388158771Ssimon			switch (i) {
389158771Ssimon			case '>':
390158771Ssimon				flag_I *= 2;
391158771Ssimon				break;
392158771Ssimon			case '<':
393158771Ssimon				flag_I /= 2;
394158771Ssimon				if (flag_I < 1000)
395158771Ssimon					flag_I = 1000;
396158771Ssimon				break;
397158771Ssimon			case 'c':
398158771Ssimon				flag_c = !flag_c;
399158771Ssimon				break;
400158771Ssimon			case 'f':
401158771Ssimon				move(0,0);
402158771Ssimon				clrtoeol();
403158771Ssimon				refresh();
404158771Ssimon				line = el_gets(el, &line_len);
405158771Ssimon				if (line == NULL)
406158771Ssimon					err(1, "el_gets");
407158771Ssimon				if (line_len > 1)
408158771Ssimon					history(hist, &hist_ev, H_ENTER, line);
409288205Sdelphij				strlcpy(tmp_f_s, line, sizeof(f_s));
410158771Ssimon				if ((p = strchr(tmp_f_s, '\n')) != NULL)
411158771Ssimon					*p = '\0';
412158771Ssimon				/*
413158771Ssimon				 * We have to clear since we messed up
414158771Ssimon				 * curses idea of the screen by using
415158771Ssimon				 * libedit.
416158771Ssimon				 */
417158771Ssimon				clear();
418158771Ssimon				refresh();
419158771Ssimon				if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED)
420158771Ssimon				    != 0) {
421158771Ssimon					move(0, 0);
422158771Ssimon					printw("Invalid filter");
423158771Ssimon					refresh();
424158771Ssimon					sleep(1);
425158771Ssimon				} else {
426288205Sdelphij					strlcpy(f_s, tmp_f_s, sizeof(f_s));
427158771Ssimon					f_re = tmp_f_re;
428158771Ssimon				}
429158771Ssimon				break;
430158771Ssimon			case 'F':
431158771Ssimon				f_s[0] = '\0';
432158771Ssimon				break;
433158771Ssimon			case 'q':
434158771Ssimon				quit = 1;
435158771Ssimon				break;
436158771Ssimon			default:
437158771Ssimon				break;
438158771Ssimon			}
439112444Sphk		}
440112444Sphk	}
441112457Skeramida
442183665Slulf	if (!flag_b) {
443183665Slulf		endwin();
444183665Slulf		el_end(el);
445183665Slulf	}
446158771Ssimon	exit(EX_OK);
447112444Sphk}
448112488Skeramida
449112488Skeramidastatic void
450112488Skeramidausage(void)
451112488Skeramida{
452268791Sdelphij	fprintf(stderr, "usage: gstat [-abcdp] [-f filter] [-I interval]\n");
453158771Ssimon	exit(EX_USAGE);
454112488Skeramida        /* NOTREACHED */
455112488Skeramida}
456