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