]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/gstat/gstat.c
nvi: import version 2.2.1
[FreeBSD/FreeBSD.git] / usr.sbin / gstat / gstat.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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
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_B, flag_c, flag_C, flag_d, flag_o, flag_p,
55            flag_s;
56 static int flag_I = 1000000;
57
58 #define HIGH_PCT_BUSY_THRESH 80
59 #define MEDIUM_PCT_BUSY_THRESH 50
60 #define PRINTMSG(...) do {                                              \
61                 if ((flag_b && !loop) || (flag_B))                      \
62                         printf(__VA_ARGS__);                            \
63                 else if (!flag_b)                                       \
64                         printw(__VA_ARGS__);                            \
65         } while(0)
66
67 static void usage(void) __dead2;
68
69 static const char*
70 el_prompt(void)
71 {
72
73         return ("Filter: ");
74 }
75
76 int
77 main(int argc, char **argv)
78 {
79         int error, i, quit;
80         int curx, cury, maxx, maxy, line_len, loop, max_flen, head_printed;
81         struct devstat *gsp, *gsq;
82         void *sp, *sq;
83         double dt;
84         struct timespec tp, tq;
85         struct gmesh gmp;
86         struct gprovider *pp;
87         struct gconsumer *cp;
88         struct gident *gid;
89         regex_t f_re, tmp_f_re;
90         short cf, cb;
91         char *p;
92         char f_s[100], pf_s[100], tmp_f_s[100];
93         char ts[100], g_name[4096];
94         const char *line;
95         long double ld[16];
96         uint64_t u64;
97         EditLine *el;
98         History *hist;
99         HistEvent hist_ev;
100
101         hist = NULL;
102         el = NULL;
103         maxx = -1;
104         curx = -1;
105         loop = 1;
106         /* Turn on batch mode if output is not tty. */
107         if (!isatty(fileno(stdout)))
108                 flag_b = 1;
109
110         f_s[0] = '\0';
111         while ((i = getopt(argc, argv, "abBdcCf:I:ops")) != -1) {
112                 switch (i) {
113                 case 'a':
114                         flag_a = 1;
115                         break;
116                 case 'b':
117                         flag_b = 1;
118                         break;
119                 case 'B':
120                         flag_B = 1;
121                         flag_b = 1;
122                         break;
123                 case 'c':
124                         flag_c = 1;
125                         break;
126                 case 'C':
127                         flag_C = 1;
128                         /* csv out implies repeating batch mode */
129                         flag_b = 1;
130                         flag_B = 1;
131                         head_printed = 0;
132                         break;
133                 case 'd':
134                         flag_d = 1;
135                         break;
136                 case 'f':
137                         if (strlen(optarg) > sizeof(f_s) - 1)
138                                 errx(EX_USAGE, "Filter string too long");
139                         if (regcomp(&f_re, optarg, REG_EXTENDED) != 0)
140                                 errx(EX_USAGE,
141                                     "Invalid filter - see re_format(7)");
142                         strlcpy(f_s, optarg, sizeof(f_s));
143                         break;
144                 case 'o':
145                         flag_o = 1;
146                         break;
147                 case 'I':
148                         p = NULL;
149                         i = strtoul(optarg, &p, 0);
150                         if (p == optarg || errno == EINVAL ||
151                             errno == ERANGE) {
152                                 errx(1, "Invalid argument to -I");
153                         } else if (!strcmp(p, "s"))
154                                 i *= 1000000;
155                         else if (!strcmp(p, "ms"))
156                                 i *= 1000;
157                         else if (!strcmp(p, "us"))
158                                 i *= 1;
159                         flag_I = i;
160                         break;
161                 case 'p':
162                         flag_p = 1;
163                         break;
164                 case 's':
165                         flag_s = 1;
166                         break;
167                 case '?':
168                 default:
169                         usage();
170                 }
171         }
172         argc -= optind;
173         argv += optind;
174         if (argc != 0)
175                 usage();
176
177         i = geom_gettree(&gmp);
178         if (i != 0)
179                 err(1, "geom_gettree = %d", i);
180         error = geom_stats_open();
181         if (error)
182                 err(1, "geom_stats_open()");
183         sq = NULL;
184         sq = geom_stats_snapshot_get();
185         if (sq == NULL)
186                 err(1, "geom_stats_snapshot()");
187         if (!flag_b) {
188                 /* Setup libedit */
189                 hist = history_init();
190                 if (hist == NULL)
191                         err(EX_SOFTWARE, "history_init()");
192                 history(hist, &hist_ev, H_SETSIZE, 100);
193                 el = el_init("gstat", stdin, stdout, stderr);
194                 if (el == NULL)
195                         err(EX_SOFTWARE, "el_init");
196                 el_set(el, EL_EDITOR, "emacs");
197                 el_set(el, EL_SIGNAL, 1);
198                 el_set(el, EL_HIST, history, hist);
199                 el_set(el, EL_PROMPT, el_prompt);
200                 if (f_s[0] != '\0')
201                         history(hist, &hist_ev, H_ENTER, f_s);
202                 /* Setup curses */
203                 initscr();
204                 start_color();
205                 use_default_colors();
206                 pair_content(0, &cf, &cb);
207                 init_pair(1, COLOR_GREEN, cb);
208                 init_pair(2, COLOR_MAGENTA, cb);
209                 init_pair(3, COLOR_RED, cb);
210                 cbreak();
211                 noecho();
212                 nonl();
213                 nodelay(stdscr, 1);
214                 intrflush(stdscr, FALSE);
215                 keypad(stdscr, TRUE);
216         }
217         geom_stats_snapshot_timestamp(sq, &tq);
218         for (quit = 0; !quit;) {
219                 sp = geom_stats_snapshot_get();
220                 if (sp == NULL)
221                         err(1, "geom_stats_snapshot()");
222                 geom_stats_snapshot_timestamp(sp, &tp);
223                 dt = tp.tv_sec - tq.tv_sec;
224                 dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9;
225                 tq = tp;
226                 if (flag_C) { /* set timestamp string */
227                         (void)strftime(ts,sizeof(ts),
228                                         "%F %T",localtime(&tq.tv_sec));
229                         (void)snprintf(ts,sizeof(ts),
230                                         "%s.%.9ld",ts,tq.tv_nsec);
231                 }
232         
233                 geom_stats_snapshot_reset(sp);
234                 geom_stats_snapshot_reset(sq);
235                 if (!flag_b)
236                         move(0,0);
237                 if (!flag_C)
238                         PRINTMSG("dT: %5.3fs  w: %.3fs", dt,
239                                         (float)flag_I / 1000000);
240                 if (!flag_C && f_s[0] != '\0') {
241                         PRINTMSG("  filter: ");
242                         if (!flag_b) {
243                                 getyx(stdscr, cury, curx);
244                                 getmaxyx(stdscr, maxy, maxx);
245                         }
246                         strlcpy(pf_s, f_s, sizeof(pf_s));
247                         max_flen = maxx - curx - 1;
248                         if ((int)strlen(f_s) > max_flen && max_flen >= 0) {
249                                 if (max_flen > 3)
250                                         pf_s[max_flen - 3] = '.';
251                                 if (max_flen > 2)
252                                         pf_s[max_flen - 2] = '.';
253                                 if (max_flen > 1)
254                                         pf_s[max_flen - 1] = '.';
255                                 pf_s[max_flen] = '\0';
256                         }
257                         PRINTMSG("%s", pf_s);
258                 }
259                 if (!flag_C) {
260                         PRINTMSG("\n");
261                         PRINTMSG(" L(q)  ops/s   ");
262                         if (flag_s) {
263                                 PRINTMSG(" r/s     kB   kBps   ms/r   ");
264                                 PRINTMSG(" w/s     kB   kBps   ms/w   ");
265                         }
266                         else {
267                                 PRINTMSG(" r/s   kBps   ms/r   ");
268                                 PRINTMSG(" w/s   kBps   ms/w   ");
269                         }
270                         if (flag_d) {
271                                 if (flag_s) {
272                                         PRINTMSG(" d/s     kB   kBps");
273                                         PRINTMSG("   ms/d   ");
274                                 } else
275                                         PRINTMSG(" d/s   kBps   ms/d   ");
276                         }
277                         if (flag_o)
278                                 PRINTMSG(" o/s   ms/o   ");
279                         PRINTMSG("%%busy Name\n");
280                 } else if (flag_C && !head_printed) {
281                         PRINTMSG("timestamp,name,q-depth,total_ops/s");
282                         if (flag_s) {
283                                 PRINTMSG(",read/s,read_sz-KiB");
284                                 PRINTMSG(",read-KiB/s,ms/read");
285                                 PRINTMSG(",write/s,write_sz-KiB");
286                                 PRINTMSG(",write-KiB/s,ms/write");
287                         } else {
288                                 PRINTMSG(",read/s,read-KiB/s,ms/read");
289                                 PRINTMSG(",write/s,write-KiB/s,ms/write");
290                         }
291                         if (flag_d) {
292                                 if (flag_s) {
293                                         PRINTMSG(",delete/s,delete-sz-KiB");
294                                         PRINTMSG(",delete-KiB/s,ms/delete");
295                                 } else {
296                                         PRINTMSG(",delete/s,delete-KiB/s");
297                                         PRINTMSG(",ms/delete");
298                                 }
299                         }
300                         if (flag_o)
301                                 PRINTMSG(",other/s,ms/other");
302                         PRINTMSG(",%%busy\n");
303                         head_printed = 1;
304                 }
305                 for (;;) {
306                         gsp = geom_stats_snapshot_next(sp);
307                         gsq = geom_stats_snapshot_next(sq);
308                         if (gsp == NULL || gsq == NULL)
309                                 break;
310                         if (gsp->id == NULL)
311                                 continue;
312                         gid = geom_lookupid(&gmp, gsp->id);
313                         if (gid == NULL) {
314                                 geom_deletetree(&gmp);
315                                 i = geom_gettree(&gmp);
316                                 if (i != 0)
317                                         err(1, "geom_gettree = %d", i);
318                                 gid = geom_lookupid(&gmp, gsp->id);
319                         }
320                         if (gid == NULL)
321                                 continue;
322                         if (gid->lg_what == ISCONSUMER && !flag_c)
323                                 continue;
324                         if (flag_p && gid->lg_what == ISPROVIDER &&
325                            ((struct gprovider *)
326                             (gid->lg_ptr))->lg_geom->lg_rank != 1)
327                                 continue;
328                         /* Do not print past end of window */
329                         if (!flag_b) {
330                                 getyx(stdscr, cury, curx);
331                                 if (curx > 0)
332                                         continue;
333                         }
334                         if ((gid->lg_what == ISPROVIDER
335                             || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') {
336                                 pp = gid->lg_ptr;
337                                 if ((regexec(&f_re, pp->lg_name, 0, NULL, 0)
338                                      != 0))
339                                   continue;
340                         }
341                         if (gsp->sequence0 != gsp->sequence1) {
342                                 /* 
343                                  * it is ok to skip entire line silently
344                                  * for CSV output
345                                  */
346                                 if (!flag_C)
347                                         PRINTMSG("*\n");
348                                 continue;
349                         }
350                         devstat_compute_statistics(gsp, gsq, dt, 
351                             DSM_QUEUE_LENGTH, &u64,
352                             DSM_TRANSFERS_PER_SECOND, &ld[0],
353
354                             DSM_TRANSFERS_PER_SECOND_READ, &ld[1],
355                             DSM_MB_PER_SECOND_READ, &ld[2],
356                             DSM_MS_PER_TRANSACTION_READ, &ld[3],
357
358                             DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4],
359                             DSM_MB_PER_SECOND_WRITE, &ld[5],
360                             DSM_MS_PER_TRANSACTION_WRITE, &ld[6],
361
362                             DSM_BUSY_PCT, &ld[7],
363
364                             DSM_TRANSFERS_PER_SECOND_FREE, &ld[8],
365                             DSM_MB_PER_SECOND_FREE, &ld[9],
366                             DSM_MS_PER_TRANSACTION_FREE, &ld[10],
367
368                             DSM_TRANSFERS_PER_SECOND_OTHER, &ld[11],
369                             DSM_MS_PER_TRANSACTION_OTHER, &ld[12],
370
371                             DSM_KB_PER_TRANSFER_READ, &ld[13],
372                             DSM_KB_PER_TRANSFER_WRITE, &ld[14],
373                             DSM_KB_PER_TRANSFER_FREE, &ld[15],
374
375                             DSM_NONE);
376
377                         if (flag_a && ld[7] < 0.1) {
378                                 *gsq = *gsp;
379                                 continue;
380                         }
381
382                         /* store name for geom device */
383                         if (gid == NULL) {
384                                 (void)snprintf(g_name, sizeof(g_name), "??");
385                         } else if (gid->lg_what == ISPROVIDER) {
386                                 pp = gid->lg_ptr;
387                                 (void)snprintf(g_name, sizeof(g_name), "%s",
388                                                 pp->lg_name);
389                         } else if (gid->lg_what == ISCONSUMER) {
390                                 cp = gid->lg_ptr;
391                                 (void)snprintf(g_name, sizeof(g_name),
392                                         "%s/%s/%s",
393                                         cp->lg_geom->lg_class->lg_name,
394                                         cp->lg_geom->lg_name,
395                                         cp->lg_provider->lg_name);
396                         }
397         
398                         if (flag_C) {
399                                 PRINTMSG("%s", ts); /* timestamp */
400                                 PRINTMSG(",%s", g_name); /* print name */
401                                 PRINTMSG(",%ju", (uintmax_t)u64);
402                                 PRINTMSG(",%.0f", (double)ld[0]);
403                                 PRINTMSG(",%.0f", (double)ld[1]);
404                                 if (flag_s)
405                                         PRINTMSG(",%.0f", (double)ld[13]);
406                                 PRINTMSG(",%.0f", (double)ld[2] * 1024);
407                                 if (ld[3] > 1e3) 
408                                         PRINTMSG(",%.0f", (double)ld[3]);
409                                 else
410                                         PRINTMSG(",%.1f", (double)ld[3]);
411                                 PRINTMSG(",%.0f", (double)ld[4]);
412                                 if (flag_s)
413                                         PRINTMSG(",%.0f", (double)ld[14]);
414                                 PRINTMSG(",%.0f", (double)ld[5] * 1024);
415                                 if (ld[6] > 1e3) 
416                                         PRINTMSG(",%.0f", (double)ld[6]);
417                                 else
418                                         PRINTMSG(",%.1f", (double)ld[6]);
419
420                                 if (flag_d) {
421                                         PRINTMSG(",%.0f", (double)ld[8]);
422                                         if (flag_s)
423                                                 PRINTMSG(",%.0f",
424                                                                 (double)ld[15]);
425                                         PRINTMSG(",%.0f", (double)ld[9] * 1024);
426                                         if (ld[10] > 1e3) 
427                                                 PRINTMSG(",%.0f",
428                                                                 (double)ld[10]);
429                                         else
430                                                 PRINTMSG(",%.1f",
431                                                                 (double)ld[10]);
432                                 }
433
434                                 if (flag_o) {
435                                         PRINTMSG(",%.0f", (double)ld[11]);
436                                         if (ld[12] > 1e3) 
437                                                 PRINTMSG(",%.0f",
438                                                                 (double)ld[12]);
439                                         else
440                                                 PRINTMSG(",%.1f", 
441                                                                 (double)ld[12]);
442                                 }
443                                 PRINTMSG(",%.1lf", (double)ld[7]);
444                         } else {
445                                 PRINTMSG(" %4ju", (uintmax_t)u64);
446                                 PRINTMSG(" %6.0f", (double)ld[0]);
447                                 PRINTMSG(" %6.0f", (double)ld[1]);
448                                 if (flag_s)
449                                         PRINTMSG(" %6.0f", (double)ld[13]);
450                                 PRINTMSG(" %6.0f", (double)ld[2] * 1024);
451                                 if (ld[3] > 1e3) 
452                                         PRINTMSG(" %6.0f", (double)ld[3]);
453                                 else
454                                         PRINTMSG(" %6.1f", (double)ld[3]);
455                                 PRINTMSG(" %6.0f", (double)ld[4]);
456                                 if (flag_s)
457                                         PRINTMSG(" %6.0f", (double)ld[14]);
458                                 PRINTMSG(" %6.0f", (double)ld[5] * 1024);
459                                 if (ld[6] > 1e3) 
460                                         PRINTMSG(" %6.0f", (double)ld[6]);
461                                 else
462                                         PRINTMSG(" %6.1f", (double)ld[6]);
463
464                                 if (flag_d) {
465                                         PRINTMSG(" %6.0f", (double)ld[8]);
466                                         if (flag_s)
467                                                 PRINTMSG(" %6.0f", 
468                                                                 (double)ld[15]);
469                                         PRINTMSG(" %6.0f", 
470                                                         (double)ld[9] * 1024);
471                                         if (ld[10] > 1e3) 
472                                                 PRINTMSG(" %6.0f",
473                                                                 (double)ld[10]);
474                                         else
475                                                 PRINTMSG(" %6.1f",
476                                                                 (double)ld[10]);
477                                 }
478
479                                 if (flag_o) {
480                                         PRINTMSG(" %6.0f", (double)ld[11]);
481                                         if (ld[12] > 1e3) 
482                                                 PRINTMSG(" %6.0f",
483                                                                 (double)ld[12]);
484                                         else
485                                                 PRINTMSG(" %6.1f", 
486                                                                 (double)ld[12]);
487                                 }
488
489                                 if (ld[7] > HIGH_PCT_BUSY_THRESH)
490                                         i = 3;
491                                 else if (ld[7] > MEDIUM_PCT_BUSY_THRESH)
492                                         i = 2;
493                                 else 
494                                         i = 1;
495                                 if (!flag_b)
496                                         attron(COLOR_PAIR(i));
497                                 PRINTMSG(" %6.1lf", (double)ld[7]);
498                                 if (!flag_b) {
499                                         attroff(COLOR_PAIR(i));
500                                         PRINTMSG("|");
501                                 } else
502                                         PRINTMSG(" ");
503                                 PRINTMSG(" %s", g_name);
504                                 if (!flag_b)
505                                         clrtoeol();
506                         }
507                         PRINTMSG("\n");
508                         *gsq = *gsp;
509                 }
510                 geom_stats_snapshot_free(sp);
511                 if (flag_b) {
512                         /* We loop extra to make sure we get the information. */
513                         if (!loop)
514                                 break;
515                         if (!flag_B)
516                                 loop = 0;
517                         else
518                                 if (fflush(stdout) == EOF)
519                                         goto out;
520                         usleep(flag_I);
521                         continue;
522                 }
523                 getyx(stdscr, cury, curx);
524                 getmaxyx(stdscr, maxy, maxx);
525                 clrtobot();
526                 if (maxy - 1 <= cury)
527                         move(maxy - 1, 0);
528                 refresh();
529                 usleep(flag_I);
530                 while((i = getch()) != ERR) {
531                         switch (i) {
532                         case '>':
533                                 flag_I *= 2;
534                                 break;
535                         case '<':
536                                 flag_I /= 2;
537                                 if (flag_I < 1000)
538                                         flag_I = 1000;
539                                 break;
540                         case 'c':
541                                 flag_c = !flag_c;
542                                 break;
543                         case 'f':
544                                 move(0,0);
545                                 clrtoeol();
546                                 refresh();
547                                 line = el_gets(el, &line_len);
548                                 if (line == NULL)
549                                         err(1, "el_gets");
550                                 if (line_len > 1)
551                                         history(hist, &hist_ev, H_ENTER, line);
552                                 strlcpy(tmp_f_s, line, sizeof(f_s));
553                                 if ((p = strchr(tmp_f_s, '\n')) != NULL)
554                                         *p = '\0';
555                                 /*
556                                  * Fix the terminal.  We messed up
557                                  * curses idea of the screen by using
558                                  * libedit.
559                                  */
560                                 clear();
561                                 refresh();
562                                 cbreak();
563                                 noecho();
564                                 nonl();
565                                 if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED)
566                                     != 0) {
567                                         move(0, 0);
568                                         printw("Invalid filter");
569                                         refresh();
570                                         sleep(1);
571                                 } else {
572                                         strlcpy(f_s, tmp_f_s, sizeof(f_s));
573                                         f_re = tmp_f_re;
574                                 }
575                                 break;
576                         case 'F':
577                                 f_s[0] = '\0';
578                                 break;
579                         case 'q':
580                                 quit = 1;
581                                 break;
582                         default:
583                                 break;
584                         }
585                 }
586         }
587 out:
588         if (!flag_b) {
589                 el_end(el);
590                 endwin();
591         }
592         exit(EX_OK);
593 }
594
595 static void
596 usage(void)
597 {
598         fprintf(stderr, "usage: gstat [-abBcCdps] [-f filter] [-I interval]\n");
599         exit(EX_USAGE);
600         /* NOTREACHED */
601 }