| | 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 |
|
} |