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