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 100
#include <string.h>
40
#include <stdint.h>
41
#include <math.h>
42
43 25
#include "vdef.h"
44
#include "vas.h"
45
#include "miniobj.h"
46
#include "vqueue.h"
47 0
#include "vtim.h"
48
#include "vapi/vsig.h"
49
50
#include "varnishstat.h"
51
#include "vcurses.h"
52 25
53
#define LINES_STATUS            3
54
#define LINES_BAR_T             1
55
#define LINES_BAR_B             1
56
#define LINES_INFO              3
57 25
#define LINES_POINTS_MIN        3
58
59
#define N_COL                   6
60
#define COLW                    14
61 25
#define COLW_NAME_MIN           24
62
63
#define VALUE_MAX               999999999999
64
65 75
#define REBUILD_NEXT            (1u << 0)
66
#define REBUILD_FIRST           (1u << 1)
67
68 25
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 0
        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 25
84
        const struct VSC_point  *vpt;
85
86 25
        char                    seen;
87
88
        uint64_t                cur, last;
89
        double                  t_cur, t_last;
90
        double                  chg, avg;
91
92 0
        struct ma               ma_10, ma_100, ma_1000;
93
};
94
95
struct hitrate {
96
        uint64_t lhit, lmiss;
97
        struct ma hr_10;
98 50
        struct ma hr_100;
99
        struct ma hr_1000;
100
};
101 0
static struct hitrate hitrate;
102
103
static VTAILQ_HEAD(, pt) ptlist = VTAILQ_HEAD_INITIALIZER(ptlist);
104 25
static int n_ptlist = 0;
105
static int n_ptarray = 0;
106
static struct pt **ptarray = NULL;
107 25
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 0
112
static int l_status, l_bar_t, l_points, l_bar_b, l_info;
113
static unsigned colw_name = COLW_NAME_MIN;
114 0
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 150
init_hitrate(void)
142
{
143 150
        memset(&hitrate, 0, sizeof (struct hitrate));
144 150
        if (main_cache_hit != NULL) {
145 150
                hitrate.lhit = *main_cache_hit;
146 150
                hitrate.lmiss = *main_cache_miss;
147 150
        }
148 150
        hitrate.hr_10.nmax = 10;
149 150
        hitrate.hr_100.nmax = 100;
150 150
        hitrate.hr_1000.nmax = 1000;
151 150
}
152
153
static void
154 89049
update_ma(struct ma *ma, double val)
155
{
156 89049
        AN(ma);
157 89049
        AN(ma->nmax);
158 89049
        if (ma->n < ma->nmax)
159 89049
                ma->n++;
160 89049
        ma->acc += (val - ma->acc) / (double)ma->n;
161 89049
}
162
163
static void
164 1250
update_position(void)
165
{
166
        int old_current, old_page_start;
167
168 1250
        old_current = current;
169 1250
        old_page_start = page_start;
170
171 1250
        if (n_ptarray == 0) {
172 550
                current = 0;
173 550
                page_start = 0;
174 550
        } else {
175 700
                current = vlimit_t(int, current, 0, n_ptarray - 1);
176 700
                page_start = vmin(page_start, current);
177 700
                if (current > page_start + (l_points - 1))
178 50
                        page_start = current - (l_points - 1);
179 700
                page_start = vlimit_t(int, page_start, 0, n_ptarray - 1);
180
        }
181
182 1250
        if (current != old_current || page_start != old_page_start)
183 250
                redraw = 1;
184 1250
}
185
186
static void
187 400
delete_pt_array(void)
188
{
189 400
        if (ptarray != NULL)
190 400
                free(ptarray);
191 400
        ptarray = NULL;
192 400
        n_ptarray = 0;
193
194 400
        update_position();
195 400
}
196
197
static void
198 550
build_pt_array(void)
199
{
200
        int i;
201
        struct pt *pt;
202 550
        struct pt *pt_current = NULL;
203 550
        int current_line = 0;
204
205 550
        if (current < n_ptarray) {
206 250
                pt_current = ptarray[current];
207 250
                current_line = current - page_start;
208 250
        }
209
210 550
        if (ptarray != NULL)
211 400
                delete_pt_array();
212 550
        AZ(n_ptarray);
213 550
        ptarray = calloc(n_ptlist, sizeof *ptarray);
214 550
        AN(ptarray);
215
216 168075
        VTAILQ_FOREACH(pt, &ptlist, list) {
217 167525
                CHECK_OBJ_NOTNULL(pt, PT_MAGIC);
218 167525
                if (pt->vpt->level > verbosity) {
219 95350
                        if (has_f && (rebuild & REBUILD_FIRST))
220 150
                                verbosity = VSC_ChangeLevel(verbosity,
221 75
                                    pt->vpt->level - verbosity);
222
                        else
223 95275
                                continue;
224 75
                }
225 72250
                if (!pt->seen && hide_unseen)
226 56527
                        continue;
227 15723
                assert(n_ptarray < n_ptlist);
228 15723
                ptarray[n_ptarray++] = pt;
229 15723
        }
230 550
        assert(n_ptarray <= n_ptlist);
231
232 10173
        for (i = 0; pt_current != NULL && i < n_ptarray; i++)
233 9873
                if (ptarray[i] == pt_current)
234 250
                        break;
235 550
        current = i;
236 550
        page_start = current - current_line;
237 550
        update_position();
238
239 550
        rebuild = 0;
240 550
        redraw = 1;
241 550
}
242
243
static void
244 515
sample_points(void)
245
{
246
        struct pt *pt;
247
        uint64_t v;
248
249 155650
        VTAILQ_FOREACH(pt, &ptlist, list) {
250 155135
                AN(pt->vpt);
251 155135
                AN(pt->vpt->ptr);
252 155135
                v = VSC_Value(pt->vpt);
253 155135
                if (v == 0 && !pt->seen)
254 117319
                        continue;
255 37816
                if (!pt->seen) {
256 11733
                        pt->seen = 1;
257 11733
                        rebuild = REBUILD_NEXT;
258 11733
                }
259 37816
                pt->last = pt->cur;
260 37816
                pt->cur = v;
261 37816
                pt->t_last = pt->t_cur;
262 37816
                pt->t_cur = VTIM_mono();
263
264 37816
                if (pt->t_last)
265 52166
                        pt->chg = ((int64_t)pt->cur - (int64_t)pt->last) /
266 26083
                            (pt->t_cur - pt->t_last);
267
268 37816
                if (pt->vpt->semantics == 'g') {
269 11460
                        pt->avg = 0.;
270 11460
                        update_ma(&pt->ma_10, (int64_t)pt->cur);
271 11460
                        update_ma(&pt->ma_100, (int64_t)pt->cur);
272 11460
                        update_ma(&pt->ma_1000, (int64_t)pt->cur);
273 37816
                } else if (pt->vpt->semantics == 'c') {
274 26016
                        if (main_uptime != NULL && *main_uptime)
275 21116
                                pt->avg = pt->cur / (double)*main_uptime;
276
                        else
277 4900
                                pt->avg = 0.;
278 26016
                        if (pt->t_last) {
279 17708
                                update_ma(&pt->ma_10, pt->chg);
280 17708
                                update_ma(&pt->ma_100, pt->chg);
281 17708
                                update_ma(&pt->ma_1000, pt->chg);
282 17708
                        }
283 26016
                }
284 37816
        }
285 515
}
286
287
static void
288 515
sample_hitrate(void)
289
{
290
        double hr, mr, ratio;
291
        uint64_t hit, miss;
292
293 515
        if (main_cache_hit == NULL)
294 0
                return;
295
296 515
        hit = *main_cache_hit;
297 515
        miss = *main_cache_miss;
298 515
        hr = hit - hitrate.lhit;
299 515
        mr = miss - hitrate.lmiss;
300 515
        hitrate.lhit = hit;
301 515
        hitrate.lmiss = miss;
302
303 515
        if (hr + mr != 0)
304 75
                ratio = hr / (hr + mr);
305
        else
306 440
                ratio = 0;
307 515
        update_ma(&hitrate.hr_10, ratio);
308 515
        update_ma(&hitrate.hr_100, ratio);
309 515
        update_ma(&hitrate.hr_1000, ratio);
310 515
}
311
312
static void
313 515
sample_data(void)
314
{
315 515
        t_sample = VTIM_mono();
316 515
        sample = 0;
317 515
        redraw = 1;
318 515
        sample_points();
319 515
        sample_hitrate();
320 515
}
321
322
static void
323 875
destroy_window(WINDOW **w)
324
{
325
326 875
        AN(w);
327 875
        if (*w == NULL)
328 750
                return;
329 125
        assert(delwin(*w) != ERR);
330 125
        *w = NULL;
331 875
}
332
333
static void
334 175
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 175
        destroy_window(&w_status);
341 175
        destroy_window(&w_bar_t);
342 175
        destroy_window(&w_points);
343 175
        destroy_window(&w_bar_b);
344 175
        destroy_window(&w_info);
345
346 175
        Y = LINES;
347 175
        X = COLS;
348
349 175
        l_status = LINES_STATUS;
350 175
        l_bar_t = LINES_BAR_T;
351 175
        l_bar_b = LINES_BAR_B;
352 175
        l_info = LINES_INFO;
353 175
        l_points = Y - (l_status + l_bar_t + l_bar_b + l_info);
354 175
        if (l_points < LINES_POINTS_MIN) {
355 0
                l_points += l_info;
356 0
                l_info = 0;
357 0
        }
358 175
        l_points = vmax(l_points, LINES_POINTS_MIN);
359
360 175
        y = 0;
361 175
        y_status = y;
362 175
        y += l_status;
363 175
        y_bar_t = y;
364 175
        y += l_bar_t;
365 175
        y_points = y;
366 175
        y += l_points;
367 175
        y_bar_b = y;
368 175
        y += l_bar_b;
369 175
        y_info = y;
370 175
        y += l_info;
371 175
        assert(y >= Y);
372
373 175
        w_status = newwin(l_status, X, y_status, 0);
374 175
        AN(w_status);
375 175
        nodelay(w_status, 1);
376 175
        keypad(w_status, 1);
377 175
        wnoutrefresh(w_status);
378
379 175
        w_bar_t = newwin(l_bar_t, X, y_bar_t, 0);
380 175
        AN(w_bar_t);
381 175
        wbkgd(w_bar_t, A_REVERSE);
382 175
        wnoutrefresh(w_bar_t);
383
384 175
        w_points = newwin(l_points, X, y_points, 0);
385 175
        AN(w_points);
386 175
        wnoutrefresh(w_points);
387
388 175
        w_bar_b = newwin(l_bar_b, X, y_bar_b, 0);
389 175
        AN(w_bar_b);
390 175
        wbkgd(w_bar_b, A_REVERSE);
391 175
        wnoutrefresh(w_bar_b);
392
393 175
        if (l_info) {
394 175
                w_info = newwin(l_info, X, y_info, 0);
395 175
                AN(w_info);
396 175
                wnoutrefresh(w_info);
397 175
        }
398
399 175
        if (X - COLW_NAME_MIN > N_COL * COLW)
400 25
                colw_name = X - (N_COL * COLW);
401
        else
402 150
                colw_name = COLW_NAME_MIN;
403
404 175
        redraw = 1;
405 175
}
406
407
static void
408 1905
print_duration(WINDOW *w, uint64_t t)
409
{
410
411 3810
        wprintw(w, "%4ju+%02ju:%02ju:%02ju",
412 1905
            (uintmax_t)t / 86400, (uintmax_t)(t % 86400) / 3600,
413 1905
            (uintmax_t)(t % 3600) / 60, (uintmax_t)t % 60);
414 1905
}
415
416
static void
417 1880
running(WINDOW *w, uint64_t up, int flg)
418
{
419 1880
        if (vsm_status & flg) {
420 1880
                print_duration(w_status, up);
421 1880
        } else {
422 0
                wattron(w, A_STANDOUT);
423 0
                wprintw(w, "  Not Running");
424 0
                wattroff(w, A_STANDOUT);
425
        }
426 1880
}
427
428
static void
429 940
draw_status(void)
430
{
431 940
        uint64_t up_mgt = 0;
432 940
        uint64_t up_chld = 0;
433
434 940
        AN(w_status);
435
436 940
        werase(w_status);
437
438 940
        if (mgt_uptime != NULL)
439 940
                up_mgt = *mgt_uptime;
440 940
        if (main_uptime != NULL)
441 940
                up_chld = *main_uptime;
442
443 940
        mvwprintw(w_status, 0, 0, "Uptime mgt:   ");
444 940
        running(w_status, up_mgt, VSM_MGT_RUNNING);
445 940
        mvwprintw(w_status, 1, 0, "Uptime child: ");
446 940
        running(w_status, up_chld, VSM_WRK_RUNNING);
447 940
        mvwprintw(w_status, 2, 0, "Press <h> to toggle help screen");
448
449 940
        if (VTIM_mono() < notification_eol)
450 444
                mvwaddstr(w_status, 2, 0, notification_message);
451
452 940
        if (COLS > 70) {
453 940
                mvwprintw(w_status, 0, getmaxx(w_status) - 37,
454 940
                    "Hitrate n: %8u %8u %8u", hitrate.hr_10.n, hitrate.hr_100.n,
455 940
                    hitrate.hr_1000.n);
456 940
                mvwprintw(w_status, 1, getmaxx(w_status) - 37,
457 940
                    "   avg(n): %8.4f %8.4f %8.4f", hitrate.hr_10.acc,
458 940
                    hitrate.hr_100.acc, hitrate.hr_1000.acc);
459 940
        }
460
461 940
        wnoutrefresh(w_status);
462 940
}
463
464
static void
465 858
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 858
        AN(w_bar_t);
479
480 858
        X = getmaxx(w_bar_t);
481 858
        x = 0;
482 858
        werase(w_bar_t);
483 858
        if (page_start > 0)
484 513
                mvwprintw(w_bar_t, 0, x, "^^^");
485 858
        x += 4;
486 858
        mvwprintw(w_bar_t, 0, x, "%.*s", colw_name - 4, "NAME");
487 858
        x += colw_name - 4;
488 858
        col = COL_CUR;
489 4556
        while (col < COL_LAST) {
490 4423
                if (X - x < COLW)
491 725
                        break;
492 3698
                switch (col) {
493
                case COL_CUR:
494 858
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "CURRENT");
495 858
                        break;
496
                case COL_CHG:
497 858
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "CHANGE");
498 858
                        break;
499
                case COL_AVG:
500 858
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVERAGE");
501 858
                        break;
502
                case COL_MA10:
503 858
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_10");
504 858
                        break;
505
                case COL_MA100:
506 133
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_100");
507 133
                        break;
508
                case COL_MA1000:
509 133
                        mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_1000");
510 133
                        break;
511
                default:
512 0
                        break;
513
                }
514 3698
                x += COLW;
515 3698
                col++;
516
        }
517
518 858
        wnoutrefresh(w_bar_t);
519 858
}
520
521
static void
522 5618
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 5618
        AN(w);
535 5618
        AN(pt);
536
537 5618
        col = COL_CUR;
538 30520
        while (col < COL_LAST) {
539 29305
                if (X - x < COLW)
540 4403
                        break;
541 24902
                switch (col) {
542
                case COL_CUR:
543 5618
                        mvwprintw(w, y, x, " %12ju", (uintmax_t)pt->cur);
544 5618
                        break;
545
                case COL_CHG:
546 5618
                        if (pt->t_last)
547 4154
                                mvwprintw(w, y, x, " %12.2f", pt->chg);
548
                        else
549 1464
                                mvwprintw(w, y, x, " %12s", ".  ");
550 5618
                        break;
551
                case COL_AVG:
552 5618
                        if (pt->avg)
553 1699
                                mvwprintw(w, y, x, " %12.2f", pt->avg);
554
                        else
555 3919
                                mvwprintw(w, y, x, " %12s", ".  ");
556 5618
                        break;
557
                case COL_MA10:
558 5618
                        mvwprintw(w, y, x, " %12.2f", pt->ma_10.acc);
559 5618
                        break;
560
                case COL_MA100:
561 1215
                        mvwprintw(w, y, x, " %12.2f", pt->ma_100.acc);
562 1215
                        break;
563
                case COL_MA1000:
564 1215
                        mvwprintw(w, y, x, " %12.2f", pt->ma_1000.acc);
565 1215
                        break;
566
                default:
567 0
                        break;
568
                }
569 24902
                x += COLW;
570 24902
                col++;
571
        }
572 5618
}
573
574
static double
575 3039
scale_bytes(double val, char *q)
576
{
577
        const char *p;
578
579 3967
        for (p = " KMGTPEZY"; *p; p++) {
580 3967
                if (fabs(val) < 1024.)
581 3039
                        break;
582 928
                val /= 1024.;
583 928
        }
584 3039
        *q = *p;
585 3039
        return (val);
586
}
587
588
static void
589 7009
print_bytes(WINDOW *w, double val)
590
{
591 7009
        char q = ' ';
592
593 7009
        if (scale)
594 3039
                val = scale_bytes(val, &q);
595 7009
        wprintw(w, " %12.2f%c", val, q);
596 7009
}
597
598
static void
599 2767
print_trunc(WINDOW *w, uintmax_t val)
600
{
601 2767
        if (val > VALUE_MAX) {
602 0
                while (val > VALUE_MAX)
603 0
                        val /= 1000;
604 0
                wprintw(w, " %9ju...", val);
605 0
        } else
606 2767
                wprintw(w, " %12ju", val);
607 2767
}
608
609
static void
610 3018
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 3018
        AN(w);
623 3018
        AN(pt);
624
625 3018
        col = COL_CUR;
626 16538
        while (col < COL_LAST) {
627 15814
                if (X - x < COLW)
628 2294
                        break;
629 13520
                wmove(w, y, x);
630 13520
                switch (col) {
631
                case COL_CUR:
632 3018
                        if (scale && pt->cur > 1024)
633 251
                                print_bytes(w, (double)pt->cur);
634
                        else
635 2767
                                print_trunc(w, (uintmax_t)pt->cur);
636 3018
                        break;
637
                case COL_CHG:
638 3018
                        if (pt->t_last)
639 1285
                                print_bytes(w, pt->chg);
640
                        else
641 1733
                                wprintw(w, " %12s", ".  ");
642 3018
                        break;
643
                case COL_AVG:
644 3018
                        if (pt->avg)
645 1007
                                print_bytes(w, pt->avg);
646
                        else
647 2011
                                wprintw(w, " %12s", ".  ");
648 3018
                        break;
649
                case COL_MA10:
650 3018
                        print_bytes(w, pt->ma_10.acc);
651 3018
                        break;
652
                case COL_MA100:
653 724
                        print_bytes(w, pt->ma_100.acc);
654 724
                        break;
655
                case COL_MA1000:
656 724
                        print_bytes(w, pt->ma_1000.acc);
657 724
                        break;
658
                default:
659 0
                        break;
660
                }
661 13520
                x += COLW;
662 13520
                col++;
663
        }
664 3018
}
665
666
static void
667 357
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 357
        AN(w);
677 357
        AN(pt);
678 357
        assert(pt->vpt->format == 'b');
679
680 357
        col = COL_VAL;
681 1071
        while (col < COL_LAST) {
682 714
                switch (col) {
683
                case COL_VAL:
684 357
                        if (X - x < COLW)
685 0
                                return;
686 714
                        mvwprintw(w, y, x, "   %10.10jx",
687 357
                            (uintmax_t)((pt->cur >> 24) & 0xffffffffffLL));
688 357
                        x += COLW;
689 357
                        break;
690
                case COL_MAP:
691 357
                        if (X - x < 2 * COLW)
692 0
                                return;
693 357
                        x += (2 * COLW) - 24;
694 8925
                        for (ch = 0x800000; ch; ch >>= 1) {
695 8568
                                if (pt->cur & ch)
696 1071
                                        mvwaddch(w, y, x, 'V');
697
                                else
698 7497
                                        mvwaddch(w, y, x, '_');
699 8568
                                x++;
700 8568
                        }
701 357
                        break;
702
                default:
703 0
                        break;
704
                }
705 714
                col++;
706
        }
707 357
}
708
709
static void
710 77
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 77
        AN(w);
718 77
        AN(pt);
719
720 77
        col = COL_DUR;
721 154
        while (col < COL_LAST) {
722 77
                if (X - x < COLW)
723 0
                        break;
724 77
                switch (col) {
725
                case COL_DUR:
726 77
                        wmove(w, y, x);
727 77
                        if (scale)
728 25
                                print_duration(w, pt->cur);
729
                        else
730 52
                                wprintw(w, " %12ju", (uintmax_t)pt->cur);
731 77
                        break;
732
                default:
733 0
                        break;
734
                }
735 77
                x += COLW;
736 77
                col++;
737
        }
738 77
}
739
740
static void
741 9070
draw_line(WINDOW *w, int y, const struct pt *pt)
742
{
743
        int x, X;
744
745 9070
        assert(colw_name >= COLW_NAME_MIN);
746 9070
        X = getmaxx(w);
747 9070
        x = 0;
748 9070
        if (strlen(pt->vpt->name) > colw_name)
749 738
                mvwprintw(w, y, x, "%.*s...", colw_name - 3, pt->vpt->name);
750
        else
751 8332
                mvwprintw(w, y, x, "%.*s", colw_name, pt->vpt->name);
752 9070
        x += colw_name;
753
754 9070
        switch (pt->vpt->format) {
755
        case 'b':
756 357
                draw_line_bitmap(w, y, x, X, pt);
757 357
                break;
758
        case 'B':
759 3018
                draw_line_bytes(w, y, x, X, pt);
760 3018
                break;
761
        case 'd':
762 77
                draw_line_duration(w, y, x, X, pt);
763 77
                break;
764
        default:
765 5618
                draw_line_default(w, y, x, X, pt);
766 5618
                break;
767
        }
768 9070
}
769
770
static void
771 858
draw_points(void)
772
{
773
        int line;
774
        int n;
775
776 858
        AN(w_points);
777
778 858
        werase(w_points);
779 858
        if (n_ptarray == 0) {
780 150
                wnoutrefresh(w_points);
781 150
                return;
782
        }
783
784 708
        assert(current >= 0);
785 708
        assert(current < n_ptarray);
786 708
        assert(page_start >= 0);
787 708
        assert(page_start < n_ptarray);
788 708
        assert(current >= page_start);
789 708
        assert(current - page_start < l_points);
790
791 9778
        for (line = 0; line < l_points; line++) {
792 9322
                n = line + page_start;
793 9322
                if (n >= n_ptarray)
794 252
                        break;
795 9070
                if (n == current)
796 708
                        wattron(w_points, A_BOLD);
797 9070
                draw_line(w_points, line, ptarray[n]);
798 9070
                if (n == current)
799 708
                        wattroff(w_points, A_BOLD);
800 9070
        }
801 708
        wnoutrefresh(w_points);
802 858
}
803
804
static void
805 82
draw_help(void)
806
{
807
        const char *const *p;
808
        int l, y, X;
809
810 82
        if (l_points >= bindings_help_len) {
811 0
                assert(help_line == 0);
812 0
                l = bindings_help_len;
813 0
        } else {
814 82
                assert(help_line >= 0);
815 82
                assert(help_line <= bindings_help_len - l_points);
816 82
                l = l_points;
817
        }
818
819 82
        X = getmaxx(w_points);
820 82
        werase(w_points);
821
822 1395
        for (y = 0, p = bindings_help + help_line; y < l; y++, p++) {
823 1313
                if (**p == '\t') {
824 684
                        mvwprintw(w_points, y, 0, "    %.*s", X - 4, *p + 1);
825 684
                } else {
826 629
                        wattron(w_points, A_BOLD);
827 629
                        mvwprintw(w_points, y, 0, "%.*s", X, *p);
828 629
                        wattroff(w_points, A_BOLD);
829
                }
830 1313
        }
831
832 82
        wnoutrefresh(w_points);
833 82
}
834
835
static void
836 858
draw_bar_b(void)
837
{
838
        int x, X;
839
        char buf[64];
840
841 858
        AN(w_bar_b);
842
843 858
        x = 0;
844 858
        X = getmaxx(w_bar_b);
845 858
        werase(w_bar_b);
846 858
        if (page_start + l_points < n_ptarray)
847 404
                mvwprintw(w_bar_b, 0, x, "vvv");
848 858
        x += 4;
849 858
        if (current < n_ptarray)
850 708
                mvwprintw(w_bar_b, 0, x, "%s", ptarray[current]->vpt->name);
851
852 858
        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 858
        mvwprintw(w_bar_b, 0, X - strlen(buf), "%s", buf);
857 858
        X -= strlen(buf) + 2;
858
859 858
        if (verbosity != NULL) {
860 1716
                mvwprintw(w_bar_b, 0, X - strlen(verbosity->label), "%s",
861 858
                    verbosity->label);
862 858
                X -= strlen(verbosity->label) + 2;
863 858
        }
864 858
        if (!hide_unseen) {
865 260
                mvwprintw(w_bar_b, 0, X - 6, "%s", "UNSEEN");
866 260
                X -= 8;
867 260
        }
868 858
        if (raw_vsc)
869 0
                mvwprintw(w_bar_b, 0, X - 3, "%s", "RAW");
870
871 858
        wnoutrefresh(w_bar_b);
872 858
}
873
874
static void
875 858
draw_info(void)
876
{
877
878 858
        if (w_info == NULL)
879 0
                return;
880
881 858
        werase(w_info);
882 858
        if (current < n_ptarray) {
883
                /* XXX: Word wrapping, and overflow handling? */
884 1416
                mvwprintw(w_info, 0, 0, "%s:",
885 708
                    ptarray[current]->vpt->sdesc);
886 1416
                mvwprintw(w_info, 1, 0, "%s",
887 708
                    ptarray[current]->vpt->ldesc);
888 708
        }
889 858
        wnoutrefresh(w_info);
890 858
}
891
892
static void
893 940
draw_screen(void)
894
{
895 940
        draw_status();
896 940
        if (show_help) {
897 82
                werase(w_bar_t);
898 82
                werase(w_bar_b);
899 82
                werase(w_info);
900 82
                wnoutrefresh(w_bar_t);
901 82
                wnoutrefresh(w_bar_b);
902 82
                wnoutrefresh(w_info);
903 82
                draw_help();
904 82
        } else {
905 858
                draw_bar_t();
906 858
                draw_points();
907 858
                draw_bar_b();
908 858
                draw_info();
909
        }
910 940
        doupdate();
911 940
        redraw = 0;
912 940
}
913
914
static void
915 50
handle_common_keypress(enum kb_e kb)
916
{
917
918 50
        switch (kb) {
919
        case KB_QUIT:
920 50
                keep_running = 0;
921 50
                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 50
}
932
933
static void
934 375
handle_points_keypress(struct vsc *vsc, enum kb_e kb)
935
{
936
937 375
        switch (kb) {
938
        case KB_HELP:
939 50
                show_help = 1;
940 50
                help_line = 0;
941 50
                redraw = 1;
942 50
                return;
943
        case KB_UP:
944 25
                if (current == 0)
945 0
                        return;
946 25
                current--;
947 25
                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 25
                current -= l_points;
955 25
                page_start -= l_points;
956 25
                break;
957
        case KB_PAGEDOWN:
958 25
                current += l_points;
959 25
                if (page_start + l_points < n_ptarray - 1)
960 25
                        page_start += l_points;
961 25
                break;
962
        case KB_TOP:
963 25
                current = 0;
964 25
                break;
965
        case KB_BOTTOM:
966 50
                current = n_ptarray - 1;
967 50
                break;
968
        case KB_UNSEEN:
969 25
                hide_unseen = 1 - hide_unseen;
970 25
                rebuild = REBUILD_NEXT;
971 25
                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 25
                scale = 1 - scale;
979 25
                rebuild = REBUILD_NEXT;
980 25
                break;
981
        case KB_ACCEL:
982 25
                interval += 0.1;
983 25
                (void)snprintf(notification_message, NOTIF_MAXLEN,
984 25
                    "Refresh interval set to %.1f seconds.", interval);
985
986 25
                notification_eol = VTIM_mono() + 1.25;
987 25
                break;
988
        case KB_DECEL:
989 25
                interval -= 0.1;
990 25
                if (interval < 0.1)
991 0
                        interval = 0.1;
992 25
                (void)snprintf(notification_message, NOTIF_MAXLEN,
993 25
                    "Refresh interval set to %.1f seconds.", interval);
994
995 25
                notification_eol = VTIM_mono() + 1.25;
996 25
                break;
997
        case KB_VERBOSE:
998 25
                verbosity = VSC_ChangeLevel(verbosity, 1);
999 25
                rebuild = REBUILD_NEXT;
1000 25
                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 50
                handle_common_keypress(kb);
1012 50
                return;
1013
        default:
1014 0
                WRONG("unhandled key binding");
1015 0
        }
1016
1017 275
        update_position();
1018 275
        redraw = 1;
1019 375
}
1020
1021
static void
1022 75
handle_help_keypress(enum kb_e kb)
1023
{
1024 75
        int hl = help_line;
1025
1026 75
        switch (kb) {
1027
        case KB_HELP:
1028 50
                show_help = 0;
1029 50
                redraw = 1;
1030 50
                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 25
                help_line = bindings_help_len;
1048 25
                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 25
        help_line = vlimit_t(int, help_line, 0, bindings_help_len - l_points);
1068 25
        redraw = (help_line != hl);
1069 75
}
1070
1071
static void
1072 450
handle_keypress(struct vsc *vsc, int ch)
1073
{
1074
        enum kb_e kb;
1075
1076 450
        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 0
                return;
1086
        }
1087
1088 450
        if (show_help)
1089 75
                handle_help_keypress(kb);
1090
        else
1091 375
                handle_points_keypress(vsc, kb);
1092 450
}
1093
1094
static void * v_matchproto_(VSC_new_f)
1095 43875
newpt(void *priv, const struct VSC_point *const vpt)
1096
{
1097
        struct pt *pt;
1098
1099 43875
        AZ(priv);
1100 43875
        ALLOC_OBJ(pt, PT_MAGIC);
1101 43875
        rebuild |= REBUILD_NEXT;
1102 43875
        AN(pt);
1103 43875
        pt->vpt = vpt;
1104 43875
        pt->last = VSC_Value(vpt);
1105 43875
        pt->ma_10.nmax = 10;
1106 43875
        pt->ma_100.nmax = 100;
1107 43875
        pt->ma_1000.nmax = 1000;
1108
1109 43875
        VTAILQ_INSERT_TAIL(&ptlist, pt, list);
1110 43875
        n_ptlist++;
1111
1112 43875
        AZ(strcmp(vpt->ctype, "uint64_t"));
1113
1114 43875
        if (!strcmp(vpt->name, "MGT.uptime"))
1115 150
                mgt_uptime = vpt->ptr;
1116 43875
        if (!strcmp(vpt->name, "MAIN.uptime"))
1117 150
                main_uptime = vpt->ptr;
1118 43875
        if (!strcmp(vpt->name, "MAIN.cache_hit"))
1119 150
                main_cache_hit = vpt->ptr;
1120 43875
        if (!strcmp(vpt->name, "MAIN.cache_miss"))
1121 150
                main_cache_miss = vpt->ptr;
1122 43875
        return (pt);
1123
}
1124
1125
static void v_matchproto_(VSC_destroy_f)
1126 43875
delpt(void *priv, const struct VSC_point *const vpt)
1127
{
1128
        struct pt *pt;
1129
1130 43875
        AZ(priv);
1131 43875
        CAST_OBJ_NOTNULL(pt, vpt->priv, PT_MAGIC);
1132 43875
        rebuild |= REBUILD_NEXT;
1133 43875
        VTAILQ_REMOVE(&ptlist, pt, list);
1134 43875
        n_ptlist--;
1135 43875
        FREE_OBJ(pt);
1136 43875
        if (vpt->ptr == mgt_uptime)
1137 150
                mgt_uptime = NULL;
1138 43875
        if (vpt->ptr == main_uptime)
1139 150
                main_uptime = NULL;
1140 43875
        if (vpt->ptr == main_cache_hit)
1141 150
                main_cache_hit = NULL;
1142 43875
        if (vpt->ptr == main_cache_miss)
1143 150
                main_cache_miss = NULL;
1144 43875
}
1145
1146
void
1147 150
do_curses(struct vsm *vsm, struct vsc *vsc)
1148
{
1149
        long t;
1150
        int ch;
1151
        double now;
1152
1153 150
        verbosity = VSC_ChangeLevel(NULL, 0);
1154
1155 150
        initscr();
1156 150
        raw();
1157 150
        noecho();
1158 150
        nonl();
1159 150
        curs_set(0);
1160
1161 150
        make_windows();
1162 150
        doupdate();
1163
1164 150
        VSC_State(vsc, newpt, delpt, NULL);
1165
1166 150
        raw_vsc = VSC_IsRaw(vsc);
1167 150
        rebuild |= REBUILD_FIRST;
1168 150
        (void)VSC_Iter(vsc, vsm, NULL, NULL);
1169 150
        build_pt_array();
1170 150
        init_hitrate();
1171
1172 1101
        while (keep_running && !VSIG_int && !VSIG_term && !VSIG_hup) {
1173 951
                (void)VSC_Iter(vsc, vsm, NULL, NULL);
1174 951
                vsm_status = VSM_Status(vsm);
1175 951
                if (vsm_status & (VSM_MGT_RESTARTED|VSM_WRK_RESTARTED))
1176 0
                        init_hitrate();
1177 951
                if (rebuild)
1178 400
                        build_pt_array();
1179
1180 951
                now = VTIM_mono();
1181 951
                if (now - t_sample > interval)
1182 515
                        sample = 1;
1183 951
                if (sample)
1184 515
                        sample_data();
1185 951
                if (redraw)
1186 940
                        draw_screen();
1187
1188 951
                t = (long)((t_sample + interval - now) * 1000);
1189 951
                wtimeout(w_status, t);
1190
1191 951
                ch = wgetch(w_status);
1192 951
                switch (ch) {
1193
                case ERR:
1194 476
                        break;
1195
#ifdef KEY_RESIZE /* sigh, Solaris lacks this.. */
1196
                case KEY_RESIZE:
1197 25
                        make_windows();
1198 25
                        update_position();
1199 25
                        break;
1200
#endif
1201
                default:
1202 450
                        handle_keypress(vsc, ch);
1203 450
                        break;
1204
                }
1205
        }
1206 150
        VSC_Destroy(&vsc, vsm);
1207 150
        AN(VTAILQ_EMPTY(&ptlist));
1208 150
        VSM_Destroy(&vsm);
1209 150
        AZ(endwin());
1210 150
}