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