varnish-cache/bin/varnishstat/varnishstat_curses.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 * Author: Dag-Erling Smørgrav <des@des.no>
7
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
8
 *
9
 * SPDX-License-Identifier: BSD-2-Clause
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 *
32
 * Statistics output program
33
 */
34
35
#include "config.h"
36
37
#include <stdlib.h>
38
#include <unistd.h>
39
#include <string.h>
40
#include <stdint.h>
41
#include <math.h>
42
43
#include "vdef.h"
44
#include "vas.h"
45
#include "miniobj.h"
46
#include "vqueue.h"
47
#include "vtim.h"
48
#include "vapi/vsig.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
#define REBUILD_NEXT            (1u << 0)
66
#define REBUILD_FIRST           (1u << 1)
67
68
enum kb_e {
69
#define BINDING(name, desc) KB_ ## name,
70
#define BINDING_SIG
71
#include "varnishstat_bindings.h"
72
};
73
74
struct ma {
75
        unsigned n, nmax;
76
        double acc;
77
};
78
79
struct pt {
80
        unsigned                magic;
81
#define PT_MAGIC                0x41698E4F
82
        VTAILQ_ENTRY(pt)        list;
83
84
        const struct VSC_point  *vpt;
85
86
        char                    seen;
87
88
        uint64_t                cur, last;
89
        double                  t_cur, t_last;
90
        double                  chg, avg;
91
92
        struct ma               ma_10, ma_100, ma_1000;
93
};
94
95
struct hitrate {
96
        uint64_t lhit, lmiss;
97
        struct ma hr_10;
98
        struct ma hr_100;
99
        struct ma hr_1000;
100
};
101
static struct hitrate hitrate;
102
103
static VTAILQ_HEAD(, pt) ptlist = VTAILQ_HEAD_INITIALIZER(ptlist);
104
static int n_ptlist = 0;
105
static int n_ptarray = 0;
106
static struct pt **ptarray = NULL;
107
static const volatile uint64_t *mgt_uptime;
108
static const volatile uint64_t *main_uptime;
109
static const volatile uint64_t *main_cache_hit;
110
static const volatile uint64_t *main_cache_miss;
111
112
static int l_status, l_bar_t, l_points, l_bar_b, l_info;
113
static unsigned colw_name = COLW_NAME_MIN;
114
static WINDOW *w_status = NULL;
115
static WINDOW *w_bar_t = NULL;
116
static WINDOW *w_points = NULL;
117
static WINDOW *w_bar_b = NULL;
118
static WINDOW *w_info = NULL;
119
120
static const struct VSC_level_desc *verbosity;
121
static int show_help = 0;
122
static int help_line = 0;
123
static int keep_running = 1;
124
static int hide_unseen = 1;
125
static int raw_vsc = 0;
126
static int page_start = 0;
127
static int current = 0;
128
static int rebuild = 0;
129
static int redraw = 0;
130
static int sample = 0;
131
static int scale = 1;
132
static double t_sample = 0.;
133
static double interval = 1.;
134
static unsigned vsm_status = 0;
135
136
#define NOTIF_MAXLEN 256
137
static char notification_message[NOTIF_MAXLEN] = "";
138
static vtim_mono notification_eol = 0.0;
139
140
static void
141 18
init_hitrate(void)
142
{
143 18
        memset(&hitrate, 0, sizeof (struct hitrate));
144 18
        if (main_cache_hit != NULL) {
145 18
                hitrate.lhit = *main_cache_hit;
146 18
                hitrate.lmiss = *main_cache_miss;
147 18
        }
148 18
        hitrate.hr_10.nmax = 10;
149 18
        hitrate.hr_100.nmax = 100;
150 18
        hitrate.hr_1000.nmax = 1000;
151 18
}
152
153
static void
154 15333
update_ma(struct ma *ma, double val)
155
{
156 15333
        AN(ma);
157 15333
        AN(ma->nmax);
158 15333
        if (ma->n < ma->nmax)
159 15333
                ma->n++;
160 15333
        ma->acc += (val - ma->acc) / (double)ma->n;
161 15333
}
162
163
static void
164 150
update_position(void)
165
{
166
        int old_current, old_page_start;
167
168 150
        old_current = current;
169 150
        old_page_start = page_start;
170
171 150
        if (n_ptarray == 0) {
172 66
                current = 0;
173 66
                page_start = 0;
174 66
        } else {
175 84
                current = vlimit_t(int, current, 0, n_ptarray - 1);
176 84
                page_start = vmin(page_start, current);
177 84
                if (current > page_start + (l_points - 1))
178 6
                        page_start = current - (l_points - 1);
179 84
                page_start = vlimit_t(int, page_start, 0, n_ptarray - 1);
180
        }
181
182 150
        if (current != old_current || page_start != old_page_start)
183 30
                redraw = 1;
184 150
}
185
186
static void
187 48
delete_pt_array(void)
188
{
189 48
        if (ptarray != NULL)
190 48
                free(ptarray);
191 48
        ptarray = NULL;
192 48
        n_ptarray = 0;
193
194 48
        update_position();
195 48
}
196
197
static void
198 66
build_pt_array(void)
199
{
200
        int i;
201
        struct pt *pt;
202 66
        struct pt *pt_current = NULL;
203 66
        int current_line = 0;
204
205 66
        if (current < n_ptarray) {
206 30
                pt_current = ptarray[current];
207 30
                current_line = current - page_start;
208 30
        }
209
210 66
        if (ptarray != NULL)
211 48
                delete_pt_array();
212 66
        AZ(n_ptarray);
213 66
        ptarray = calloc(n_ptlist, sizeof *ptarray);
214 66
        AN(ptarray);
215
216 20853
        VTAILQ_FOREACH(pt, &ptlist, list) {
217 20787
                CHECK_OBJ_NOTNULL(pt, PT_MAGIC);
218 20787
                if (pt->vpt->level > verbosity) {
219 11934
                        if (has_f && (rebuild & REBUILD_FIRST))
220 18
                                verbosity = VSC_ChangeLevel(verbosity,
221 9
                                    pt->vpt->level - verbosity);
222
                        else
223 11925
                                continue;
224 9
                }
225 8862
                if (!pt->seen && hide_unseen)
226 6949
                        continue;
227 1913
                assert(n_ptarray < n_ptlist);
228 1913
                ptarray[n_ptarray++] = pt;
229 1913
        }
230 66
        assert(n_ptarray <= n_ptlist);
231
232 1244
        for (i = 0; pt_current != NULL && i < n_ptarray; i++)
233 1208
                if (ptarray[i] == pt_current)
234 30
                        break;
235 66
        current = i;
236 66
        page_start = current - current_line;
237 66
        update_position();
238
239 66
        rebuild = 0;
240 66
        redraw = 1;
241 66
}
242
243
static void
244 75
sample_points(void)
245
{
246
        struct pt *pt;
247
        uint64_t v;
248
249 24156
        VTAILQ_FOREACH(pt, &ptlist, list) {
250 24081
                AN(pt->vpt);
251 24081
                AN(pt->vpt->ptr);
252 24081
                v = VSC_Value(pt->vpt);
253 24081
                if (v == 0 && !pt->seen)
254 17978
                        continue;
255 6103
                if (!pt->seen) {
256 1421
                        pt->seen = 1;
257 1421
                        rebuild = REBUILD_NEXT;
258 1421
                }
259 6103
                pt->last = pt->cur;
260 6103
                pt->cur = v;
261 6103
                pt->t_last = pt->t_cur;
262 6103
                pt->t_cur = VTIM_mono();
263
264 6103
                if (pt->t_last)
265 9364
                        pt->chg = ((int64_t)pt->cur - (int64_t)pt->last) /
266 4682
                            (pt->t_cur - pt->t_last);
267
268 6103
                if (pt->vpt->semantics == 'g') {
269 1758
                        pt->avg = 0.;
270 1758
                        update_ma(&pt->ma_10, (int64_t)pt->cur);
271 1758
                        update_ma(&pt->ma_100, (int64_t)pt->cur);
272 1758
                        update_ma(&pt->ma_1000, (int64_t)pt->cur);
273 6103
                } else if (pt->vpt->semantics == 'c') {
274 4285
                        if (main_uptime != NULL && *main_uptime)
275 3688
                                pt->avg = pt->cur / (double)*main_uptime;
276
                        else
277 597
                                pt->avg = 0.;
278 4285
                        if (pt->t_last) {
279 3278
                                update_ma(&pt->ma_10, pt->chg);
280 3278
                                update_ma(&pt->ma_100, pt->chg);
281 3278
                                update_ma(&pt->ma_1000, pt->chg);
282 3278
                        }
283 4285
                }
284 6103
        }
285 75
}
286
287
static void
288 75
sample_hitrate(void)
289
{
290
        double hr, mr, ratio;
291
        uint64_t hit, miss;
292
293 75
        if (main_cache_hit == NULL)
294 0
                return;
295
296 75
        hit = *main_cache_hit;
297 75
        miss = *main_cache_miss;
298 75
        hr = hit - hitrate.lhit;
299 75
        mr = miss - hitrate.lmiss;
300 75
        hitrate.lhit = hit;
301 75
        hitrate.lmiss = miss;
302
303 75
        if (hr + mr != 0)
304 9
                ratio = hr / (hr + mr);
305
        else
306 66
                ratio = 0;
307 75
        update_ma(&hitrate.hr_10, ratio);
308 75
        update_ma(&hitrate.hr_100, ratio);
309 75
        update_ma(&hitrate.hr_1000, ratio);
310 75
}
311
312
static void
313 75
sample_data(void)
314
{
315 75
        t_sample = VTIM_mono();
316 75
        sample = 0;
317 75
        redraw = 1;
318 75
        sample_points();
319 75
        sample_hitrate();
320 75
}
321
322
static void
323 105
destroy_window(WINDOW **w)
324
{
325
326 105
        AN(w);
327 105
        if (*w == NULL)
328 90
                return;
329 15
        assert(delwin(*w) != ERR);
330 15
        *w = NULL;
331 105
}
332
333
static void
334 21
make_windows(void)
335
{
336
        int Y, X;
337
        int y;
338
        int y_status, y_bar_t, y_points, y_bar_b, y_info;
339
340 21
        destroy_window(&w_status);
341 21
        destroy_window(&w_bar_t);
342 21
        destroy_window(&w_points);
343 21
        destroy_window(&w_bar_b);
344 21
        destroy_window(&w_info);
345
346 21
        Y = LINES;
347 21
        X = COLS;
348
349 21
        l_status = LINES_STATUS;
350 21
        l_bar_t = LINES_BAR_T;
351 21
        l_bar_b = LINES_BAR_B;
352 21
        l_info = LINES_INFO;
353 21
        l_points = Y - (l_status + l_bar_t + l_bar_b + l_info);
354 21
        if (l_points < LINES_POINTS_MIN) {
355 0
                l_points += l_info;
356 0
                l_info = 0;
357 0
        }
358 21
        l_points = vmax(l_points, LINES_POINTS_MIN);
359
360 21
        y = 0;
361 21
        y_status = y;
362 21
        y += l_status;
363 21
        y_bar_t = y;
364 21
        y += l_bar_t;
365 21
        y_points = y;
366 21
        y += l_points;
367 21
        y_bar_b = y;
368 21
        y += l_bar_b;
369 21
        y_info = y;
370 21
        y += l_info;
371 21
        assert(y >= Y);
372
373 21
        w_status = newwin(l_status, X, y_status, 0);
374 21
        AN(w_status);
375 21
        nodelay(w_status, 1);
376 21
        keypad(w_status, 1);
377 21
        wnoutrefresh(w_status);
378
379 21
        w_bar_t = newwin(l_bar_t, X, y_bar_t, 0);
380 21
        AN(w_bar_t);
381 21
        wbkgd(w_bar_t, A_REVERSE);
382 21
        wnoutrefresh(w_bar_t);
383
384 21
        w_points = newwin(l_points, X, y_points, 0);
385 21
        AN(w_points);
386 21
        wnoutrefresh(w_points);
387
388 21
        w_bar_b = newwin(l_bar_b, X, y_bar_b, 0);
389 21
        AN(w_bar_b);
390 21
        wbkgd(w_bar_b, A_REVERSE);
391 21
        wnoutrefresh(w_bar_b);
392
393 21
        if (l_info) {
394 21
                w_info = newwin(l_info, X, y_info, 0);
395 21
                AN(w_info);
396 21
                wnoutrefresh(w_info);
397 21
        }
398
399 21
        if (X - COLW_NAME_MIN > N_COL * COLW)
400 3
                colw_name = X - (N_COL * COLW);
401
        else
402 18
                colw_name = COLW_NAME_MIN;
403
404 21
        redraw = 1;
405 21
}
406
407
static void
408 207
print_duration(WINDOW *w, uint64_t t)
409
{
410
411 414
        wprintw(w, "%4ju+%02ju:%02ju:%02ju",
412 207
            (uintmax_t)t / 86400, (uintmax_t)(t % 86400) / 3600,
413 207
            (uintmax_t)(t % 3600) / 60, (uintmax_t)t % 60);
414 207
}
415
416
static void
417 252
running(WINDOW *w, uint64_t up, int flg)
418
{
419 252
        if (vsm_status & flg) {
420 204
                print_duration(w_status, up);
421 204
        } else {
422 48
                wattron(w, A_STANDOUT);
423 48
                wprintw(w, "  Not Running");
424 48
                wattroff(w, A_STANDOUT);
425
        }
426 252
}
427
428
static void
429 126
draw_status(void)
430
{
431 126
        uint64_t up_mgt = 0;
432 126
        uint64_t up_chld = 0;
433
434 126
        AN(w_status);
435
436 126
        werase(w_status);
437
438 126
        if (mgt_uptime != NULL)
439 126
                up_mgt = *mgt_uptime;
440 126
        if (main_uptime != NULL)
441 126
                up_chld = *main_uptime;
442
443 126
        mvwprintw(w_status, 0, 0, "Uptime mgt:   ");
444 126
        running(w_status, up_mgt, VSM_MGT_RUNNING);
445 126
        mvwprintw(w_status, 1, 0, "Uptime child: ");
446 126
        running(w_status, up_chld, VSM_WRK_RUNNING);
447 126
        mvwprintw(w_status, 2, 0, "Press <h> to toggle help screen");
448
449 126
        if (VTIM_mono() < notification_eol)
450 18
                mvwaddstr(w_status, 2, 0, notification_message);
451
452 126
        if (COLS > 70) {
453 126
                mvwprintw(w_status, 0, getmaxx(w_status) - 37,
454 126
                    "Hitrate n: %8u %8u %8u", hitrate.hr_10.n, hitrate.hr_100.n,
455 126
                    hitrate.hr_1000.n);
456 126
                mvwprintw(w_status, 1, getmaxx(w_status) - 37,
457 126
                    "   avg(n): %8.4f %8.4f %8.4f", hitrate.hr_10.acc,
458 126
                    hitrate.hr_100.acc, hitrate.hr_1000.acc);
459 126
        }
460
461 126
        wnoutrefresh(w_status);
462 126
}
463
464
static void
465 117
draw_bar_t(void)
466
{
467
        int X, x;
468
        enum {
469
                COL_CUR,
470
                COL_CHG,
471
                COL_AVG,
472
                COL_MA10,
473
                COL_MA100,
474
                COL_MA1000,
475
                COL_LAST
476
        } col;
477
478 117
        AN(w_bar_t);
479
480 117
        X = getmaxx(w_bar_t);
481 117
        x = 0;
482 117
        werase(w_bar_t);
483 117
        if (page_start > 0)
484 77
                mvwprintw(w_bar_t, 0, x, "^^^");
485 117
        x += 4;
486 117
        mvwprintw(w_bar_t, 0, x, "%.*s", colw_name - 4, "NAME");
487 117
        x += colw_name - 4;
488 117
        col = COL_CUR;
489 615
        while (col < COL_LAST) {
490 600
                if (X - x < COLW)
491 102
                        break;
492 498
                switch (col) {
493
                case COL_CUR:
494 117
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "CURRENT");
495 117
                        break;
496
                case COL_CHG:
497 117
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "CHANGE");
498 117
                        break;
499
                case COL_AVG:
500 117
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVERAGE");
501 117
                        break;
502
                case COL_MA10:
503 117
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_10");
504 117
                        break;
505
                case COL_MA100:
506 15
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_100");
507 15
                        break;
508
                case COL_MA1000:
509 15
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_1000");
510 15
                        break;
511
                default:
512 0
                        break;
513
                }
514 498
                x += COLW;
515 498
                col++;
516
        }
517
518 117
        wnoutrefresh(w_bar_t);
519 117
}
520
521
static void
522 840
draw_line_default(WINDOW *w, int y, int x, int X, const struct pt *pt)
523
{
524
        enum {
525
                COL_CUR,
526
                COL_CHG,
527
                COL_AVG,
528
                COL_MA10,
529
                COL_MA100,
530
                COL_MA1000,
531
                COL_LAST
532
        } col;
533
534 840
        AN(w);
535 840
        AN(pt);
536
537 840
        col = COL_CUR;
538 4476
        while (col < COL_LAST) {
539 4338
                if (X - x < COLW)
540 702
                        break;
541 3636
                switch (col) {
542
                case COL_CUR:
543 840
                        mvwprintw(w, y, x, " %12ju", (uintmax_t)pt->cur);
544 840
                        break;
545
                case COL_CHG:
546 840
                        if (pt->t_last)
547 672
                                mvwprintw(w, y, x, " %12.2f", pt->chg);
548
                        else
549 168
                                mvwprintw(w, y, x, " %12s", ".  ");
550 840
                        break;
551
                case COL_AVG:
552 840
                        if (pt->avg)
553 279
                                mvwprintw(w, y, x, " %12.2f", pt->avg);
554
                        else
555 561
                                mvwprintw(w, y, x, " %12s", ".  ");
556 840
                        break;
557
                case COL_MA10:
558 840
                        mvwprintw(w, y, x, " %12.2f", pt->ma_10.acc);
559 840
                        break;
560
                case COL_MA100:
561 138
                        mvwprintw(w, y, x, " %12.2f", pt->ma_100.acc);
562 138
                        break;
563
                case COL_MA1000:
564 138
                        mvwprintw(w, y, x, " %12.2f", pt->ma_1000.acc);
565 138
                        break;
566
                default:
567 0
                        break;
568
                }
569 3636
                x += COLW;
570 3636
                col++;
571
        }
572 840
}
573
574
static double
575 543
scale_bytes(double val, char *q)
576
{
577
        const char *p;
578
579 678
        for (p = " KMGTPEZY"; *p; p++) {
580 678
                if (fabs(val) < 1024.)
581 543
                        break;
582 135
                val /= 1024.;
583 135
        }
584 543
        *q = *p;
585 543
        return (val);
586
}
587
588
static void
589 996
print_bytes(WINDOW *w, double val)
590
{
591 996
        char q = ' ';
592
593 996
        if (scale)
594 543
                val = scale_bytes(val, &q);
595 996
        wprintw(w, " %12.2f%c", val, q);
596 996
}
597
598
static void
599 381
print_trunc(WINDOW *w, uintmax_t val)
600
{
601 381
        if (val > VALUE_MAX) {
602 0
                while (val > VALUE_MAX)
603 0
                        val /= 1000;
604 0
                wprintw(w, " %9ju...", val);
605 0
        } else
606 381
                wprintw(w, " %12ju", val);
607 381
}
608
609
static void
610 417
draw_line_bytes(WINDOW *w, int y, int x, int X, const struct pt *pt)
611
{
612
        enum {
613
                COL_CUR,
614
                COL_CHG,
615
                COL_AVG,
616
                COL_MA10,
617
                COL_MA100,
618
                COL_MA1000,
619
                COL_LAST
620
        } col;
621
622 417
        AN(w);
623 417
        AN(pt);
624
625 417
        col = COL_CUR;
626 2247
        while (col < COL_LAST) {
627 2166
                if (X - x < COLW)
628 336
                        break;
629 1830
                wmove(w, y, x);
630 1830
                switch (col) {
631
                case COL_CUR:
632 417
                        if (scale && pt->cur > 1024)
633 36
                                print_bytes(w, (double)pt->cur);
634
                        else
635 381
                                print_trunc(w, (uintmax_t)pt->cur);
636 417
                        break;
637
                case COL_CHG:
638 417
                        if (pt->t_last)
639 216
                                print_bytes(w, pt->chg);
640
                        else
641 201
                                wprintw(w, " %12s", ".  ");
642 417
                        break;
643
                case COL_AVG:
644 417
                        if (pt->avg)
645 165
                                print_bytes(w, pt->avg);
646
                        else
647 252
                                wprintw(w, " %12s", ".  ");
648 417
                        break;
649
                case COL_MA10:
650 417
                        print_bytes(w, pt->ma_10.acc);
651 417
                        break;
652
                case COL_MA100:
653 81
                        print_bytes(w, pt->ma_100.acc);
654 81
                        break;
655
                case COL_MA1000:
656 81
                        print_bytes(w, pt->ma_1000.acc);
657 81
                        break;
658
                default:
659 0
                        break;
660
                }
661 1830
                x += COLW;
662 1830
                col++;
663
        }
664 417
}
665
666
static void
667 48
draw_line_bitmap(WINDOW *w, int y, int x, int X, const struct pt *pt)
668
{
669
        unsigned ch;
670
        enum {
671
                COL_VAL,
672
                COL_MAP,
673
                COL_LAST
674
        } col;
675
676 48
        AN(w);
677 48
        AN(pt);
678 48
        assert(pt->vpt->format == 'b');
679
680 48
        col = COL_VAL;
681 144
        while (col < COL_LAST) {
682 96
                switch (col) {
683
                case COL_VAL:
684 48
                        if (X - x < COLW)
685 0
                                return;
686 96
                        mvwprintw(w, y, x, "   %10.10jx",
687 48
                            (uintmax_t)((pt->cur >> 24) & 0xffffffffffLL));
688 48
                        x += COLW;
689 48
                        break;
690
                case COL_MAP:
691 48
                        if (X - x < 2 * COLW)
692 0
                                return;
693 48
                        x += (2 * COLW) - 24;
694 1200
                        for (ch = 0x800000; ch; ch >>= 1) {
695 1152
                                if (pt->cur & ch)
696 144
                                        mvwaddch(w, y, x, 'V');
697
                                else
698 1008
                                        mvwaddch(w, y, x, '_');
699 1152
                                x++;
700 1152
                        }
701 48
                        break;
702
                default:
703 0
                        break;
704
                }
705 96
                col++;
706
        }
707 48
}
708
709
static void
710 9
draw_line_duration(WINDOW *w, int y, int x, int X, const struct pt *pt)
711
{
712
        enum {
713
                COL_DUR,
714
                COL_LAST
715
        } col;
716
717 9
        AN(w);
718 9
        AN(pt);
719
720 9
        col = COL_DUR;
721 18
        while (col < COL_LAST) {
722 9
                if (X - x < COLW)
723 0
                        break;
724 9
                switch (col) {
725
                case COL_DUR:
726 9
                        wmove(w, y, x);
727 9
                        if (scale)
728 3
                                print_duration(w, pt->cur);
729
                        else
730 6
                                wprintw(w, " %12ju", (uintmax_t)pt->cur);
731 9
                        break;
732
                default:
733 0
                        break;
734
                }
735 9
                x += COLW;
736 9
                col++;
737
        }
738 9
}
739
740
static void
741 1314
draw_line(WINDOW *w, int y, const struct pt *pt)
742
{
743
        int x, X;
744
745 1314
        assert(colw_name >= COLW_NAME_MIN);
746 1314
        X = getmaxx(w);
747 1314
        x = 0;
748 1314
        if (strlen(pt->vpt->name) > colw_name)
749 105
                mvwprintw(w, y, x, "%.*s...", colw_name - 3, pt->vpt->name);
750
        else
751 1209
                mvwprintw(w, y, x, "%.*s", colw_name, pt->vpt->name);
752 1314
        x += colw_name;
753
754 1314
        switch (pt->vpt->format) {
755
        case 'b':
756 48
                draw_line_bitmap(w, y, x, X, pt);
757 48
                break;
758
        case 'B':
759 417
                draw_line_bytes(w, y, x, X, pt);
760 417
                break;
761
        case 'd':
762 9
                draw_line_duration(w, y, x, X, pt);
763 9
                break;
764
        default:
765 840
                draw_line_default(w, y, x, X, pt);
766 840
                break;
767
        }
768 1314
}
769
770
static void
771 117
draw_points(void)
772
{
773
        int line;
774
        int n;
775
776 117
        AN(w_points);
777
778 117
        werase(w_points);
779 117
        if (n_ptarray == 0) {
780 18
                wnoutrefresh(w_points);
781 18
                return;
782
        }
783
784 99
        assert(current >= 0);
785 99
        assert(current < n_ptarray);
786 99
        assert(page_start >= 0);
787 99
        assert(page_start < n_ptarray);
788 99
        assert(current >= page_start);
789 99
        assert(current - page_start < l_points);
790
791 1413
        for (line = 0; line < l_points; line++) {
792 1344
                n = line + page_start;
793 1344
                if (n >= n_ptarray)
794 30
                        break;
795 1314
                if (n == current)
796 99
                        wattron(w_points, A_BOLD);
797 1314
                draw_line(w_points, line, ptarray[n]);
798 1314
                if (n == current)
799 99
                        wattroff(w_points, A_BOLD);
800 1314
        }
801 99
        wnoutrefresh(w_points);
802 117
}
803
804
static void
805 9
draw_help(void)
806
{
807
        const char *const *p;
808
        int l, y, X;
809
810 9
        if (l_points >= bindings_help_len) {
811 0
                assert(help_line == 0);
812 0
                l = bindings_help_len;
813 0
        } else {
814 9
                assert(help_line >= 0);
815 9
                assert(help_line <= bindings_help_len - l_points);
816 9
                l = l_points;
817
        }
818
819 9
        X = getmaxx(w_points);
820 9
        werase(w_points);
821
822 153
        for (y = 0, p = bindings_help + help_line; y < l; y++, p++) {
823 144
                if (**p == '\t') {
824 75
                        mvwprintw(w_points, y, 0, "    %.*s", X - 4, *p + 1);
825 75
                } else {
826 69
                        wattron(w_points, A_BOLD);
827 69
                        mvwprintw(w_points, y, 0, "%.*s", X, *p);
828 69
                        wattroff(w_points, A_BOLD);
829
                }
830 144
        }
831
832 9
        wnoutrefresh(w_points);
833 9
}
834
835
static void
836 117
draw_bar_b(void)
837
{
838
        int x, X;
839
        char buf[64];
840
841 117
        AN(w_bar_b);
842
843 117
        x = 0;
844 117
        X = getmaxx(w_bar_b);
845 117
        werase(w_bar_b);
846 117
        if (page_start + l_points < n_ptarray)
847 57
                mvwprintw(w_bar_b, 0, x, "vvv");
848 117
        x += 4;
849 117
        if (current < n_ptarray)
850 99
                mvwprintw(w_bar_b, 0, x, "%s", ptarray[current]->vpt->name);
851
852 117
        bprintf(buf, "%d-%d/%d", page_start + 1,
853
            page_start + l_points < n_ptarray ?
854
                page_start + l_points : n_ptarray,
855
            n_ptarray);
856 117
        mvwprintw(w_bar_b, 0, X - strlen(buf), "%s", buf);
857 117
        X -= strlen(buf) + 2;
858
859 117
        if (verbosity != NULL) {
860 234
                mvwprintw(w_bar_b, 0, X - strlen(verbosity->label), "%s",
861 117
                    verbosity->label);
862 117
                X -= strlen(verbosity->label) + 2;
863 117
        }
864 117
        if (!hide_unseen) {
865 30
                mvwprintw(w_bar_b, 0, X - 6, "%s", "UNSEEN");
866 30
                X -= 8;
867 30
        }
868 117
        if (raw_vsc)
869 0
                mvwprintw(w_bar_b, 0, X - 3, "%s", "RAW");
870
871 117
        wnoutrefresh(w_bar_b);
872 117
}
873
874
static void
875 117
draw_info(void)
876
{
877
878 117
        if (w_info == NULL)
879 0
                return;
880
881 117
        werase(w_info);
882 117
        if (current < n_ptarray) {
883
                /* XXX: Word wrapping, and overflow handling? */
884 198
                mvwprintw(w_info, 0, 0, "%s:",
885 99
                    ptarray[current]->vpt->sdesc);
886 198
                mvwprintw(w_info, 1, 0, "%s",
887 99
                    ptarray[current]->vpt->ldesc);
888 99
        }
889 117
        wnoutrefresh(w_info);
890 117
}
891
892
static void
893 126
draw_screen(void)
894
{
895 126
        draw_status();
896 126
        if (show_help) {
897 9
                werase(w_bar_t);
898 9
                werase(w_bar_b);
899 9
                werase(w_info);
900 9
                wnoutrefresh(w_bar_t);
901 9
                wnoutrefresh(w_bar_b);
902 9
                wnoutrefresh(w_info);
903 9
                draw_help();
904 9
        } else {
905 117
                draw_bar_t();
906 117
                draw_points();
907 117
                draw_bar_b();
908 117
                draw_info();
909
        }
910 126
        doupdate();
911 126
        redraw = 0;
912 126
}
913
914
static void
915 6
handle_common_keypress(enum kb_e kb)
916
{
917
918 6
        switch (kb) {
919
        case KB_QUIT:
920 6
                keep_running = 0;
921 6
                return;
922
        case KB_SIG_INT:
923 0
                AZ(raise(SIGINT));
924 0
                return;
925
        case KB_SIG_TSTP:
926 0
                AZ(raise(SIGTSTP));
927 0
                return;
928
        default:
929 0
                WRONG("unexpected key binding");
930 0
        }
931 6
}
932
933
static void
934 45
handle_points_keypress(struct vsc *vsc, enum kb_e kb)
935
{
936
937 45
        switch (kb) {
938
        case KB_HELP:
939 6
                show_help = 1;
940 6
                help_line = 0;
941 6
                redraw = 1;
942 6
                return;
943
        case KB_UP:
944 3
                if (current == 0)
945 0
                        return;
946 3
                current--;
947 3
                break;
948
        case KB_DOWN:
949 0
                if (current == n_ptarray - 1)
950 0
                        return;
951 0
                current++;
952 0
                break;
953
        case KB_PAGEUP:
954 3
                current -= l_points;
955 3
                page_start -= l_points;
956 3
                break;
957
        case KB_PAGEDOWN:
958 3
                current += l_points;
959 3
                if (page_start + l_points < n_ptarray - 1)
960 3
                        page_start += l_points;
961 3
                break;
962
        case KB_TOP:
963 3
                current = 0;
964 3
                break;
965
        case KB_BOTTOM:
966 6
                current = n_ptarray - 1;
967 6
                break;
968
        case KB_UNSEEN:
969 3
                hide_unseen = 1 - hide_unseen;
970 3
                rebuild = REBUILD_NEXT;
971 3
                break;
972
        case KB_RAW:
973 0
                AN(VSC_Arg(vsc, 'r', NULL));
974 0
                raw_vsc = VSC_IsRaw(vsc);
975 0
                rebuild = REBUILD_NEXT;
976 0
                break;
977
        case KB_SCALE:
978 3
                scale = 1 - scale;
979 3
                rebuild = REBUILD_NEXT;
980 3
                break;
981
        case KB_ACCEL:
982 3
                interval += 0.1;
983 3
                (void)snprintf(notification_message, NOTIF_MAXLEN,
984 3
                    "Refresh interval set to %.1f seconds.", interval);
985
986 3
                notification_eol = VTIM_mono() + 1.25;
987 3
                break;
988
        case KB_DECEL:
989 3
                interval -= 0.1;
990 3
                if (interval < 0.1)
991 0
                        interval = 0.1;
992 3
                (void)snprintf(notification_message, NOTIF_MAXLEN,
993 3
                    "Refresh interval set to %.1f seconds.", interval);
994
995 3
                notification_eol = VTIM_mono() + 1.25;
996 3
                break;
997
        case KB_VERBOSE:
998 3
                verbosity = VSC_ChangeLevel(verbosity, 1);
999 3
                rebuild = REBUILD_NEXT;
1000 3
                break;
1001
        case KB_QUIET:
1002 0
                verbosity = VSC_ChangeLevel(verbosity, -1);
1003 0
                rebuild = REBUILD_NEXT;
1004 0
                break;
1005
        case KB_SAMPLE:
1006 0
                sample = 1;
1007 0
                return;
1008
        case KB_QUIT:
1009
        case KB_SIG_INT:
1010
        case KB_SIG_TSTP:
1011 6
                handle_common_keypress(kb);
1012 6
                return;
1013
        default:
1014 0
                WRONG("unhandled key binding");
1015 0
        }
1016
1017 33
        update_position();
1018 33
        redraw = 1;
1019 45
}
1020
1021
static void
1022 9
handle_help_keypress(enum kb_e kb)
1023
{
1024 9
        int hl = help_line;
1025
1026 9
        switch (kb) {
1027
        case KB_HELP:
1028 6
                show_help = 0;
1029 6
                redraw = 1;
1030 6
                return;
1031
        case KB_UP:
1032 0
                help_line--;
1033 0
                break;
1034
        case KB_DOWN:
1035 0
                help_line++;
1036 0
                break;
1037
        case KB_PAGEUP:
1038 0
                help_line -= l_points;
1039 0
                break;
1040
        case KB_PAGEDOWN:
1041 0
                help_line += l_points;
1042 0
                break;
1043
        case KB_TOP:
1044 0
                help_line = 0;
1045 0
                break;
1046
        case KB_BOTTOM:
1047 3
                help_line = bindings_help_len;
1048 3
                break;
1049
        case KB_UNSEEN:
1050
        case KB_RAW:
1051
        case KB_SCALE:
1052
        case KB_ACCEL:
1053
        case KB_DECEL:
1054
        case KB_VERBOSE:
1055
        case KB_QUIET:
1056
        case KB_SAMPLE:
1057 0
                break;
1058
        case KB_QUIT:
1059
        case KB_SIG_INT:
1060
        case KB_SIG_TSTP:
1061 0
                handle_common_keypress(kb);
1062 0
                return;
1063
        default:
1064 0
                WRONG("unhandled key binding");
1065 0
        }
1066
1067 3
        help_line = vlimit_t(int, help_line, 0, bindings_help_len - l_points);
1068 3
        redraw = (help_line != hl);
1069 9
}
1070
1071
static void
1072 54
handle_keypress(struct vsc *vsc, int ch)
1073
{
1074
        enum kb_e kb;
1075
1076 54
        switch (ch) {
1077
#define BINDING_KEY(chr, name, or)      \
1078
        case chr:
1079
#define BINDING(name, desc)             \
1080
                kb = KB_ ## name;       \
1081
                break;
1082
#define BINDING_SIG
1083
#include "varnishstat_bindings.h"
1084
        default:
1085
                return;
1086
        }
1087
1088 54
        if (show_help)
1089 9
                handle_help_keypress(kb);
1090
        else
1091 45
                handle_points_keypress(vsc, kb);
1092 54
}
1093
1094
static void * v_matchproto_(VSC_new_f)
1095 5445
newpt(void *priv, const struct VSC_point *const vpt)
1096
{
1097
        struct pt *pt;
1098
1099 5445
        AZ(priv);
1100 5445
        ALLOC_OBJ(pt, PT_MAGIC);
1101 5445
        rebuild |= REBUILD_NEXT;
1102 5445
        AN(pt);
1103 5445
        pt->vpt = vpt;
1104 5445
        pt->last = VSC_Value(vpt);
1105 5445
        pt->ma_10.nmax = 10;
1106 5445
        pt->ma_100.nmax = 100;
1107 5445
        pt->ma_1000.nmax = 1000;
1108
1109 5445
        VTAILQ_INSERT_TAIL(&ptlist, pt, list);
1110 5445
        n_ptlist++;
1111
1112 5445
        AZ(strcmp(vpt->ctype, "uint64_t"));
1113
1114 5445
        if (!strcmp(vpt->name, "MGT.uptime"))
1115 18
                mgt_uptime = vpt->ptr;
1116 5445
        if (!strcmp(vpt->name, "MAIN.uptime"))
1117 18
                main_uptime = vpt->ptr;
1118 5445
        if (!strcmp(vpt->name, "MAIN.cache_hit"))
1119 18
                main_cache_hit = vpt->ptr;
1120 5445
        if (!strcmp(vpt->name, "MAIN.cache_miss"))
1121 18
                main_cache_miss = vpt->ptr;
1122 5445
        return (pt);
1123
}
1124
1125
static void v_matchproto_(VSC_destroy_f)
1126 5445
delpt(void *priv, const struct VSC_point *const vpt)
1127
{
1128
        struct pt *pt;
1129
1130 5445
        AZ(priv);
1131 5445
        CAST_OBJ_NOTNULL(pt, vpt->priv, PT_MAGIC);
1132 5445
        rebuild |= REBUILD_NEXT;
1133 5445
        VTAILQ_REMOVE(&ptlist, pt, list);
1134 5445
        n_ptlist--;
1135 5445
        FREE_OBJ(pt);
1136 5445
        if (vpt->ptr == mgt_uptime)
1137 18
                mgt_uptime = NULL;
1138 5445
        if (vpt->ptr == main_uptime)
1139 18
                main_uptime = NULL;
1140 5445
        if (vpt->ptr == main_cache_hit)
1141 18
                main_cache_hit = NULL;
1142 5445
        if (vpt->ptr == main_cache_miss)
1143 18
                main_cache_miss = NULL;
1144 5445
}
1145
1146
void
1147 18
do_curses(struct vsm *vsm, struct vsc *vsc)
1148
{
1149
        long t;
1150
        int ch;
1151
        double now;
1152
1153 18
        verbosity = VSC_ChangeLevel(NULL, 0);
1154
1155 18
        initscr();
1156 18
        raw();
1157 18
        noecho();
1158 18
        nonl();
1159 18
        curs_set(0);
1160
1161 18
        make_windows();
1162 18
        doupdate();
1163
1164 18
        VSC_State(vsc, newpt, delpt, NULL);
1165
1166 18
        raw_vsc = VSC_IsRaw(vsc);
1167 18
        rebuild |= REBUILD_FIRST;
1168 18
        (void)VSC_Iter(vsc, vsm, NULL, NULL);
1169 18
        build_pt_array();
1170 18
        init_hitrate();
1171
1172 144
        while (keep_running && !VSIG_int && !VSIG_term && !VSIG_hup) {
1173 126
                (void)VSC_Iter(vsc, vsm, NULL, NULL);
1174 126
                vsm_status = VSM_Status(vsm);
1175 126
                if (vsm_status & (VSM_MGT_RESTARTED|VSM_WRK_RESTARTED))
1176 0
                        init_hitrate();
1177 126
                if (rebuild)
1178 48
                        build_pt_array();
1179
1180 126
                now = VTIM_mono();
1181 126
                if (now - t_sample > interval)
1182 75
                        sample = 1;
1183 126
                if (sample)
1184 75
                        sample_data();
1185 126
                if (redraw)
1186 126
                        draw_screen();
1187
1188 126
                t = (long)((t_sample + interval - now) * 1000);
1189 126
                wtimeout(w_status, t);
1190
1191 126
                ch = wgetch(w_status);
1192 126
                switch (ch) {
1193
                case ERR:
1194 69
                        break;
1195
#ifdef KEY_RESIZE /* sigh, Solaris lacks this.. */
1196
                case KEY_RESIZE:
1197 3
                        make_windows();
1198 3
                        update_position();
1199 3
                        break;
1200
#endif
1201
                default:
1202 54
                        handle_keypress(vsc, ch);
1203 54
                        break;
1204
                }
1205
        }
1206 18
        VSC_Destroy(&vsc, vsm);
1207 18
        AN(VTAILQ_EMPTY(&ptlist));
1208 18
        VSM_Destroy(&vsm);
1209 18
        AZ(endwin());
1210 18
}