varnish-cache/bin/varnishtop/varnishtop.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: Guillaume Quintard <guillaume.quintard@gmail.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
 * Log tailer for Varnish
33
 */
34
35
#include "config.h"
36
37
#include <ctype.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
#include "vapi/vsig.h"
59
60
#if 0
61
#define AC(x) assert((x) != ERR)
62
#else
63
#define AC(x) x
64
#endif
65
66
static struct VUT *vut;
67
68
struct top {
69
        uint8_t                 tag;
70
        const char              *rec_data;
71
        char                    *rec_buf;
72
        int                     clen;
73
        unsigned                hash;
74
        VRBT_ENTRY(top)         e_order;
75
        VRBT_ENTRY(top)         e_key;
76
        double                  count;
77
};
78
79
static unsigned period = 60; /* seconds */
80
static int end_of_file = 0;
81
static unsigned ntop;
82
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
83
static int f_flag = 0;
84
static unsigned maxfieldlen = 0;
85
static const char *ident;
86
87
static VRBT_HEAD(t_order, top) h_order = VRBT_INITIALIZER(&h_order);
88
static VRBT_HEAD(t_key, top) h_key = VRBT_INITIALIZER(&h_key);
89
90
static inline int
91 264846
cmp_key(const struct top *a, const struct top *b)
92
{
93 264846
        if (a->hash != b->hash)
94 249892
                return (a->hash - b->hash);
95 14954
        if (a->tag != b->tag)
96 14045
                return (a->tag - b->tag);
97 909
        if (a->clen != b->clen)
98 0
                return (a->clen - b->clen);
99 909
        return (memcmp(a->rec_data, b->rec_data, a->clen));
100 264846
}
101
102
static inline int
103 91714
cmp_order(const struct top *a, const struct top *b)
104
{
105 91714
        if (a->count > b->count)
106 4099
                return (-1);
107 87615
        else if (a->count < b->count)
108 245
                return (1);
109 87370
        return (cmp_key(a, b));
110 91714
}
111
112 55031
VRBT_GENERATE_INSERT_COLOR(t_order, top, e_order, static)
113 18840
VRBT_GENERATE_INSERT_FINISH(t_order, top, e_order, static)
114 110554
VRBT_GENERATE_INSERT(t_order, top, e_order, cmp_order, static)
115 1200
VRBT_GENERATE_REMOVE_COLOR(t_order, top, e_order, static)
116 5519
VRBT_GENERATE_MINMAX(t_order, top, e_order, static)
117 94412
VRBT_GENERATE_NEXT(t_order, top, e_order, static)
118 2400
VRBT_GENERATE_REMOVE(t_order, top, e_order, static)
119
120 53598
VRBT_GENERATE_INSERT_COLOR(t_key, top, e_key, static)
121 0
VRBT_GENERATE_REMOVE_COLOR(t_key, top, e_key, static)
122 18120
VRBT_GENERATE_INSERT_FINISH(t_key, top, e_key, static)
123 105535
VRBT_GENERATE_INSERT(t_key, top, e_key, cmp_key, static)
124 0
VRBT_GENERATE_REMOVE(t_key, top, e_key, static)
125 108181
VRBT_GENERATE_FIND(t_key, top, e_key, cmp_key, static)
126
127
static int v_matchproto_(VSLQ_dispatch_f)
128 1200
accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[],
129
        void *priv)
130
{
131
        struct top *tp, t;
132
        unsigned int u;
133
        unsigned tag;
134
        const char *b, *e, *p;
135
        unsigned len;
136
        struct VSL_transaction *tr;
137
138 1200
        (void)priv;
139
140 2400
        for (tr = pt[0]; tr != NULL; tr = *++pt) {
141 37320
                while ((1 == VSL_Next(tr->c))) {
142 36120
                        tag = VSL_TAG(tr->c->rec.ptr);
143 36120
                        if (VSL_tagflags[tag])
144 4000
                                continue;
145 32120
                        if (!VSL_Match(vsl, tr->c))
146 12080
                                continue;
147 20040
                        b = VSL_CDATA(tr->c->rec.ptr);
148 20040
                        e = b + VSL_LEN(tr->c->rec.ptr);
149 20040
                        u = 0;
150 378760
                        for (p = b; p <= e; p++) {
151 378760
                                if (*p == '\0')
152 19720
                                        break;
153 359040
                                if (f_flag && (*p == ':' || isspace(*p)))
154 320
                                        break;
155 358720
                                u += *p;
156 358720
                        }
157 20040
                        len = p - b;
158 20040
                        if (len == 0)
159 1200
                                continue;
160
161 18840
                        t.hash = u;
162 18840
                        t.tag = tag;
163 18840
                        t.clen = len;
164 18840
                        t.rec_data = VSL_CDATA(tr->c->rec.ptr);
165
166 18840
                        PTOK(pthread_mutex_lock(&mtx));
167 18840
                        tp = VRBT_FIND(t_key, &h_key, &t);
168 18840
                        if (tp) {
169 720
                                VRBT_REMOVE(t_order, &h_order, tp);
170 720
                                tp->count += 1.0;
171
                                /* Reinsert to rebalance */
172 720
                                VRBT_INSERT(t_order, &h_order, tp);
173 720
                        } else {
174 18120
                                ntop++;
175 18120
                                tp = calloc(1, sizeof *tp);
176 18120
                                assert(tp != NULL);
177 18120
                                tp->hash = u;
178 18120
                                tp->count = 1.0;
179 18120
                                tp->clen = len;
180 18120
                                tp->tag = tag;
181 18120
                                tp->rec_buf = strdup(t.rec_data);
182 18120
                                tp->rec_data = tp->rec_buf;
183 18120
                                AN(tp->rec_data);
184 18120
                                VRBT_INSERT(t_key, &h_key, tp);
185 18120
                                VRBT_INSERT(t_order, &h_order, tp);
186
                        }
187 18840
                        PTOK(pthread_mutex_unlock(&mtx));
188
189
                }
190 1200
        }
191
192 1200
        return (0);
193
}
194
195
static void
196 1011
update(unsigned p)
197
{
198
        struct top *tp, *tp2;
199
        int l, len;
200 1011
        double t = 0;
201
        static time_t last = 0;
202
        static unsigned n = 0;
203
        const char *q;
204
        time_t now;
205
206 1011
        now = time(NULL);
207 1011
        if (now == last)
208 58
                return;
209 953
        last = now;
210
211 953
        l = 1;
212 953
        if (n < p)
213 953
                n++;
214 953
        AC(erase());
215 953
        q = ident;
216 953
        len = COLS - strlen(q);
217 953
        if (end_of_file)
218 109
                AC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
219
        else
220 844
                AC(mvprintw(0, len - 1, "%s", q));
221 953
        AC(mvprintw(0, 0, "list length %u", ntop));
222 43902
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
223 42949
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
224
225 42949
                if (++l < LINES) {
226 13317
                        len = vmin(tp->clen, COLS - 20);
227 13317
                        AC(mvprintw(l, 0, "%9.2f %-*.*s %*.*s\n",
228
                                tp->count, maxfieldlen, maxfieldlen,
229
                                VSL_tags[tp->tag],
230
                                len, len, tp->rec_data));
231 13317
                        t = tp->count;
232 13317
                }
233 42949
                if (end_of_file)
234 3007
                        continue;
235 39942
                tp->count += (1.0/3.0 - tp->count) / (double)n;
236 39942
                if (tp->count * 10 < t || l > LINES * 10) {
237 0
                        VRBT_REMOVE(t_key, &h_key, tp);
238 0
                        VRBT_REMOVE(t_order, &h_order, tp);
239 0
                        free(tp->rec_buf);
240 0
                        free(tp);
241 0
                        ntop--;
242 0
                }
243 39942
        }
244 953
        AC(refresh());
245 1011
}
246
247
static void *
248 200
do_curses(void *arg)
249
{
250
        int i;
251
252 200
        (void)arg;
253 51400
        for (i = 0; i < 256; i++) {
254 51200
                if (VSL_tags[i] == NULL)
255 32600
                        continue;
256 18600
                if (maxfieldlen < strlen(VSL_tags[i]))
257 1400
                        maxfieldlen = strlen(VSL_tags[i]);
258 18600
        }
259
260 200
        (void)initscr();
261 200
        AC(raw());
262 200
        AC(noecho());
263 200
        AC(nonl());
264 200
        AC(intrflush(stdscr, FALSE));
265 200
        (void)curs_set(0);
266 200
        AC(erase());
267 200
        timeout(1000);
268 1211
        while (!VSIG_int && !VSIG_term && !VSIG_hup) {
269 1011
                PTOK(pthread_mutex_lock(&mtx));
270 1011
                update(period);
271 1011
                PTOK(pthread_mutex_unlock(&mtx));
272
273 1011
                switch (getch()) {
274
                case ERR:
275 771
                        break;
276
#ifdef KEY_RESIZE
277
                case KEY_RESIZE:
278 40
                        AC(erase());
279 40
                        break;
280
#endif
281
                case '\014': /* Ctrl-L */
282
                case '\024': /* Ctrl-T */
283 40
                        AC(redrawwin(stdscr));
284 40
                        AC(refresh());
285 40
                        break;
286
                case '\032': /* Ctrl-Z */
287 0
                        AC(endwin());
288 0
                        AZ(raise(SIGTSTP));
289 0
                        break;
290
                case '\003': /* Ctrl-C */
291
                case '\021': /* Ctrl-Q */
292
                case 'Q':
293
                case 'q':
294 120
                        AZ(raise(SIGINT));
295 120
                        break;
296
                default:
297 40
                        AC(beep());
298 40
                        break;
299
                }
300
        }
301 200
        AC(endwin());
302 200
        return (NULL);
303
}
304
305
static void
306 200
dump(void)
307
{
308
        struct top *tp, *tp2;
309 6640
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
310 6440
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
311 6440
                printf("%9.2f %s %*.*s\n",
312 6440
                        tp->count, VSL_tags[tp->tag],
313 6440
                        tp->clen, tp->clen, tp->rec_data);
314 6440
        }
315 200
}
316
317
int
318 560
main(int argc, char **argv)
319
{
320 560
        int o, once = 0;
321
        pthread_t thr;
322 560
        char *e = NULL;
323
324 560
        vut = VUT_InitProg(argc, argv, &vopt_spec);
325 560
        AN(vut);
326
327 1440
        while ((o = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
328 1000
                switch (o) {
329
                case '1':
330 200
                        AN(VUT_Arg(vut, 'd', NULL));
331 200
                        once = 1;
332 200
                        break;
333
                case 'f':
334 40
                        f_flag = 1;
335 40
                        break;
336
                case 'h':
337
                        /* Usage help */
338 40
                        VUT_Usage(vut, &vopt_spec, 0);
339
                case 'p':
340 80
                        errno = 0;
341 80
                        e = NULL;
342 80
                        period = strtoul(optarg, &e, 0);
343 80
                        if (errno != 0 || e == NULL || *e != '\0') {
344 80
                                fprintf(stderr,
345 40
                                    "Syntax error, %s is not a number", optarg);
346 40
                                exit(1);
347
                        }
348 40
                        break;
349
                default:
350 640
                        if (!VUT_Arg(vut, o, optarg))
351 40
                                VUT_Usage(vut, &vopt_spec, 1);
352 600
                }
353
        }
354
355 440
        if (optind != argc)
356 40
                VUT_Usage(vut, &vopt_spec, 1);
357
358 400
        VUT_Setup(vut);
359 400
        if (vut->vsm)
360 400
                ident = VSM_Dup(vut->vsm, "Arg", "-i");
361
        else
362 0
                ident = strdup("");
363 400
        AN(ident);
364 400
        vut->dispatch_f = accumulate;
365 400
        vut->dispatch_priv = NULL;
366 400
        if (once) {
367 200
                (void)VUT_Main(vut);
368 200
                dump();
369 200
        } else {
370 200
                PTOK(pthread_create(&thr, NULL, do_curses, NULL));
371 200
                (void)VUT_Main(vut);
372 200
                end_of_file = 1;
373 200
                PTOK(pthread_join(thr, NULL));
374
        }
375 400
        VUT_Fini(&vut);
376 400
        return (0);
377
}