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_c, flag_d, flag_o, flag_p; 55 static int flag_I = 1000000; 56 57 #define PRINTMSG(...) do { \ 58 if (flag_b && !loop) \ 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[13]; 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, "abdcf:I:op")) != -1) { 108 switch (i) { 109 case 'a': 110 flag_a = 1; 111 break; 112 case 'b': 113 flag_b = 1; 114 break; 115 case 'c': 116 flag_c = 1; 117 break; 118 case 'd': 119 flag_d = 1; 120 break; 121 case 'f': 122 if (strlen(optarg) > sizeof(f_s) - 1) 123 errx(EX_USAGE, "Filter string too long"); 124 if (regcomp(&f_re, optarg, REG_EXTENDED) != 0) 125 errx(EX_USAGE, 126 "Invalid filter - see re_format(7)"); 127 strlcpy(f_s, optarg, sizeof(f_s)); 128 break; 129 case 'o': 130 flag_o = 1; 131 break; 132 case 'I': 133 p = NULL; 134 i = strtoul(optarg, &p, 0); 135 if (p == optarg || errno == EINVAL || 136 errno == ERANGE) { 137 errx(1, "Invalid argument to -I"); 138 } else if (!strcmp(p, "s")) 139 i *= 1000000; 140 else if (!strcmp(p, "ms")) 141 i *= 1000; 142 else if (!strcmp(p, "us")) 143 i *= 1; 144 flag_I = i; 145 break; 146 case 'p': 147 flag_p = 1; 148 break; 149 case '?': 150 default: 151 usage(); 152 } 153 } 154 argc -= optind; 155 argv += optind; 156 if (argc != 0) 157 usage(); 158 159 i = geom_gettree(&gmp); 160 if (i != 0) 161 err(1, "geom_gettree = %d", i); 162 error = geom_stats_open(); 163 if (error) 164 err(1, "geom_stats_open()"); 165 sq = NULL; 166 sq = geom_stats_snapshot_get(); 167 if (sq == NULL) 168 err(1, "geom_stats_snapshot()"); 169 if (!flag_b) { 170 /* Setup libedit */ 171 hist = history_init(); 172 if (hist == NULL) 173 err(EX_SOFTWARE, "history_init()"); 174 history(hist, &hist_ev, H_SETSIZE, 100); 175 el = el_init("gstat", stdin, stdout, stderr); 176 if (el == NULL) 177 err(EX_SOFTWARE, "el_init"); 178 el_set(el, EL_EDITOR, "emacs"); 179 el_set(el, EL_SIGNAL, 1); 180 el_set(el, EL_HIST, history, hist); 181 el_set(el, EL_PROMPT, el_prompt); 182 if (f_s[0] != '\0') 183 history(hist, &hist_ev, H_ENTER, f_s); 184 /* Setup curses */ 185 initscr(); 186 start_color(); 187 use_default_colors(); 188 pair_content(0, &cf, &cb); 189 init_pair(1, COLOR_GREEN, cb); 190 init_pair(2, COLOR_MAGENTA, cb); 191 init_pair(3, COLOR_RED, cb); 192 cbreak(); 193 noecho(); 194 nonl(); 195 nodelay(stdscr, 1); 196 intrflush(stdscr, FALSE); 197 keypad(stdscr, TRUE); 198 } 199 geom_stats_snapshot_timestamp(sq, &tq); 200 for (quit = 0; !quit;) { 201 sp = geom_stats_snapshot_get(); 202 if (sp == NULL) 203 err(1, "geom_stats_snapshot()"); 204 geom_stats_snapshot_timestamp(sp, &tp); 205 dt = tp.tv_sec - tq.tv_sec; 206 dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9; 207 tq = tp; 208 209 geom_stats_snapshot_reset(sp); 210 geom_stats_snapshot_reset(sq); 211 move(0,0); 212 PRINTMSG("dT: %5.3fs w: %.3fs", dt, (float)flag_I / 1000000); 213 if (f_s[0] != '\0') { 214 PRINTMSG(" filter: "); 215 if (!flag_b) { 216 getyx(stdscr, cury, curx); 217 getmaxyx(stdscr, maxy, maxx); 218 } 219 strlcpy(pf_s, f_s, sizeof(pf_s)); 220 max_flen = maxx - curx - 1; 221 if ((int)strlen(f_s) > max_flen && max_flen >= 0) { 222 if (max_flen > 3) 223 pf_s[max_flen - 3] = '.'; 224 if (max_flen > 2) 225 pf_s[max_flen - 2] = '.'; 226 if (max_flen > 1) 227 pf_s[max_flen - 1] = '.'; 228 pf_s[max_flen] = '\0'; 229 } 230 PRINTMSG("%s", pf_s); 231 } 232 PRINTMSG("\n"); 233 PRINTMSG(" L(q) ops/s "); 234 PRINTMSG(" r/s kBps ms/r "); 235 PRINTMSG(" w/s kBps ms/w "); 236 if (flag_d) 237 PRINTMSG(" d/s kBps ms/d "); 238 if (flag_o) 239 PRINTMSG(" o/s ms/o "); 240 PRINTMSG("%%busy Name\n"); 241 for (;;) { 242 gsp = geom_stats_snapshot_next(sp); 243 gsq = geom_stats_snapshot_next(sq); 244 if (gsp == NULL || gsq == NULL) 245 break; 246 if (gsp->id == NULL) 247 continue; 248 gid = geom_lookupid(&gmp, gsp->id); 249 if (gid == NULL) { 250 geom_deletetree(&gmp); 251 i = geom_gettree(&gmp); 252 if (i != 0) 253 err(1, "geom_gettree = %d", i); 254 gid = geom_lookupid(&gmp, gsp->id); 255 } 256 if (gid == NULL) 257 continue; 258 if (gid->lg_what == ISCONSUMER && !flag_c) 259 continue; 260 if (flag_p && gid->lg_what == ISPROVIDER && 261 ((struct gprovider *)(gid->lg_ptr))->lg_geom->lg_rank != 1) 262 continue; 263 /* Do not print past end of window */ 264 if (!flag_b) { 265 getyx(stdscr, cury, curx); 266 if (curx > 0) 267 continue; 268 } 269 if ((gid->lg_what == ISPROVIDER 270 || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') { 271 pp = gid->lg_ptr; 272 if ((regexec(&f_re, pp->lg_name, 0, NULL, 0) 273 != 0)) 274 continue; 275 } 276 if (gsp->sequence0 != gsp->sequence1) { 277 PRINTMSG("*\n"); 278 continue; 279 } 280 devstat_compute_statistics(gsp, gsq, dt, 281 DSM_QUEUE_LENGTH, &u64, 282 DSM_TRANSFERS_PER_SECOND, &ld[0], 283 284 DSM_TRANSFERS_PER_SECOND_READ, &ld[1], 285 DSM_MB_PER_SECOND_READ, &ld[2], 286 DSM_MS_PER_TRANSACTION_READ, &ld[3], 287 288 DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4], 289 DSM_MB_PER_SECOND_WRITE, &ld[5], 290 DSM_MS_PER_TRANSACTION_WRITE, &ld[6], 291 292 DSM_BUSY_PCT, &ld[7], 293 294 DSM_TRANSFERS_PER_SECOND_FREE, &ld[8], 295 DSM_MB_PER_SECOND_FREE, &ld[9], 296 DSM_MS_PER_TRANSACTION_FREE, &ld[10], 297 298 DSM_TRANSFERS_PER_SECOND_OTHER, &ld[11], 299 DSM_MS_PER_TRANSACTION_OTHER, &ld[12], 300 301 DSM_NONE); 302 303 if (flag_a && ld[7] < 0.1) { 304 *gsq = *gsp; 305 continue; 306 } 307 308 PRINTMSG(" %4ju", (uintmax_t)u64); 309 PRINTMSG(" %6.0f", (double)ld[0]); 310 PRINTMSG(" %6.0f", (double)ld[1]); 311 PRINTMSG(" %6.0f", (double)ld[2] * 1024); 312 if (ld[3] > 1e3) 313 PRINTMSG(" %6.0f", (double)ld[3]); 314 else 315 PRINTMSG(" %6.1f", (double)ld[3]); 316 PRINTMSG(" %6.0f", (double)ld[4]); 317 PRINTMSG(" %6.0f", (double)ld[5] * 1024); 318 if (ld[6] > 1e3) 319 PRINTMSG(" %6.0f", (double)ld[6]); 320 else 321 PRINTMSG(" %6.1f", (double)ld[6]); 322 323 if (flag_d) { 324 PRINTMSG(" %6.0f", (double)ld[8]); 325 PRINTMSG(" %6.0f", (double)ld[9] * 1024); 326 if (ld[10] > 1e3) 327 PRINTMSG(" %6.0f", (double)ld[10]); 328 else 329 PRINTMSG(" %6.1f", (double)ld[10]); 330 } 331 332 if (flag_o) { 333 PRINTMSG(" %6.0f", (double)ld[11]); 334 if (ld[12] > 1e3) 335 PRINTMSG(" %6.0f", (double)ld[12]); 336 else 337 PRINTMSG(" %6.1f", (double)ld[12]); 338 } 339 340 if (ld[7] > 80) 341 i = 3; 342 else if (ld[7] > 50) 343 i = 2; 344 else 345 i = 1; 346 if (!flag_b) 347 attron(COLOR_PAIR(i)); 348 PRINTMSG(" %6.1lf", (double)ld[7]); 349 if (!flag_b) { 350 attroff(COLOR_PAIR(i)); 351 PRINTMSG("|"); 352 } else 353 PRINTMSG(" "); 354 if (gid == NULL) { 355 PRINTMSG(" ??"); 356 } else if (gid->lg_what == ISPROVIDER) { 357 pp = gid->lg_ptr; 358 PRINTMSG(" %s", pp->lg_name); 359 } else if (gid->lg_what == ISCONSUMER) { 360 cp = gid->lg_ptr; 361 PRINTMSG(" %s/%s/%s", 362 cp->lg_geom->lg_class->lg_name, 363 cp->lg_geom->lg_name, 364 cp->lg_provider->lg_name); 365 } 366 if (!flag_b) 367 clrtoeol(); 368 PRINTMSG("\n"); 369 *gsq = *gsp; 370 } 371 geom_stats_snapshot_free(sp); 372 if (flag_b) { 373 /* We loop extra to make sure we get the information. */ 374 if (!loop) 375 break; 376 loop = 0; 377 usleep(flag_I); 378 continue; 379 } 380 getyx(stdscr, cury, curx); 381 getmaxyx(stdscr, maxy, maxx); 382 clrtobot(); 383 if (maxy - 1 <= cury) 384 move(maxy - 1, 0); 385 refresh(); 386 usleep(flag_I); 387 while((i = getch()) != ERR) { 388 switch (i) { 389 case '>': 390 flag_I *= 2; 391 break; 392 case '<': 393 flag_I /= 2; 394 if (flag_I < 1000) 395 flag_I = 1000; 396 break; 397 case 'c': 398 flag_c = !flag_c; 399 break; 400 case 'f': 401 move(0,0); 402 clrtoeol(); 403 refresh(); 404 line = el_gets(el, &line_len); 405 if (line == NULL) 406 err(1, "el_gets"); 407 if (line_len > 1) 408 history(hist, &hist_ev, H_ENTER, line); 409 strlcpy(tmp_f_s, line, sizeof(f_s)); 410 if ((p = strchr(tmp_f_s, '\n')) != NULL) 411 *p = '\0'; 412 /* 413 * Fix the terminal. We messed up 414 * curses idea of the screen by using 415 * libedit. 416 */ 417 clear(); 418 refresh(); 419 cbreak(); 420 noecho(); 421 nonl(); 422 if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED) 423 != 0) { 424 move(0, 0); 425 printw("Invalid filter"); 426 refresh(); 427 sleep(1); 428 } else { 429 strlcpy(f_s, tmp_f_s, sizeof(f_s)); 430 f_re = tmp_f_re; 431 } 432 break; 433 case 'F': 434 f_s[0] = '\0'; 435 break; 436 case 'q': 437 quit = 1; 438 break; 439 default: 440 break; 441 } 442 } 443 } 444 445 if (!flag_b) { 446 el_end(el); 447 endwin(); 448 } 449 exit(EX_OK); 450 } 451 452 static void 453 usage(void) 454 { 455 fprintf(stderr, "usage: gstat [-abcdp] [-f filter] [-I interval]\n"); 456 exit(EX_USAGE); 457 /* NOTREACHED */ 458 } 459