blob: 81ffa827edb002a5de47792421575f671779b812 [file] [log] [blame]
Willy Tarreau72c28532009-01-22 18:56:50 +01001/*
2 * haproxy log time reporter
3 *
4 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13/*
Willy Tarreau214c2032009-02-20 11:02:32 +010014 * gcc -O2 -o halog2 halog2.c -Iinclude src/ebtree.c src/eb32tree.c fgets2.c
Willy Tarreau72c28532009-01-22 18:56:50 +010015 *
16 * Usage:
17 * $0 [ min_delay [ min_count [ field_shift ]]] < haproxy.log
18 * Note: if min_delay < 0, it only outputs lines with status codes 5xx.
19 */
20
21#include <errno.h>
22#include <fcntl.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <syslog.h>
26#include <string.h>
27#include <unistd.h>
28#include <ctype.h>
29
Willy Tarreau45cb4fb2009-10-26 21:10:04 +010030#include <eb32tree.h>
Willy Tarreau72c28532009-01-22 18:56:50 +010031
32#define ACCEPT_FIELD 6
33#define TIME_FIELD 9
34#define STATUS_FIELD 10
35#define CONN_FIELD 15
36#define MAXLINE 16384
37#define QBITS 4
38
39#define SKIP_CHAR(p,c) do { while (1) if (!*p) break; else if (*(p++) == c) break; } while (0)
40
41/* [0] = err/date, [1] = req, [2] = conn, [3] = resp, [4] = data */
42static struct eb_root timers[5] = {
43 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
44 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
45};
46
47struct timer {
48 struct eb32_node node;
49 unsigned int count;
50};
51
52
53#define FILT_COUNT_ONLY 0x01
54#define FILT_INVERT 0x02
55#define FILT_QUIET 0x04
56#define FILT_ERRORS_ONLY 0x08
57#define FILT_ACC_DELAY 0x10
58#define FILT_ACC_COUNT 0x20
59#define FILT_GRAPH_TIMERS 0x40
Willy Tarreau214c2032009-02-20 11:02:32 +010060#define FILT_PERCENTILE 0x80
Willy Tarreau5bdfd962009-10-14 15:16:29 +020061#define FILT_TIME_RESP 0x100
62
63#define FILT_INVERT_ERRORS 0x200
64#define FILT_INVERT_TIME_RESP 0x400
Willy Tarreau72c28532009-01-22 18:56:50 +010065
Willy Tarreau0f423a72010-05-03 10:50:54 +020066#define FILT_COUNT_STATUS 0x800
67
Willy Tarreau72c28532009-01-22 18:56:50 +010068unsigned int filter = 0;
69unsigned int filter_invert = 0;
Willy Tarreau214c2032009-02-20 11:02:32 +010070const char *line;
Willy Tarreau72c28532009-01-22 18:56:50 +010071
Willy Tarreau214c2032009-02-20 11:02:32 +010072const char *fgets2(FILE *stream);
Willy Tarreau72c28532009-01-22 18:56:50 +010073
74void die(const char *msg)
75{
76 fprintf(stderr,
77 "%s"
Willy Tarreau0f423a72010-05-03 10:50:54 +020078 "Usage: halog [-c] [-v] [-gt] [-pct] [-st] [-s <skip>] [-e|-E] [-rt|-RT <time>] [-ad <delay>] [-ac <count>] < file.log\n"
Willy Tarreau72c28532009-01-22 18:56:50 +010079 "\n",
80 msg ? msg : ""
81 );
82 exit(1);
83}
84
85
86/* return pointer to first char not part of current field starting at <p>. */
87const char *field_stop(const char *p)
88{
89 unsigned char c;
90
91 while (1) {
92 c = *(p++);
93 if (c > ' ')
94 continue;
95 if (c == ' ' || c == '\t' || c == 0)
96 break;
97 }
98 return p - 1;
99}
100
101/* return field <field> (starting from 1) in string <p>. Only consider
102 * contiguous spaces (or tabs) as one delimiter. May return pointer to
103 * last char if field is not found. Equivalent to awk '{print $field}'.
104 */
105const char *field_start(const char *p, int field)
106{
107 unsigned char c;
108 while (1) {
109 /* skip spaces */
110 while (1) {
111 c = *p;
112 if (c > ' ')
113 break;
114 if (c == ' ' || c == '\t')
115 goto next;
116 if (!c) /* end of line */
117 return p;
118 /* other char => new field */
119 break;
120 next:
121 p++;
122 }
123
124 /* start of field */
125 field--;
126 if (!field)
127 return p;
128
129 /* skip this field */
130 while (1) {
131 c = *(p++);
132 if (c > ' ')
133 continue;
134 if (c == ' ' || c == '\t')
135 break;
136 if (c == '\0')
137 return p;
138 }
139 }
140}
141
142/* keep only the <bits> higher bits of <i> */
143static inline unsigned int quantify_u32(unsigned int i, int bits)
144{
145 int high;
146
147 if (!bits)
148 return 0;
149
150 if (i)
151 high = fls_auto(i); // 1 to 32
152 else
153 high = 0;
154
155 if (high <= bits)
156 return i;
157
158 return i & ~((1 << (high - bits)) - 1);
159}
160
161/* keep only the <bits> higher bits of the absolute value of <i>, as well as
162 * its sign. */
163static inline int quantify(int i, int bits)
164{
165 if (i >= 0)
166 return quantify_u32(i, bits);
167 else
168 return -quantify_u32(-i, bits);
169}
170
171/* Insert timer value <v> into tree <r>. A pre-allocated node must be passed
172 * in <alloc>. It may be NULL, in which case the function will allocate it
173 * itself. It will be reset to NULL once consumed. The caller is responsible
174 * for freeing the node once not used anymore. The node where the value was
175 * inserted is returned.
176 */
177struct timer *insert_timer(struct eb_root *r, struct timer **alloc, int v)
178{
179 struct timer *t = *alloc;
180 struct eb32_node *n;
181
182 if (!t) {
183 t = calloc(sizeof(*t), 1);
184 if (unlikely(!t)) {
185 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
186 exit(1);
187 }
188 }
189 t->node.key = quantify(v, QBITS); // keep only the higher QBITS bits
190
191 n = eb32i_insert(r, &t->node);
192 if (n == &t->node)
193 t = NULL; /* node inserted, will malloc next time */
194
195 *alloc = t;
196 return container_of(n, struct timer, node);
197}
198
199/* Insert value value <v> into tree <r>. A pre-allocated node must be passed
200 * in <alloc>. It may be NULL, in which case the function will allocate it
201 * itself. It will be reset to NULL once consumed. The caller is responsible
202 * for freeing the node once not used anymore. The node where the value was
203 * inserted is returned.
204 */
205struct timer *insert_value(struct eb_root *r, struct timer **alloc, int v)
206{
207 struct timer *t = *alloc;
208 struct eb32_node *n;
209
210 if (!t) {
211 t = calloc(sizeof(*t), 1);
212 if (unlikely(!t)) {
213 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
214 exit(1);
215 }
216 }
217 t->node.key = v;
218
219 n = eb32i_insert(r, &t->node);
220 if (n == &t->node)
221 t = NULL; /* node inserted, will malloc next time */
222
223 *alloc = t;
224 return container_of(n, struct timer, node);
225}
226
227int str2ic(const char *s)
228{
229 int i = 0;
230 int j, k;
231
232 if (*s != '-') {
233 /* positive number */
234 while (1) {
235 j = (*s++) - '0';
236 k = i * 10;
237 if ((unsigned)j > 9)
238 break;
239 i = k + j;
240 }
241 } else {
242 /* negative number */
243 s++;
244 while (1) {
245 j = (*s++) - '0';
246 k = i * 10;
247 if ((unsigned)j > 9)
248 break;
249 i = k - j;
250 }
251 }
252
253 return i;
254}
255
256
257/* Equivalent to strtoul with a length. */
258static inline unsigned int __strl2ui(const char *s, int len)
259{
260 unsigned int i = 0;
261 while (len-- > 0) {
262 i = i * 10 - '0';
263 i += (unsigned char)*s++;
264 }
265 return i;
266}
267
268unsigned int strl2ui(const char *s, int len)
269{
270 return __strl2ui(s, len);
271}
272
273/* Convert "[04/Dec/2008:09:49:40.555]" to an integer equivalent to the time of
274 * the day in milliseconds. It returns -1 for all unparsable values. The parser
275 * looks ugly but gcc emits far better code that way.
276 */
277int convert_date(const char *field)
278{
279 unsigned int h, m, s, ms;
280 unsigned char c;
281 const char *b, *e;
282
283 h = m = s = ms = 0;
284 e = field;
285
286 /* skip the date */
287 while (1) {
288 c = *(e++);
289 if (c == ':')
290 break;
291 if (!c)
292 goto out_err;
293 }
294
295 /* hour + ':' */
296 b = e;
297 while (1) {
298 c = *(e++) - '0';
299 if (c > 9)
300 break;
301 h = h * 10 + c;
302 }
303 if (c == (unsigned char)(0 - '0'))
304 goto out_err;
305
306 /* minute + ':' */
307 b = e;
308 while (1) {
309 c = *(e++) - '0';
310 if (c > 9)
311 break;
312 m = m * 10 + c;
313 }
314 if (c == (unsigned char)(0 - '0'))
315 goto out_err;
316
317 /* second + '.' or ']' */
318 b = e;
319 while (1) {
320 c = *(e++) - '0';
321 if (c > 9)
322 break;
323 s = s * 10 + c;
324 }
325 if (c == (unsigned char)(0 - '0'))
326 goto out_err;
327
328 /* if there's a '.', we have milliseconds */
329 if (c == (unsigned char)('.' - '0')) {
330 /* millisecond second + ']' */
331 b = e;
332 while (1) {
333 c = *(e++) - '0';
334 if (c > 9)
335 break;
336 ms = ms * 10 + c;
337 }
338 if (c == (unsigned char)(0 - '0'))
339 goto out_err;
340 }
341 return (((h * 60) + m) * 60 + s) * 1000 + ms;
342 out_err:
343 return -1;
344}
345
346void truncated_line(int linenum, const char *line)
347{
348 if (!(filter & FILT_QUIET))
349 fprintf(stderr, "Truncated line %d: %s\n", linenum, line);
350}
351
352int main(int argc, char **argv)
353{
354 const char *b, *e, *p;
355 const char *output_file = NULL;
356 int f, tot, last, linenum, err, parse_err;
357 struct timer *t = NULL, *t2;
358 struct eb32_node *n;
359 int val, test;
360 int array[5];
361 int filter_acc_delay = 0, filter_acc_count = 0;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200362 int filter_time_resp = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100363 int skip_fields = 1;
364
365 argc--; argv++;
366 while (argc > 0) {
367 if (*argv[0] != '-')
368 break;
369
370 if (strcmp(argv[0], "-ad") == 0) {
371 if (argc < 2) die("missing option for -ad");
372 argc--; argv++;
373 filter |= FILT_ACC_DELAY;
374 filter_acc_delay = atol(*argv);
375 }
376 else if (strcmp(argv[0], "-ac") == 0) {
377 if (argc < 2) die("missing option for -ac");
378 argc--; argv++;
379 filter |= FILT_ACC_COUNT;
380 filter_acc_count = atol(*argv);
381 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200382 else if (strcmp(argv[0], "-rt") == 0) {
383 if (argc < 2) die("missing option for -rt");
384 argc--; argv++;
385 filter |= FILT_TIME_RESP;
386 filter_time_resp = atol(*argv);
387 }
388 else if (strcmp(argv[0], "-RT") == 0) {
389 if (argc < 2) die("missing option for -RT");
390 argc--; argv++;
391 filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
392 filter_time_resp = atol(*argv);
393 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100394 else if (strcmp(argv[0], "-s") == 0) {
395 if (argc < 2) die("missing option for -s");
396 argc--; argv++;
397 skip_fields = atol(*argv);
398 }
399 else if (strcmp(argv[0], "-e") == 0)
400 filter |= FILT_ERRORS_ONLY;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200401 else if (strcmp(argv[0], "-E") == 0)
402 filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
Willy Tarreau72c28532009-01-22 18:56:50 +0100403 else if (strcmp(argv[0], "-c") == 0)
404 filter |= FILT_COUNT_ONLY;
405 else if (strcmp(argv[0], "-q") == 0)
406 filter |= FILT_QUIET;
407 else if (strcmp(argv[0], "-v") == 0)
408 filter_invert = !filter_invert;
409 else if (strcmp(argv[0], "-gt") == 0)
410 filter |= FILT_GRAPH_TIMERS;
Willy Tarreau214c2032009-02-20 11:02:32 +0100411 else if (strcmp(argv[0], "-pct") == 0)
412 filter |= FILT_PERCENTILE;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200413 else if (strcmp(argv[0], "-st") == 0)
414 filter |= FILT_COUNT_STATUS;
Willy Tarreau72c28532009-01-22 18:56:50 +0100415 else if (strcmp(argv[0], "-o") == 0) {
416 if (output_file)
417 die("Fatal: output file name already specified.\n");
418 if (argc < 2)
419 die("Fatal: missing output file name.\n");
420 output_file = argv[1];
421 }
422 argc--;
423 argv++;
424 }
425
426 if (!filter)
427 die("No action specified.\n");
428
429 if (filter & FILT_ACC_COUNT && !filter_acc_count)
430 filter_acc_count=1;
431
432 if (filter & FILT_ACC_DELAY && !filter_acc_delay)
433 filter_acc_delay = 1;
434
435 linenum = 0;
436 tot = 0;
437 parse_err = 0;
438
Willy Tarreau214c2032009-02-20 11:02:32 +0100439 while ((line = fgets2(stdin)) != NULL) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100440 linenum++;
441
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200442 test = 1;
Willy Tarreau2651ac32010-05-05 12:20:19 +0200443 if (unlikely(filter & FILT_TIME_RESP)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200444 int tps;
445
446 /* only report lines with response times larger than filter_time_resp */
447 b = field_start(line, TIME_FIELD + skip_fields);
448 if (!*b) {
449 truncated_line(linenum, line);
450 continue;
451 }
452
453 e = field_stop(b + 1);
454 /* we have field TIME_FIELD in [b]..[e-1] */
455
456 p = b;
457 err = 0;
458 for (f = 0; f < 4 && *p; f++) {
459 tps = str2ic(p);
460 if (tps < 0) {
461 tps = -1;
462 err = 1;
463 }
464
465 SKIP_CHAR(p, '/');
466 }
467
468 if (f < 4) {
469 parse_err++;
470 continue;
471 }
472
473 test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
474 }
475
Willy Tarreau2651ac32010-05-05 12:20:19 +0200476 if (unlikely(filter & FILT_ERRORS_ONLY)) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100477 /* only report erroneous status codes */
478 b = field_start(line, STATUS_FIELD + skip_fields);
479 if (!*b) {
480 truncated_line(linenum, line);
481 continue;
482 }
483 if (*b == '-') {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200484 test &= !!(filter & FILT_INVERT_ERRORS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100485 } else {
486 val = strl2ui(b, 3);
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200487 test &= (val >= 500 && val <= 599) ^ !!(filter & FILT_INVERT_ERRORS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100488 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100489 }
490
Willy Tarreau0f423a72010-05-03 10:50:54 +0200491 test ^= filter_invert;
492 if (!test)
493 continue;
494
Willy Tarreau2651ac32010-05-05 12:20:19 +0200495 if (unlikely(filter & (FILT_ACC_COUNT|FILT_ACC_DELAY))) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100496 b = field_start(line, ACCEPT_FIELD + skip_fields);
497 if (!*b) {
498 truncated_line(linenum, line);
499 continue;
500 }
501
Willy Tarreau214c2032009-02-20 11:02:32 +0100502 tot++;
Willy Tarreau72c28532009-01-22 18:56:50 +0100503 val = convert_date(b);
504 //printf("date=%s => %d\n", b, val);
505 if (val < 0) {
506 parse_err++;
507 continue;
508 }
509
510 t2 = insert_value(&timers[0], &t, val);
511 t2->count++;
512 continue;
513 }
514
Willy Tarreau2651ac32010-05-05 12:20:19 +0200515 if (unlikely(filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE))) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100516 int f;
517
518 b = field_start(line, TIME_FIELD + skip_fields);
519 if (!*b) {
520 truncated_line(linenum, line);
521 continue;
522 }
523
524 e = field_stop(b + 1);
525 /* we have field TIME_FIELD in [b]..[e-1] */
526
527 p = b;
528 err = 0;
529 for (f = 0; f < 5 && *p; f++) {
530 array[f] = str2ic(p);
531 if (array[f] < 0) {
532 array[f] = -1;
533 err = 1;
534 }
535
536 SKIP_CHAR(p, '/');
537 }
538
539 if (f < 5) {
540 parse_err++;
541 continue;
542 }
543
544 /* if we find at least one negative time, we count one error
545 * with a time equal to the total session time. This will
546 * emphasize quantum timing effects associated to known
547 * timeouts. Note that on some buggy machines, it is possible
548 * that the total time is negative, hence the reason to reset
549 * it.
550 */
Willy Tarreau214c2032009-02-20 11:02:32 +0100551
552 if (filter & FILT_GRAPH_TIMERS) {
553 if (err) {
554 if (array[4] < 0)
555 array[4] = -1;
556 t2 = insert_timer(&timers[0], &t, array[4]); // total time
557 t2->count++;
558 } else {
559 int v;
Willy Tarreau72c28532009-01-22 18:56:50 +0100560
Willy Tarreau214c2032009-02-20 11:02:32 +0100561 t2 = insert_timer(&timers[1], &t, array[0]); t2->count++; // req
562 t2 = insert_timer(&timers[2], &t, array[2]); t2->count++; // conn
563 t2 = insert_timer(&timers[3], &t, array[3]); t2->count++; // resp
564
565 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
566 if (v < 0 && !(filter & FILT_QUIET))
567 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
568 line, array[0], array[1], array[2], array[3], array[4], v);
569 t2 = insert_timer(&timers[4], &t, v); t2->count++;
570 tot++;
571 }
572 } else { /* percentile */
573 if (err) {
574 if (array[4] < 0)
575 array[4] = -1;
576 t2 = insert_value(&timers[0], &t, array[4]); // total time
577 t2->count++;
578 } else {
579 int v;
Willy Tarreau72c28532009-01-22 18:56:50 +0100580
Willy Tarreau214c2032009-02-20 11:02:32 +0100581 t2 = insert_value(&timers[1], &t, array[0]); t2->count++; // req
582 t2 = insert_value(&timers[2], &t, array[2]); t2->count++; // conn
583 t2 = insert_value(&timers[3], &t, array[3]); t2->count++; // resp
584
585 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
586 if (v < 0 && !(filter & FILT_QUIET))
587 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
588 line, array[0], array[1], array[2], array[3], array[4], v);
589 t2 = insert_value(&timers[4], &t, v); t2->count++;
590 tot++;
591 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100592 }
593 continue;
594 }
595
Willy Tarreau2651ac32010-05-05 12:20:19 +0200596 if (unlikely(filter & FILT_COUNT_STATUS)) {
Willy Tarreau0f423a72010-05-03 10:50:54 +0200597 b = field_start(line, STATUS_FIELD + skip_fields);
598 if (!*b) {
599 truncated_line(linenum, line);
600 continue;
601 }
602 val = str2ic(b);
603
604 t2 = insert_value(&timers[0], &t, val);
605 t2->count++;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200606 continue;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200607 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200608
Willy Tarreau72c28532009-01-22 18:56:50 +0100609 /* all other cases mean we just want to count lines */
610 tot++;
Willy Tarreau2651ac32010-05-05 12:20:19 +0200611 if (unlikely(!(filter & FILT_COUNT_ONLY)))
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200612 puts(line);
Willy Tarreau72c28532009-01-22 18:56:50 +0100613 }
614
615 if (t)
616 free(t);
617
618 if (filter & FILT_COUNT_ONLY) {
619 printf("%d\n", tot);
620 exit(0);
621 }
622
Willy Tarreau72c28532009-01-22 18:56:50 +0100623 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
624 /* sort and count all timers. Output will look like this :
625 * <accept_date> <delta_ms from previous one> <nb entries>
626 */
627 n = eb32_first(&timers[0]);
628
629 if (n)
630 last = n->key;
631 while (n) {
632 unsigned int d, h, m, s, ms;
633
634 t = container_of(n, struct timer, node);
635 h = n->key;
636 d = h - last;
637 last = h;
638
639 if (d >= filter_acc_delay && t->count >= filter_acc_count) {
640 ms = h % 1000; h = h / 1000;
641 s = h % 60; h = h / 60;
642 m = h % 60; h = h / 60;
643 tot++;
644 printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
645 }
646 n = eb32_next(n);
647 }
648 }
649 else if (filter & FILT_GRAPH_TIMERS) {
650 /* sort all timers */
651 for (f = 0; f < 5; f++) {
652 struct eb32_node *n;
653 int val;
654
655 val = 0;
656 n = eb32_first(&timers[f]);
657 while (n) {
658 int i;
659 double d;
660
661 t = container_of(n, struct timer, node);
662 last = n->key;
663 val = t->count;
664
665 i = (last < 0) ? -last : last;
666 i = fls_auto(i) - QBITS;
667
668 if (i > 0)
669 d = val / (double)(1 << i);
670 else
671 d = val;
672
673 if (d > 0.0) {
674 printf("%d %d %f\n", f, last, d+1.0);
675 tot++;
676 }
677
678 n = eb32_next(n);
679 }
Willy Tarreau214c2032009-02-20 11:02:32 +0100680 }
681 }
682 else if (filter & FILT_PERCENTILE) {
683 /* report timers by percentile :
684 * <percent> <total> <max_req_time> <max_conn_time> <max_resp_time> <max_data_time>
685 * We don't count errs.
686 */
687 struct eb32_node *n[5];
688 unsigned long cum[5];
689 double step;
690
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100691 if (!tot)
692 goto empty;
693
Willy Tarreau214c2032009-02-20 11:02:32 +0100694 for (f = 1; f < 5; f++) {
695 n[f] = eb32_first(&timers[f]);
696 cum[f] = container_of(n[f], struct timer, node)->count;
697 }
698
699 for (step = 1; step <= 1000;) {
700 unsigned int thres = tot * (step / 1000.0);
701
702 printf("%3.1f %d ", step/10.0, thres);
703 for (f = 1; f < 5; f++) {
704 struct eb32_node *next;
705 while (cum[f] < thres) {
706 /* need to find other keys */
707 next = eb32_next(n[f]);
708 if (!next)
709 break;
710 n[f] = next;
711 cum[f] += container_of(next, struct timer, node)->count;
712 }
713
714 /* value still within $step % of total */
715 printf("%d ", n[f]->key);
716 }
717 putchar('\n');
718 if (step >= 100 && step < 900)
719 step += 50; // jump 5% by 5% between those steps.
720 else if (step >= 20 && step < 980)
721 step += 10;
722 else
723 step += 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100724 }
725 }
Willy Tarreau0f423a72010-05-03 10:50:54 +0200726 else if (filter & FILT_COUNT_STATUS) {
727 /* output all statuses in the form of <status> <occurrences> */
728 n = eb32_first(&timers[0]);
729 while (n) {
730 t = container_of(n, struct timer, node);
731 printf("%d %d\n", n->key, t->count);
732 n = eb32_next(n);
733 }
734 }
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100735 empty:
Willy Tarreau72c28532009-01-22 18:56:50 +0100736 if (!(filter & FILT_QUIET))
737 fprintf(stderr, "%d lines in, %d lines out, %d parsing errors\n",
738 linenum, tot, parse_err);
739 exit(0);
740}
741
742/*
743 * Local variables:
744 * c-indent-level: 8
745 * c-basic-offset: 8
746 * End:
747 */