varnish-cache/bin/varnishtop/varnishtop.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: Guillaume Quintard <guillaume.quintard@gmail.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
 * Log tailer for Varnish
32
 */
33
34
#include "config.h"
35
36
#include <ctype.h>
37
#include <errno.h>
38
#include <pthread.h>
39
#include <signal.h>
40
#include <stdarg.h>
41
#include <stdint.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <unistd.h>
45
46
#define VOPT_DEFINITION
47
#define VOPT_INC "varnishtop_options.h"
48
49
#include "vdef.h"
50
51
#include "vcurses.h"
52
#include "vapi/vsl.h"
53
#include "vapi/vsm.h"
54
#include "vapi/voptget.h"
55
#include "vas.h"
56
#include "vtree.h"
57
#include "vut.h"
58
59
#if 0
60
#define AC(x) assert((x) != ERR)
61
#else
62
#define AC(x) x
63
#endif
64
65
static struct VUT *vut;
66
67
struct top {
68
        uint8_t                 tag;
69
        const char              *rec_data;
70
        char                    *rec_buf;
71
        int                     clen;
72
        unsigned                hash;
73
        VRB_ENTRY(top)          e_order;
74
        VRB_ENTRY(top)          e_key;
75
        double                  count;
76
};
77
78
static int period = 60; /* seconds */
79
static int end_of_file = 0;
80
static unsigned ntop;
81
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
82
static int f_flag = 0;
83
static unsigned maxfieldlen = 0;
84
static const char *ident;
85
86
static volatile sig_atomic_t quit = 0;
87
88
static VRB_HEAD(t_order, top) h_order = VRB_INITIALIZER(&h_order);
89
static VRB_HEAD(t_key, top) h_key = VRB_INITIALIZER(&h_key);
90
91
static inline int
92 5424
cmp_key(const struct top *a, const struct top *b)
93
{
94 5424
        if (a->hash != b->hash)
95 5145
                return (a->hash - b->hash);
96 279
        if (a->tag != b->tag)
97 243
                return (a->tag - b->tag);
98 36
        if (a->clen != b->clen)
99 0
                return (a->clen - b->clen);
100 36
        return (memcmp(a->rec_data, b->rec_data, a->clen));
101
}
102
103
static inline int
104 1872
cmp_order(const struct top *a, const struct top *b)
105
{
106 1872
        if (a->count > b->count)
107 72
                return (-1);
108 1800
        else if (a->count < b->count)
109 0
                return (1);
110 1800
        return (cmp_key(a, b));
111
}
112
113
VRB_PROTOTYPE_STATIC(t_order, top, e_order, cmp_order)
114 1690
VRB_GENERATE_STATIC(t_order, top, e_order, cmp_order)
115
VRB_PROTOTYPE_STATIC(t_key, top, e_key, cmp_key)
116 1248
VRB_GENERATE_STATIC(t_key, top, e_key, cmp_key)
117
118
static int v_matchproto_(VSLQ_dispatch_f)
119 42
accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[],
120
        void *priv)
121
{
122
        struct top *tp, t;
123
        unsigned int u;
124
        unsigned tag;
125
        const char *b, *e, *p;
126
        unsigned len;
127
        struct VSL_transaction *tr;
128
129
        (void)priv;
130
131 84
        for (tr = pt[0]; tr != NULL; tr = *++pt) {
132 1052
                while ((1 == VSL_Next(tr->c))) {
133 968
                        tag = VSL_TAG(tr->c->rec.ptr);
134 968
                        if (VSL_tagflags[tag])
135 0
                                continue;
136 968
                        if (!VSL_Match(vsl, tr->c))
137 520
                                continue;
138 448
                        b = VSL_CDATA(tr->c->rec.ptr);
139 448
                        e = b + VSL_LEN(tr->c->rec.ptr);
140 448
                        u = 0;
141 8090
                        for (p = b; p <= e; p++) {
142 8090
                                if (*p == '\0')
143 434
                                        break;
144 7656
                                if (f_flag && (*p == ':' || isspace(*p)))
145
                                        break;
146 7642
                                u += *p;
147
                        }
148 448
                        len = p - b;
149 448
                        if (len == 0)
150 24
                                continue;
151
152 424
                        t.hash = u;
153 424
                        t.tag = tag;
154 424
                        t.clen = len;
155 424
                        t.rec_data = VSL_CDATA(tr->c->rec.ptr);
156
157 424
                        AZ(pthread_mutex_lock(&mtx));
158 424
                        tp = VRB_FIND(t_key, &h_key, &t);
159 424
                        if (tp) {
160 12
                                VRB_REMOVE(t_order, &h_order, tp);
161 12
                                tp->count += 1.0;
162
                                /* Reinsert to rebalance */
163 12
                                VRB_INSERT(t_order, &h_order, tp);
164
                        } else {
165 412
                                ntop++;
166 412
                                tp = calloc(1, sizeof *tp);
167 412
                                assert(tp != NULL);
168 412
                                tp->hash = u;
169 412
                                tp->count = 1.0;
170 412
                                tp->clen = len;
171 412
                                tp->tag = tag;
172 412
                                tp->rec_buf = strdup(t.rec_data);
173 412
                                tp->rec_data = tp->rec_buf;
174 412
                                AN(tp->rec_data);
175 412
                                VRB_INSERT(t_key, &h_key, tp);
176 412
                                VRB_INSERT(t_order, &h_order, tp);
177
                        }
178 424
                        AZ(pthread_mutex_unlock(&mtx));
179
180
                }
181
        }
182
183 42
        return (0);
184
}
185
186
static int v_matchproto_(VUT_cb_f)
187 0
sighup(struct VUT *v)
188
{
189 0
        assert(v == vut);
190 0
        quit = 1;
191 0
        return (1);
192
}
193
194
static void
195 6
vut_sighandler(int sig)
196
{
197
198 6
        if (vut != NULL)
199 6
                VUT_Signaled(vut, sig);
200 6
}
201
202
static void
203 25
update(int p)
204
{
205
        struct top *tp, *tp2;
206
        int l, len;
207 25
        double t = 0;
208
        static time_t last = 0;
209
        static unsigned n = 0;
210
        const char *q;
211
        time_t now;
212
213 25
        now = time(NULL);
214 25
        if (now == last)
215 5
                return;
216 20
        last = now;
217
218 20
        l = 1;
219 20
        if (n < p)
220 20
                n++;
221 20
        AC(erase());
222 20
        q = ident;
223 20
        len = COLS - strlen(q);
224 20
        if (end_of_file)
225 4
                AC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
226
        else
227 16
                AC(mvprintw(0, len - 1, "%s", q));
228 20
        AC(mvprintw(0, 0, "list length %u", ntop));
229 660
        for (tp = VRB_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
230 640
                tp2 = VRB_NEXT(t_order, &h_order, tp);
231
232 640
                if (++l < LINES) {
233 234
                        len = tp->clen;
234 234
                        if (len > COLS - 20)
235 0
                                len = COLS - 20;
236 234
                        AC(mvprintw(l, 0, "%9.2f %-*.*s %*.*s\n",
237
                                tp->count, maxfieldlen, maxfieldlen,
238
                                VSL_tags[tp->tag],
239
                                len, len, tp->rec_data));
240 234
                        t = tp->count;
241
                }
242 640
                if (end_of_file)
243 128
                        continue;
244 512
                tp->count += (1.0/3.0 - tp->count) / (double)n;
245 512
                if (tp->count * 10 < t || l > LINES * 10) {
246 0
                        VRB_REMOVE(t_key, &h_key, tp);
247 0
                        VRB_REMOVE(t_order, &h_order, tp);
248 0
                        free(tp->rec_buf);
249 0
                        free(tp);
250 0
                        ntop--;
251
                }
252
        }
253 20
        AC(refresh());
254
}
255
256
static void *
257 6
do_curses(void *arg)
258
{
259
        int i;
260
261
        (void)arg;
262 1542
        for (i = 0; i < 256; i++) {
263 1536
                if (VSL_tags[i] == NULL)
264 978
                        continue;
265 558
                if (maxfieldlen < strlen(VSL_tags[i]))
266 42
                        maxfieldlen = strlen(VSL_tags[i]);
267
        }
268
269 6
        (void)initscr();
270 6
        AC(raw());
271 6
        AC(noecho());
272 6
        AC(nonl());
273 6
        AC(intrflush(stdscr, FALSE));
274 6
        (void)curs_set(0);
275 6
        AC(erase());
276 6
        timeout(1000);
277 37
        while (!quit) {
278 25
                AZ(pthread_mutex_lock(&mtx));
279 25
                update(period);
280 25
                AZ(pthread_mutex_unlock(&mtx));
281
282 25
                switch (getch()) {
283
                case ERR:
284 13
                        break;
285
#ifdef KEY_RESIZE
286
                case KEY_RESIZE:
287 2
                        AC(erase());
288 2
                        break;
289
#endif
290
                case '\014': /* Ctrl-L */
291
                case '\024': /* Ctrl-T */
292 2
                        AC(redrawwin(stdscr));
293 2
                        AC(refresh());
294 2
                        break;
295
                case '\032': /* Ctrl-Z */
296 0
                        AC(endwin());
297 0
                        AZ(raise(SIGTSTP));
298 0
                        break;
299
                case '\003': /* Ctrl-C */
300
                case '\021': /* Ctrl-Q */
301
                case 'Q':
302
                case 'q':
303 6
                        AZ(raise(SIGINT));
304 6
                        quit = 1;
305 6
                        break;
306
                default:
307 2
                        AC(beep());
308 2
                        break;
309
                }
310
        }
311 6
        AC(endwin());
312 6
        return (NULL);
313
}
314
315
static void
316 8
dump(void)
317
{
318
        struct top *tp, *tp2;
319 164
        for (tp = VRB_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
320 156
                tp2 = VRB_NEXT(t_order, &h_order, tp);
321 312
                printf("%9.2f %s %*.*s\n",
322 156
                        tp->count, VSL_tags[tp->tag],
323
                        tp->clen, tp->clen, tp->rec_data);
324
        }
325 8
}
326
327
//lint -sem(usage, r_no)
328
static void v_noreturn_
329 6
usage(int status)
330
{
331
        const char **opt;
332
333 6
        fprintf(stderr, "Usage: %s <options>\n\n", vut->progname);
334 6
        fprintf(stderr, "Options:\n");
335 126
        for (opt = vopt_spec.vopt_usage; *opt != NULL; opt +=2)
336 120
                fprintf(stderr, " %-25s %s\n", *opt, *(opt + 1));
337 6
        exit(status);
338
}
339
340
int
341 28
main(int argc, char **argv)
342
{
343 28
        int o, once = 0;
344
        pthread_t thr;
345 28
        char *e = NULL;
346
347 28
        vut = VUT_InitProg(argc, argv, &vopt_spec);
348 24
        AN(vut);
349
350 86
        while ((o = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
351 46
                switch (o) {
352
                case '1':
353 8
                        AN(VUT_Arg(vut, 'd', NULL));
354 8
                        once = 1;
355 8
                        break;
356
                case 'f':
357 2
                        f_flag = 1;
358 2
                        break;
359
                case 'h':
360
                        /* Usage help */
361 2
                        usage(0);
362
                case 'p':
363 4
                        errno = 0;
364 4
                        e = NULL;
365 4
                        period = strtoul(optarg, &e, 0);
366 4
                        if (errno != 0 || e == NULL || *e != '\0') {
367 2
                                fprintf(stderr,
368
                                    "Syntax error, %s is not a number", optarg);
369 2
                                exit(1);
370
                        }
371 2
                        break;
372
                default:
373 30
                        if (!VUT_Arg(vut, o, optarg))
374 2
                                usage(1);
375
                }
376
        }
377
378 16
        if (optind != argc)
379 2
                usage(1);
380
381 14
        VUT_Signal(vut_sighandler);
382 14
        VUT_Setup(vut);
383 14
        if (vut->vsm)
384 14
                ident = VSM_Dup(vut->vsm, "Arg", "-i");
385
        else
386 0
                ident = strdup("");
387 14
        vut->dispatch_f = accumulate;
388 14
        vut->dispatch_priv = NULL;
389 14
        vut->sighup_f = sighup;
390 14
        if (once) {
391 8
                (void)VUT_Main(vut);
392 8
                dump();
393
        } else {
394 6
                AZ(pthread_create(&thr, NULL, do_curses, NULL));
395 6
                (void)VUT_Main(vut);
396 6
                end_of_file = 1;
397 6
                AZ(pthread_join(thr, NULL));
398
        }
399 14
        VUT_Fini(&vut);
400 14
        return (0);
401
}