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