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