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