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 printw(" %6.1f", (double)ld[3]); 273 printw(" %6.0f", (double)ld[4]); 274 printw(" %6.0f", (double)ld[5] * 1024); 275 printw(" %6.1f", (double)ld[6]); 276 277 if (flag_d) { 278 printw(" %6.0f", (double)ld[8]); 279 printw(" %6.0f", (double)ld[9] * 1024); 280 printw(" %6.1f", (double)ld[10]); 281 } 282 283 if (ld[7] > 80) 284 i = 3; 285 else if (ld[7] > 50) 286 i = 2; 287 else 288 i = 1; 289 attron(COLOR_PAIR(i)); 290 printw(" %6.1lf", (double)ld[7]); 291 attroff(COLOR_PAIR(i)); 292 printw("|"); 293 if (gid == NULL) { 294 printw(" ??"); 295 } else if (gid->lg_what == ISPROVIDER) { 296 pp = gid->lg_ptr; 297 printw(" %s", pp->lg_name); 298 } else if (gid->lg_what == ISCONSUMER) { 299 cp = gid->lg_ptr; 300 printw(" %s/%s/%s", 301 cp->lg_geom->lg_class->lg_name, 302 cp->lg_geom->lg_name, 303 cp->lg_provider->lg_name); 304 } 305 clrtoeol(); 306 printw("\n"); 307 *gsq = *gsp; 308 } 309 geom_stats_snapshot_free(sp); 310 getyx(stdscr, cury, curx); 311 getmaxyx(stdscr, maxy, maxx); 312 clrtobot(); 313 if (maxy - 1 <= cury) 314 move(maxy - 1, 0); 315 refresh(); 316 usleep(flag_I); 317 while((i = getch()) != ERR) { 318 switch (i) { 319 case '>': 320 flag_I *= 2; 321 break; 322 case '<': 323 flag_I /= 2; 324 if (flag_I < 1000) 325 flag_I = 1000; 326 break; 327 case 'c': 328 flag_c = !flag_c; 329 break; 330 case 'f': 331 move(0,0); 332 clrtoeol(); 333 refresh(); 334 line = el_gets(el, &line_len); 335 if (line == NULL) 336 err(1, "el_gets"); 337 if (line_len > 1) 338 history(hist, &hist_ev, H_ENTER, line); 339 strncpy(tmp_f_s, line, sizeof(f_s)); 340 if ((p = strchr(tmp_f_s, '\n')) != NULL) 341 *p = '\0'; 342 /* 343 * We have to clear since we messed up 344 * curses idea of the screen by using 345 * libedit. 346 */ 347 clear(); 348 refresh(); 349 if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED) 350 != 0) { 351 move(0, 0); 352 printw("Invalid filter"); 353 refresh(); 354 sleep(1); 355 } else { 356 strncpy(f_s, tmp_f_s, sizeof(f_s)); 357 f_re = tmp_f_re; 358 } 359 break; 360 case 'F': 361 f_s[0] = '\0'; 362 break; 363 case 'q': 364 quit = 1; 365 break; 366 default: 367 break; 368 } 369 } 370 } 371 372 endwin(); 373 el_end(el); 374 exit(EX_OK); 375 } 376 377 static void 378 usage(void) 379 { 380 fprintf(stderr, "usage: gstat [-acd] [-f filter] [-I interval]\n"); 381 exit(EX_USAGE); 382 /* NOTREACHED */ 383 } 384