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_c, flag_d; 55 static int flag_I = 1000000; 56 57 static void usage(void); 58 59 static const char* 60 el_prompt(void) 61 { 62 63 return ("Filter: "); 64 } 65 66 int 67 main(int argc, char **argv) 68 { 69 int error, i, quit; 70 int curx, cury, maxx, maxy, line_len, max_flen; 71 struct devstat *gsp, *gsq; 72 void *sp, *sq; 73 double dt; 74 struct timespec tp, tq; 75 struct gmesh gmp; 76 struct gprovider *pp; 77 struct gconsumer *cp; 78 struct gident *gid; 79 regex_t f_re, tmp_f_re; 80 short cf, cb; 81 char *p; 82 char f_s[100], pf_s[100], tmp_f_s[100]; 83 const char *line; 84 long double ld[11]; 85 uint64_t u64; 86 EditLine *el; 87 History *hist; 88 HistEvent hist_ev; 89 90 f_s[0] = '\0'; 91 while ((i = getopt(argc, argv, "adcf:I:")) != -1) { 92 switch (i) { 93 case 'a': 94 flag_a = 1; 95 break; 96 case 'c': 97 flag_c = 1; 98 break; 99 case 'd': 100 flag_d = 1; 101 break; 102 case 'f': 103 if (strlen(optarg) > sizeof(f_s) - 1) 104 errx(EX_USAGE, "Filter string too long"); 105 if (regcomp(&f_re, optarg, REG_EXTENDED) != 0) 106 errx(EX_USAGE, 107 "Invalid filter - see re_format(7)"); 108 strncpy(f_s, optarg, sizeof(f_s)); 109 break; 110 case 'I': 111 p = NULL; 112 i = strtoul(optarg, &p, 0); 113 if (p == optarg || errno == EINVAL || 114 errno == ERANGE) { 115 errx(1, "Invalid argument to -I"); 116 } else if (!strcmp(p, "s")) 117 i *= 1000000; 118 else if (!strcmp(p, "ms")) 119 i *= 1000; 120 else if (!strcmp(p, "us")) 121 i *= 1; 122 flag_I = i; 123 break; 124 case '?': 125 default: 126 usage(); 127 } 128 } 129 argc -= optind; 130 argv += optind; 131 if (argc != 0) 132 usage(); 133 134 i = geom_gettree(&gmp); 135 if (i != 0) 136 err(1, "geom_gettree = %d", i); 137 error = geom_stats_open(); 138 if (error) 139 err(1, "geom_stats_open()"); 140 sq = NULL; 141 sq = geom_stats_snapshot_get(); 142 if (sq == NULL) 143 err(1, "geom_stats_snapshot()"); 144 /* Setup curses */ 145 initscr(); 146 start_color(); 147 use_default_colors(); 148 pair_content(0, &cf, &cb); 149 init_pair(1, COLOR_GREEN, cb); 150 init_pair(2, COLOR_MAGENTA, cb); 151 init_pair(3, COLOR_RED, cb); 152 cbreak(); 153 noecho(); 154 nonl(); 155 nodelay(stdscr, 1); 156 intrflush(stdscr, FALSE); 157 keypad(stdscr, TRUE); 158 /* Setup libedit */ 159 hist = history_init(); 160 if (hist == NULL) 161 err(EX_SOFTWARE, "history_init()"); 162 history(hist, &hist_ev, H_SETSIZE, 100); 163 el = el_init("gstat", stdin, stdout, stderr); 164 if (el == NULL) 165 err(EX_SOFTWARE, "el_init"); 166 el_set(el, EL_EDITOR, "emacs"); 167 el_set(el, EL_SIGNAL, 1); 168 el_set(el, EL_HIST, history, hist); 169 el_set(el, EL_PROMPT, el_prompt); 170 if (f_s[0] != '\0') 171 history(hist, &hist_ev, H_ENTER, f_s); 172 geom_stats_snapshot_timestamp(sq, &tq); 173 for (quit = 0; !quit;) { 174 sp = geom_stats_snapshot_get(); 175 if (sp == NULL) 176 err(1, "geom_stats_snapshot()"); 177 geom_stats_snapshot_timestamp(sp, &tp); 178 dt = tp.tv_sec - tq.tv_sec; 179 dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9; 180 tq = tp; 181 182 geom_stats_snapshot_reset(sp); 183 geom_stats_snapshot_reset(sq); 184 move(0,0); 185 printw("dT: %5.3fs w: %.3fs", 186 dt, (float)flag_I / 1000000); 187 if (f_s[0] != '\0') { 188 printw(" filter: "); 189 getyx(stdscr, cury, curx); 190 getmaxyx(stdscr, maxy, maxx); 191 strncpy(pf_s, f_s, sizeof(pf_s)); 192 max_flen = maxx - curx - 1; 193 if ((int)strlen(f_s) > max_flen && max_flen >= 0) { 194 if (max_flen > 3) 195 pf_s[max_flen - 3] = '.'; 196 if (max_flen > 2) 197 pf_s[max_flen - 2] = '.'; 198 if (max_flen > 1) 199 pf_s[max_flen - 1] = '.'; 200 pf_s[max_flen] = '\0'; 201 } 202 printw("%s", pf_s); 203 } 204 printw("\n"); 205 printw(" L(q) ops/s "); 206 printw(" r/s kBps ms/r "); 207 printw(" w/s kBps ms/w "); 208 if (flag_d) 209 printw(" d/s kBps ms/d "); 210 printw("%%busy Name\n"); 211 for (;;) { 212 gsp = geom_stats_snapshot_next(sp); 213 gsq = geom_stats_snapshot_next(sq); 214 if (gsp == NULL || gsq == NULL) 215 break; 216 if (gsp->id == NULL) 217 continue; 218 gid = geom_lookupid(&gmp, gsp->id); 219 if (gid == NULL) { 220 geom_deletetree(&gmp); 221 i = geom_gettree(&gmp); 222 if (i != 0) 223 err(1, "geom_gettree = %d", i); 224 gid = geom_lookupid(&gmp, gsp->id); 225 } 226 if (gid == NULL) 227 continue; 228 if (gid->lg_what == ISCONSUMER && !flag_c) 229 continue; 230 /* Do not print past end of window */ 231 getyx(stdscr, cury, curx); 232 if (curx > 0) 233 continue; 234 if ((gid->lg_what == ISPROVIDER 235 || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') { 236 pp = gid->lg_ptr; 237 if ((regexec(&f_re, pp->lg_name, 0, NULL, 0) 238 != 0)) 239 continue; 240 } 241 if (gsp->sequence0 != gsp->sequence1) { 242 printw("*\n"); 243 continue; 244 } 245 devstat_compute_statistics(gsp, gsq, dt, 246 DSM_QUEUE_LENGTH, &u64, 247 DSM_TRANSFERS_PER_SECOND, &ld[0], 248 249 DSM_TRANSFERS_PER_SECOND_READ, &ld[1], 250 DSM_MB_PER_SECOND_READ, &ld[2], 251 DSM_MS_PER_TRANSACTION_READ, &ld[3], 252 253 DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4], 254 DSM_MB_PER_SECOND_WRITE, &ld[5], 255 DSM_MS_PER_TRANSACTION_WRITE, &ld[6], 256 257 DSM_BUSY_PCT, &ld[7], 258 DSM_TRANSFERS_PER_SECOND_FREE, &ld[8], 259 DSM_MB_PER_SECOND_FREE, &ld[9], 260 DSM_MS_PER_TRANSACTION_FREE, &ld[10], 261 DSM_NONE); 262 263 if (flag_a && ld[7] < 0.1) { 264 *gsq = *gsp; 265 continue; 266 } 267 268 printw(" %4ju", (uintmax_t)u64); 269 printw(" %6.0f", (double)ld[0]); 270 printw(" %6.0f", (double)ld[1]); 271 printw(" %6.0f", (double)ld[2] * 1024); 272 if (ld[3] > 1e3) 273 printw(" %6.0f", (double)ld[3]); 274 else 275 printw(" %6.1f", (double)ld[3]); 276 printw(" %6.0f", (double)ld[4]); 277 printw(" %6.0f", (double)ld[5] * 1024); 278 if (ld[6] > 1e3) 279 printw(" %6.0f", (double)ld[6]); 280 else 281 printw(" %6.1f", (double)ld[6]); 282 283 if (flag_d) { 284 printw(" %6.0f", (double)ld[8]); 285 printw(" %6.0f", (double)ld[9] * 1024); 286 if (ld[10] > 1e3) 287 printw(" %6.0f", (double)ld[10]); 288 else 289 printw(" %6.1f", (double)ld[10]); 290 } 291 292 if (ld[7] > 80) 293 i = 3; 294 else if (ld[7] > 50) 295 i = 2; 296 else 297 i = 1; 298 attron(COLOR_PAIR(i)); 299 printw(" %6.1lf", (double)ld[7]); 300 attroff(COLOR_PAIR(i)); 301 printw("|"); 302 if (gid == NULL) { 303 printw(" ??"); 304 } else if (gid->lg_what == ISPROVIDER) { 305 pp = gid->lg_ptr; 306 printw(" %s", pp->lg_name); 307 } else if (gid->lg_what == ISCONSUMER) { 308 cp = gid->lg_ptr; 309 printw(" %s/%s/%s", 310 cp->lg_geom->lg_class->lg_name, 311 cp->lg_geom->lg_name, 312 cp->lg_provider->lg_name); 313 } 314 clrtoeol(); 315 printw("\n"); 316 *gsq = *gsp; 317 } 318 geom_stats_snapshot_free(sp); 319 getyx(stdscr, cury, curx); 320 getmaxyx(stdscr, maxy, maxx); 321 clrtobot(); 322 if (maxy - 1 <= cury) 323 move(maxy - 1, 0); 324 refresh(); 325 usleep(flag_I); 326 while((i = getch()) != ERR) { 327 switch (i) { 328 case '>': 329 flag_I *= 2; 330 break; 331 case '<': 332 flag_I /= 2; 333 if (flag_I < 1000) 334 flag_I = 1000; 335 break; 336 case 'c': 337 flag_c = !flag_c; 338 break; 339 case 'f': 340 move(0,0); 341 clrtoeol(); 342 refresh(); 343 line = el_gets(el, &line_len); 344 if (line == NULL) 345 err(1, "el_gets"); 346 if (line_len > 1) 347 history(hist, &hist_ev, H_ENTER, line); 348 strncpy(tmp_f_s, line, sizeof(f_s)); 349 if ((p = strchr(tmp_f_s, '\n')) != NULL) 350 *p = '\0'; 351 /* 352 * We have to clear since we messed up 353 * curses idea of the screen by using 354 * libedit. 355 */ 356 clear(); 357 refresh(); 358 if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED) 359 != 0) { 360 move(0, 0); 361 printw("Invalid filter"); 362 refresh(); 363 sleep(1); 364 } else { 365 strncpy(f_s, tmp_f_s, sizeof(f_s)); 366 f_re = tmp_f_re; 367 } 368 break; 369 case 'F': 370 f_s[0] = '\0'; 371 break; 372 case 'q': 373 quit = 1; 374 break; 375 default: 376 break; 377 } 378 } 379 } 380 381 endwin(); 382 el_end(el); 383 exit(EX_OK); 384 } 385 386 static void 387 usage(void) 388 { 389 fprintf(stderr, "usage: gstat [-acd] [-f filter] [-I interval]\n"); 390 exit(EX_USAGE); 391 /* NOTREACHED */ 392 } 393