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 2721
cmp_key(const struct top *a, const struct top *b)
93
{
94 2721
        if (a->hash != b->hash)
95 2592
                return (a->hash - b->hash);
96 129
        if (a->tag != b->tag)
97 117
                return (a->tag - b->tag);
98 12
        if (a->clen != b->clen)
99 0
                return (a->clen - b->clen);
100 12
        return (memcmp(a->rec_data, b->rec_data, a->clen));
101
}
102
103
static inline int
104 939
cmp_order(const struct top *a, const struct top *b)
105
{
106 939
        if (a->count > b->count)
107 36
                return (-1);
108 903
        else if (a->count < b->count)
109 0
                return (1);
110 903
        return (cmp_key(a, b));
111
}
112
113
VRB_PROTOTYPE_STATIC(t_order, top, e_order, cmp_order)
114 650
VRB_GENERATE_STATIC(t_order, top, e_order, cmp_order)
115
VRB_PROTOTYPE_STATIC(t_key, top, e_key, cmp_key)
116 624
VRB_GENERATE_STATIC(t_key, top, e_key, cmp_key)
117
118
static int v_matchproto_(VSLQ_dispatch_f)
119 21
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 42
        for (tr = pt[0]; tr != NULL; tr = *++pt) {
132 526
                while ((1 == VSL_Next(tr->c))) {
133 484
                        tag = VSL_TAG(tr->c->rec.ptr);
134 484
                        if (VSL_tagflags[tag])
135 0
                                continue;
136 484
                        if (!VSL_Match(vsl, tr->c))
137 260
                                continue;
138 224
                        b = VSL_CDATA(tr->c->rec.ptr);
139 224
                        e = b + VSL_LEN(tr->c->rec.ptr);
140 224
                        u = 0;
141 4045
                        for (p = b; p <= e; p++) {
142 4045
                                if (*p == '\0')
143 217
                                        break;
144 3828
                                if (f_flag && (*p == ':' || isspace(*p)))
145
                                        break;
146 3821
                                u += *p;
147
                        }
148 224
                        len = p - b;
149 224
                        if (len == 0)
150 12
                                continue;
151
152 212
                        t.hash = u;
153 212
                        t.tag = tag;
154 212
                        t.clen = len;
155 212
                        t.rec_data = VSL_CDATA(tr->c->rec.ptr);
156
157 212
                        AZ(pthread_mutex_lock(&mtx));
158 212
                        tp = VRB_FIND(t_key, &h_key, &t);
159 212
                        if (tp) {
160 6
                                VRB_REMOVE(t_order, &h_order, tp);
161 6
                                tp->count += 1.0;
162
                                /* Reinsert to rebalance */
163 6
                                VRB_INSERT(t_order, &h_order, tp);
164
                        } else {
165 206
                                ntop++;
166 206
                                tp = calloc(1, sizeof *tp);
167 206
                                assert(tp != NULL);
168 206
                                tp->hash = u;
169 206
                                tp->count = 1.0;
170 206
                                tp->clen = len;
171 206
                                tp->tag = tag;
172 206
                                tp->rec_buf = strdup(t.rec_data);
173 206
                                tp->rec_data = tp->rec_buf;
174 206
                                AN(tp->rec_data);
175 206
                                VRB_INSERT(t_key, &h_key, tp);
176 206
                                VRB_INSERT(t_order, &h_order, tp);
177
                        }
178 212
                        AZ(pthread_mutex_unlock(&mtx));
179
180
                }
181
        }
182
183 21
        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 3
vut_sighandler(int sig)
196
{
197
198 3
        if (vut != NULL)
199 3
                VUT_Signaled(vut, sig);
200 3
}
201
202
static void
203 7
update(int p)
204
{
205
        struct top *tp, *tp2;
206
        int l, len;
207 7
        double t = 0;
208
        static time_t last = 0;
209
        static unsigned n = 0;
210
        const char *q;
211
        time_t now;
212
213 7
        now = time(NULL);
214 7
        if (now == last)
215 0
                return;
216 7
        last = now;
217
218 7
        l = 1;
219 7
        if (n < p)
220 7
                n++;
221 7
        AC(erase());
222 7
        q = ident;
223 7
        len = COLS - strlen(q);
224 7
        if (end_of_file)
225 2
                AC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
226
        else
227 5
                AC(mvprintw(0, len - 1, "%s", q));
228 7
        AC(mvprintw(0, 0, "list length %u", ntop));
229 135
        for (tp = VRB_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
230 128
                tp2 = VRB_NEXT(t_order, &h_order, tp);
231
232 128
                if (++l < LINES) {
233 51
                        len = tp->clen;
234 51
                        if (len > COLS - 20)
235 0
                                len = COLS - 20;
236 51
                        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 51
                        t = tp->count;
241
                }
242 128
                if (end_of_file)
243 64
                        continue;
244 64
                tp->count += (1.0/3.0 - tp->count) / (double)n;
245 64
                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 7
        AC(refresh());
254
}
255
256
static void *
257 3
do_curses(void *arg)
258
{
259
        int i;
260
261
        (void)arg;
262 771
        for (i = 0; i < 256; i++) {
263 768
                if (VSL_tags[i] == NULL)
264 492
                        continue;
265 276
                if (maxfieldlen < strlen(VSL_tags[i]))
266 21
                        maxfieldlen = strlen(VSL_tags[i]);
267
        }
268
269 3
        (void)initscr();
270 3
        AC(raw());
271 3
        AC(noecho());
272 3
        AC(nonl());
273 3
        AC(intrflush(stdscr, FALSE));
274 3
        (void)curs_set(0);
275 3
        AC(erase());
276 3
        timeout(1000);
277 10
        while (!quit) {
278 7
                AZ(pthread_mutex_lock(&mtx));
279 7
                update(period);
280 7
                AZ(pthread_mutex_unlock(&mtx));
281
282 7
                switch (getch()) {
283
                case ERR:
284 4
                        break;
285
#ifdef KEY_RESIZE
286
                case KEY_RESIZE:
287 0
                        AC(erase());
288 0
                        break;
289
#endif
290
                case '\014': /* Ctrl-L */
291
                case '\024': /* Ctrl-T */
292 0
                        AC(redrawwin(stdscr));
293 0
                        AC(refresh());
294 0
                        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 3
                        AZ(raise(SIGINT));
304 3
                        AC(endwin());
305 3
                        return (NULL);
306
                default:
307 0
                        AC(beep());
308 0
                        break;
309
                }
310
        }
311 0
        AC(endwin());
312 0
        return (NULL);
313
}
314
315
static void
316 4
dump(void)
317
{
318
        struct top *tp, *tp2;
319 82
        for (tp = VRB_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
320 78
                tp2 = VRB_NEXT(t_order, &h_order, tp);
321 156
                printf("%9.2f %s %*.*s\n",
322 78
                        tp->count, VSL_tags[tp->tag],
323
                        tp->clen, tp->clen, tp->rec_data);
324
        }
325 4
}
326
327
//lint -sem(usage, r_no)
328
static void v_noreturn_
329 2
usage(int status)
330
{
331
        const char **opt;
332
333 2
        fprintf(stderr, "Usage: %s <options>\n\n", vut->progname);
334 2
        fprintf(stderr, "Options:\n");
335 42
        for (opt = vopt_spec.vopt_usage; *opt != NULL; opt +=2)
336 40
                fprintf(stderr, " %-25s %s\n", *opt, *(opt + 1));
337 2
        exit(status);
338
}
339
340
int
341 12
main(int argc, char **argv)
342
{
343 12
        int o, once = 0;
344
        pthread_t thr;
345
346 12
        vut = VUT_InitProg(argc, argv, &vopt_spec);
347 10
        AN(vut);
348
349 38
        while ((o = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
350 20
                switch (o) {
351
                case '1':
352 4
                        AN(VUT_Arg(vut, 'd', NULL));
353 4
                        once = 1;
354 4
                        break;
355
                case 'f':
356 1
                        f_flag = 1;
357 1
                        break;
358
                case 'h':
359
                        /* Usage help */
360 1
                        usage(0);
361
                case 'p':
362 0
                        errno = 0;
363 0
                        period = strtol(optarg, NULL, 0);
364 0
                        if (errno != 0)  {
365 0
                                fprintf(stderr,
366
                                    "Syntax error, %s is not a number", optarg);
367 0
                                exit(1);
368
                        }
369 0
                        break;
370
                default:
371 14
                        if (!VUT_Arg(vut, o, optarg))
372 0
                                usage(1);
373
                }
374
        }
375
376 8
        if (optind != argc)
377 1
                usage(1);
378
379 7
        VUT_Signal(vut_sighandler);
380 7
        VUT_Setup(vut);
381 7
        if (vut->vsm)
382 7
                ident = VSM_Dup(vut->vsm, "Arg", "-i");
383
        else
384 0
                ident = strdup("");
385 7
        vut->dispatch_f = accumulate;
386 7
        vut->dispatch_priv = NULL;
387 7
        vut->sighup_f = sighup;
388 7
        if (once) {
389 4
                VUT_Main(vut);
390 4
                dump();
391
        } else {
392 3
                if (pthread_create(&thr, NULL, do_curses, NULL) != 0) {
393 0
                        fprintf(stderr, "pthread_create(): %s\n",
394 0
                            strerror(errno));
395 0
                        exit(1);
396
                }
397 3
                VUT_Main(vut);
398 3
                end_of_file = 1;
399 3
                AZ(pthread_join(thr, NULL));
400
        }
401 7
        VUT_Fini(&vut);
402 7
        exit(0);
403
}