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 6621
cmp_key(const struct top *a, const struct top *b)
86
{
87 6621
        if (a->hash != b->hash)
88 6246
                return (a->hash - b->hash);
89 375
        if (a->tag != b->tag)
90 348
                return (a->tag - b->tag);
91 27
        if (a->clen != b->clen)
92 0
                return (a->clen - b->clen);
93 27
        return (memcmp(a->rec_data, b->rec_data, a->clen));
94 6621
}
95
96
static inline int
97 2293
cmp_order(const struct top *a, const struct top *b)
98
{
99 2293
        if (a->count > b->count)
100 102
                return (-1);
101 2191
        else if (a->count < b->count)
102 6
                return (1);
103 2185
        return (cmp_key(a, b));
104 2293
}
105
106 1365
VRBT_GENERATE_INSERT_COLOR(t_order, top, e_order, static)
107 471
VRBT_GENERATE_INSERT_FINISH(t_order, top, e_order, static)
108 2764
VRBT_GENERATE_INSERT(t_order, top, e_order, cmp_order, static)
109 30
VRBT_GENERATE_REMOVE_COLOR(t_order, top, e_order, static)
110 129
VRBT_GENERATE_MINMAX(t_order, top, e_order, static)
111 2262
VRBT_GENERATE_NEXT(t_order, top, e_order, static)
112 60
VRBT_GENERATE_REMOVE(t_order, top, e_order, static)
113
114 1329
VRBT_GENERATE_INSERT_COLOR(t_key, top, e_key, static)
115 0
VRBT_GENERATE_REMOVE_COLOR(t_key, top, e_key, static)
116 453
VRBT_GENERATE_INSERT_FINISH(t_key, top, e_key, static)
117 2638
VRBT_GENERATE_INSERT(t_key, top, e_key, cmp_key, static)
118 0
VRBT_GENERATE_REMOVE(t_key, top, e_key, static)
119 2704
VRBT_GENERATE_FIND(t_key, top, e_key, cmp_key, static)
120
121
static int v_matchproto_(VSLQ_dispatch_f)
122 30
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 30
        (void)priv;
133
134 60
        for (tr = pt[0]; tr != NULL; tr = *++pt) {
135 943
                while ((1 == VSL_Next(tr->c))) {
136 913
                        tag = VSL_TAG(tr->c->rec.ptr);
137 913
                        if (VSL_tagflags[tag])
138 110
                                continue;
139 803
                        if (!VSL_Match(vsl, tr->c))
140 302
                                continue;
141 501
                        b = VSL_CDATA(tr->c->rec.ptr);
142 501
                        e = b + VSL_LEN(tr->c->rec.ptr);
143 501
                        u = 0;
144 9469
                        for (p = b; p <= e; p++) {
145 9469
                                if (*p == '\0')
146 493
                                        break;
147 8976
                                if (f_flag && (*p == ':' || isspace(*p)))
148 8
                                        break;
149 8968
                                u += *p;
150 8968
                        }
151 501
                        len = p - b;
152 501
                        if (len == 0)
153 30
                                continue;
154
155 471
                        t.hash = u;
156 471
                        t.tag = tag;
157 471
                        t.clen = len;
158 471
                        t.rec_data = VSL_CDATA(tr->c->rec.ptr);
159
160 471
                        PTOK(pthread_mutex_lock(&mtx));
161 471
                        tp = VRBT_FIND(t_key, &h_key, &t);
162 471
                        if (tp) {
163 18
                                VRBT_REMOVE(t_order, &h_order, tp);
164 18
                                tp->count += 1.0;
165
                                /* Reinsert to rebalance */
166 18
                                VRBT_INSERT(t_order, &h_order, tp);
167 18
                        } else {
168 453
                                ntop++;
169 453
                                tp = calloc(1, sizeof *tp);
170 453
                                assert(tp != NULL);
171 453
                                tp->hash = u;
172 453
                                tp->count = 1.0;
173 453
                                tp->clen = len;
174 453
                                tp->tag = tag;
175 453
                                tp->rec_buf = strdup(t.rec_data);
176 453
                                tp->rec_data = tp->rec_buf;
177 453
                                AN(tp->rec_data);
178 453
                                VRBT_INSERT(t_key, &h_key, tp);
179 453
                                VRBT_INSERT(t_order, &h_order, tp);
180
                        }
181 471
                        PTOK(pthread_mutex_unlock(&mtx));
182
183
                }
184 30
        }
185
186 30
        return (0);
187
}
188
189
static void
190 22
update(unsigned p)
191
{
192
        struct top *tp, *tp2;
193
        int l, len;
194 22
        double t = 0;
195
        static time_t last = 0;
196
        static unsigned n = 0;
197
        const char *q;
198
        time_t now;
199
200 22
        now = time(NULL);
201 22
        if (now == last)
202 2
                return;
203 20
        last = now;
204
205 20
        l = 1;
206 20
        if (n < p)
207 20
                n++;
208 20
        AC(erase());
209 20
        q = ident;
210 20
        len = COLS - strlen(q);
211 20
        if (end_of_file)
212 2
                IC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
213
        else
214 18
                IC(mvprintw(0, len - 1, "%s", q));
215 20
        IC(mvprintw(0, 0, "list length %u", ntop));
216 1042
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
217 1022
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
218
219 1022
                if (++l < LINES) {
220 321
                        len = vmin(tp->clen, COLS - 20);
221 321
                        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 321
                        t = tp->count;
226 321
                }
227 1022
                if (end_of_file)
228 73
                        continue;
229 949
                tp->count += (1.0/3.0 - tp->count) / (double)n;
230 949
                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 949
        }
238 20
        AC(refresh());
239 22
}
240
241
static void *
242 5
do_curses(void *arg)
243
{
244
        int i;
245
246 5
        (void)arg;
247 1285
        for (i = 0; i < 256; i++) {
248 1280
                if (VSL_tags[i] == NULL)
249 815
                        continue;
250 465
                if (maxfieldlen < strlen(VSL_tags[i]))
251 35
                        maxfieldlen = strlen(VSL_tags[i]);
252 465
        }
253
254 5
        (void)initscr();
255 5
        AC(raw());
256 5
        AC(noecho());
257 5
        AC(nonl());
258 5
        AC(intrflush(stdscr, FALSE));
259 5
        IC(curs_set(0));
260 5
        AC(erase());
261 5
        timeout(1000);
262 27
        while (!VSIG_int && !VSIG_term && !VSIG_hup) {
263 22
                PTOK(pthread_mutex_lock(&mtx));
264 22
                update(period);
265 22
                PTOK(pthread_mutex_unlock(&mtx));
266
267 22
                switch (getch()) {
268
                case ERR:
269 16
                        break;
270
#ifdef KEY_RESIZE
271
                case KEY_RESIZE:
272 1
                        AC(erase());
273 1
                        break;
274
#endif
275
                case '\014': /* Ctrl-L */
276
                case '\024': /* Ctrl-T */
277 1
                        AC(redrawwin(stdscr));
278 1
                        AC(refresh());
279 1
                        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 3
                        AZ(raise(SIGINT));
289 3
                        break;
290
                default:
291 1
                        AC(beep());
292 1
                        break;
293
                }
294
        }
295 5
        AC(endwin());
296 5
        return (NULL);
297
}
298
299
static void
300 5
dump(void)
301
{
302
        struct top *tp, *tp2;
303 166
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
304 161
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
305 161
                printf("%9.2f %s %*.*s\n",
306 161
                        tp->count, VSL_tags[tp->tag],
307 161
                        tp->clen, tp->clen, tp->rec_data);
308 161
        }
309 5
}
310
311
int
312 14
main(int argc, char **argv)
313
{
314 14
        int o, once = 0;
315
        pthread_t thr;
316 14
        char *e = NULL;
317
318 14
        vut = VUT_InitProg(argc, argv, &vopt_spec);
319 14
        AN(vut);
320
321 36
        while ((o = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
322 25
                switch (o) {
323
                case '1':
324 5
                        AN(VUT_Arg(vut, 'd', NULL));
325 5
                        once = 1;
326 5
                        break;
327
                case 'f':
328 1
                        f_flag = 1;
329 1
                        break;
330
                case 'h':
331
                        /* Usage help */
332 1
                        VUT_Usage(vut, &vopt_spec, 0);
333
                case 'p':
334 2
                        errno = 0;
335 2
                        e = NULL;
336 2
                        period = strtoul(optarg, &e, 0);
337 2
                        if (errno != 0 || e == NULL || *e != '\0') {
338 2
                                fprintf(stderr,
339 1
                                    "Syntax error, %s is not a number", optarg);
340 1
                                exit(1);
341
                        }
342 1
                        break;
343
                default:
344 16
                        if (!VUT_Arg(vut, o, optarg))
345 1
                                VUT_Usage(vut, &vopt_spec, 1);
346 15
                }
347
        }
348
349 11
        if (optind != argc)
350 1
                VUT_Usage(vut, &vopt_spec, 1);
351
352 10
        VUT_Setup(vut);
353 10
        if (vut->vsm)
354 10
                ident = VSM_Dup(vut->vsm, "Arg", "-i");
355
        else
356 0
                ident = strdup("");
357 10
        AN(ident);
358 10
        vut->dispatch_f = accumulate;
359 10
        vut->dispatch_priv = NULL;
360 10
        if (once) {
361 5
                (void)VUT_Main(vut);
362 5
                dump();
363 5
        } else {
364 5
                PTOK(pthread_create(&thr, NULL, do_curses, NULL));
365 5
                (void)VUT_Main(vut);
366 5
                end_of_file = 1;
367 5
                PTOK(pthread_join(thr, NULL));
368
        }
369 10
        VUT_Fini(&vut);
370 10
        return (0);
371
}