varnish-cache/bin/varnishstat/varnishstat_curses.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 * Author: Dag-Erling Smørgrav <des@des.no>
8
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
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 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
 * Statistics output program
32
 */
33
34
#include "config.h"
35
36
#include <stdlib.h>
37
#include <unistd.h>
38
#include <string.h>
39
#include <signal.h>
40
#include <poll.h>
41
#include <stdint.h>
42
#include <math.h>
43
44
#include "vdef.h"
45
#include "vas.h"
46
#include "miniobj.h"
47
#include "vqueue.h"
48
#include "vtim.h"
49
50
#include "varnishstat.h"
51
#include "vcurses.h"
52
53
#define LINES_STATUS            3
54
#define LINES_BAR_T             1
55
#define LINES_BAR_B             1
56
#define LINES_INFO              3
57
#define LINES_POINTS_MIN        3
58
59
#define N_COL                   6
60
#define COLW                    14
61
#define COLW_NAME_MIN           24
62
63
#define VALUE_MAX               999999999999
64
65
struct ma {
66
        unsigned n, nmax;
67
        double acc;
68
};
69
70
struct pt {
71
        unsigned                magic;
72
#define PT_MAGIC                0x41698E4F
73
        VTAILQ_ENTRY(pt)        list;
74
75
        const struct VSC_point  *vpt;
76
77
        char                    seen;
78
79
        uint64_t                cur, last;
80
        double                  t_cur, t_last;
81
        double                  chg, avg;
82
83
        struct ma               ma_10, ma_100, ma_1000;
84
};
85
86
struct hitrate {
87
        uint64_t lhit, lmiss;
88
        struct ma hr_10;
89
        struct ma hr_100;
90
        struct ma hr_1000;
91
};
92
static struct hitrate hitrate;
93
94
static VTAILQ_HEAD(, pt) ptlist = VTAILQ_HEAD_INITIALIZER(ptlist);
95
static int n_ptlist = 0;
96
static int n_ptarray = 0;
97
static struct pt **ptarray = NULL;
98
static const volatile uint64_t *mgt_uptime;
99
static const volatile uint64_t *main_uptime;
100
static const volatile uint64_t *main_cache_hit;
101
static const volatile uint64_t *main_cache_miss;
102
103
static int l_status, l_bar_t, l_points, l_bar_b, l_info;
104
static int colw_name = COLW_NAME_MIN;
105
static WINDOW *w_status = NULL;
106
static WINDOW *w_bar_t = NULL;
107
static WINDOW *w_points = NULL;
108
static WINDOW *w_bar_b = NULL;
109
static WINDOW *w_info = NULL;
110
111
static const struct VSC_level_desc *verbosity;
112
static int keep_running = 1;
113
static int hide_unseen = 1;
114
static int page_start = 0;
115
static int current = 0;
116
static int rebuild = 0;
117
static int redraw = 0;
118
static int sample = 0;
119
static int scale = 1;
120
static double t_sample = 0.;
121
static double interval = 1.;
122
static int vsm_status = 0;
123
124
static void
125 0
init_hitrate(void)
126
{
127 0
        memset(&hitrate, 0, sizeof (struct hitrate));
128 0
        if (main_cache_hit != NULL) {
129 0
                hitrate.lhit = *main_cache_hit;
130 0
                hitrate.lmiss = *main_cache_miss;
131
        }
132 0
        hitrate.hr_10.nmax = 10;
133 0
        hitrate.hr_100.nmax = 100;
134 0
        hitrate.hr_1000.nmax = 1000;
135 0
}
136
137
static void
138 0
update_ma(struct ma *ma, double val)
139
{
140 0
        AN(ma);
141 0
        AN(ma->nmax);
142 0
        if (ma->n < ma->nmax)
143 0
                ma->n++;
144 0
        ma->acc += (val - ma->acc) / (double)ma->n;
145 0
}
146
147
static void
148 0
update_position(void)
149
{
150
        int old_current, old_page_start;
151
152 0
        old_current = current;
153 0
        old_page_start = page_start;
154
155 0
        if (n_ptarray == 0) {
156 0
                current = 0;
157 0
                page_start = 0;
158
        } else {
159 0
                if (current < 0)
160 0
                        current = 0;
161 0
                if (current > n_ptarray - 1)
162 0
                        current = n_ptarray - 1;
163 0
                if (current < page_start)
164 0
                        page_start = current;
165 0
                if (current > page_start + (l_points - 1))
166 0
                        page_start = current - (l_points - 1);
167 0
                if (page_start < 0)
168 0
                        page_start = 0;
169 0
                if (page_start > n_ptarray - 1)
170 0
                        page_start = n_ptarray - 1;
171
        }
172
173 0
        if (current != old_current || page_start != old_page_start)
174 0
                redraw = 1;
175 0
}
176
177
static void
178 0
delete_pt_array(void)
179
{
180 0
        if (ptarray != NULL)
181 0
                free(ptarray);
182 0
        ptarray = NULL;
183 0
        n_ptarray = 0;
184
185 0
        update_position();
186 0
}
187
188
static void
189 0
build_pt_array(void)
190
{
191
        int i;
192
        struct pt *pt;
193 0
        struct pt *pt_current = NULL;
194 0
        int current_line = 0;
195
196 0
        if (current < n_ptarray) {
197 0
                pt_current = ptarray[current];
198 0
                current_line = current - page_start;
199
        }
200
201 0
        if (ptarray != NULL)
202 0
                delete_pt_array();
203 0
        AZ(n_ptarray);
204 0
        ptarray = calloc(n_ptlist, sizeof *ptarray);
205 0
        AN(ptarray);
206
207 0
        VTAILQ_FOREACH(pt, &ptlist, list) {
208 0
                CHECK_OBJ_NOTNULL(pt, PT_MAGIC);
209 0
                if (!pt->seen && hide_unseen)
210 0
                        continue;
211 0
                if (pt->vpt->level > verbosity)
212 0
                        continue;
213 0
                assert(n_ptarray < n_ptlist);
214 0
                ptarray[n_ptarray++] = pt;
215
        }
216 0
        assert(n_ptarray <= n_ptlist);
217
218 0
        for (i = 0; pt_current != NULL && i < n_ptarray; i++)
219 0
                if (ptarray[i] == pt_current)
220 0
                        break;
221 0
        current = i;
222 0
        page_start = current - current_line;
223 0
        update_position();
224
225 0
        rebuild = 0;
226 0
        redraw = 1;
227 0
}
228
229
static void
230 0
sample_points(void)
231
{
232
        struct pt *pt;
233
        uint64_t v;
234
235 0
        VTAILQ_FOREACH(pt, &ptlist, list) {
236 0
                AN(pt->vpt);
237 0
                AN(pt->vpt->ptr);
238 0
                v = *pt->vpt->ptr;
239 0
                if (v == 0 && !pt->seen)
240 0
                        continue;
241 0
                if (!pt->seen) {
242 0
                        pt->seen = 1;
243 0
                        rebuild = 1;
244
                }
245 0
                pt->last = pt->cur;
246 0
                pt->cur = v;
247 0
                pt->t_last = pt->t_cur;
248 0
                pt->t_cur = VTIM_mono();
249
250 0
                if (pt->t_last)
251 0
                        pt->chg = ((int64_t)pt->cur - (int64_t)pt->last) /
252 0
                            (pt->t_cur - pt->t_last);
253
254 0
                if (pt->vpt->semantics == 'g') {
255 0
                        pt->avg = 0.;
256 0
                        update_ma(&pt->ma_10, (int64_t)pt->cur);
257 0
                        update_ma(&pt->ma_100, (int64_t)pt->cur);
258 0
                        update_ma(&pt->ma_1000, (int64_t)pt->cur);
259 0
                } else if (pt->vpt->semantics == 'c') {
260 0
                        if (main_uptime != NULL && *main_uptime)
261 0
                                pt->avg = pt->cur / *main_uptime;
262
                        else
263 0
                                pt->avg = 0.;
264 0
                        if (pt->t_last) {
265 0
                                update_ma(&pt->ma_10, pt->chg);
266 0
                                update_ma(&pt->ma_100, pt->chg);
267 0
                                update_ma(&pt->ma_1000, pt->chg);
268
                        }
269
                }
270
        }
271 0
}
272
273
static void
274 0
sample_hitrate(void)
275
{
276
        double hr, mr, ratio;
277
        uint64_t hit, miss;
278
279 0
        if (main_cache_hit == NULL)
280 0
                return;
281
282 0
        hit = *main_cache_hit;
283 0
        miss = *main_cache_miss;
284 0
        hr = hit - hitrate.lhit;
285 0
        mr = miss - hitrate.lmiss;
286 0
        hitrate.lhit = hit;
287 0
        hitrate.lmiss = miss;
288
289 0
        if (hr + mr != 0)
290 0
                ratio = hr / (hr + mr);
291
        else
292 0
                ratio = 0;
293 0
        update_ma(&hitrate.hr_10, ratio);
294 0
        update_ma(&hitrate.hr_100, ratio);
295 0
        update_ma(&hitrate.hr_1000, ratio);
296
}
297
298
static void
299 0
sample_data(void)
300
{
301 0
        t_sample = VTIM_mono();
302 0
        sample = 0;
303 0
        redraw = 1;
304 0
        sample_points();
305 0
        sample_hitrate();
306 0
}
307
308
static void
309 0
make_windows(void)
310
{
311
        int Y, X;
312
        int y;
313
        int y_status, y_bar_t, y_points, y_bar_b, y_info;
314
315 0
        if (w_status) {
316 0
                delwin(w_status);
317 0
                w_status = NULL;
318
        }
319 0
        if (w_bar_t) {
320 0
                delwin(w_bar_t);
321 0
                w_bar_t = NULL;
322
        }
323 0
        if (w_points) {
324 0
                delwin(w_points);
325 0
                w_points = NULL;
326
        }
327 0
        if (w_bar_b) {
328 0
                delwin(w_bar_b);
329 0
                w_bar_b = NULL;
330
        }
331 0
        if (w_info) {
332 0
                delwin(w_info);
333 0
                w_info = NULL;
334
        }
335
336 0
        Y = LINES;
337 0
        X = COLS;
338
339 0
        l_status = LINES_STATUS;
340 0
        l_bar_t = LINES_BAR_T;
341 0
        l_bar_b = LINES_BAR_B;
342 0
        l_info = LINES_INFO;
343 0
        l_points = Y - (l_status + l_bar_t + l_bar_b + l_info);
344 0
        if (l_points < LINES_POINTS_MIN) {
345 0
                l_points += l_info;
346 0
                l_info = 0;
347
        }
348 0
        if (l_points < LINES_POINTS_MIN)
349 0
                l_points = LINES_POINTS_MIN;
350
351 0
        y = 0;
352 0
        y_status = y;
353 0
        y += l_status;
354 0
        y_bar_t = y;
355 0
        y += l_bar_t;
356 0
        y_points = y;
357 0
        y += l_points;
358 0
        y_bar_b = y;
359 0
        y += l_bar_b;
360 0
        y_info = y;
361 0
        y += l_info;
362 0
        assert(y >= Y);
363
364 0
        w_status = newwin(l_status, X, y_status, 0);
365 0
        AN(w_status);
366 0
        nodelay(w_status, 1);
367 0
        keypad(w_status, 1);
368 0
        wnoutrefresh(w_status);
369
370 0
        w_bar_t = newwin(l_bar_t, X, y_bar_t, 0);
371 0
        AN(w_bar_t);
372 0
        wbkgd(w_bar_t, A_REVERSE);
373 0
        wnoutrefresh(w_bar_t);
374
375 0
        w_points = newwin(l_points, X, y_points, 0);
376 0
        AN(w_points);
377 0
        wnoutrefresh(w_points);
378
379 0
        w_bar_b = newwin(l_bar_b, X, y_bar_b, 0);
380 0
        AN(w_bar_b);
381 0
        wbkgd(w_bar_b, A_REVERSE);
382 0
        wnoutrefresh(w_bar_b);
383
384 0
        if (l_info) {
385 0
                w_info = newwin(l_info, X, y_info, 0);
386 0
                AN(w_info);
387 0
                wnoutrefresh(w_info);
388
        }
389
390 0
        if (X - COLW_NAME_MIN > N_COL * COLW)
391 0
                colw_name = X - (N_COL * COLW);
392
        else
393 0
                colw_name = COLW_NAME_MIN;
394
395 0
        redraw = 1;
396 0
}
397
398
static void
399 0
print_duration(WINDOW *w, time_t t)
400
{
401
402 0
        wprintw(w, "%4jd+%02jd:%02jd:%02jd",
403 0
            (intmax_t)t / 86400, (intmax_t)(t % 86400) / 3600,
404 0
            (intmax_t)(t % 3600) / 60, (intmax_t)t % 60);
405 0
}
406
407
static void
408 0
running(WINDOW *w, time_t up, int flg)
409
{
410 0
        if (vsm_status & flg) {
411 0
                print_duration(w_status, up);
412
        } else {
413 0
                wattron(w, A_STANDOUT);
414 0
                wprintw(w, "  Not Running");
415 0
                wattroff(w, A_STANDOUT);
416
        }
417 0
}
418
419
static void
420 0
draw_status(void)
421
{
422 0
        time_t up_mgt = 0;
423 0
        time_t up_chld = 0;
424
425 0
        AN(w_status);
426
427 0
        werase(w_status);
428
429 0
        if (mgt_uptime != NULL)
430 0
                up_mgt = *mgt_uptime;
431 0
        if (main_uptime != NULL)
432 0
                up_chld = *main_uptime;
433
434 0
        mvwprintw(w_status, 0, 0, "Uptime mgt:   ");
435 0
        running(w_status, up_mgt, VSM_MGT_RUNNING);
436 0
        mvwprintw(w_status, 1, 0, "Uptime child: ");
437 0
        running(w_status, up_chld, VSM_WRK_RUNNING);
438
439 0
        if (COLS > 70) {
440 0
                mvwprintw(w_status, 0, getmaxx(w_status) - 37,
441
                    "Hitrate n: %8u %8u %8u", hitrate.hr_10.n, hitrate.hr_100.n,
442
                    hitrate.hr_1000.n);
443 0
                mvwprintw(w_status, 1, getmaxx(w_status) - 37,
444
                    "   avg(n): %8.4f %8.4f %8.4f", hitrate.hr_10.acc,
445
                    hitrate.hr_100.acc, hitrate.hr_1000.acc);
446
        }
447
448 0
        wnoutrefresh(w_status);
449 0
}
450
451
static void
452 0
draw_bar_t(void)
453
{
454
        int X, x;
455
        enum {
456
                COL_CUR,
457
                COL_CHG,
458
                COL_AVG,
459
                COL_MA10,
460
                COL_MA100,
461
                COL_MA1000,
462
                COL_LAST
463
        } col;
464
465 0
        AN(w_bar_t);
466
467 0
        X = getmaxx(w_bar_t);
468 0
        x = 0;
469 0
        werase(w_bar_t);
470 0
        if (page_start > 0)
471 0
                mvwprintw(w_bar_t, 0, x, "^^^");
472 0
        x += 4;
473 0
        mvwprintw(w_bar_t, 0, x, "%.*s", colw_name - 4, "NAME");
474 0
        x += colw_name - 4;
475 0
        col = 0;
476 0
        while (col < COL_LAST) {
477 0
                if (X - x < COLW)
478 0
                        break;
479 0
                switch (col) {
480
                case COL_CUR:
481 0
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "CURRENT");
482 0
                        break;
483
                case COL_CHG:
484 0
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "CHANGE");
485 0
                        break;
486
                case COL_AVG:
487 0
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVERAGE");
488 0
                        break;
489
                case COL_MA10:
490 0
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_10");
491 0
                        break;
492
                case COL_MA100:
493 0
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_100");
494 0
                        break;
495
                case COL_MA1000:
496 0
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_1000");
497 0
                        break;
498
                default:
499 0
                        break;
500
                }
501 0
                x += COLW;
502 0
                col++;
503
        }
504
505 0
        wnoutrefresh(w_bar_t);
506 0
}
507
508
static void
509 0
draw_line_default(WINDOW *w, int y, int x, int X, const struct pt *pt)
510
{
511
        enum {
512
                COL_CUR,
513
                COL_CHG,
514
                COL_AVG,
515
                COL_MA10,
516
                COL_MA100,
517
                COL_MA1000,
518
                COL_LAST
519
        } col;
520
521 0
        AN(w);
522 0
        AN(pt);
523
524 0
        col = 0;
525 0
        while (col < COL_LAST) {
526 0
                if (X - x < COLW)
527 0
                        break;
528 0
                switch (col) {
529
                case COL_CUR:
530 0
                        mvwprintw(w, y, x, " %12ju", (uintmax_t)pt->cur);
531 0
                        break;
532
                case COL_CHG:
533 0
                        if (pt->t_last)
534 0
                                mvwprintw(w, y, x, " %12.2f", pt->chg);
535
                        else
536 0
                                mvwprintw(w, y, x, " %12s", ".  ");
537 0
                        break;
538
                case COL_AVG:
539 0
                        if (pt->avg)
540 0
                                mvwprintw(w, y, x, " %12.2f", pt->avg);
541
                        else
542 0
                                mvwprintw(w, y, x, " %12s", ".  ");
543 0
                        break;
544
                case COL_MA10:
545 0
                        mvwprintw(w, y, x, " %12.2f", pt->ma_10.acc);
546 0
                        break;
547
                case COL_MA100:
548 0
                        mvwprintw(w, y, x, " %12.2f", pt->ma_100.acc);
549 0
                        break;
550
                case COL_MA1000:
551 0
                        mvwprintw(w, y, x, " %12.2f", pt->ma_1000.acc);
552 0
                        break;
553
                default:
554 0
                        break;
555
                }
556 0
                x += COLW;
557 0
                col++;
558
        }
559 0
}
560
561
static double
562 0
scale_bytes(double val, char *q)
563
{
564
        const char *p;
565
566 0
        for (p = " KMGTPEZY"; *p; p++) {
567 0
                if (fabs(val) < 1024.)
568 0
                        break;
569 0
                val /= 1024.;
570
        }
571 0
        *q = *p;
572 0
        return (val);
573
}
574
575
static void
576 0
print_bytes(WINDOW *w, double val)
577
{
578 0
        char q = ' ';
579
580 0
        if (scale)
581 0
                val = scale_bytes(val, &q);
582 0
        wprintw(w, " %12.2f%c", val, q);
583 0
}
584
585
static void
586 0
print_trunc(WINDOW *w, uintmax_t val)
587
{
588 0
        if (val > VALUE_MAX) {
589 0
                while (val > VALUE_MAX)
590 0
                        val /= 1000;
591 0
                wprintw(w, " %9ju...", val);
592
        } else
593 0
                wprintw(w, " %12ju", val);
594 0
}
595
596
static void
597 0
draw_line_bytes(WINDOW *w, int y, int x, int X, const struct pt *pt)
598
{
599
        enum {
600
                COL_CUR,
601
                COL_CHG,
602
                COL_AVG,
603
                COL_MA10,
604
                COL_MA100,
605
                COL_MA1000,
606
                COL_LAST
607
        } col;
608
609 0
        AN(w);
610 0
        AN(pt);
611
612 0
        col = 0;
613 0
        while (col < COL_LAST) {
614 0
                if (X - x < COLW)
615 0
                        break;
616 0
                wmove(w, y, x);
617 0
                switch (col) {
618
                case COL_CUR:
619 0
                        if (scale && pt->cur > 1024)
620 0
                                print_bytes(w, (double)pt->cur);
621
                        else
622 0
                                print_trunc(w, (uintmax_t)pt->cur);
623 0
                        break;
624
                case COL_CHG:
625 0
                        if (pt->t_last)
626 0
                                print_bytes(w, pt->chg);
627
                        else
628 0
                                wprintw(w, " %12s", ".  ");
629 0
                        break;
630
                case COL_AVG:
631 0
                        if (pt->avg)
632 0
                                print_bytes(w, pt->avg);
633
                        else
634 0
                                wprintw(w, " %12s", ".  ");
635 0
                        break;
636
                case COL_MA10:
637 0
                        print_bytes(w, pt->ma_10.acc);
638 0
                        break;
639
                case COL_MA100:
640 0
                        print_bytes(w, pt->ma_100.acc);
641 0
                        break;
642
                case COL_MA1000:
643 0
                        print_bytes(w, pt->ma_1000.acc);
644 0
                        break;
645
                default:
646 0
                        break;
647
                }
648 0
                x += COLW;
649 0
                col++;
650
        }
651 0
}
652
653
static void
654 0
draw_line_bitmap(WINDOW *w, int y, int x, int X, const struct pt *pt)
655
{
656
        int ch;
657
        enum {
658
                COL_VAL,
659
                COL_MAP,
660
                COL_LAST
661
        } col;
662
663 0
        AN(w);
664 0
        AN(pt);
665 0
        assert(pt->vpt->format == 'b');
666
667 0
        col = 0;
668 0
        while (col < COL_LAST) {
669 0
                switch (col) {
670
                case COL_VAL:
671 0
                        if (X - x < COLW)
672 0
                                return;
673 0
                        mvwprintw(w, y, x, "   %10.10jx",
674 0
                            (uintmax_t)((pt->cur >> 24) & 0xffffffffffLL));
675 0
                        x += COLW;
676 0
                        break;
677
                case COL_MAP:
678 0
                        if (X - x < 2 * COLW)
679 0
                                return;
680 0
                        x += (2 * COLW) - 24;
681 0
                        for (ch = 0x800000; ch; ch >>= 1) {
682 0
                                if (pt->cur & ch)
683 0
                                        mvwaddch(w, y, x, 'V');
684
                                else
685 0
                                        mvwaddch(w, y, x, '_');
686 0
                                x++;
687
                        }
688 0
                        break;
689
                default:
690 0
                        break;
691
                }
692 0
                col++;
693
        }
694
}
695
696
static void
697 0
draw_line_duration(WINDOW *w, int y, int x, int X, const struct pt *pt)
698
{
699
        enum {
700
                COL_DUR,
701
                COL_LAST
702
        } col;
703
704 0
        AN(w);
705 0
        AN(pt);
706
707 0
        col = 0;
708 0
        while (col < COL_LAST) {
709 0
                if (X - x < COLW)
710 0
                        break;
711 0
                switch (col) {
712
                case COL_DUR:
713 0
                        wmove(w, y, x);
714 0
                        if (scale)
715 0
                                print_duration(w, pt->cur);
716
                        else
717 0
                                wprintw(w, " %12ju", (uintmax_t)pt->cur);
718 0
                        break;
719
                default:
720 0
                        break;
721
                }
722 0
                x += COLW;
723 0
                col++;
724
        }
725 0
}
726
727
static void
728 0
draw_line(WINDOW *w, int y, const struct pt *pt)
729
{
730
        int x, X;
731
732 0
        assert(colw_name >= COLW_NAME_MIN);
733 0
        X = getmaxx(w);
734 0
        x = 0;
735 0
        if (strlen(pt->vpt->name) > colw_name)
736 0
                mvwprintw(w, y, x, "%.*s...", colw_name - 3, pt->vpt->name);
737
        else
738 0
                mvwprintw(w, y, x, "%.*s", colw_name, pt->vpt->name);
739 0
        x += colw_name;
740
741 0
        switch (pt->vpt->format) {
742
        case 'b':
743 0
                draw_line_bitmap(w, y, x, X, pt);
744 0
                break;
745
        case 'B':
746 0
                draw_line_bytes(w, y, x, X, pt);
747 0
                break;
748
        case 'd':
749 0
                draw_line_duration(w, y, x, X, pt);
750 0
                break;
751
        default:
752 0
                draw_line_default(w, y, x, X, pt);
753 0
                break;
754
        }
755 0
}
756
757
static void
758 0
draw_points(void)
759
{
760
        int Y, X;
761
        int line;
762
        int n;
763
764 0
        AN(w_points);
765
766 0
        werase(w_points);
767 0
        if (n_ptarray == 0) {
768 0
                wnoutrefresh(w_points);
769 0
                return;
770
        }
771
772 0
        assert(current >= 0);
773 0
        assert(current < n_ptarray);
774 0
        assert(page_start >= 0);
775 0
        assert(page_start < n_ptarray);
776 0
        assert(current >= page_start);
777 0
        assert(current - page_start < l_points);
778
779 0
        getmaxyx(w_points, Y, X);
780
        (void)Y;
781
        (void)X;
782 0
        for (line = 0; line < l_points; line++) {
783 0
                n = line + page_start;
784 0
                if (n >= n_ptarray)
785 0
                        break;
786 0
                if (n == current)
787 0
                        wattron(w_points, A_BOLD);
788 0
                draw_line(w_points, line, ptarray[n]);
789 0
                if (n == current)
790 0
                        wattroff(w_points, A_BOLD);
791
        }
792 0
        wnoutrefresh(w_points);
793
}
794
795
static void
796 0
draw_bar_b(void)
797
{
798
        int x, X;
799
        char buf[64];
800
801 0
        AN(w_bar_b);
802
803 0
        x = 0;
804 0
        X = getmaxx(w_bar_b);
805 0
        werase(w_bar_b);
806 0
        if (page_start + l_points < n_ptarray)
807 0
                mvwprintw(w_bar_b, 0, x, "vvv");
808 0
        x += 4;
809 0
        if (current < n_ptarray - 1)
810 0
                mvwprintw(w_bar_b, 0, x, "%s", ptarray[current]->vpt->name);
811
812 0
        bprintf(buf, "%d-%d/%d", page_start + 1,
813
            page_start + l_points < n_ptarray ?
814
                page_start + l_points : n_ptarray,
815
            n_ptarray);
816 0
        mvwprintw(w_bar_b, 0, X - strlen(buf), "%s", buf);
817 0
        X -= strlen(buf) + 2;
818
819 0
        if (verbosity != NULL) {
820 0
                mvwprintw(w_bar_b, 0, X - strlen(verbosity->label), "%s",
821 0
                    verbosity->label);
822 0
                X -= strlen(verbosity->label) + 2;
823
        }
824 0
        if (!hide_unseen)
825 0
                mvwprintw(w_bar_b, 0, X - 6, "%s", "UNSEEN");
826
827 0
        wnoutrefresh(w_bar_b);
828 0
}
829
830
static void
831 0
draw_info(void)
832
{
833
834 0
        if (w_info == NULL)
835 0
                return;
836
837 0
        werase(w_info);
838 0
        if (current < n_ptarray - 1) {
839
                /* XXX: Word wrapping, and overflow handling? */
840 0
                mvwprintw(w_info, 0, 0, "%s:",
841 0
                    ptarray[current]->vpt->sdesc);
842 0
                mvwprintw(w_info, 1, 0, "%s",
843 0
                    ptarray[current]->vpt->ldesc);
844
        }
845 0
        wnoutrefresh(w_info);
846
}
847
848
static void
849 0
draw_screen(void)
850
{
851 0
        draw_status();
852 0
        draw_bar_t();
853 0
        draw_points();
854 0
        draw_bar_b();
855 0
        draw_info();
856 0
        doupdate();
857 0
        redraw = 0;
858 0
}
859
860
static void
861 0
handle_keypress(int ch)
862
{
863 0
        switch (ch) {
864
        case KEY_UP:
865
        case 107:  /* k */
866 0
                if (current == 0)
867 0
                        return;
868 0
                current--;
869 0
                break;
870
        case KEY_DOWN:
871
        case 106:  /* j */
872 0
                if (current == n_ptarray - 1)
873 0
                        return;
874 0
                current++;
875 0
                break;
876
        case KEY_PPAGE:
877
        case 'b':
878 0
                current -= l_points;
879 0
                page_start -= l_points;
880 0
                break;
881
        case KEY_NPAGE:
882
        case ' ':
883 0
                current += l_points;
884 0
                if (page_start + l_points < n_ptarray - 1)
885 0
                        page_start += l_points;
886 0
                break;
887
        case 'd':
888 0
                hide_unseen = 1 - hide_unseen;
889 0
                rebuild = 1;
890 0
                break;
891
        case 'e':
892 0
                scale = 1 - scale;
893 0
                rebuild = 1;
894 0
                break;
895
        case 'g':
896 0
                current = 0;
897 0
                page_start = 0;
898 0
                break;
899
        case 'G':
900 0
                current = n_ptarray - 1;
901 0
                page_start = (current - l_points) + 1;
902 0
                break;
903
        case 'v':
904 0
                verbosity = VSC_ChangeLevel(verbosity, 1);
905 0
                rebuild = 1;
906 0
                break;
907
        case 'V':
908 0
                verbosity = VSC_ChangeLevel(verbosity, -1);
909 0
                rebuild = 1;
910 0
                break;
911
        case 'q':
912 0
                keep_running = 0;
913 0
                return;
914
        case '\003':            /* Ctrl-C */
915 0
                AZ(raise(SIGINT));
916 0
                return;
917
        case '\024':            /* Ctrl-T */
918 0
                sample = 1;
919 0
                return;
920
        case '\032':            /* Ctrl-Z */
921 0
                AZ(raise(SIGTSTP));
922 0
                return;
923
        default:
924 0
                return;
925
        }
926
927 0
        update_position();
928 0
        redraw = 1;
929
}
930
931
static void * v_matchproto_(VSC_new_f)
932 0
newpt(void *priv, const struct VSC_point *const vpt)
933
{
934
        struct pt *pt;
935
936 0
        ALLOC_OBJ(pt, PT_MAGIC);
937 0
        AN(pt);
938 0
        AZ(priv);
939 0
        pt->vpt = vpt;
940 0
        pt->last = *pt->vpt->ptr;
941 0
        pt->ma_10.nmax = 10;
942 0
        pt->ma_100.nmax = 100;
943 0
        pt->ma_1000.nmax = 1000;
944
945 0
        VTAILQ_INSERT_TAIL(&ptlist, pt, list);
946 0
        n_ptlist++;
947
948 0
        AZ(strcmp(vpt->ctype, "uint64_t"));
949
950 0
        if (!strcmp(vpt->name, "MGT.uptime"))
951 0
                mgt_uptime = vpt->ptr;
952 0
        if (!strcmp(vpt->name, "MAIN.uptime"))
953 0
                main_uptime = vpt->ptr;
954 0
        if (!strcmp(vpt->name, "MAIN.cache_hit"))
955 0
                main_cache_hit = vpt->ptr;
956 0
        if (!strcmp(vpt->name, "MAIN.cache_miss"))
957 0
                main_cache_miss = vpt->ptr;
958 0
        return (pt);
959
}
960
961
static void v_matchproto_(VSC_destroy_f)
962 0
delpt(void *priv, const struct VSC_point *const vpt)
963
{
964
        struct pt *pt;
965
966 0
        AZ(priv);
967 0
        CAST_OBJ_NOTNULL(pt, vpt->priv, PT_MAGIC);
968 0
        VTAILQ_REMOVE(&ptlist, pt, list);
969 0
        n_ptlist--;
970 0
        FREE_OBJ(pt);
971 0
        if (vpt->ptr == mgt_uptime)
972 0
                mgt_uptime = NULL;
973 0
        if (vpt->ptr == main_uptime)
974 0
                main_uptime = NULL;
975 0
        if (vpt->ptr == main_cache_hit)
976 0
                main_cache_hit = NULL;
977 0
        if (vpt->ptr == main_cache_miss)
978 0
                main_cache_miss = NULL;
979 0
}
980
981
void
982 0
do_curses(struct vsm *vsm, struct vsc *vsc, double delay)
983
{
984
        struct pollfd pollfd;
985
        long t;
986
        int ch;
987
        double now;
988
989 0
        interval = delay;
990
991 0
        verbosity = VSC_ChangeLevel(NULL, 0);
992
993 0
        initscr();
994 0
        raw();
995 0
        noecho();
996 0
        nonl();
997 0
        curs_set(0);
998
999 0
        pollfd.fd = STDIN_FILENO;
1000 0
        pollfd.events = POLLIN;
1001
1002 0
        make_windows();
1003 0
        doupdate();
1004
1005 0
        VSC_State(vsc, newpt, delpt, NULL);
1006
1007 0
        rebuild = VSM_WRK_RESTARTED;
1008 0
        while (keep_running) {
1009 0
                vsm_status = VSM_Status(vsm);
1010 0
                rebuild |= vsm_status & ~(VSM_MGT_RUNNING|VSM_WRK_RUNNING);
1011 0
                if (rebuild) {
1012 0
                        (void)VSC_Iter(vsc, vsm, NULL, NULL);
1013 0
                        if (rebuild & (VSM_MGT_RESTARTED|VSM_WRK_RESTARTED))
1014 0
                                init_hitrate();
1015 0
                        build_pt_array();
1016 0
                        redraw = 1;
1017
                }
1018
1019 0
                now = VTIM_mono();
1020 0
                if (now - t_sample > interval)
1021 0
                        sample = 1;
1022 0
                if (sample)
1023 0
                        sample_data();
1024 0
                if (redraw)
1025 0
                        draw_screen();
1026
1027 0
                t = (t_sample + interval - now) * 1000;
1028 0
                if (t > 0)
1029 0
                        (void)poll(&pollfd, 1, t);
1030
1031 0
                switch (ch = wgetch(w_status)) {
1032
                case ERR:
1033 0
                        break;
1034
#ifdef KEY_RESIZE /* sigh, Solaris lacks this.. */
1035
                case KEY_RESIZE:
1036 0
                        make_windows();
1037 0
                        update_position();
1038 0
                        break;
1039
#endif
1040
                default:
1041 0
                        handle_keypress(ch);
1042 0
                        break;
1043
                }
1044
        }
1045 0
        VSC_Destroy(&vsc, vsm);
1046 0
        AN(VTAILQ_EMPTY(&ptlist));
1047 0
        VSM_Destroy(&vsm);
1048 0
        AZ(endwin());
1049 0
}