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