1 /*- 2 * Copyright (c) 2003 Poul-Henning Kamp 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The names of the authors may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 33 #include <sys/devicestat.h> 34 #include <sys/mman.h> 35 #include <sys/resource.h> 36 #include <sys/time.h> 37 38 #include <curses.h> 39 #include <devstat.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <histedit.h> 44 #include <libgeom.h> 45 #include <paths.h> 46 #include <regex.h> 47 #include <stdint.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <sysexits.h> 52 #include <unistd.h> 53 54 static int flag_a, flag_b, flag_B, flag_c, flag_d, flag_o, flag_p, flag_s; 55 static int flag_I = 1000000; 56 57 #define PRINTMSG(...) do { \ 58 if ((flag_b && !loop) || (flag_B)) \ 59 printf(__VA_ARGS__); \ 60 else if (!flag_b) \ 61 printw(__VA_ARGS__); \ 62 } while(0) 63 64 static void usage(void); 65 66 static const char* 67 el_prompt(void) 68 { 69 70 return ("Filter: "); 71 } 72 73 int 74 main(int argc, char **argv) 75 { 76 int error, i, quit; 77 int curx, cury, maxx, maxy, line_len, loop, max_flen; 78 struct devstat *gsp, *gsq; 79 void *sp, *sq; 80 double dt; 81 struct timespec tp, tq; 82 struct gmesh gmp; 83 struct gprovider *pp; 84 struct gconsumer *cp; 85 struct gident *gid; 86 regex_t f_re, tmp_f_re; 87 short cf, cb; 88 char *p; 89 char f_s[100], pf_s[100], tmp_f_s[100]; 90 const char *line; 91 long double ld[16]; 92 uint64_t u64; 93 EditLine *el; 94 History *hist; 95 HistEvent hist_ev; 96 97 hist = NULL; 98 el = NULL; 99 maxx = -1; 100 curx = -1; 101 loop = 1; 102 /* Turn on batch mode if output is not tty. */ 103 if (!isatty(fileno(stdout))) 104 flag_b = 1; 105 106 f_s[0] = '\0'; 107 while ((i = getopt(argc, argv, "abBdcf:I:ops")) != -1) { 108 switch (i) { 109 case 'a': 110 flag_a = 1; 111 break; 112 case 'b': 113 flag_b = 1; 114 break; 115 case 'B': 116 flag_B = 1; 117 flag_b = 1; 118 break; 119 case 'c': 120 flag_c = 1; 121 break; 122 case 'd': 123 flag_d = 1; 124 break; 125 case 'f': 126 if (strlen(optarg) > sizeof(f_s) - 1) 127 errx(EX_USAGE, "Filter string too long"); 128 if (regcomp(&f_re, optarg, REG_EXTENDED) != 0) 129 errx(EX_USAGE, 130 "Invalid filter - see re_format(7)"); 131 strlcpy(f_s, optarg, sizeof(f_s)); 132 break; 133 case 'o': 134 flag_o = 1; 135 break; 136 case 'I': 137 p = NULL; 138 i = strtoul(optarg, &p, 0); 139 if (p == optarg || errno == EINVAL || 140 errno == ERANGE) { 141 errx(1, "Invalid argument to -I"); 142 } else if (!strcmp(p, "s")) 143 i *= 1000000; 144 else if (!strcmp(p, "ms")) 145 i *= 1000; 146 else if (!strcmp(p, "us")) 147 i *= 1; 148 flag_I = i; 149 break; 150 case 'p': 151 flag_p = 1; 152 break; 153 case 's': 154 flag_s = 1; 155 break; 156 case '?': 157 default: 158 usage(); 159 } 160 } 161 argc -= optind; 162 argv += optind; 163 if (argc != 0) 164 usage(); 165 166 i = geom_gettree(&gmp); 167 if (i != 0) 168 err(1, "geom_gettree = %d", i); 169 error = geom_stats_open(); 170 if (error) 171 err(1, "geom_stats_open()"); 172 sq = NULL; 173 sq = geom_stats_snapshot_get(); 174 if (sq == NULL) 175 err(1, "geom_stats_snapshot()"); 176 if (!flag_b) { 177 /* Setup libedit */ 178 hist = history_init(); 179 if (hist == NULL) 180 err(EX_SOFTWARE, "history_init()"); 181 history(hist, &hist_ev, H_SETSIZE, 100); 182 el = el_init("gstat", stdin, stdout, stderr); 183 if (el == NULL) 184 err(EX_SOFTWARE, "el_init"); 185 el_set(el, EL_EDITOR, "emacs"); 186 el_set(el, EL_SIGNAL, 1); 187 el_set(el, EL_HIST, history, hist); 188 el_set(el, EL_PROMPT, el_prompt); 189 if (f_s[0] != '\0') 190 history(hist, &hist_ev, H_ENTER, f_s); 191 /* Setup curses */ 192 initscr(); 193 start_color(); 194 use_default_colors(); 195 pair_content(0, &cf, &cb); 196 init_pair(1, COLOR_GREEN, cb); 197 init_pair(2, COLOR_MAGENTA, cb); 198 init_pair(3, COLOR_RED, cb); 199 cbreak(); 200 noecho(); 201 nonl(); 202 nodelay(stdscr, 1); 203 intrflush(stdscr, FALSE); 204 keypad(stdscr, TRUE); 205 } 206 geom_stats_snapshot_timestamp(sq, &tq); 207 for (quit = 0; !quit;) { 208 sp = geom_stats_snapshot_get(); 209 if (sp == NULL) 210 err(1, "geom_stats_snapshot()"); 211 geom_stats_snapshot_timestamp(sp, &tp); 212 dt = tp.tv_sec - tq.tv_sec; 213 dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9; 214 tq = tp; 215 216 geom_stats_snapshot_reset(sp); 217 geom_stats_snapshot_reset(sq); 218 if (!flag_b) 219 move(0,0); 220 PRINTMSG("dT: %5.3fs w: %.3fs", dt, (float)flag_I / 1000000); 221 if (f_s[0] != '\0') { 222 PRINTMSG(" filter: "); 223 if (!flag_b) { 224 getyx(stdscr, cury, curx); 225 getmaxyx(stdscr, maxy, maxx); 226 } 227 strlcpy(pf_s, f_s, sizeof(pf_s)); 228 max_flen = maxx - curx - 1; 229 if ((int)strlen(f_s) > max_flen && max_flen >= 0) { 230 if (max_flen > 3) 231 pf_s[max_flen - 3] = '.'; 232 if (max_flen > 2) 233 pf_s[max_flen - 2] = '.'; 234 if (max_flen > 1) 235 pf_s[max_flen - 1] = '.'; 236 pf_s[max_flen] = '\0'; 237 } 238 PRINTMSG("%s", pf_s); 239 } 240 PRINTMSG("\n"); 241 PRINTMSG(" L(q) ops/s "); 242 if (flag_s) { 243 PRINTMSG(" r/s kB kBps ms/r "); 244 PRINTMSG(" w/s kB kBps ms/w "); 245 } 246 else { 247 PRINTMSG(" r/s kBps ms/r "); 248 PRINTMSG(" w/s kBps ms/w "); 249 } 250 if (flag_d) { 251 if (flag_s) 252 PRINTMSG(" d/s kB kBps ms/d "); 253 else 254 PRINTMSG(" d/s kBps ms/d "); 255 } 256 if (flag_o) 257 PRINTMSG(" o/s ms/o "); 258 PRINTMSG("%%busy Name\n"); 259 for (;;) { 260 gsp = geom_stats_snapshot_next(sp); 261 gsq = geom_stats_snapshot_next(sq); 262 if (gsp == NULL || gsq == NULL) 263 break; 264 if (gsp->id == NULL) 265 continue; 266 gid = geom_lookupid(&gmp, gsp->id); 267 if (gid == NULL) { 268 geom_deletetree(&gmp); 269 i = geom_gettree(&gmp); 270 if (i != 0) 271 err(1, "geom_gettree = %d", i); 272 gid = geom_lookupid(&gmp, gsp->id); 273 } 274 if (gid == NULL) 275 continue; 276 if (gid->lg_what == ISCONSUMER && !flag_c) 277 continue; 278 if (flag_p && gid->lg_what == ISPROVIDER && 279 ((struct gprovider *)(gid->lg_ptr))->lg_geom->lg_rank != 1) 280 continue; 281 /* Do not print past end of window */ 282 if (!flag_b) { 283 getyx(stdscr, cury, curx); 284 if (curx > 0) 285 continue; 286 } 287 if ((gid->lg_what == ISPROVIDER 288 || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') { 289 pp = gid->lg_ptr; 290 if ((regexec(&f_re, pp->lg_name, 0, NULL, 0) 291 != 0)) 292 continue; 293 } 294 if (gsp->sequence0 != gsp->sequence1) { 295 PRINTMSG("*\n"); 296 continue; 297 } 298 devstat_compute_statistics(gsp, gsq, dt, 299 DSM_QUEUE_LENGTH, &u64, 300 DSM_TRANSFERS_PER_SECOND, &ld[0], 301 302 DSM_TRANSFERS_PER_SECOND_READ, &ld[1], 303 DSM_MB_PER_SECOND_READ, &ld[2], 304 DSM_MS_PER_TRANSACTION_READ, &ld[3], 305 306 DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4], 307 DSM_MB_PER_SECOND_WRITE, &ld[5], 308 DSM_MS_PER_TRANSACTION_WRITE, &ld[6], 309 310 DSM_BUSY_PCT, &ld[7], 311 312 DSM_TRANSFERS_PER_SECOND_FREE, &ld[8], 313 DSM_MB_PER_SECOND_FREE, &ld[9], 314 DSM_MS_PER_TRANSACTION_FREE, &ld[10], 315 316 DSM_TRANSFERS_PER_SECOND_OTHER, &ld[11], 317 DSM_MS_PER_TRANSACTION_OTHER, &ld[12], 318 319 DSM_KB_PER_TRANSFER_READ, &ld[13], 320 DSM_KB_PER_TRANSFER_WRITE, &ld[14], 321 DSM_KB_PER_TRANSFER_FREE, &ld[15], 322 323 DSM_NONE); 324 325 if (flag_a && ld[7] < 0.1) { 326 *gsq = *gsp; 327 continue; 328 } 329 330 PRINTMSG(" %4ju", (uintmax_t)u64); 331 PRINTMSG(" %6.0f", (double)ld[0]); 332 PRINTMSG(" %6.0f", (double)ld[1]); 333 if (flag_s) 334 PRINTMSG(" %6.0f", (double)ld[13]); 335 PRINTMSG(" %6.0f", (double)ld[2] * 1024); 336 if (ld[3] > 1e3) 337 PRINTMSG(" %6.0f", (double)ld[3]); 338 else 339 PRINTMSG(" %6.1f", (double)ld[3]); 340 PRINTMSG(" %6.0f", (double)ld[4]); 341 if (flag_s) 342 PRINTMSG(" %6.0f", (double)ld[14]); 343 PRINTMSG(" %6.0f", (double)ld[5] * 1024); 344 if (ld[6] > 1e3) 345 PRINTMSG(" %6.0f", (double)ld[6]); 346 else 347 PRINTMSG(" %6.1f", (double)ld[6]); 348 349 if (flag_d) { 350 PRINTMSG(" %6.0f", (double)ld[8]); 351 if (flag_s) 352 PRINTMSG(" %6.0f", (double)ld[15]); 353 PRINTMSG(" %6.0f", (double)ld[9] * 1024); 354 if (ld[10] > 1e3) 355 PRINTMSG(" %6.0f", (double)ld[10]); 356 else 357 PRINTMSG(" %6.1f", (double)ld[10]); 358 } 359 360 if (flag_o) { 361 PRINTMSG(" %6.0f", (double)ld[11]); 362 if (ld[12] > 1e3) 363 PRINTMSG(" %6.0f", (double)ld[12]); 364 else 365 PRINTMSG(" %6.1f", (double)ld[12]); 366 } 367 368 if (ld[7] > 80) 369 i = 3; 370 else if (ld[7] > 50) 371 i = 2; 372 else 373 i = 1; 374 if (!flag_b) 375 attron(COLOR_PAIR(i)); 376 PRINTMSG(" %6.1lf", (double)ld[7]); 377 if (!flag_b) { 378 attroff(COLOR_PAIR(i)); 379 PRINTMSG("|"); 380 } else 381 PRINTMSG(" "); 382 if (gid == NULL) { 383 PRINTMSG(" ??"); 384 } else if (gid->lg_what == ISPROVIDER) { 385 pp = gid->lg_ptr; 386 PRINTMSG(" %s", pp->lg_name); 387 } else if (gid->lg_what == ISCONSUMER) { 388 cp = gid->lg_ptr; 389 PRINTMSG(" %s/%s/%s", 390 cp->lg_geom->lg_class->lg_name, 391 cp->lg_geom->lg_name, 392 cp->lg_provider->lg_name); 393 } 394 if (!flag_b) 395 clrtoeol(); 396 PRINTMSG("\n"); 397 *gsq = *gsp; 398 } 399 geom_stats_snapshot_free(sp); 400 if (flag_b) { 401 /* We loop extra to make sure we get the information. */ 402 if (!loop) 403 break; 404 if (!flag_B) 405 loop = 0; 406 else 407 fflush(stdout); 408 usleep(flag_I); 409 continue; 410 } 411 getyx(stdscr, cury, curx); 412 getmaxyx(stdscr, maxy, maxx); 413 clrtobot(); 414 if (maxy - 1 <= cury) 415 move(maxy - 1, 0); 416 refresh(); 417 usleep(flag_I); 418 while((i = getch()) != ERR) { 419 switch (i) { 420 case '>': 421 flag_I *= 2; 422 break; 423 case '<': 424 flag_I /= 2; 425 if (flag_I < 1000) 426 flag_I = 1000; 427 break; 428 case 'c': 429 flag_c = !flag_c; 430 break; 431 case 'f': 432 move(0,0); 433 clrtoeol(); 434 refresh(); 435 line = el_gets(el, &line_len); 436 if (line == NULL) 437 err(1, "el_gets"); 438 if (line_len > 1) 439 history(hist, &hist_ev, H_ENTER, line); 440 strlcpy(tmp_f_s, line, sizeof(f_s)); 441 if ((p = strchr(tmp_f_s, '\n')) != NULL) 442 *p = '\0'; 443 /* 444 * Fix the terminal. We messed up 445 * curses idea of the screen by using 446 * libedit. 447 */ 448 clear(); 449 refresh(); 450 cbreak(); 451 noecho(); 452 nonl(); 453 if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED) 454 != 0) { 455 move(0, 0); 456 printw("Invalid filter"); 457 refresh(); 458 sleep(1); 459 } else { 460 strlcpy(f_s, tmp_f_s, sizeof(f_s)); 461 f_re = tmp_f_re; 462 } 463 break; 464 case 'F': 465 f_s[0] = '\0'; 466 break; 467 case 'q': 468 quit = 1; 469 break; 470 default: 471 break; 472 } 473 } 474 } 475 476 if (!flag_b) { 477 el_end(el); 478 endwin(); 479 } 480 exit(EX_OK); 481 } 482 483 static void 484 usage(void) 485 { 486 fprintf(stderr, "usage: gstat [-abBcdps] [-f filter] [-I interval]\n"); 487 exit(EX_USAGE); 488 /* NOTREACHED */ 489 } 490