From me via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=7709
[metze/wireshark/wip.git] / epan / stats_tree.c
1 /* stats_tree.c
2  * API for a counter tree for Wireshark
3  * 2004, Luis E. G. Ontanon
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <glib.h>
31 #include <epan/stats_tree_priv.h>
32 #include <string.h>
33
34 #include "stats_tree.h"
35
36 /*
37 TODO:
38    - sort out the sorting issue
39
40  */
41
42 /* used to contain the registered stat trees */
43 static GHashTable *registry = NULL;
44
45 /* writes into the buffers pointed by value, rate and percent
46    the string representations of a node*/
47 extern void
48 stats_tree_get_strs_from_node(const stat_node *node, gchar *value, gchar *rate, gchar *percent)
49 {
50         float f;
51
52         if (value) g_snprintf(value,NUM_BUF_SIZE,"%u",node->counter);
53
54         if (rate) {
55                 *rate = '\0';
56                 if (node->st->elapsed > 0.0) {
57                         f = ((float)node->counter) / (float)node->st->elapsed;
58                         g_snprintf(rate,NUM_BUF_SIZE,"%f",f);
59                 }
60         }
61
62         if (percent) {
63                 *percent = '\0';
64                 if (node->parent->counter > 0) {
65                         f = (float)(((float)node->counter * 100.0) / node->parent->counter);
66                         g_snprintf(percent,NUM_BUF_SIZE,"%.2f%%",f);
67                 }
68         }
69 }
70
71
72 /* a text representation of a node
73 if buffer is NULL returns a newly allocated string */
74 extern gchar*
75 stats_tree_node_to_str(const stat_node *node, gchar *buffer, guint len)
76 {
77         if (buffer) {
78                 g_snprintf(buffer,len,"%s: %i",node->name, node->counter);
79                 return buffer;
80         } else {
81                 return g_strdup_printf("%s: %i",node->name, node->counter);
82         }
83 }
84
85 extern guint
86 stats_tree_branch_max_namelen(const stat_node *node, guint indent)
87 {
88         stat_node *child;
89         guint maxlen = 0;
90         guint len;
91
92         indent = indent > INDENT_MAX ? INDENT_MAX : indent;
93
94         if (node->children) {
95                 for (child = node->children; child; child = child->next ) {
96                         len = stats_tree_branch_max_namelen(child,indent+1);
97                         maxlen = len > maxlen ? len : maxlen;
98                 }
99         }
100
101         len = (guint) strlen(node->name) + indent;
102         maxlen = len > maxlen ? len : maxlen;
103
104         return maxlen;
105 }
106
107 static gchar *format;
108
109 /* populates the given GString with a tree representation of a branch given by node,
110 using indent spaces as initial indentation */
111 extern void
112 stats_tree_branch_to_str(const stat_node *node, GString *s, guint indent)
113 {
114         stat_node *child;
115         static gchar indentation[INDENT_MAX+1];
116         static gchar value[NUM_BUF_SIZE];
117         static gchar rate[NUM_BUF_SIZE];
118         static gchar percent[NUM_BUF_SIZE];
119
120         guint i = 0;
121
122         if (indent == 0) {
123                 format = g_strdup_printf(" %%s%%-%us%%12s   %%12s    %%12s\n",stats_tree_branch_max_namelen(node,0));
124         }
125
126         stats_tree_get_strs_from_node(node, value, rate, percent);
127
128         indent = indent > INDENT_MAX ? INDENT_MAX : indent;
129
130         /* fill indentation with indent spaces */
131         if (indent > 0) {
132                 while(i<indent)
133                         indentation[i++] = ' ';
134         }
135
136         indentation[i] = '\0';
137
138         g_string_append_printf(s,format,
139                                           indentation,node->name,value,rate,percent);
140
141         if (node->children) {
142                 for (child = node->children; child; child = child->next ) {
143                         stats_tree_branch_to_str(child,s,indent+1);
144                 }
145         }
146
147         if (indent == 0) {
148                 g_free(format);
149         }
150 }
151
152
153 /* frees the resources allocated by a stat_tree node */
154 static void
155 free_stat_node(stat_node *node)
156 {
157         stat_node *child;
158         stat_node *next;
159
160         if (node->children) {
161         for (child = node->children; child; child = next ) {
162             /* child->next will be gone after free_stat_node, so cache it here */
163             next = child->next;
164                         free_stat_node(child);
165         }
166         }
167
168         if(node->st->cfg->free_node_pr) node->st->cfg->free_node_pr(node);
169
170         if (node->hash) g_hash_table_destroy(node->hash);
171
172         g_free(node->rng);
173         g_free(node->name);
174         g_free(node);
175 }
176
177 /* destroys the whole tree instance */
178 extern void
179 stats_tree_free(stats_tree *st)
180 {
181         stat_node *child;
182         stat_node *next;
183
184         g_free(st->filter);
185         g_hash_table_destroy(st->names);
186         g_ptr_array_free(st->parents,TRUE);
187
188         for (child = st->root.children; child; child = next ) {
189                 /* child->next will be gone after free_stat_node, so cache it here */
190                 next = child->next;
191                 free_stat_node(child);
192         }
193
194         if (st->cfg->free_tree_pr)
195                 st->cfg->free_tree_pr(st);
196
197         if (st->cfg->cleanup)
198                 st->cfg->cleanup(st);
199
200         g_free(st);
201 }
202
203
204 /* reset a node to its original state */
205 static void
206 reset_stat_node(stat_node *node)
207 {
208         stat_node *child;
209
210         if (node->children) {
211                 for (child = node->children; child; child = child->next )
212                         reset_stat_node(child);
213         }
214
215         node->counter = 0;
216
217         if(node->st->cfg->reset_node) {
218                 node->st->cfg->reset_node(node);
219         }
220
221 }
222
223 /* reset the whole stats_tree */
224 extern void
225 stats_tree_reset(void *p)
226 {
227         stats_tree *st = p;
228
229         st->start = -1.0;
230         st->elapsed = 0.0;
231
232         reset_stat_node(&st->root);
233
234         if (st->cfg->reset_tree) {
235                 st->cfg->reset_tree(st);
236         }
237 }
238
239 extern void
240 stats_tree_reinit(void *p)
241 {
242         stats_tree *st = p;
243         stat_node *child;
244         stat_node *next;
245
246         for (child = st->root.children; child; child = next) {
247         /* child->next will be gone after free_stat_node, so cache it here */
248         next = child->next;
249                 free_stat_node(child);
250         }
251
252         st->root.children = NULL;
253         st->root.counter = 0;
254
255         if (st->cfg->init) {
256                 st->cfg->init(st);
257         }
258 }
259
260 /* register a new stats_tree */
261 extern void
262 stats_tree_register_with_group(const char *tapname, const char *abbr, const char *name,
263                     guint flags,
264                     stat_tree_packet_cb packet, stat_tree_init_cb init,
265                     stat_tree_cleanup_cb cleanup, register_stat_group_t stat_group)
266 {
267
268         stats_tree_cfg *cfg = g_malloc( sizeof(stats_tree_cfg) );
269
270         /* at the very least the abbrev and the packet function should be given */
271         g_assert( tapname && abbr && packet );
272
273         cfg->plugin = FALSE;
274         cfg->tapname = g_strdup(tapname);
275         cfg->abbr = g_strdup(abbr);
276         cfg->name = name ? g_strdup(name) : g_strdup(abbr);
277         cfg->stat_group = stat_group;
278
279         cfg->packet = packet;
280         cfg->init = init;
281         cfg->cleanup = cleanup;
282
283         cfg->flags = flags;
284
285         /* these have to be filled in by implementations */
286         cfg->setup_node_pr = NULL;
287         cfg->new_tree_pr = NULL;
288         cfg->free_node_pr = NULL;
289         cfg->free_tree_pr = NULL;
290         cfg->draw_node = NULL;
291         cfg->draw_tree = NULL;
292         cfg->reset_node = NULL;
293         cfg->reset_tree = NULL;
294
295         if (!registry) registry = g_hash_table_new(g_str_hash,g_str_equal);
296
297         g_hash_table_insert(registry,cfg->abbr,cfg);
298 }
299
300 /* register a new stats_tree with default group REGISTER_STAT_GROUP_UNSORTED */
301 extern void
302 stats_tree_register(const char *tapname, const char *abbr, const char *name,
303                     guint flags,
304                     stat_tree_packet_cb packet, stat_tree_init_cb init,
305                     stat_tree_cleanup_cb cleanup)
306 {
307         stats_tree_register_with_group(tapname, abbr, name,
308                     flags,
309                     packet, init,
310                     cleanup, REGISTER_STAT_GROUP_UNSORTED);
311 }
312
313 /* register a new stat_tree with default group REGISTER_STAT_GROUP_UNSORTED from a plugin */
314 extern void
315 stats_tree_register_plugin(const char *tapname, const char *abbr, const char *name,
316                     guint flags,
317                     stat_tree_packet_cb packet, stat_tree_init_cb init,
318                     stat_tree_cleanup_cb cleanup)
319 {
320         stats_tree_cfg *cfg;
321
322         stats_tree_register(tapname, abbr, name,
323                     flags,
324                     packet, init,
325                     cleanup);
326         cfg = stats_tree_get_cfg_by_abbr((char*)abbr);
327         cfg->plugin = TRUE;
328 }
329
330 extern stats_tree*
331 stats_tree_new(stats_tree_cfg *cfg, tree_pres *pr, const char *filter)
332 {
333         stats_tree *st = g_malloc(sizeof(stats_tree));
334
335         st->cfg = cfg;
336         st->pr = pr;
337
338         st->names = g_hash_table_new(g_str_hash,g_str_equal);
339         st->parents = g_ptr_array_new();
340         st->filter = g_strdup(filter);
341
342         st->start = -1.0;
343         st->elapsed = 0.0;
344
345         st->root.counter = 0;
346         st->root.name = g_strdup(cfg->name);
347         st->root.st = st;
348         st->root.parent = NULL;
349         st->root.children = NULL;
350         st->root.next = NULL;
351         st->root.hash = NULL;
352         st->root.pr = NULL;
353
354         g_ptr_array_add(st->parents,&st->root);
355
356         return st;
357 }
358
359 /* will be the tap packet cb */
360 extern int
361 stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *pri)
362 {
363         stats_tree *st = p;
364         double now = nstime_to_msec(&pinfo->fd->rel_ts);
365
366         if (st->start < 0.0) st->start = now;
367
368         st->elapsed = now - st->start;
369
370         if (st->cfg->packet)
371                 return st->cfg->packet(st,pinfo,edt,pri);
372         else
373                 return 0;
374 }
375
376 extern stats_tree_cfg*
377 stats_tree_get_cfg_by_abbr(char *abbr)
378 {
379         return g_hash_table_lookup(registry,abbr);
380 }
381
382 extern GList*
383 stats_tree_get_cfg_list(void)
384 {
385         return g_hash_table_get_values(registry);
386 }
387
388 struct _stats_tree_pres_cbs {
389         void (*setup_node_pr)(stat_node*);
390         void (*free_node_pr)(stat_node*);
391         void (*draw_node)(stat_node*);
392         void (*reset_node)(stat_node*);
393         tree_pres *(*new_tree_pr)(stats_tree*);
394         void (*free_tree_pr)(stats_tree*);
395         void (*draw_tree)(stats_tree*);
396         void (*reset_tree)(stats_tree*);
397 };
398
399 static void
400 setup_tree_presentation(gpointer k _U_, gpointer v, gpointer p)
401 {
402         stats_tree_cfg *cfg = v;
403         struct _stats_tree_pres_cbs *d = p;
404
405         cfg->in_use = FALSE;
406         cfg->setup_node_pr = d->setup_node_pr;
407         cfg->new_tree_pr = d->new_tree_pr;
408         cfg->free_node_pr = d->free_node_pr;
409         cfg->free_tree_pr = d->free_tree_pr;
410         cfg->draw_node = d->draw_node;
411         cfg->draw_tree = d->draw_tree;
412         cfg->reset_node = d->reset_node;
413         cfg->reset_tree = d->reset_tree;
414
415 }
416
417 extern void
418 stats_tree_presentation(void (*registry_iterator)(gpointer,gpointer,gpointer),
419                         void (*setup_node_pr)(stat_node*),
420                         void (*free_node_pr)(stat_node*),
421                         void (*draw_node)(stat_node*),
422                         void (*reset_node)(stat_node*),
423                         tree_pres *(*new_tree_pr)(stats_tree*),
424                         void (*free_tree_pr)(stats_tree*),
425                         void (*draw_tree)(stats_tree*),
426                         void (*reset_tree)(stats_tree*),
427                         void *data)
428 {
429         static struct _stats_tree_pres_cbs d;
430
431         d.setup_node_pr = setup_node_pr;
432         d.new_tree_pr = new_tree_pr;
433         d.free_node_pr = free_node_pr;
434         d.free_tree_pr = free_tree_pr;
435         d.draw_node = draw_node;
436         d.draw_tree = draw_tree;
437         d.reset_node = reset_node;
438         d.reset_tree = reset_tree;
439
440         if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d);
441
442         if (registry_iterator && registry)
443                 g_hash_table_foreach(registry,registry_iterator,data);
444
445 }
446
447
448 /* creates a stat_tree node
449 *    name: the name of the stats_tree node
450 *    parent_name: the name of the ALREADY REGISTERED parent
451 *    with_hash: whether or not it should keep a hash with it's children names
452 *    as_named_node: whether or not it has to be registered in the root namespace
453 */
454 static stat_node*
455 new_stat_node(stats_tree *st, const gchar *name, int parent_id,
456               gboolean with_hash, gboolean as_parent_node)
457 {
458
459         stat_node *node = g_malloc (sizeof(stat_node));
460         stat_node *last_chld = NULL;
461
462         node->counter = 0;
463         node->name = g_strdup(name);
464         node->children = NULL;
465         node->next = NULL;
466         node->st = (stats_tree*) st;
467         node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL;
468         node->parent = NULL;
469         node->rng  =  NULL;
470
471         if (as_parent_node) {
472                 g_hash_table_insert(st->names,
473                                                         node->name,
474                                                         node);
475
476                 g_ptr_array_add(st->parents,node);
477
478                 node->id = st->parents->len - 1;
479         } else {
480                 node->id = -1;
481         }
482
483         if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
484                 node->parent = g_ptr_array_index(st->parents,parent_id);
485         } else {
486                 /* ??? should we set the parent to be root ??? */
487                 g_assert_not_reached();
488         }
489
490         if (node->parent->children) {
491                 /* insert as last child */
492
493                 for (last_chld = node->parent->children;
494                          last_chld->next;
495                          last_chld = last_chld->next ) ;
496
497                 last_chld->next = node;
498
499         } else {
500                 /* insert as first child */
501                 node->parent->children = node;
502         }
503
504         if(node->parent->hash) {
505                 g_hash_table_insert(node->parent->hash,node->name,node);
506         }
507
508         if (st->cfg->setup_node_pr) {
509                 st->cfg->setup_node_pr(node);
510         } else {
511                 node->pr = NULL;
512         }
513
514         return node;
515 }
516 /***/
517
518 extern int
519 stats_tree_create_node(stats_tree *st, const gchar *name, int parent_id, gboolean with_hash)
520 {
521         stat_node *node = new_stat_node(st,name,parent_id,with_hash,TRUE);
522
523         if (node)
524                 return node->id;
525         else
526                 return 0;
527 }
528
529 /* XXX: should this be a macro? */
530 extern int
531 stats_tree_create_node_by_pname(stats_tree *st, const gchar *name,
532                                 const gchar *parent_name, gboolean with_children)
533 {
534         return stats_tree_create_node(st,name,stats_tree_parent_id_by_name(st,parent_name),with_children);
535 }
536
537
538
539 /*
540  * Increases by delta the counter of the node whose name is given
541  * if the node does not exist yet it's created (with counter=1)
542  * using parent_name as parent node.
543  * with_hash=TRUE to indicate that the created node will have a parent
544  */
545 extern int
546 stats_tree_manip_node(manip_node_mode mode, stats_tree *st, const char *name,
547                       int parent_id, gboolean with_hash, gint value)
548 {
549         stat_node *node = NULL;
550         stat_node *parent = NULL;
551
552         g_assert( parent_id >= 0 && parent_id < (int) st->parents->len );
553
554         parent = g_ptr_array_index(st->parents,parent_id);
555
556         if( parent->hash ) {
557                 node = g_hash_table_lookup(parent->hash,name);
558         } else {
559                 node = g_hash_table_lookup(st->names,name);
560         }
561
562         if ( node == NULL )
563                 node = new_stat_node(st,name,parent_id,with_hash,with_hash);
564
565         switch (mode) {
566                 case MN_INCREASE: node->counter += value; break;
567                 case MN_SET: node->counter = value; break;
568         }
569
570         if (node)
571                 return node->id;
572         else
573                 return -1;
574 }
575
576
577 extern char*
578 stats_tree_get_abbr(const char *optarg)
579 {
580         guint i;
581
582         /* XXX: this fails when tshark is given any options
583            after the -z */
584         g_assert(optarg != NULL);
585
586         for (i=0; optarg[i] && optarg[i] != ','; i++);
587
588         if (optarg[i] == ',') {
589                 return g_strndup(optarg,i);
590         } else {
591                 return NULL;
592         }
593 }
594
595
596 /*
597  * This function accepts an input string which should define a long integer range.
598  * The normal result is a struct containing the floor and ceil value of this
599  * range.
600  *
601  * It is allowed to define a range string in the following ways :
602  *
603  * "0-10" -> { 0, 10 }
604  * "-0" -> { G_MININT, 0 }
605  * "0-" -> { 0, G_MAXINT }
606  * "-" -> { G_MININT, G_MAXINT }
607  *
608  * Note that this function is robust to buggy input string. If in some cases it
609  * returns NULL, it but may also return a pair with undefined values.
610  *
611  */
612 static range_pair_t*
613 get_range(char *rngstr)
614 {
615         gchar **split;
616         range_pair_t *rng;
617
618         split = g_strsplit((gchar*)rngstr,"-",2);
619
620         /* empty string */
621         if (split[0] == NULL) {
622           g_strfreev(split);
623           return NULL;
624         }
625
626         /* means we have a non empty string
627          * which does not contain a delimiter */
628         if (split[1] == NULL) {
629           g_strfreev(split);
630           return NULL;
631         }
632
633         rng = g_malloc(sizeof(range_pair_t));
634
635         /* string == "X-?" */
636         if (*(split[0]) != '\0') {
637             rng->floor = strtol(split[0],NULL,10);
638         } else
639           /* string == "-?" */
640           rng->floor = G_MININT;
641
642         /* string != "?-" */
643         if (*(split[1]) != '\0') {
644           rng->ceil  = strtol(split[1],NULL,10);
645         } else
646           /* string == "?-" */
647           rng->ceil = G_MAXINT;
648
649         g_strfreev(split);
650
651         return rng;
652 }
653
654
655 extern int
656 stats_tree_create_range_node(stats_tree *st, const gchar *name, int parent_id, ...)
657 {
658         va_list list;
659         gchar *curr_range;
660         stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
661         stat_node *range_node = NULL;
662
663         va_start( list, parent_id );
664         while (( curr_range = va_arg(list, gchar*) )) {
665                 range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
666                 range_node->rng = get_range(curr_range);
667         }
668         va_end( list );
669
670         return rng_root->id;
671 }
672
673 extern int 
674 stats_tree_create_range_node_string(stats_tree *st, const gchar *name,
675                                     int parent_id, int num_str_ranges,
676                                     gchar** str_ranges)
677 {
678         int i;
679         stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
680         stat_node *range_node = NULL;
681
682         for (i = 0; i < num_str_ranges; i++) {
683                 range_node = new_stat_node(st, str_ranges[i], rng_root->id, FALSE, FALSE);
684                 range_node->rng = get_range(str_ranges[i]);
685         }
686
687         return rng_root->id;
688 }
689
690 /****/
691 extern int
692 stats_tree_parent_id_by_name(stats_tree *st, const gchar *parent_name)
693 {
694         stat_node *node = g_hash_table_lookup(st->names,parent_name);
695
696         if (node)
697                 return node->id;
698         else
699                 return 0; /* XXX: this is the root shoud we return -1 instead?*/
700 }
701
702
703 extern int
704 stats_tree_range_node_with_pname(stats_tree *st, const gchar *name,
705                                  const gchar *parent_name, ...)
706 {
707         va_list list;
708         gchar *curr_range;
709         stat_node *range_node = NULL;
710         int parent_id = stats_tree_parent_id_by_name(st,parent_name);
711         stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
712
713         va_start( list, parent_name );
714         while (( curr_range = va_arg(list, gchar*) )) {
715                 range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
716                 range_node->rng = get_range(curr_range);
717         }
718         va_end( list );
719
720         return rng_root->id;
721 }
722
723
724 extern int
725 stats_tree_tick_range(stats_tree *st, const gchar *name, int parent_id,
726                       int value_in_range)
727 {
728
729         stat_node *node = NULL;
730         stat_node *parent = NULL;
731         stat_node *child = NULL;
732         gint floor, ceil;
733
734         if (parent_id >= 0 && parent_id < (int) st->parents->len) {
735                 parent = g_ptr_array_index(st->parents,parent_id);
736         } else {
737                 g_assert_not_reached();
738         }
739
740         if( parent->hash ) {
741                 node = g_hash_table_lookup(parent->hash,name);
742         } else {
743                 node = g_hash_table_lookup(st->names,name);
744         }
745
746         if ( node == NULL )
747                 g_assert_not_reached();
748
749         for ( child = node->children; child; child = child->next) {
750                 floor =  child->rng->floor;
751                 ceil = child->rng->ceil;
752
753                 if ( value_in_range >= floor && value_in_range <= ceil ) {
754                         child->counter++;
755                         return node->id;
756                 }
757         }
758
759         return node->id;
760 }
761
762 extern int
763 stats_tree_create_pivot(stats_tree *st, const gchar *name, int parent_id)
764 {
765         stat_node *node = new_stat_node(st,name,parent_id,TRUE,TRUE);
766
767         if (node)
768                 return node->id;
769         else
770                 return 0;
771 }
772
773 extern int
774 stats_tree_create_pivot_by_pname(stats_tree *st, const gchar *name,
775                                  const gchar *parent_name)
776 {
777         int parent_id = stats_tree_parent_id_by_name(st,parent_name);
778         stat_node *node;
779
780         node = new_stat_node(st,name,parent_id,TRUE,TRUE);
781
782         if (node)
783                 return node->id;
784         else
785                 return 0;
786 }
787
788 extern int
789 stats_tree_tick_pivot(stats_tree *st, int pivot_id, const gchar *pivot_value)
790 {
791
792         stat_node *parent = g_ptr_array_index(st->parents,pivot_id);
793
794         parent->counter++;
795         stats_tree_manip_node( MN_INCREASE, st, pivot_value, pivot_id, FALSE, 1);
796
797         return pivot_id;
798 }
799