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 "miniobj.h"
52
#include "vcurses.h"
53
#include "vapi/vsl.h"
54
#include "vapi/vsm.h"
55
#include "vapi/voptget.h"
56
#include "vas.h"
57
#include "vtree.h"
58
#include "vut.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
        VRB_ENTRY(top)          e_order;
75
        VRB_ENTRY(top)          e_key;
76
        double                  count;
77
};
78
79
static float 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 char *ident;
86
87
static volatile sig_atomic_t quit = 0;
88
89
static VRB_HEAD(t_order, top) h_order = VRB_INITIALIZER(&h_order);
90
static VRB_HEAD(t_key, top) h_key = VRB_INITIALIZER(&h_key);
91
92
static inline int
93 1925
cmp_key(const struct top *a, const struct top *b)
94
{
95 1925
        if (a->hash != b->hash)
96 1786
                return (a->hash - b->hash);
97 139
        if (a->tag != b->tag)
98 132
                return (a->tag - b->tag);
99 7
        if (a->clen != b->clen)
100 0
                return (a->clen - b->clen);
101 7
        return (memcmp(a->rec_data, b->rec_data, a->clen));
102
}
103
104
static inline int
105 661
cmp_order(const struct top *a, const struct top *b)
106
{
107 661
        if (a->count > b->count)
108 24
                return (-1);
109 637
        else if (a->count < b->count)
110 0
                return (1);
111 637
        return (cmp_key(a, b));
112
}
113
114
VRB_PROTOTYPE_STATIC(t_order, top, e_order, cmp_order)
115 292
VRB_GENERATE_STATIC(t_order, top, e_order, cmp_order)
116
VRB_PROTOTYPE_STATIC(t_key, top, e_key, cmp_key)
117 406
VRB_GENERATE_STATIC(t_key, top, e_key, cmp_key)
118
119
static int v_matchproto_(VSLQ_dispatch_f)
120 6
accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[],
121
        void *priv)
122
{
123
        struct top *tp, t;
124
        unsigned int u;
125
        unsigned tag;
126
        const char *b, *e, *p;
127
        unsigned len;
128
        struct VSL_transaction *tr;
129
130
        (void)priv;
131
132 12
        for (tr = pt[0]; tr != NULL; tr = *++pt) {
133 156
                while ((1 == VSL_Next(tr->c))) {
134 144
                        if (!VSL_Match(vsl, tr->c))
135 0
                                continue;
136 144
                        tag = VSL_TAG(tr->c->rec.ptr);
137 144
                        b = VSL_CDATA(tr->c->rec.ptr);
138 144
                        e = b + VSL_LEN(tr->c->rec.ptr);
139 144
                        u = 0;
140 2574
                        for (p = b; p <= e; p++) {
141 2574
                                if (*p == '\0')
142 144
                                        break;
143 2430
                                if (f_flag && (*p == ':' || isspace(*p)))
144
                                        break;
145 2430
                                u += *p;
146
                        }
147 144
                        len = p - b;
148 144
                        if (len == 0)
149 6
                                continue;
150
151 138
                        t.hash = u;
152 138
                        t.tag = tag;
153 138
                        t.clen = len;
154 138
                        t.rec_data = VSL_CDATA(tr->c->rec.ptr);
155
156 138
                        AZ(pthread_mutex_lock(&mtx));
157 138
                        tp = VRB_FIND(t_key, &h_key, &t);
158 138
                        if (tp) {
159 4
                                VRB_REMOVE(t_order, &h_order, tp);
160 4
                                tp->count += 1.0;
161
                                /* Reinsert to rebalance */
162 4
                                VRB_INSERT(t_order, &h_order, tp);
163
                        } else {
164 134
                                ntop++;
165 134
                                tp = calloc(1, sizeof *tp);
166 134
                                assert(tp != NULL);
167 134
                                tp->hash = u;
168 134
                                tp->count = 1.0;
169 134
                                tp->clen = len;
170 134
                                tp->tag = tag;
171 134
                                tp->rec_buf = strdup(t.rec_data);
172 134
                                tp->rec_data = tp->rec_buf;
173 134
                                AN(tp->rec_data);
174 134
                                VRB_INSERT(t_key, &h_key, tp);
175 134
                                VRB_INSERT(t_order, &h_order, tp);
176
                        }
177 138
                        AZ(pthread_mutex_unlock(&mtx));
178
179
                }
180
        }
181
182 6
        return (0);
183
}
184
185
static int v_matchproto_(VUT_cb_f)
186 0
sighup(struct VUT *v)
187
{
188 0
        assert(v == vut);
189 0
        quit = 1;
190 0
        return (1);
191
}
192
193
static void
194 0
vut_sighandler(int sig)
195
{
196 0
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
197 0
        VUT_Signaled(vut, sig);
198 0
}
199
200
static void
201 0
update(int p)
202
{
203
        struct top *tp, *tp2;
204
        int l, len;
205 0
        double t = 0;
206
        static time_t last = 0;
207
        static unsigned n;
208
        const char *q;
209
        time_t now;
210
211 0
        now = time(NULL);
212 0
        if (now == last)
213 0
                return;
214 0
        last = now;
215
216 0
        l = 1;
217 0
        if (n < p)
218 0
                n++;
219 0
        AC(erase());
220 0
        q = ident;
221 0
        len = COLS - strlen(q);
222 0
        if (end_of_file)
223 0
                AC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
224
        else
225 0
                AC(mvprintw(0, len - 1, "%s", q));
226 0
        AC(mvprintw(0, 0, "list length %u", ntop));
227 0
        for (tp = VRB_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
228 0
                tp2 = VRB_NEXT(t_order, &h_order, tp);
229
230 0
                if (++l < LINES) {
231 0
                        len = tp->clen;
232 0
                        if (len > COLS - 20)
233 0
                                len = COLS - 20;
234 0
                        AC(mvprintw(l, 0, "%9.2f %-*.*s %*.*s\n",
235
                                tp->count, maxfieldlen, maxfieldlen,
236
                                VSL_tags[tp->tag],
237
                                len, len, tp->rec_data));
238 0
                        t = tp->count;
239
                }
240 0
                if (end_of_file)
241 0
                        continue;
242 0
                tp->count += (1.0/3.0 - tp->count) / (double)n;
243 0
                if (tp->count * 10 < t || l > LINES * 10) {
244 0
                        VRB_REMOVE(t_key, &h_key, tp);
245 0
                        VRB_REMOVE(t_order, &h_order, tp);
246 0
                        free(tp->rec_buf);
247 0
                        free(tp);
248 0
                        ntop--;
249
                }
250
        }
251 0
        AC(refresh());
252
}
253
254
static void *
255 0
do_curses(void *arg)
256
{
257
        int i;
258
259
        (void)arg;
260 0
        for (i = 0; i < 256; i++) {
261 0
                if (VSL_tags[i] == NULL)
262 0
                        continue;
263 0
                if (maxfieldlen < strlen(VSL_tags[i]))
264 0
                        maxfieldlen = strlen(VSL_tags[i]);
265
        }
266
267 0
        (void)initscr();
268 0
        AC(raw());
269 0
        AC(noecho());
270 0
        AC(nonl());
271 0
        AC(intrflush(stdscr, FALSE));
272 0
        (void)curs_set(0);
273 0
        AC(erase());
274 0
        while (!quit) {
275 0
                AZ(pthread_mutex_lock(&mtx));
276 0
                update(period);
277 0
                AZ(pthread_mutex_unlock(&mtx));
278
279 0
                timeout(1000);
280 0
                switch (getch()) {
281
                case ERR:
282 0
                        break;
283
#ifdef KEY_RESIZE
284
                case KEY_RESIZE:
285 0
                        AC(erase());
286 0
                        break;
287
#endif
288
                case '\014': /* Ctrl-L */
289
                case '\024': /* Ctrl-T */
290 0
                        AC(redrawwin(stdscr));
291 0
                        AC(refresh());
292 0
                        break;
293
                case '\032': /* Ctrl-Z */
294 0
                        AC(endwin());
295 0
                        AZ(raise(SIGTSTP));
296 0
                        break;
297
                case '\003': /* Ctrl-C */
298
                case '\021': /* Ctrl-Q */
299
                case 'Q':
300
                case 'q':
301 0
                        AZ(raise(SIGINT));
302 0
                        AC(endwin());
303 0
                        return (NULL);
304
                default:
305 0
                        AC(beep());
306 0
                        break;
307
                }
308
        }
309 0
        AC(endwin());
310 0
        return (NULL);
311
}
312
313
static void
314 2
dump(void)
315
{
316
        struct top *tp, *tp2;
317 6
        for (tp = VRB_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
318 6
                tp2 = VRB_NEXT(t_order, &h_order, tp);
319 6
                if (tp->count <= 1.0)
320 2
                        break;
321 8
                printf("%9.2f %s %*.*s\n",
322 4
                        tp->count, VSL_tags[tp->tag],
323
                        tp->clen, tp->clen, tp->rec_data);
324
        }
325 2
}
326
327
//lint -sem(usage, r_no)
328
static void v_noreturn_
329 4
usage(int status)
330
{
331
        const char **opt;
332
333 4
        fprintf(stderr, "Usage: %s <options>\n\n", vut->progname);
334 4
        fprintf(stderr, "Options:\n");
335 84
        for (opt = vopt_spec.vopt_usage; *opt != NULL; opt +=2)
336 80
                fprintf(stderr, " %-25s %s\n", *opt, *(opt + 1));
337 4
        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 8
        AN(vut);
348
349 22
        while ((o = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
350 10
                switch (o) {
351
                case '1':
352 2
                        AN(VUT_Arg(vut, 'd', NULL));
353 2
                        once = 1;
354 2
                        break;
355
                case 'f':
356 0
                        f_flag = 1;
357 0
                        break;
358
                case 'h':
359
                        /* Usage help */
360 2
                        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 6
                        if (!VUT_Arg(vut, o, optarg))
372 0
                                usage(1);
373
                }
374
        }
375
376 4
        if (optind != argc)
377 2
                usage(1);
378
379 2
        VUT_Signal(vut_sighandler);
380 2
        VUT_Setup(vut);
381 2
        ident = VSM_Dup(vut->vsm, "Arg", "-i");
382 2
        if (!once) {
383 0
                if (pthread_create(&thr, NULL, do_curses, NULL) != 0) {
384 0
                        fprintf(stderr, "pthread_create(): %s\n",
385 0
                            strerror(errno));
386 0
                        exit(1);
387
                }
388
        }
389 2
        vut->dispatch_f = accumulate;
390 2
        vut->dispatch_priv = NULL;
391 2
        vut->sighup_f = sighup;
392 2
        VUT_Main(vut);
393 2
        end_of_file = 1;
394 2
        if (once)
395 2
                dump();
396
        else
397 0
                pthread_join(thr, NULL);
398 2
        VUT_Fini(&vut);
399 2
        exit(0);
400
}