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