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 reset_averages = 0;
132
static int scale = 1;
133
static double t_sample = 0.;
134
static double interval = 1.;
135
static unsigned vsm_status = 0;
136
137
#define NOTIF_MAXLEN 256
138
static char notification_message[NOTIF_MAXLEN] = "";
139
static vtim_mono notification_eol = 0.0;
140
141
static void
142 280
init_hitrate(void)
143
{
144 280
        memset(&hitrate, 0, sizeof (struct hitrate));
145 280
        if (main_cache_hit != NULL) {
146 280
                hitrate.lhit = *main_cache_hit;
147 280
                hitrate.lmiss = *main_cache_miss;
148 280
        }
149 280
        hitrate.hr_10.nmax = 10;
150 280
        hitrate.hr_100.nmax = 100;
151 280
        hitrate.hr_1000.nmax = 1000;
152 280
}
153
154
static void
155 264243
update_ma(struct ma *ma, double val)
156
{
157 264243
        AN(ma);
158 264243
        AN(ma->nmax);
159 264243
        if (ma->n < ma->nmax)
160 264243
                ma->n++;
161 264243
        ma->acc += (val - ma->acc) / (double)ma->n;
162 264243
}
163
164
static void
165 2306
update_position(void)
166
{
167
        int old_current, old_page_start;
168
169 2306
        old_current = current;
170 2306
        old_page_start = page_start;
171
172 2306
        if (n_ptarray == 0) {
173 1053
                current = 0;
174 1053
                page_start = 0;
175 1053
        } else {
176 1253
                current = vlimit_t(int, current, 0, n_ptarray - 1);
177 1253
                page_start = vmin(page_start, current);
178 1253
                if (current > page_start + (l_points - 1))
179 80
                        page_start = current - (l_points - 1);
180 1253
                page_start = vlimit_t(int, page_start, 0, n_ptarray - 1);
181
        }
182
183 2306
        if (current != old_current || page_start != old_page_start)
184 453
                redraw = 1;
185 2306
}
186
187
static void
188 773
delete_pt_array(void)
189
{
190 773
        if (ptarray != NULL)
191 773
                free(ptarray);
192 773
        ptarray = NULL;
193 773
        n_ptarray = 0;
194
195 773
        update_position();
196 773
}
197
198
static void
199 1053
build_pt_array(void)
200
{
201
        int i;
202
        struct pt *pt;
203 1053
        struct pt *pt_current = NULL;
204 1053
        int current_line = 0;
205
206 1053
        if (current < n_ptarray) {
207 493
                pt_current = ptarray[current];
208 493
                current_line = current - page_start;
209 493
        }
210
211 1053
        if (ptarray != NULL)
212 773
                delete_pt_array();
213 1053
        AZ(n_ptarray);
214 1053
        ptarray = calloc(n_ptlist, sizeof *ptarray);
215 1053
        AN(ptarray);
216
217 346196
        VTAILQ_FOREACH(pt, &ptlist, list) {
218 345143
                CHECK_OBJ_NOTNULL(pt, PT_MAGIC);
219 345143
                if (pt->vpt->level > verbosity) {
220 206116
                        if (has_f && (rebuild & REBUILD_FIRST))
221 240
                                verbosity = VSC_ChangeLevel(verbosity,
222 120
                                    pt->vpt->level - verbosity);
223
                        else
224 205996
                                continue;
225 120
                }
226 139147
                if (!pt->seen && hide_unseen)
227 111399
                        continue;
228 27748
                assert(n_ptarray < n_ptlist);
229 27748
                ptarray[n_ptarray++] = pt;
230 27748
        }
231 1053
        assert(n_ptarray <= n_ptlist);
232
233 17216
        for (i = 0; pt_current != NULL && i < n_ptarray; i++)
234 16656
                if (ptarray[i] == pt_current)
235 493
                        break;
236 1053
        current = i;
237 1053
        page_start = current - current_line;
238 1053
        update_position();
239
240 1053
        rebuild = 0;
241 1053
        redraw = 1;
242 1053
}
243
244
static void
245 1227
sample_points(void)
246
{
247
        struct pt *pt;
248
        uint64_t v;
249
250 410924
        VTAILQ_FOREACH(pt, &ptlist, list) {
251 409697
                AN(pt->vpt);
252 409697
                AN(pt->vpt->ptr);
253 409697
                v = VSC_Value(pt->vpt);
254 409697
                if (v == 0 && !pt->seen)
255 304481
                        continue;
256 105216
                if (!pt->seen) {
257 24087
                        pt->seen = 1;
258 24087
                        rebuild = REBUILD_NEXT;
259 24087
                }
260 105216
                pt->last = pt->cur;
261 105216
                pt->cur = v;
262 105216
                pt->t_last = pt->t_cur;
263 105216
                pt->t_cur = VTIM_mono();
264
265 105216
                if (reset_averages) {
266 4752
                        pt->chg = 0;
267 4752
                        pt->ma_10.n = 0;
268 4752
                        pt->ma_100.n = 0;
269 4752
                        pt->ma_1000.n = 0;
270 4752
                }
271 105216
                if (pt->t_last)
272 162258
                        pt->chg = ((int64_t)pt->cur - (int64_t)pt->last) /
273 81129
                            (pt->t_cur - pt->t_last);
274
275 105216
                if (pt->vpt->semantics == 'g') {
276 29673
                        pt->avg = 0.;
277 29673
                        update_ma(&pt->ma_10, (int64_t)pt->cur);
278 29673
                        update_ma(&pt->ma_100, (int64_t)pt->cur);
279 29673
                        update_ma(&pt->ma_1000, (int64_t)pt->cur);
280 105216
                } else if (pt->vpt->semantics == 'c') {
281 74516
                        if (main_uptime != NULL && *main_uptime)
282 64516
                                pt->avg = pt->cur / (double)*main_uptime;
283
                        else
284 10000
                                pt->avg = 0.;
285 74516
                        if (pt->t_last) {
286 57181
                                update_ma(&pt->ma_10, pt->chg);
287 57181
                                update_ma(&pt->ma_100, pt->chg);
288 57181
                                update_ma(&pt->ma_1000, pt->chg);
289 57181
                        }
290 74516
                }
291 105216
        }
292 1227
}
293
294
static void
295 1227
sample_hitrate(void)
296
{
297
        double hr, mr, ratio;
298
        uint64_t hit, miss;
299
300 1227
        if (main_cache_hit == NULL)
301 0
                return;
302
303 1227
        hit = *main_cache_hit;
304 1227
        miss = *main_cache_miss;
305 1227
        hr = hit - hitrate.lhit;
306 1227
        mr = miss - hitrate.lmiss;
307 1227
        hitrate.lhit = hit;
308 1227
        hitrate.lmiss = miss;
309
310 1227
        if (hr + mr != 0)
311 167
                ratio = hr / (hr + mr);
312
        else
313 1060
                ratio = 0;
314 1227
        if (reset_averages) {
315 40
                hitrate.hr_10.n = 0;
316 40
                hitrate.hr_100.n = 0;
317 40
                hitrate.hr_1000.n = 0;
318 40
        }
319 1227
        update_ma(&hitrate.hr_10, ratio);
320 1227
        update_ma(&hitrate.hr_100, ratio);
321 1227
        update_ma(&hitrate.hr_1000, ratio);
322 1227
}
323
324
static void
325 1227
sample_data(void)
326
{
327 1227
        t_sample = VTIM_mono();
328 1227
        sample = 0;
329 1227
        redraw = 1;
330 1227
        sample_points();
331 1227
        sample_hitrate();
332 1227
        reset_averages = 0;
333 1227
}
334
335
static void
336 1600
destroy_window(WINDOW **w)
337
{
338
339 1600
        AN(w);
340 1600
        if (*w == NULL)
341 1400
                return;
342 200
        AC(delwin(*w));
343 200
        *w = NULL;
344 1600
}
345
346
static void
347 320
make_windows(void)
348
{
349
        int Y, X;
350
        int y;
351
        int y_status, y_bar_t, y_points, y_bar_b, y_info;
352
353 320
        destroy_window(&w_status);
354 320
        destroy_window(&w_bar_t);
355 320
        destroy_window(&w_points);
356 320
        destroy_window(&w_bar_b);
357 320
        destroy_window(&w_info);
358
359 320
        Y = LINES;
360 320
        X = COLS;
361
362 320
        l_status = LINES_STATUS;
363 320
        l_bar_t = LINES_BAR_T;
364 320
        l_bar_b = LINES_BAR_B;
365 320
        l_info = LINES_INFO;
366 320
        l_points = Y - (l_status + l_bar_t + l_bar_b + l_info);
367 320
        if (l_points < LINES_POINTS_MIN) {
368 0
                l_points += l_info;
369 0
                l_info = 0;
370 0
        }
371 320
        l_points = vmax(l_points, LINES_POINTS_MIN);
372
373 320
        y = 0;
374 320
        y_status = y;
375 320
        y += l_status;
376 320
        y_bar_t = y;
377 320
        y += l_bar_t;
378 320
        y_points = y;
379 320
        y += l_points;
380 320
        y_bar_b = y;
381 320
        y += l_bar_b;
382 320
        y_info = y;
383 320
        y += l_info;
384 320
        assert(y >= Y);
385
386 320
        w_status = newwin(l_status, X, y_status, 0);
387 320
        AN(w_status);
388 320
        AC(nodelay(w_status, 1));
389 320
        AC(keypad(w_status, 1));
390 320
        AC(wnoutrefresh(w_status));
391
392 320
        w_bar_t = newwin(l_bar_t, X, y_bar_t, 0);
393 320
        AN(w_bar_t);
394 320
        wbkgd(w_bar_t, A_REVERSE);
395 320
        AC(wnoutrefresh(w_bar_t));
396
397 320
        w_points = newwin(l_points, X, y_points, 0);
398 320
        AN(w_points);
399 320
        AC(wnoutrefresh(w_points));
400
401 320
        w_bar_b = newwin(l_bar_b, X, y_bar_b, 0);
402 320
        AN(w_bar_b);
403 320
        wbkgd(w_bar_b, A_REVERSE);
404 320
        AC(wnoutrefresh(w_bar_b));
405
406 320
        if (l_info) {
407 320
                w_info = newwin(l_info, X, y_info, 0);
408 320
                AN(w_info);
409 320
                AC(wnoutrefresh(w_info));
410 320
        }
411
412 320
        if (X - COLW_NAME_MIN > N_COL * COLW)
413 40
                colw_name = X - (N_COL * COLW);
414
        else
415 280
                colw_name = COLW_NAME_MIN;
416
417 320
        redraw = 1;
418 320
}
419
420
static void
421 3202
print_duration(WINDOW *w, uint64_t t)
422
{
423
424 3202
        IC(wprintw(w, "%4ju+%02ju:%02ju:%02ju",
425
            (uintmax_t)t / 86400, (uintmax_t)(t % 86400) / 3600,
426
            (uintmax_t)(t % 3600) / 60, (uintmax_t)t % 60));
427 3202
}
428
429
static void
430 3816
running(WINDOW *w, uint64_t up, int flg)
431
{
432 3816
        if (vsm_status & flg) {
433 3162
                print_duration(w_status, up);
434 3162
        } else {
435 654
                wattron(w, A_STANDOUT);
436 654
                IC(wprintw(w, "  Not Running"));
437 654
                wattroff(w, A_STANDOUT);
438
        }
439 3816
}
440
441
static void
442 1908
draw_status(void)
443
{
444 1908
        uint64_t up_mgt = 0;
445 1908
        uint64_t up_chld = 0;
446
447 1908
        AN(w_status);
448
449 1908
        AC(werase(w_status));
450
451 1908
        if (mgt_uptime != NULL)
452 1908
                up_mgt = *mgt_uptime;
453 1908
        if (main_uptime != NULL)
454 1908
                up_chld = *main_uptime;
455
456 1908
        IC(mvwprintw(w_status, 0, 0, "Uptime mgt:   "));
457 1908
        running(w_status, up_mgt, VSM_MGT_RUNNING);
458 1908
        IC(mvwprintw(w_status, 1, 0, "Uptime child: "));
459 1908
        running(w_status, up_chld, VSM_WRK_RUNNING);
460 1908
        IC(mvwprintw(w_status, 2, 0, "Press <h> to toggle help screen"));
461
462 1908
        if (VTIM_mono() < notification_eol)
463 237
                mvwaddstr(w_status, 2, 0, notification_message);
464
465 1908
        if (COLS > 70) {
466 1908
                IC(mvwprintw(w_status, 0, getmaxx(w_status) - 37,
467
                    "Hitrate n: %8u %8u %8u", hitrate.hr_10.n, hitrate.hr_100.n,
468
                    hitrate.hr_1000.n));
469 1908
                IC(mvwprintw(w_status, 1, getmaxx(w_status) - 37,
470
                    "   avg(n): %8.4f %8.4f %8.4f", hitrate.hr_10.acc,
471
                    hitrate.hr_100.acc, hitrate.hr_1000.acc));
472 1908
        }
473
474 1908
        AC(wnoutrefresh(w_status));
475 1908
}
476
477
static void
478 1788
draw_bar_t(void)
479
{
480
        int X, x;
481
        enum {
482
                COL_CUR,
483
                COL_CHG,
484
                COL_AVG,
485
                COL_MA10,
486
                COL_MA100,
487
                COL_MA1000,
488
                COL_LAST
489
        } col;
490
491 1788
        AN(w_bar_t);
492
493 1788
        X = getmaxx(w_bar_t);
494 1788
        x = 0;
495 1788
        AC(werase(w_bar_t));
496 1788
        if (page_start > 0)
497 1157
                IC(mvwprintw(w_bar_t, 0, x, "^^^"));
498 1788
        x += 4;
499 1788
        IC(mvwprintw(w_bar_t, 0, x, "%.*s", colw_name - 4, "NAME"));
500 1788
        x += colw_name - 4;
501 1788
        col = COL_CUR;
502 9340
        while (col < COL_LAST) {
503 9140
                if (X - x < COLW)
504 1588
                        break;
505 7552
                switch (col) {
506
                case COL_CUR:
507 1788
                        IC(mvwprintw(w_bar_t, 0, x, " %12.12s", "CURRENT"));
508 1788
                        break;
509
                case COL_CHG:
510 1788
                        IC(mvwprintw(w_bar_t, 0, x, " %12.12s", "CHANGE"));
511 1788
                        break;
512
                case COL_AVG:
513 1788
                        IC(mvwprintw(w_bar_t, 0, x, " %12.12s", "AVERAGE"));
514 1788
                        break;
515
                case COL_MA10:
516 1788
                        IC(mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_10"));
517 1788
                        break;
518
                case COL_MA100:
519 200
                        IC(mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_100"));
520 200
                        break;
521
                case COL_MA1000:
522 200
                        IC(mvwprintw(w_bar_t, 0, x, " %12.12s", "AVG_1000"));
523 200
                        break;
524
                default:
525 0
                        break;
526
                }
527 7552
                x += COLW;
528 7552
                col++;
529
        }
530
531 1788
        AC(wnoutrefresh(w_bar_t));
532 1788
}
533
534
static void
535 12997
draw_line_default(WINDOW *w, int y, int x, int X, const struct pt *pt)
536
{
537
        enum {
538
                COL_CUR,
539
                COL_CHG,
540
                COL_AVG,
541
                COL_MA10,
542
                COL_MA100,
543
                COL_MA1000,
544
                COL_LAST
545
        } col;
546
547 12997
        AN(w);
548 12997
        AN(pt);
549
550 12997
        col = COL_CUR;
551 68665
        while (col < COL_LAST) {
552 66825
                if (X - x < COLW)
553 11157
                        break;
554 55668
                switch (col) {
555
                case COL_CUR:
556 12997
                        IC(mvwprintw(w, y, x, " %12ju", (uintmax_t)pt->cur));
557 12997
                        break;
558
                case COL_CHG:
559 12997
                        if (pt->t_last)
560 10745
                                IC(mvwprintw(w, y, x, " %12.2f", pt->chg));
561
                        else
562 2252
                                IC(mvwprintw(w, y, x, " %12s", ".  "));
563 12997
                        break;
564
                case COL_AVG:
565 12997
                        if (pt->avg)
566 4286
                                IC(mvwprintw(w, y, x, " %12.2f", pt->avg));
567
                        else
568 8711
                                IC(mvwprintw(w, y, x, " %12s", ".  "));
569 12997
                        break;
570
                case COL_MA10:
571 12997
                        IC(mvwprintw(w, y, x, " %12.2f", pt->ma_10.acc));
572 12997
                        break;
573
                case COL_MA100:
574 1840
                        IC(mvwprintw(w, y, x, " %12.2f", pt->ma_100.acc));
575 1840
                        break;
576
                case COL_MA1000:
577 1840
                        IC(mvwprintw(w, y, x, " %12.2f", pt->ma_1000.acc));
578 1840
                        break;
579
                default:
580 0
                        break;
581
                }
582 55668
                x += COLW;
583 55668
                col++;
584
        }
585 12997
}
586
587
static double
588 8454
scale_bytes(double val, char *q)
589
{
590
        const char *p;
591
592 10726
        for (p = " KMGTPEZY"; *p; p++) {
593 10726
                if (fabs(val) < 1024.)
594 8454
                        break;
595 2272
                val /= 1024.;
596 2272
        }
597 8454
        *q = *p;
598 8454
        return (val);
599
}
600
601
static void
602 14494
print_bytes(WINDOW *w, double val)
603
{
604 14494
        char q = ' ';
605
606 14494
        if (scale)
607 8454
                val = scale_bytes(val, &q);
608 14494
        IC(wprintw(w, " %12.2f%c", val, q));
609 14494
}
610
611
static void
612 5269
print_trunc(WINDOW *w, uintmax_t val)
613
{
614 5269
        if (val > VALUE_MAX) {
615 0
                while (val > VALUE_MAX)
616 0
                        val /= 1000;
617 0
                IC(wprintw(w, " %9ju...", val));
618 0
        } else
619 5269
                IC(wprintw(w, " %12ju", val));
620 5269
}
621
622
static void
623 5936
draw_line_bytes(WINDOW *w, int y, int x, int X, const struct pt *pt)
624
{
625
        enum {
626
                COL_CUR,
627
                COL_CHG,
628
                COL_AVG,
629
                COL_MA10,
630
                COL_MA100,
631
                COL_MA1000,
632
                COL_LAST
633
        } col;
634
635 5936
        AN(w);
636 5936
        AN(pt);
637
638 5936
        col = COL_CUR;
639 31840
        while (col < COL_LAST) {
640 30760
                if (X - x < COLW)
641 4856
                        break;
642 25904
                wmove(w, y, x);
643 25904
                switch (col) {
644
                case COL_CUR:
645 5936
                        if (scale && pt->cur > 1024)
646 667
                                print_bytes(w, (double)pt->cur);
647
                        else
648 5269
                                print_trunc(w, (uintmax_t)pt->cur);
649 5936
                        break;
650
                case COL_CHG:
651 5936
                        if (pt->t_last)
652 3251
                                print_bytes(w, pt->chg);
653
                        else
654 2685
                                IC(wprintw(w, " %12s", ".  "));
655 5936
                        break;
656
                case COL_AVG:
657 5936
                        if (pt->avg)
658 2480
                                print_bytes(w, pt->avg);
659
                        else
660 3456
                                IC(wprintw(w, " %12s", ".  "));
661 5936
                        break;
662
                case COL_MA10:
663 5936
                        print_bytes(w, pt->ma_10.acc);
664 5936
                        break;
665
                case COL_MA100:
666 1080
                        print_bytes(w, pt->ma_100.acc);
667 1080
                        break;
668
                case COL_MA1000:
669 1080
                        print_bytes(w, pt->ma_1000.acc);
670 1080
                        break;
671
                default:
672 0
                        break;
673
                }
674 25904
                x += COLW;
675 25904
                col++;
676
        }
677 5936
}
678
679
static void
680 735
draw_line_bitmap(WINDOW *w, int y, int x, int X, const struct pt *pt)
681
{
682
        unsigned ch;
683
        enum {
684
                COL_VAL,
685
                COL_MAP,
686
                COL_LAST
687
        } col;
688
689 735
        AN(w);
690 735
        AN(pt);
691 735
        assert(pt->vpt->format == 'b');
692
693 735
        col = COL_VAL;
694 2205
        while (col < COL_LAST) {
695 1470
                switch (col) {
696
                case COL_VAL:
697 735
                        if (X - x < COLW)
698 0
                                return;
699 735
                        IC(mvwprintw(w, y, x, "   %10.10jx",
700
                            (uintmax_t)((pt->cur >> 24) & 0xffffffffffLL)));
701 735
                        x += COLW;
702 735
                        break;
703
                case COL_MAP:
704 735
                        if (X - x < 2 * COLW)
705 0
                                return;
706 735
                        x += (2 * COLW) - 24;
707 18375
                        for (ch = 0x800000; ch; ch >>= 1) {
708 17640
                                if (pt->cur & ch)
709 4242
                                        mvwaddch(w, y, x, 'V');
710
                                else
711 13398
                                        mvwaddch(w, y, x, '_');
712 17640
                                x++;
713 17640
                        }
714 735
                        break;
715
                default:
716 0
                        break;
717
                }
718 1470
                col++;
719
        }
720 735
}
721
722
static void
723 120
draw_line_duration(WINDOW *w, int y, int x, int X, const struct pt *pt)
724
{
725
        enum {
726
                COL_DUR,
727
                COL_LAST
728
        } col;
729
730 120
        AN(w);
731 120
        AN(pt);
732
733 120
        col = COL_DUR;
734 240
        while (col < COL_LAST) {
735 120
                if (X - x < COLW)
736 0
                        break;
737 120
                switch (col) {
738
                case COL_DUR:
739 120
                        wmove(w, y, x);
740 120
                        if (scale)
741 40
                                print_duration(w, pt->cur);
742
                        else
743 80
                                IC(wprintw(w, " %12ju", (uintmax_t)pt->cur));
744 120
                        break;
745
                default:
746 0
                        break;
747
                }
748 120
                x += COLW;
749 120
                col++;
750
        }
751 120
}
752
753
static void
754 19788
draw_line(WINDOW *w, int y, const struct pt *pt)
755
{
756
        int x, X;
757
758 19788
        assert(colw_name >= COLW_NAME_MIN);
759 19788
        X = getmaxx(w);
760 19788
        x = 0;
761 19788
        if (strlen(pt->vpt->name) > colw_name)
762 1396
                IC(mvwprintw(w, y, x, "%.*s...", colw_name - 3, pt->vpt->name));
763
        else
764 18392
                IC(mvwprintw(w, y, x, "%.*s", colw_name, pt->vpt->name));
765 19788
        x += colw_name;
766
767 19788
        switch (pt->vpt->format) {
768
        case 'b':
769 735
                draw_line_bitmap(w, y, x, X, pt);
770 735
                break;
771
        case 'B':
772 5936
                draw_line_bytes(w, y, x, X, pt);
773 5936
                break;
774
        case 'd':
775 120
                draw_line_duration(w, y, x, X, pt);
776 120
                break;
777
        default:
778 12997
                draw_line_default(w, y, x, X, pt);
779 12997
                break;
780
        }
781 19788
}
782
783
static void
784 1788
draw_points(void)
785
{
786
        int line;
787
        int n;
788
789 1788
        AN(w_points);
790
791 1788
        AC(werase(w_points));
792 1788
        if (n_ptarray == 0) {
793 280
                AC(wnoutrefresh(w_points));
794 280
                return;
795
        }
796
797 1508
        assert(current >= 0);
798 1508
        assert(current < n_ptarray);
799 1508
        assert(page_start >= 0);
800 1508
        assert(page_start < n_ptarray);
801 1508
        assert(current >= page_start);
802 1508
        assert(current - page_start < l_points);
803
804 21296
        for (line = 0; line < l_points; line++) {
805 20284
                n = line + page_start;
806 20284
                if (n >= n_ptarray)
807 496
                        break;
808 19788
                if (n == current)
809 1508
                        wattron(w_points, A_BOLD);
810 19788
                draw_line(w_points, line, ptarray[n]);
811 19788
                if (n == current)
812 1508
                        wattroff(w_points, A_BOLD);
813 19788
        }
814 1508
        AC(wnoutrefresh(w_points));
815 1788
}
816
817
static void
818 120
draw_help(void)
819
{
820
        const char *const *p;
821
        int l, y, X;
822
823 120
        if (l_points >= bindings_help_len) {
824 0
                assert(help_line == 0);
825 0
                l = bindings_help_len;
826 0
        } else {
827 120
                assert(help_line >= 0);
828 120
                assert(help_line <= bindings_help_len - l_points);
829 120
                l = l_points;
830
        }
831
832 120
        X = getmaxx(w_points);
833 120
        AC(werase(w_points));
834
835 2040
        for (y = 0, p = bindings_help + help_line; y < l; y++, p++) {
836 1920
                if (**p == '\t') {
837 1000
                        IC(mvwprintw(w_points, y, 0, "    %.*s", X - 4, *p + 1));
838 1000
                } else {
839 920
                        wattron(w_points, A_BOLD);
840 920
                        IC(mvwprintw(w_points, y, 0, "%.*s", X, *p));
841 920
                        wattroff(w_points, A_BOLD);
842
                }
843 1920
        }
844
845 120
        AC(wnoutrefresh(w_points));
846 120
}
847
848
static void
849 1788
draw_bar_b(void)
850
{
851
        int x, X;
852
        char buf[64];
853
854 1788
        AN(w_bar_b);
855
856 1788
        x = 0;
857 1788
        X = getmaxx(w_bar_b);
858 1788
        AC(werase(w_bar_b));
859 1788
        if (page_start + l_points < n_ptarray)
860 854
                IC(mvwprintw(w_bar_b, 0, x, "vvv"));
861 1788
        x += 4;
862 1788
        if (current < n_ptarray)
863 1508
                IC(mvwprintw(w_bar_b, 0, x, "%s", ptarray[current]->vpt->name));
864
865 1788
        bprintf(buf, "%d-%d/%d", page_start + 1,
866
            page_start + l_points < n_ptarray ?
867
                page_start + l_points : n_ptarray,
868
            n_ptarray);
869 1788
        IC(mvwprintw(w_bar_b, 0, X - strlen(buf), "%s", buf));
870 1788
        X -= strlen(buf) + 2;
871
872 1788
        if (verbosity != NULL) {
873 1788
                IC(mvwprintw(w_bar_b, 0, X - strlen(verbosity->label), "%s",
874
                    verbosity->label));
875 1788
                X -= strlen(verbosity->label) + 2;
876 1788
        }
877 1788
        if (!hide_unseen) {
878 400
                IC(mvwprintw(w_bar_b, 0, X - 6, "%s", "UNSEEN"));
879 400
                X -= 8;
880 400
        }
881 1788
        if (raw_vsc)
882 0
                IC(mvwprintw(w_bar_b, 0, X - 3, "%s", "RAW"));
883
884 1788
        AC(wnoutrefresh(w_bar_b));
885 1788
}
886
887
static void
888 1788
draw_info(void)
889
{
890
891 1788
        if (w_info == NULL)
892 0
                return;
893
894 1788
        AC(werase(w_info));
895 1788
        if (current < n_ptarray) {
896
                /* XXX: Word wrapping, and overflow handling? */
897 1508
                IC(mvwprintw(w_info, 0, 0, "%s:",
898
                    ptarray[current]->vpt->sdesc));
899 1508
                IC(mvwprintw(w_info, 1, 0, "%s",
900
                    ptarray[current]->vpt->ldesc));
901 1508
        }
902 1788
        AC(wnoutrefresh(w_info));
903 1788
}
904
905
static void
906 1908
draw_screen(void)
907
{
908 1908
        draw_status();
909 1908
        if (show_help) {
910 120
                AC(werase(w_bar_t));
911 120
                AC(werase(w_bar_b));
912 120
                AC(werase(w_info));
913 120
                AC(wnoutrefresh(w_bar_t));
914 120
                AC(wnoutrefresh(w_bar_b));
915 120
                AC(wnoutrefresh(w_info));
916 120
                draw_help();
917 120
        } else {
918 1788
                draw_bar_t();
919 1788
                draw_points();
920 1788
                draw_bar_b();
921 1788
                draw_info();
922
        }
923 1908
        AC(doupdate());
924 1908
        redraw = 0;
925 1908
}
926
927
static void
928 80
handle_common_keypress(enum kb_e kb)
929
{
930
931 80
        switch (kb) {
932
        case KB_QUIT:
933 80
                keep_running = 0;
934 80
                return;
935
        case KB_SIG_INT:
936 0
                AZ(raise(SIGINT));
937 0
                return;
938
        case KB_SIG_TSTP:
939 0
                AZ(raise(SIGTSTP));
940 0
                return;
941
        default:
942 0
                WRONG("unexpected key binding");
943 0
        }
944 80
}
945
946
static void
947 640
handle_points_keypress(struct vsc *vsc, enum kb_e kb)
948
{
949
950 640
        switch (kb) {
951
        case KB_HELP:
952 80
                show_help = 1;
953 80
                help_line = 0;
954 80
                redraw = 1;
955 80
                return;
956
        case KB_UP:
957 40
                if (current == 0)
958 0
                        return;
959 40
                current--;
960 40
                break;
961
        case KB_DOWN:
962 0
                if (current == n_ptarray - 1)
963 0
                        return;
964 0
                current++;
965 0
                break;
966
        case KB_PAGEUP:
967 40
                current -= l_points;
968 40
                page_start -= l_points;
969 40
                break;
970
        case KB_PAGEDOWN:
971 40
                current += l_points;
972 40
                if (page_start + l_points < n_ptarray - 1)
973 40
                        page_start += l_points;
974 40
                break;
975
        case KB_TOP:
976 40
                current = 0;
977 40
                break;
978
        case KB_BOTTOM:
979 80
                current = n_ptarray - 1;
980 80
                break;
981
        case KB_UNSEEN:
982 40
                hide_unseen = 1 - hide_unseen;
983 40
                rebuild = REBUILD_NEXT;
984 40
                break;
985
        case KB_RAW:
986 0
                AN(VSC_Arg(vsc, 'r', NULL));
987 0
                raw_vsc = VSC_IsRaw(vsc);
988 0
                rebuild = REBUILD_NEXT;
989 0
                break;
990
        case KB_SCALE:
991 40
                scale = 1 - scale;
992 40
                rebuild = REBUILD_NEXT;
993 40
                break;
994
        case KB_ACCEL:
995 40
                interval += 0.1;
996 40
                (void)snprintf(notification_message, NOTIF_MAXLEN,
997 40
                    "Refresh interval set to %.1f seconds.", interval);
998
999 40
                notification_eol = VTIM_mono() + 1.25;
1000 40
                break;
1001
        case KB_DECEL:
1002 40
                interval -= 0.1;
1003 40
                if (interval < 0.1)
1004 0
                        interval = 0.1;
1005 40
                (void)snprintf(notification_message, NOTIF_MAXLEN,
1006 40
                    "Refresh interval set to %.1f seconds.", interval);
1007
1008 40
                notification_eol = VTIM_mono() + 1.25;
1009 40
                break;
1010
        case KB_VERBOSE:
1011 40
                verbosity = VSC_ChangeLevel(verbosity, 1);
1012 40
                rebuild = REBUILD_NEXT;
1013 40
                break;
1014
        case KB_QUIET:
1015 0
                verbosity = VSC_ChangeLevel(verbosity, -1);
1016 0
                rebuild = REBUILD_NEXT;
1017 0
                break;
1018
        case KB_SAMPLE:
1019 0
                sample = 1;
1020 0
                return;
1021
        case KB_RESET_AVERAGES:
1022 40
                reset_averages = 1;
1023 40
                return;
1024
        case KB_QUIT:
1025
        case KB_SIG_INT:
1026
        case KB_SIG_TSTP:
1027 80
                handle_common_keypress(kb);
1028 80
                return;
1029
        default:
1030 0
                WRONG("unhandled key binding");
1031 0
        }
1032
1033 440
        update_position();
1034 440
        redraw = 1;
1035 640
}
1036
1037
static void
1038 120
handle_help_keypress(enum kb_e kb)
1039
{
1040 120
        int hl = help_line;
1041
1042 120
        switch (kb) {
1043
        case KB_HELP:
1044 80
                show_help = 0;
1045 80
                redraw = 1;
1046 80
                return;
1047
        case KB_UP:
1048 0
                help_line--;
1049 0
                break;
1050
        case KB_DOWN:
1051 0
                help_line++;
1052 0
                break;
1053
        case KB_PAGEUP:
1054 0
                help_line -= l_points;
1055 0
                break;
1056
        case KB_PAGEDOWN:
1057 0
                help_line += l_points;
1058 0
                break;
1059
        case KB_TOP:
1060 0
                help_line = 0;
1061 0
                break;
1062
        case KB_BOTTOM:
1063 40
                help_line = bindings_help_len;
1064 40
                break;
1065
        case KB_UNSEEN:
1066
        case KB_RAW:
1067
        case KB_SCALE:
1068
        case KB_ACCEL:
1069
        case KB_DECEL:
1070
        case KB_VERBOSE:
1071
        case KB_QUIET:
1072
        case KB_SAMPLE:
1073
        case KB_RESET_AVERAGES:
1074 0
                break;
1075
        case KB_QUIT:
1076
        case KB_SIG_INT:
1077
        case KB_SIG_TSTP:
1078 0
                handle_common_keypress(kb);
1079 0
                return;
1080
        default:
1081 0
                WRONG("unhandled key binding");
1082 0
        }
1083
1084 40
        help_line = vlimit_t(int, help_line, 0, bindings_help_len - l_points);
1085 40
        redraw = (help_line != hl);
1086 120
}
1087
1088
static void
1089 760
handle_keypress(struct vsc *vsc, int ch)
1090
{
1091
        enum kb_e kb;
1092
1093 760
        switch (ch) {
1094
#define BINDING_KEY(chr, name, or)      \
1095
        case chr:
1096
#define BINDING(name, desc)             \
1097
                kb = KB_ ## name;       \
1098
                break;
1099
#define BINDING_SIG
1100
#include "varnishstat_bindings.h"
1101
        default:
1102
                return;
1103
        }
1104
1105 760
        if (show_help)
1106 120
                handle_help_keypress(kb);
1107
        else
1108 640
                handle_points_keypress(vsc, kb);
1109 760
}
1110
1111
static void * v_matchproto_(VSC_new_f)
1112 88440
newpt(void *priv, const struct VSC_point *const vpt)
1113
{
1114
        struct pt *pt;
1115
1116 88440
        AZ(priv);
1117 88440
        ALLOC_OBJ(pt, PT_MAGIC);
1118 88440
        rebuild |= REBUILD_NEXT;
1119 88440
        AN(pt);
1120 88440
        pt->vpt = vpt;
1121 88440
        pt->last = VSC_Value(vpt);
1122 88440
        pt->ma_10.nmax = 10;
1123 88440
        pt->ma_100.nmax = 100;
1124 88440
        pt->ma_1000.nmax = 1000;
1125
1126 88440
        VTAILQ_INSERT_TAIL(&ptlist, pt, list);
1127 88440
        n_ptlist++;
1128
1129 88440
        AZ(strcmp(vpt->ctype, "uint64_t"));
1130
1131 88440
        if (!strcmp(vpt->name, "MGT.uptime"))
1132 280
                mgt_uptime = vpt->ptr;
1133 88440
        if (!strcmp(vpt->name, "MAIN.uptime"))
1134 280
                main_uptime = vpt->ptr;
1135 88440
        if (!strcmp(vpt->name, "MAIN.cache_hit"))
1136 280
                main_cache_hit = vpt->ptr;
1137 88440
        if (!strcmp(vpt->name, "MAIN.cache_miss"))
1138 280
                main_cache_miss = vpt->ptr;
1139 88440
        return (pt);
1140
}
1141
1142
static void v_matchproto_(VSC_destroy_f)
1143 88440
delpt(void *priv, const struct VSC_point *const vpt)
1144
{
1145
        struct pt *pt;
1146
1147 88440
        AZ(priv);
1148 88440
        CAST_OBJ_NOTNULL(pt, vpt->priv, PT_MAGIC);
1149 88440
        rebuild |= REBUILD_NEXT;
1150 88440
        VTAILQ_REMOVE(&ptlist, pt, list);
1151 88440
        n_ptlist--;
1152 88440
        FREE_OBJ(pt);
1153 88440
        if (vpt->ptr == mgt_uptime)
1154 280
                mgt_uptime = NULL;
1155 88440
        if (vpt->ptr == main_uptime)
1156 280
                main_uptime = NULL;
1157 88440
        if (vpt->ptr == main_cache_hit)
1158 280
                main_cache_hit = NULL;
1159 88440
        if (vpt->ptr == main_cache_miss)
1160 280
                main_cache_miss = NULL;
1161 88440
}
1162
1163
void
1164 280
do_curses(struct vsm *vsm, struct vsc *vsc)
1165
{
1166
        long t;
1167
        int ch;
1168
        double now;
1169
1170 280
        verbosity = VSC_ChangeLevel(NULL, 0);
1171
1172 280
        (void)initscr();
1173 280
        AC(raw());
1174 280
        AC(noecho());
1175 280
        AC(nonl());
1176 280
        IC(curs_set(0));
1177
1178 280
        make_windows();
1179 280
        AC(doupdate());
1180
1181 280
        VSC_State(vsc, newpt, delpt, NULL);
1182
1183 280
        raw_vsc = VSC_IsRaw(vsc);
1184 280
        rebuild |= REBUILD_FIRST;
1185 280
        (void)VSC_Iter(vsc, vsm, NULL, NULL);
1186 280
        build_pt_array();
1187 280
        init_hitrate();
1188
1189 2226
        while (keep_running && !VSIG_int && !VSIG_term && !VSIG_hup) {
1190 1946
                (void)VSC_Iter(vsc, vsm, NULL, NULL);
1191 1946
                vsm_status = VSM_Status(vsm);
1192 1946
                if (vsm_status & (VSM_MGT_RESTARTED|VSM_WRK_RESTARTED))
1193 0
                        init_hitrate();
1194 1946
                if (rebuild)
1195 773
                        build_pt_array();
1196
1197 1946
                now = VTIM_mono();
1198 1946
                if (now - t_sample > interval)
1199 1227
                        sample = 1;
1200 1946
                if (sample)
1201 1227
                        sample_data();
1202 1946
                if (redraw)
1203 1908
                        draw_screen();
1204
1205 1946
                t = (long)((t_sample + interval - now) * 1000);
1206 1946
                wtimeout(w_status, t);
1207
1208 1946
                ch = wgetch(w_status);
1209 1946
                switch (ch) {
1210
                case ERR:
1211 1146
                        break;
1212
#ifdef KEY_RESIZE /* sigh, Solaris lacks this.. */
1213
                case KEY_RESIZE:
1214 40
                        make_windows();
1215 40
                        update_position();
1216 40
                        break;
1217
#endif
1218
                default:
1219 760
                        handle_keypress(vsc, ch);
1220 760
                        break;
1221
                }
1222
        }
1223 280
        VSC_Destroy(&vsc, vsm);
1224 280
        AN(VTAILQ_EMPTY(&ptlist));
1225 280
        VSM_Destroy(&vsm);
1226 280
        AZ(endwin());
1227 280
}