]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/gstat/gstat.c
Update to bmake-201802222
[FreeBSD/FreeBSD.git] / usr.sbin / gstat / gstat.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2003 Poul-Henning Kamp
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The names of the authors may not be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33
34
35 #include <sys/devicestat.h>
36 #include <sys/mman.h>
37 #include <sys/resource.h>
38 #include <sys/time.h>
39
40 #include <curses.h>
41 #include <devstat.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <histedit.h>
46 #include <libgeom.h>
47 #include <paths.h>
48 #include <regex.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sysexits.h>
54 #include <unistd.h>
55
56 static int flag_a, flag_b, flag_B, flag_c, flag_d, flag_o, flag_p, flag_s;
57 static int flag_I = 1000000;
58
59 #define PRINTMSG(...) do {                                              \
60                 if ((flag_b && !loop) || (flag_B))                      \
61                         printf(__VA_ARGS__);                            \
62                 else if (!flag_b)                                       \
63                         printw(__VA_ARGS__);                            \
64         } while(0)
65
66 static void usage(void);
67
68 static const char*
69 el_prompt(void)
70 {
71
72         return ("Filter: ");
73 }
74
75 int
76 main(int argc, char **argv)
77 {
78         int error, i, quit;
79         int curx, cury, maxx, maxy, line_len, loop, max_flen;
80         struct devstat *gsp, *gsq;
81         void *sp, *sq;
82         double dt;
83         struct timespec tp, tq;
84         struct gmesh gmp;
85         struct gprovider *pp;
86         struct gconsumer *cp;
87         struct gident *gid;
88         regex_t f_re, tmp_f_re;
89         short cf, cb;
90         char *p;
91         char f_s[100], pf_s[100], tmp_f_s[100];
92         const char *line;
93         long double ld[16];
94         uint64_t u64;
95         EditLine *el;
96         History *hist;
97         HistEvent hist_ev;
98
99         hist = NULL;
100         el = NULL;
101         maxx = -1;
102         curx = -1;
103         loop = 1;
104         /* Turn on batch mode if output is not tty. */
105         if (!isatty(fileno(stdout)))
106                 flag_b = 1;
107
108         f_s[0] = '\0';
109         while ((i = getopt(argc, argv, "abBdcf:I:ops")) != -1) {
110                 switch (i) {
111                 case 'a':
112                         flag_a = 1;
113                         break;
114                 case 'b':
115                         flag_b = 1;
116                         break;
117                 case 'B':
118                         flag_B = 1;
119                         flag_b = 1;
120                         break;
121                 case 'c':
122                         flag_c = 1;
123                         break;
124                 case 'd':
125                         flag_d = 1;
126                         break;
127                 case 'f':
128                         if (strlen(optarg) > sizeof(f_s) - 1)
129                                 errx(EX_USAGE, "Filter string too long");
130                         if (regcomp(&f_re, optarg, REG_EXTENDED) != 0)
131                                 errx(EX_USAGE,
132                                     "Invalid filter - see re_format(7)");
133                         strlcpy(f_s, optarg, sizeof(f_s));
134                         break;
135                 case 'o':
136                         flag_o = 1;
137                         break;
138                 case 'I':
139                         p = NULL;
140                         i = strtoul(optarg, &p, 0);
141                         if (p == optarg || errno == EINVAL ||
142                             errno == ERANGE) {
143                                 errx(1, "Invalid argument to -I");
144                         } else if (!strcmp(p, "s"))
145                                 i *= 1000000;
146                         else if (!strcmp(p, "ms"))
147                                 i *= 1000;
148                         else if (!strcmp(p, "us"))
149                                 i *= 1;
150                         flag_I = i;
151                         break;
152                 case 'p':
153                         flag_p = 1;
154                         break;
155                 case 's':
156                         flag_s = 1;
157                         break;
158                 case '?':
159                 default:
160                         usage();
161                 }
162         }
163         argc -= optind;
164         argv += optind;
165         if (argc != 0)
166                 usage();
167
168         i = geom_gettree(&gmp);
169         if (i != 0)
170                 err(1, "geom_gettree = %d", i);
171         error = geom_stats_open();
172         if (error)
173                 err(1, "geom_stats_open()");
174         sq = NULL;
175         sq = geom_stats_snapshot_get();
176         if (sq == NULL)
177                 err(1, "geom_stats_snapshot()");
178         if (!flag_b) {
179                 /* Setup libedit */
180                 hist = history_init();
181                 if (hist == NULL)
182                         err(EX_SOFTWARE, "history_init()");
183                 history(hist, &hist_ev, H_SETSIZE, 100);
184                 el = el_init("gstat", stdin, stdout, stderr);
185                 if (el == NULL)
186                         err(EX_SOFTWARE, "el_init");
187                 el_set(el, EL_EDITOR, "emacs");
188                 el_set(el, EL_SIGNAL, 1);
189                 el_set(el, EL_HIST, history, hist);
190                 el_set(el, EL_PROMPT, el_prompt);
191                 if (f_s[0] != '\0')
192                         history(hist, &hist_ev, H_ENTER, f_s);
193                 /* Setup curses */
194                 initscr();
195                 start_color();
196                 use_default_colors();
197                 pair_content(0, &cf, &cb);
198                 init_pair(1, COLOR_GREEN, cb);
199                 init_pair(2, COLOR_MAGENTA, cb);
200                 init_pair(3, COLOR_RED, cb);
201                 cbreak();
202                 noecho();
203                 nonl();
204                 nodelay(stdscr, 1);
205                 intrflush(stdscr, FALSE);
206                 keypad(stdscr, TRUE);
207         }
208         geom_stats_snapshot_timestamp(sq, &tq);
209         for (quit = 0; !quit;) {
210                 sp = geom_stats_snapshot_get();
211                 if (sp == NULL)
212                         err(1, "geom_stats_snapshot()");
213                 geom_stats_snapshot_timestamp(sp, &tp);
214                 dt = tp.tv_sec - tq.tv_sec;
215                 dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9;
216                 tq = tp;
217         
218                 geom_stats_snapshot_reset(sp);
219                 geom_stats_snapshot_reset(sq);
220                 if (!flag_b)
221                         move(0,0);
222                 PRINTMSG("dT: %5.3fs  w: %.3fs", dt, (float)flag_I / 1000000);
223                 if (f_s[0] != '\0') {
224                         PRINTMSG("  filter: ");
225                         if (!flag_b) {
226                                 getyx(stdscr, cury, curx);
227                                 getmaxyx(stdscr, maxy, maxx);
228                         }
229                         strlcpy(pf_s, f_s, sizeof(pf_s));
230                         max_flen = maxx - curx - 1;
231                         if ((int)strlen(f_s) > max_flen && max_flen >= 0) {
232                                 if (max_flen > 3)
233                                         pf_s[max_flen - 3] = '.';
234                                 if (max_flen > 2)
235                                         pf_s[max_flen - 2] = '.';
236                                 if (max_flen > 1)
237                                         pf_s[max_flen - 1] = '.';
238                                 pf_s[max_flen] = '\0';
239                         }
240                         PRINTMSG("%s", pf_s);
241                 }
242                 PRINTMSG("\n");
243                 PRINTMSG(" L(q)  ops/s   ");
244                 if (flag_s) {
245                         PRINTMSG(" r/s     kB   kBps   ms/r   ");
246                         PRINTMSG(" w/s     kB   kBps   ms/w   ");
247                 }
248                 else {
249                         PRINTMSG(" r/s   kBps   ms/r   ");
250                         PRINTMSG(" w/s   kBps   ms/w   ");
251                 }
252                 if (flag_d) {
253                         if (flag_s)
254                                 PRINTMSG(" d/s     kB   kBps   ms/d   ");
255                         else
256                                 PRINTMSG(" d/s   kBps   ms/d   ");
257                 }
258                 if (flag_o)
259                         PRINTMSG(" o/s   ms/o   ");
260                 PRINTMSG("%%busy Name\n");
261                 for (;;) {
262                         gsp = geom_stats_snapshot_next(sp);
263                         gsq = geom_stats_snapshot_next(sq);
264                         if (gsp == NULL || gsq == NULL)
265                                 break;
266                         if (gsp->id == NULL)
267                                 continue;
268                         gid = geom_lookupid(&gmp, gsp->id);
269                         if (gid == NULL) {
270                                 geom_deletetree(&gmp);
271                                 i = geom_gettree(&gmp);
272                                 if (i != 0)
273                                         err(1, "geom_gettree = %d", i);
274                                 gid = geom_lookupid(&gmp, gsp->id);
275                         }
276                         if (gid == NULL)
277                                 continue;
278                         if (gid->lg_what == ISCONSUMER && !flag_c)
279                                 continue;
280                         if (flag_p && gid->lg_what == ISPROVIDER &&
281                            ((struct gprovider *)(gid->lg_ptr))->lg_geom->lg_rank != 1)
282                                 continue;
283                         /* Do not print past end of window */
284                         if (!flag_b) {
285                                 getyx(stdscr, cury, curx);
286                                 if (curx > 0)
287                                         continue;
288                         }
289                         if ((gid->lg_what == ISPROVIDER
290                             || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') {
291                                 pp = gid->lg_ptr;
292                                 if ((regexec(&f_re, pp->lg_name, 0, NULL, 0)
293                                      != 0))
294                                   continue;
295                         }
296                         if (gsp->sequence0 != gsp->sequence1) {
297                                 PRINTMSG("*\n");
298                                 continue;
299                         }
300                         devstat_compute_statistics(gsp, gsq, dt, 
301                             DSM_QUEUE_LENGTH, &u64,
302                             DSM_TRANSFERS_PER_SECOND, &ld[0],
303
304                             DSM_TRANSFERS_PER_SECOND_READ, &ld[1],
305                             DSM_MB_PER_SECOND_READ, &ld[2],
306                             DSM_MS_PER_TRANSACTION_READ, &ld[3],
307
308                             DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4],
309                             DSM_MB_PER_SECOND_WRITE, &ld[5],
310                             DSM_MS_PER_TRANSACTION_WRITE, &ld[6],
311
312                             DSM_BUSY_PCT, &ld[7],
313
314                             DSM_TRANSFERS_PER_SECOND_FREE, &ld[8],
315                             DSM_MB_PER_SECOND_FREE, &ld[9],
316                             DSM_MS_PER_TRANSACTION_FREE, &ld[10],
317
318                             DSM_TRANSFERS_PER_SECOND_OTHER, &ld[11],
319                             DSM_MS_PER_TRANSACTION_OTHER, &ld[12],
320
321                             DSM_KB_PER_TRANSFER_READ, &ld[13],
322                             DSM_KB_PER_TRANSFER_WRITE, &ld[14],
323                             DSM_KB_PER_TRANSFER_FREE, &ld[15],
324
325                             DSM_NONE);
326
327                         if (flag_a && ld[7] < 0.1) {
328                                 *gsq = *gsp;
329                                 continue;
330                         }
331
332                         PRINTMSG(" %4ju", (uintmax_t)u64);
333                         PRINTMSG(" %6.0f", (double)ld[0]);
334                         PRINTMSG(" %6.0f", (double)ld[1]);
335                         if (flag_s)
336                                 PRINTMSG(" %6.0f", (double)ld[13]);
337                         PRINTMSG(" %6.0f", (double)ld[2] * 1024);
338                         if (ld[3] > 1e3) 
339                                 PRINTMSG(" %6.0f", (double)ld[3]);
340                         else
341                                 PRINTMSG(" %6.1f", (double)ld[3]);
342                         PRINTMSG(" %6.0f", (double)ld[4]);
343                         if (flag_s)
344                                 PRINTMSG(" %6.0f", (double)ld[14]);
345                         PRINTMSG(" %6.0f", (double)ld[5] * 1024);
346                         if (ld[6] > 1e3) 
347                                 PRINTMSG(" %6.0f", (double)ld[6]);
348                         else
349                                 PRINTMSG(" %6.1f", (double)ld[6]);
350
351                         if (flag_d) {
352                                 PRINTMSG(" %6.0f", (double)ld[8]);
353                                 if (flag_s)
354                                         PRINTMSG(" %6.0f", (double)ld[15]);
355                                 PRINTMSG(" %6.0f", (double)ld[9] * 1024);
356                                 if (ld[10] > 1e3) 
357                                         PRINTMSG(" %6.0f", (double)ld[10]);
358                                 else
359                                         PRINTMSG(" %6.1f", (double)ld[10]);
360                         }
361
362                         if (flag_o) {
363                                 PRINTMSG(" %6.0f", (double)ld[11]);
364                                 if (ld[12] > 1e3) 
365                                         PRINTMSG(" %6.0f", (double)ld[12]);
366                                 else
367                                         PRINTMSG(" %6.1f", (double)ld[12]);
368                         }
369
370                         if (ld[7] > 80)
371                                 i = 3;
372                         else if (ld[7] > 50)
373                                 i = 2;
374                         else 
375                                 i = 1;
376                         if (!flag_b)
377                                 attron(COLOR_PAIR(i));
378                         PRINTMSG(" %6.1lf", (double)ld[7]);
379                         if (!flag_b) {
380                                 attroff(COLOR_PAIR(i));
381                                 PRINTMSG("|");
382                         } else
383                                 PRINTMSG(" ");
384                         if (gid == NULL) {
385                                 PRINTMSG(" ??");
386                         } else if (gid->lg_what == ISPROVIDER) {
387                                 pp = gid->lg_ptr;
388                                 PRINTMSG(" %s", pp->lg_name);
389                         } else if (gid->lg_what == ISCONSUMER) {
390                                 cp = gid->lg_ptr;
391                                 PRINTMSG(" %s/%s/%s",
392                                     cp->lg_geom->lg_class->lg_name,
393                                     cp->lg_geom->lg_name,
394                                     cp->lg_provider->lg_name);
395                         }
396                         if (!flag_b)
397                                 clrtoeol();
398                         PRINTMSG("\n");
399                         *gsq = *gsp;
400                 }
401                 geom_stats_snapshot_free(sp);
402                 if (flag_b) {
403                         /* We loop extra to make sure we get the information. */
404                         if (!loop)
405                                 break;
406                         if (!flag_B)
407                                 loop = 0;
408                         else
409                                 fflush(stdout);
410                         usleep(flag_I);
411                         continue;
412                 }
413                 getyx(stdscr, cury, curx);
414                 getmaxyx(stdscr, maxy, maxx);
415                 clrtobot();
416                 if (maxy - 1 <= cury)
417                         move(maxy - 1, 0);
418                 refresh();
419                 usleep(flag_I);
420                 while((i = getch()) != ERR) {
421                         switch (i) {
422                         case '>':
423                                 flag_I *= 2;
424                                 break;
425                         case '<':
426                                 flag_I /= 2;
427                                 if (flag_I < 1000)
428                                         flag_I = 1000;
429                                 break;
430                         case 'c':
431                                 flag_c = !flag_c;
432                                 break;
433                         case 'f':
434                                 move(0,0);
435                                 clrtoeol();
436                                 refresh();
437                                 line = el_gets(el, &line_len);
438                                 if (line == NULL)
439                                         err(1, "el_gets");
440                                 if (line_len > 1)
441                                         history(hist, &hist_ev, H_ENTER, line);
442                                 strlcpy(tmp_f_s, line, sizeof(f_s));
443                                 if ((p = strchr(tmp_f_s, '\n')) != NULL)
444                                         *p = '\0';
445                                 /*
446                                  * Fix the terminal.  We messed up
447                                  * curses idea of the screen by using
448                                  * libedit.
449                                  */
450                                 clear();
451                                 refresh();
452                                 cbreak();
453                                 noecho();
454                                 nonl();
455                                 if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED)
456                                     != 0) {
457                                         move(0, 0);
458                                         printw("Invalid filter");
459                                         refresh();
460                                         sleep(1);
461                                 } else {
462                                         strlcpy(f_s, tmp_f_s, sizeof(f_s));
463                                         f_re = tmp_f_re;
464                                 }
465                                 break;
466                         case 'F':
467                                 f_s[0] = '\0';
468                                 break;
469                         case 'q':
470                                 quit = 1;
471                                 break;
472                         default:
473                                 break;
474                         }
475                 }
476         }
477
478         if (!flag_b) {
479                 el_end(el);
480                 endwin();
481         }
482         exit(EX_OK);
483 }
484
485 static void
486 usage(void)
487 {
488         fprintf(stderr, "usage: gstat [-abBcdps] [-f filter] [-I interval]\n");
489         exit(EX_USAGE);
490         /* NOTREACHED */
491 }