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