diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-04-24 11:02:35 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-04-24 11:02:35 +0300 |
commit | c36ee7102c3095f70e8db52aa55046c1393ff4cd (patch) | |
tree | e21b479c6e1d9c4b1c2fc8e6f0eed515b0588fe7 | |
parent | 0d24087bca70de572cda0e35b91678816c5d9b40 (diff) | |
download | tagr-c36ee7102c3095f70e8db52aa55046c1393ff4cd.tar.gz tagr-c36ee7102c3095f70e8db52aa55046c1393ff4cd.tar.bz2 |
Fix statistics gathering algorithm. Start implementing drawing functions.
* src/output.c: New file.
* src/Makefile.am (tagr_SOURCES): Add output.c
* src/graph.c (draw_graph): New function.
* src/main.c (mkfilename): Allow for suffix=NULL
* src/report.c (tr_init): Set queue sizes to exactly the number
of the corresponding samples.
(update_output): New function.
(rebuild): Implement
* src/stat.c (interpolate): Avoid interpolation if time span is
less than the step.
Use queue_put to store the data and always call the overflow
procedure, if supplied.
(overflow): Fix condition.
(compute_avg): New function.
(ovf_monthly, ovf_weekly, ovf_daily): Add verbose diagnostics.
(_convert): Additional argument: overflow function.
(convert_yearly, convert_monthly, convert_weekly): Compute
the average.
Add verbose diagnostics.
(convert_daily): Add verbose diagnostics.
* src/tagr.h (update_output, draw_graph): New protos.
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/graph.c | 94 | ||||
-rw-r--r-- | src/main.c | 23 | ||||
-rw-r--r-- | src/output.c | 123 | ||||
-rw-r--r-- | src/report.c | 53 | ||||
-rw-r--r-- | src/stat.c | 78 | ||||
-rw-r--r-- | src/tagr.h | 11 |
7 files changed, 351 insertions, 32 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7f067d5..4ef8905 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,7 @@ tagr_SOURCES=\ html.lex.l\ log.c\ main.c\ + output.c\ queue.c\ readconfig.c\ report.c\ diff --git a/src/graph.c b/src/graph.c index b88a5e8..1026f35 100644 --- a/src/graph.c +++ b/src/graph.c @@ -1,5 +1,5 @@ /* This file is part of tagr. - Copyright (C) 2006, Sergey Poznyakoff + Copyright (C) 2006, 2009 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -50,8 +50,98 @@ int color_in_max[3] = { 0,166,33 }; int color_out_max[3] = { 255,0,255 }; int color_percent[3] = { 239,159,79 }; +int graph_xsize = 460; +int graph_ysize = 100; + static char *short_suffix[] = {"", "k", "M", "G", "T"}; char **number_suffix = short_suffix; size_t number_suffix_count = sizeof (short_suffix) / sizeof (short_suffix[0]); -/* No functions so far */ +#define make_color_index(g, ar) \ + gdImageColorAllocate (g, (ar)[0], (ar)[1], (ar)[2]) + +int +draw_graph (FILE *fp, + queue_t *dataq, const struct avg_acc *avg, time_t now, + int xstep, unsigned long xmax, unsigned long ymax, + int growright) +{ + int x; + int i, n; + gdImagePtr graph, brush_out, brush_outm, brush_outp; + int i_background, i_light, i_dark; + int i_major, i_in, i_out, i_grid, i_inm, i_outm; + int i_outp, i_outpg; + double xscale, yscale; + + yscale = ((double) graph_ysize - 35) / ymax; + xscale = ((double) graph_xsize - 100 - percent_option * 30) / xmax; + +#define ytr(y) \ + (unsigned long) ((ymax - (y)) * yscale + 14) +#define xtr(x) \ + (unsigned long) (growright ? ((graph_xsize - (x))) : (x)) + + graph = gdImageCreate (graph_xsize, graph_ysize); + brush_out = gdImageCreate (1, 2); + brush_outm = gdImageCreate (1, 2); + brush_outp = gdImageCreate (1, 2); + + i_background = make_color_index (graph, color_background); + i_light = make_color_index (graph, color_light); + i_dark = make_color_index (graph, color_dark); + + if (transparent_option) + gdImageColorTransparent (graph, i_background); + + gdImageInterlace (graph, 1); + + i_major = make_color_index (graph, color_major); + i_in = make_color_index (graph, color_in); + i_out = make_color_index (brush_out, color_out); + i_grid = make_color_index (graph, color_grid); + i_inm = make_color_index (graph, color_in_max); + i_outm = make_color_index (brush_outm, color_out_max); + i_outp = make_color_index (brush_outp, color_percent); + i_outpg = make_color_index (graph, color_percent); + + /* Draw the image border */ + gdImageLine (graph, 0, 0, graph_xsize - 1, 0, i_light); + gdImageLine (graph, 1, 1, graph_xsize - 2, 1, i_light); + gdImageLine (graph, 0, 0, 0, graph_ysize - 1, i_light); + gdImageLine (graph, 1, 1, 1, graph_ysize - 2, i_light); + gdImageLine (graph, graph_xsize - 1, 0, graph_xsize - 1, + graph_ysize - 1, i_dark); + gdImageLine (graph, 0, graph_ysize - 1, graph_xsize - 1, + graph_ysize - 1, i_dark); + gdImageLine (graph, graph_xsize - 2, 1, graph_xsize - 2, + graph_ysize - 2, i_dark); + gdImageLine (graph, 1, graph_ysize - 2, graph_xsize - 2, + graph_ysize - 2, i_dark); + + n = queue_count (dataq); + for (i = n - 1, x = 0; i > 0; i--, x++) + { + struct traffic_history *th = queue_get_ptr (dataq, i); + struct traffic_history *tnext = queue_get_ptr (dataq, i - 1); + if (fill_incoming_option) + gdImageLine (graph, + xtr (x), ytr (0), + xtr (x), ytr (tnext->inrate), i_in); + gdImageLine (graph, xtr (x), ytr (th->inrate), + xtr (x+1), ytr (tnext->inrate), i_in); +#if 0 + gdImageLine (graph, x + 0.5, ytr (0), + x + 0.5, ytr (th->inrate), i_in); +#endif + } + + /* FIXME: Drow outgoing traffic, grids and legends */ + gdImagePng (graph, fp); + + gdImageDestroy (graph); + gdImageDestroy (brush_out); + gdImageDestroy (brush_outm); + gdImageDestroy (brush_outp); + return 0; +} @@ -636,22 +636,23 @@ char * mkfilename (char *dir, char *name, char *suffix) { int ret; - int len = strlen (name) + strlen (suffix); char *buf; + int len = strlen (name) + (suffix ? strlen (suffix) : 0); + if (dir) len += strlen (dir) + 1; - buf = malloc (len + 1); - if (buf) + + buf = xmalloc (len + 1); + buf[0] = 0; + if (dir) { - buf[0] = 0; - if (dir) - { - strcat (buf, dir); - strcat (buf, "/"); - } - strcat (buf, name); - strcat (buf, suffix); + strcat (buf, dir); + strcat (buf, "/"); } + strcat (buf, name); + if (suffix) + strcat (buf, suffix); + return buf; } diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..b205a4e --- /dev/null +++ b/src/output.c @@ -0,0 +1,123 @@ +/* This file is part of tagr. + Copyright (C) 2009 Sergey Poznyakoff + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> +#include <tagr.h> + +int +create_hierarchy (char *dir, int perm) +{ + int rc; + struct stat st; + char *p; + + if (stat (dir, &st) == 0) + { + if (!S_ISDIR (st.st_mode)) + { + logmsg (L_ERR, _("component %s is not a directory"), dir); + return 1; + } + return 0; + } + else if (errno != ENOENT) + { + logmsg (L_ERR, _("cannot stat file %s: %s"), dir, strerror (errno)); + return 1; + } + + p = strrchr (dir, '/'); + if (p) + *p = 0; + rc = create_hierarchy (dir, perm); + if (rc == 0) + { + if (p) + *p = '/'; + if (mkdir (dir, perm)) + { + logmsg (L_ERR, _("cannot create directory %s: %s"), + dir, strerror (errno)); + rc = 1; + } + } + return rc; +} + +static int +do_update_output (SD *host, + queue_t *queue, const struct avg_acc *avg, + time_t timestamp, + size_t step, size_t count, + char *dirname, char *img_suffix) +{ + char *fname; + FILE *fp; + int rc; + + if (create_hierarchy (dirname, 0755)) + { + logmsg (L_ERR, _("cannot create directory %s"), + dirname); + return 1; + } + + fname = mkfilename (dirname, host->name, img_suffix); + fp = fopen (fname, "w"); + if (!fp) + { + logmsg (L_ERR, _("cannot create file %s: %s"), fname, strerror (errno)); + rc = 1; + } + else + { + rc = draw_graph (fp, queue, avg, timestamp, step, count, + host->max, 0); + fclose (fp); + } + free (fname); + return rc; +} + +int +update_output (SD *host, struct traffic_record *tr, time_t timestamp) +{ + char *dirname = mkfilename (basedir, host->dir, NULL); + int rc; + + rc = do_update_output (host, &tr->day_hist, NULL, + DAY_SAMPLE, DAY_COUNT, timestamp, + dirname, "-day.png"); + rc += do_update_output (host, &tr->week_hist, &tr->week_avg, + WEEK_SAMPLE, WEEK_COUNT, timestamp, + dirname, "-week.png"); + rc += do_update_output (host, &tr->month_hist, &tr->month_avg, + MONTH_SAMPLE, MONTH_COUNT, timestamp, + dirname, "-month.png"); + rc += do_update_output (host, &tr->year_hist, &tr->year_avg, + YEAR_SAMPLE, YEAR_COUNT, timestamp, + dirname, "-year.png"); + free (dirname); + return rc; +} diff --git a/src/report.c b/src/report.c index 08989ec..3a7fc1c 100644 --- a/src/report.c +++ b/src/report.c @@ -72,16 +72,16 @@ void tr_init (struct traffic_record *tr) { tr->day_hist.queue = tr->history; - tr->day_hist.size = DAY_COUNT + 2; + tr->day_hist.size = DAY_COUNT; tr->week_hist.queue = tr->day_hist.queue + tr->day_hist.size; - tr->week_hist.size = WEEK_COUNT + 2; + tr->week_hist.size = WEEK_COUNT; tr->month_hist.queue = tr->week_hist.queue + tr->week_hist.size; - tr->month_hist.size = MONTH_COUNT + 2; + tr->month_hist.size = MONTH_COUNT; tr->year_hist.queue = tr->month_hist.queue + tr->month_hist.size; - tr->year_hist.size = YEAR_COUNT + 2; + tr->year_hist.size = YEAR_COUNT; } static void @@ -234,8 +234,51 @@ report (Stat *stat, time_t timestamp) logmsg (L_WARNING, "%s not found in config", stat->name); } +int +update_router (datum key, time_t timestamp) +{ + char id[MAX_NAME_LENGTH+1]; + struct traffic_record *tr = NULL; + SD *sd; + + if (key.dsize > MAX_NAME_LENGTH) + { + logmsg (L_ERR, _("tag name too long (%u)"), key.dsize); + return 1; + } + memcpy (id, key.dptr, key.dsize); + id[key.dsize] = 0; + sd = find_router_id (id); + if (!sd) + { + logmsg (L_ERR, _("%s: no such router"), id); + return 1; + } + _read_db (key, &tr); + if (tr) + { + update_output (sd, tr, timestamp); + free (tr); + } + + return 0; +} + void rebuild () { - die (EX_SOFTWARE, "Rebuild is not yet implemented"); + datum key; + datum content; + time_t now = time (NULL); + + open_db (); + key = gdbm_firstkey (dbf); + while (key.dptr) + { + datum nextkey = gdbm_nextkey ( dbf, key ); + update_router (key, now); + free (key.dptr); + key = nextkey; + } + close_db (); } @@ -41,17 +41,28 @@ interpolate (queue_t *q, struct traffic_record *tr) { time_t next; + struct traffic_history th; + if (now - last_time <= step) + { + th.inrate = inrate; + th.outrate = outrate; + verbose (3, "Insert %lu %g %g", next, th.inrate, th.outrate); + queue_put (q, &th); + if (ovf) + ovf (&th, tr, now); + return; + } + for (next = last_time + step; next <= now; next += step) { - struct traffic_history th; - th.inrate = (inrate - last_rates->inrate) * (next - last_time) / interval + last_rates->inrate; th.outrate = (outrate - last_rates->outrate) * (next - last_time) / interval + last_rates->outrate; verbose (3, "Insert %lu %g %g", next, th.inrate, th.outrate); - if (queue_xchg (q, &th) && ovf) + queue_put (q, &th); + if (ovf) ovf (&th, tr, now); } } @@ -66,7 +77,7 @@ overflow (struct traffic_history *th, int maxcount, int step) { - if (now - avg->time > step) + if (now - avg->time >= step) { struct traffic_history *lastp = queue_get_tail (q); if (lastp) @@ -99,22 +110,28 @@ overflow (struct traffic_history *th, int ovf_monthly (struct traffic_history *th, struct traffic_record *tr, time_t now) { + verbose (2, "begin overflow_monthly %lu %g %g", now, th->inrate, th->outrate); overflow (th, tr, now, NULL, &tr->year_avg, &tr->year_hist, YEAR_COUNT, YEAR_SAMPLE); + verbose (2, "end overflow_monthly"); } int ovf_weekly (struct traffic_history *th, struct traffic_record *tr, time_t now) { + verbose (2, "begin overflow_weekly %lu %g %g", now, th->inrate, th->outrate); overflow (th, tr, now, ovf_monthly, &tr->month_avg, &tr->month_hist, MONTH_COUNT, MONTH_SAMPLE); + verbose (2, "end overflow_daily"); } int ovf_daily (struct traffic_history *th, struct traffic_record *tr, time_t now) { + verbose (2, "begin overflow_daily %lu %g %g", now, th->inrate, th->outrate); overflow (th, tr, now, ovf_weekly, &tr->week_avg, &tr->week_hist, WEEK_COUNT, WEEK_SAMPLE); + verbose (2, "end overflow_daily"); } void @@ -161,16 +178,35 @@ update_stats (SD *sd, struct traffic_record *tr) + +static void +compute_avg (struct avg_acc *avg, queue_t *q, struct last_sample *last) +{ + int i, n = queue_count (q); + avg->inrate = avg->outrate = 0; + avg->count = n; + + for (i = 0; i < n; i++) + { + struct traffic_history *th = queue_get_ptr (q, i); + avg->inrate += th->inrate; + avg->outrate += th->outrate; + } + avg->inrate /= n; + avg->outrate /= n; + avg->time = last->time; +} + #define hist_rec last_sample static void -_convert (queue_t *q, +_convert (queue_t *q, ovf_t ovf, struct traffic_record *tr, struct hist_rec *hist, size_t count, time_t sample_interval) { size_t i; struct hist_rec *hp; - + tr->last.time = hist[count-1].time - sample_interval; for (hp = hist + count - 1; hp >= hist; hp--) { @@ -181,7 +217,7 @@ _convert (queue_t *q, interval = hp->time - tr->last.time; inrate = (double) hp->in; outrate = (double) hp->out; - + if (interval == 0) { logmsg (L_ERR, "Ignoring zero interval"); @@ -196,7 +232,7 @@ _convert (queue_t *q, &tr->last_rates, interval, inrate, outrate, - NULL, tr); + ovf, tr); } else { @@ -212,37 +248,51 @@ _convert (queue_t *q, tr->last.out = hp->out; tr->last_rates.inrate = inrate; tr->last_rates.outrate = outrate; - } + } } static void convert_yearly (struct traffic_record *tr, struct hist_rec *hp, size_t count) { - _convert (&tr->year_hist, tr, hp, YEAR_COUNT + 1, YEAR_SAMPLE); + verbose (2, "begin convert_yearly"); + _convert (&tr->year_hist, NULL, tr, + hp, count, YEAR_SAMPLE); + compute_avg (&tr->year_avg, &tr->year_hist, &tr->last); + verbose (2, "end convert_yearly"); } static void convert_monthly (struct traffic_record *tr, struct hist_rec *hp, size_t count) { + verbose (2, "begin convert_monthly"); if (count > MONTH_COUNT+1) convert_yearly (tr, hp + MONTH_COUNT + 1, count - (MONTH_COUNT + 1)); - _convert (&tr->month_hist, tr, hp, MONTH_COUNT + 1, MONTH_SAMPLE); + _convert (&tr->month_hist, ovf_monthly, tr, + hp, MONTH_COUNT + 1, MONTH_SAMPLE); + compute_avg (&tr->month_avg, &tr->month_hist, &tr->last); + verbose (2, "end convert_monthly"); } static void convert_weekly (struct traffic_record *tr, struct hist_rec *hp, size_t count) { + verbose (2, "begin convert_weekly"); if (count > WEEK_COUNT+1) convert_monthly (tr, hp + WEEK_COUNT + 1, count - (WEEK_COUNT + 1)); - _convert (&tr->week_hist, tr, hp, WEEK_COUNT + 1, WEEK_SAMPLE); + _convert (&tr->week_hist, ovf_weekly, + tr, hp, WEEK_COUNT + 1, WEEK_SAMPLE); + compute_avg (&tr->week_avg, &tr->week_hist, &tr->last); + verbose (2, "end convert_weekly"); } static void convert_daily (struct traffic_record *tr, struct hist_rec *hp, size_t count) { + verbose (2, "begin convert_daily"); if (count > DAY_COUNT+1) convert_weekly (tr, hp + DAY_COUNT + 1, count - (DAY_COUNT + 1)); - _convert (&tr->day_hist, tr, hp, DAY_COUNT + 1, DAY_SAMPLE); + _convert (&tr->day_hist, ovf_daily, tr, hp, DAY_COUNT + 1, DAY_SAMPLE); + verbose (2, "end convert_daily"); } static void @@ -255,7 +305,7 @@ convert_stats (SD *sd, struct hist_rec *last, tr_init (&tr); convert_daily (&tr, hp, count); - + read_db (sd, &trp); *trp = tr; trp->last = *last; @@ -211,3 +211,14 @@ void queue_put (queue_t *q, struct traffic_history *elt); int queue_xchg (queue_t *q, struct traffic_history *elt); char *mkfilename (char *dir, char *name, char *suffix); + + +/* output.c */ +int update_output (SD *host, struct traffic_record *tr, time_t timestamp); + + +/* graph.c */ +int draw_graph (FILE *fp, + queue_t *dataq, const struct avg_acc *avg, time_t now, + int xstep, unsigned long xmax, unsigned long ymax, + int growright); |