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 139852
cmp_key(const struct top *a, const struct top *b)
92
{
93 139852
        if (a->hash != b->hash)
94 132074
                return (a->hash - b->hash);
95 7778
        if (a->tag != b->tag)
96 7283
                return (a->tag - b->tag);
97 495
        if (a->clen != b->clen)
98 0
                return (a->clen - b->clen);
99 495
        return (memcmp(a->rec_data, b->rec_data, a->clen));
100 139852
}
101
102
static inline int
103 48380
cmp_order(const struct top *a, const struct top *b)
104
{
105 48380
        if (a->count > b->count)
106 2125
                return (-1);
107 46255
        else if (a->count < b->count)
108 125
                return (1);
109 46130
        return (cmp_key(a, b));
110 48380
}
111
112 28948
VRBT_GENERATE_INSERT_COLOR(t_order, top, e_order, static)
113 9900
VRBT_GENERATE_INSERT_FINISH(t_order, top, e_order, static)
114 58280
VRBT_GENERATE_INSERT(t_order, top, e_order, cmp_order, static)
115 628
VRBT_GENERATE_REMOVE_COLOR(t_order, top, e_order, static)
116 3152
VRBT_GENERATE_MINMAX(t_order, top, e_order, static)
117 53096
VRBT_GENERATE_NEXT(t_order, top, e_order, static)
118 1250
VRBT_GENERATE_REMOVE(t_order, top, e_order, static)
119
120 28178
VRBT_GENERATE_INSERT_COLOR(t_key, top, e_key, static)
121 0
VRBT_GENERATE_REMOVE_COLOR(t_key, top, e_key, static)
122 9525
VRBT_GENERATE_INSERT_FINISH(t_key, top, e_key, static)
123 55682
VRBT_GENERATE_INSERT(t_key, top, e_key, cmp_key, static)
124 0
VRBT_GENERATE_REMOVE(t_key, top, e_key, static)
125 57090
VRBT_GENERATE_FIND(t_key, top, e_key, cmp_key, static)
126
127
static int v_matchproto_(VSLQ_dispatch_f)
128 675
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 675
        (void)priv;
139
140 1350
        for (tr = pt[0]; tr != NULL; tr = *++pt) {
141 21000
                while ((1 == VSL_Next(tr->c))) {
142 20325
                        tag = VSL_TAG(tr->c->rec.ptr);
143 20325
                        if (VSL_tagflags[tag])
144 2250
                                continue;
145 18075
                        if (!VSL_Match(vsl, tr->c))
146 7550
                                continue;
147 10525
                        b = VSL_CDATA(tr->c->rec.ptr);
148 10525
                        e = b + VSL_LEN(tr->c->rec.ptr);
149 10525
                        u = 0;
150 199900
                        for (p = b; p <= e; p++) {
151 199900
                                if (*p == '\0')
152 10325
                                        break;
153 189575
                                if (f_flag && (*p == ':' || isspace(*p)))
154 200
                                        break;
155 189375
                                u += *p;
156 189375
                        }
157 10525
                        len = p - b;
158 10525
                        if (len == 0)
159 625
                                continue;
160
161 9900
                        t.hash = u;
162 9900
                        t.tag = tag;
163 9900
                        t.clen = len;
164 9900
                        t.rec_data = VSL_CDATA(tr->c->rec.ptr);
165
166 9900
                        PTOK(pthread_mutex_lock(&mtx));
167 9900
                        tp = VRBT_FIND(t_key, &h_key, &t);
168 9900
                        if (tp) {
169 375
                                VRBT_REMOVE(t_order, &h_order, tp);
170 375
                                tp->count += 1.0;
171
                                /* Reinsert to rebalance */
172 375
                                VRBT_INSERT(t_order, &h_order, tp);
173 375
                        } else {
174 9525
                                ntop++;
175 9525
                                tp = calloc(1, sizeof *tp);
176 9525
                                assert(tp != NULL);
177 9525
                                tp->hash = u;
178 9525
                                tp->count = 1.0;
179 9525
                                tp->clen = len;
180 9525
                                tp->tag = tag;
181 9525
                                tp->rec_buf = strdup(t.rec_data);
182 9525
                                tp->rec_data = tp->rec_buf;
183 9525
                                AN(tp->rec_data);
184 9525
                                VRBT_INSERT(t_key, &h_key, tp);
185 9525
                                VRBT_INSERT(t_order, &h_order, tp);
186
                        }
187 9900
                        PTOK(pthread_mutex_unlock(&mtx));
188
189
                }
190 675
        }
191
192 675
        return (0);
193
}
194
195
static void
196 613
update(unsigned p)
197
{
198
        struct top *tp, *tp2;
199
        int l, len;
200 613
        double t = 0;
201
        static time_t last = 0;
202
        static unsigned n = 0;
203
        const char *q;
204
        time_t now;
205
206 613
        now = time(NULL);
207 613
        if (now == last)
208 41
                return;
209 572
        last = now;
210
211 572
        l = 1;
212 572
        if (n < p)
213 572
                n++;
214 572
        AC(erase());
215 572
        q = ident;
216 572
        len = COLS - strlen(q);
217 572
        if (end_of_file)
218 63
                AC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
219
        else
220 509
                AC(mvprintw(0, len - 1, "%s", q));
221 572
        AC(mvprintw(0, 0, "list length %u", ntop));
222 26135
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
223 25563
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
224
225 25563
                if (++l < LINES) {
226 7918
                        len = vmin(tp->clen, COLS - 20);
227 7918
                        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 7918
                        t = tp->count;
232 7918
                }
233 25563
                if (end_of_file)
234 1838
                        continue;
235 23725
                tp->count += (1.0/3.0 - tp->count) / (double)n;
236 23725
                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 23725
        }
244 572
        AC(refresh());
245 613
}
246
247
static void *
248 125
do_curses(void *arg)
249
{
250
        int i;
251
252 125
        (void)arg;
253 32125
        for (i = 0; i < 256; i++) {
254 32000
                if (VSL_tags[i] == NULL)
255 20375
                        continue;
256 11625
                if (maxfieldlen < strlen(VSL_tags[i]))
257 875
                        maxfieldlen = strlen(VSL_tags[i]);
258 11625
        }
259
260 125
        (void)initscr();
261 125
        AC(raw());
262 125
        AC(noecho());
263 125
        AC(nonl());
264 125
        AC(intrflush(stdscr, FALSE));
265 125
        (void)curs_set(0);
266 125
        AC(erase());
267 125
        timeout(1000);
268 738
        while (!VSIG_int && !VSIG_term && !VSIG_hup) {
269 613
                PTOK(pthread_mutex_lock(&mtx));
270 613
                update(period);
271 613
                PTOK(pthread_mutex_unlock(&mtx));
272
273 613
                switch (getch()) {
274
                case ERR:
275 463
                        break;
276
#ifdef KEY_RESIZE
277
                case KEY_RESIZE:
278 25
                        AC(erase());
279 25
                        break;
280
#endif
281
                case '\014': /* Ctrl-L */
282
                case '\024': /* Ctrl-T */
283 25
                        AC(redrawwin(stdscr));
284 25
                        AC(refresh());
285 25
                        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 75
                        AZ(raise(SIGINT));
295 75
                        break;
296
                default:
297 25
                        AC(beep());
298 25
                        break;
299
                }
300
        }
301 125
        AC(endwin());
302 125
        return (NULL);
303
}
304
305
static void
306 100
dump(void)
307
{
308
        struct top *tp, *tp2;
309 2325
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
310 2225
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
311 2225
                printf("%9.2f %s %*.*s\n",
312 2225
                        tp->count, VSL_tags[tp->tag],
313 2225
                        tp->clen, tp->clen, tp->rec_data);
314 2225
        }
315 100
}
316
317
int
318 325
main(int argc, char **argv)
319
{
320 325
        int o, once = 0;
321
        pthread_t thr;
322 325
        char *e = NULL;
323
324 325
        vut = VUT_InitProg(argc, argv, &vopt_spec);
325 325
        AN(vut);
326
327 850
        while ((o = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
328 600
                switch (o) {
329
                case '1':
330 100
                        AN(VUT_Arg(vut, 'd', NULL));
331 100
                        once = 1;
332 100
                        break;
333
                case 'f':
334 25
                        f_flag = 1;
335 25
                        break;
336
                case 'h':
337
                        /* Usage help */
338 25
                        VUT_Usage(vut, &vopt_spec, 0);
339
                case 'p':
340 50
                        errno = 0;
341 50
                        e = NULL;
342 50
                        period = strtoul(optarg, &e, 0);
343 50
                        if (errno != 0 || e == NULL || *e != '\0') {
344 50
                                fprintf(stderr,
345 25
                                    "Syntax error, %s is not a number", optarg);
346 25
                                exit(1);
347
                        }
348 25
                        break;
349
                default:
350 400
                        if (!VUT_Arg(vut, o, optarg))
351 25
                                VUT_Usage(vut, &vopt_spec, 1);
352 375
                }
353
        }
354
355 250
        if (optind != argc)
356 25
                VUT_Usage(vut, &vopt_spec, 1);
357
358 225
        VUT_Setup(vut);
359 225
        if (vut->vsm)
360 225
                ident = VSM_Dup(vut->vsm, "Arg", "-i");
361
        else
362 0
                ident = strdup("");
363 225
        AN(ident);
364 225
        vut->dispatch_f = accumulate;
365 225
        vut->dispatch_priv = NULL;
366 225
        if (once) {
367 100
                (void)VUT_Main(vut);
368 100
                dump();
369 100
        } else {
370 125
                PTOK(pthread_create(&thr, NULL, do_curses, NULL));
371 125
                (void)VUT_Main(vut);
372 125
                end_of_file = 1;
373 125
                PTOK(pthread_join(thr, NULL));
374
        }
375 225
        VUT_Fini(&vut);
376 225
        return (0);
377
}