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