2 * API for a counter tree for Wireshark
3 * 2004, Luis E. G. Ontanon
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #include <epan/stats_tree_priv.h>
34 #include "stats_tree.h"
38 - sort out the sorting issue
42 /* used to contain the registered stat trees */
43 static GHashTable *registry = NULL;
45 /* writes into the buffers pointed by value, rate and percent
46 the string representations of a node*/
48 stats_tree_get_strs_from_node(const stat_node *node, gchar *value, gchar *rate, gchar *percent)
52 if (value) g_snprintf(value,NUM_BUF_SIZE,"%u",node->counter);
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);
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);
72 /* a text representation of a node
73 if buffer is NULL returns a newly allocated string */
75 stats_tree_node_to_str(const stat_node *node, gchar *buffer, guint len)
78 g_snprintf(buffer,len,"%s: %i",node->name, node->counter);
81 return g_strdup_printf("%s: %i",node->name, node->counter);
86 stats_tree_branch_max_namelen(const stat_node *node, guint indent)
92 indent = indent > INDENT_MAX ? INDENT_MAX : indent;
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;
101 len = strlen(node->name) + indent;
102 maxlen = len > maxlen ? len : maxlen;
107 static gchar *format;
109 /* populates the given GString with a tree representation of a branch given by node,
110 using indent spaces as initial indentation */
112 stats_tree_branch_to_str(const stat_node *node, GString *s, guint indent)
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];
123 format = g_strdup_printf(" %%s%%-%us%%12s %%12s %%12s\n",stats_tree_branch_max_namelen(node,0));
126 stats_tree_get_strs_from_node(node, value, rate, percent);
128 indent = indent > INDENT_MAX ? INDENT_MAX : indent;
130 /* fill indentation with indent spaces */
133 indentation[i++] = ' ';
136 indentation[i++] = '\0';
138 g_string_append_printf(s,format,
139 indentation,node->name,value,rate,percent);
141 if (node->children) {
142 for (child = node->children; child; child = child->next ) {
143 stats_tree_branch_to_str(child,s,indent+1);
153 /* frees the resources allocated by a stat_tree node */
155 free_stat_node(stat_node *node)
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 */
164 free_stat_node(child);
168 if(node->st->cfg->free_node_pr) node->st->cfg->free_node_pr(node);
170 if (node->hash) g_hash_table_destroy(node->hash);
172 if (node->rng) g_free(node->rng);
174 if (node->name) g_free(node->name);
179 /* destroys the whole tree instance */
181 stats_tree_free(stats_tree *st)
187 g_hash_table_destroy(st->names);
188 g_ptr_array_free(st->parents,TRUE);
190 for (child = st->root.children; child; child = next ) {
191 /* child->next will be gone after free_stat_node, so cache it here */
193 free_stat_node(child);
196 if (st->cfg->free_tree_pr)
197 st->cfg->free_tree_pr(st);
199 if (st->cfg->cleanup)
200 st->cfg->cleanup(st);
206 /* reset a node to its original state */
208 reset_stat_node(stat_node *node)
212 if (node->children) {
213 for (child = node->children; child; child = child->next )
214 reset_stat_node(child);
219 if(node->st->cfg->reset_node) {
220 node->st->cfg->reset_node(node);
225 /* reset the whole stats_tree */
227 stats_tree_reset(void *p)
234 reset_stat_node(&st->root);
236 if (st->cfg->reset_tree) {
237 st->cfg->reset_tree(st);
242 stats_tree_reinit(void *p)
248 for (child = st->root.children; child; child = next) {
249 /* child->next will be gone after free_stat_node, so cache it here */
251 free_stat_node(child);
254 st->root.children = NULL;
255 st->root.counter = 0;
262 /* register a new stats_tree */
264 stats_tree_register_with_group(const char *tapname, const char *abbr, const char *name,
265 stat_tree_packet_cb packet, stat_tree_init_cb init,
266 stat_tree_cleanup_cb cleanup, register_stat_group_t stat_group)
269 stats_tree_cfg *cfg = g_malloc( sizeof(stats_tree_cfg) );
271 /* at the very least the abbrev and the packet function should be given */
272 g_assert( tapname && abbr && packet );
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;
279 cfg->packet = packet;
281 cfg->cleanup = cleanup;
283 /* these have to be filled in by implementations */
284 cfg->setup_node_pr = NULL;
285 cfg->new_tree_pr = NULL;
286 cfg->free_node_pr = NULL;
287 cfg->free_tree_pr = NULL;
288 cfg->draw_node = NULL;
289 cfg->draw_tree = NULL;
290 cfg->reset_node = NULL;
291 cfg->reset_tree = NULL;
293 if (!registry) registry = g_hash_table_new(g_str_hash,g_str_equal);
295 g_hash_table_insert(registry,cfg->abbr,cfg);
299 /* register a new stats_tree with default group REGISTER_STAT_GROUP_UNSORTED */
301 stats_tree_register(const char *tapname, const char *abbr, const char *name,
302 stat_tree_packet_cb packet, stat_tree_init_cb init,
303 stat_tree_cleanup_cb cleanup)
305 stats_tree_register_with_group(tapname, abbr, name,
307 cleanup, REGISTER_STAT_GROUP_UNSORTED);
311 stats_tree_new(stats_tree_cfg *cfg, tree_pres *pr, char *filter)
313 stats_tree *st = g_malloc(sizeof(stats_tree));
318 st->names = g_hash_table_new(g_str_hash,g_str_equal);
319 st->parents = g_ptr_array_new();
320 st->filter = g_strdup(filter);
325 st->root.counter = 0;
326 st->root.name = g_strdup(cfg->name);
328 st->root.parent = NULL;
329 st->root.children = NULL;
330 st->root.next = NULL;
331 st->root.hash = NULL;
334 g_ptr_array_add(st->parents,&st->root);
339 /* will be the tap packet cb */
341 stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *pri)
344 double now = nstime_to_msec(&pinfo->fd->rel_ts);
346 if (st->start < 0.0) st->start = now;
348 st->elapsed = now - st->start;
351 return st->cfg->packet(st,pinfo,edt,pri);
356 extern stats_tree_cfg*
357 stats_tree_get_cfg_by_abbr(char *abbr)
359 return g_hash_table_lookup(registry,abbr);
363 struct _stats_tree_pres_cbs {
364 void (*setup_node_pr)(stat_node*);
365 void (*free_node_pr)(stat_node*);
366 void (*draw_node)(stat_node*);
367 void (*reset_node)(stat_node*);
368 tree_pres *(*new_tree_pr)(stats_tree*);
369 void (*free_tree_pr)(stats_tree*);
370 void (*draw_tree)(stats_tree*);
371 void (*reset_tree)(stats_tree*);
375 setup_tree_presentation(gpointer k _U_, gpointer v, gpointer p)
377 stats_tree_cfg *cfg = v;
378 struct _stats_tree_pres_cbs *d = p;
381 cfg->setup_node_pr = d->setup_node_pr;
382 cfg->new_tree_pr = d->new_tree_pr;
383 cfg->free_node_pr = d->free_node_pr;
384 cfg->free_tree_pr = d->free_tree_pr;
385 cfg->draw_node = d->draw_node;
386 cfg->draw_tree = d->draw_tree;
387 cfg->reset_node = d->reset_node;
388 cfg->reset_tree = d->reset_tree;
393 stats_tree_presentation(void (*registry_iterator)(gpointer,gpointer,gpointer),
394 void (*setup_node_pr)(stat_node*),
395 void (*free_node_pr)(stat_node*),
396 void (*draw_node)(stat_node*),
397 void (*reset_node)(stat_node*),
398 tree_pres *(*new_tree_pr)(stats_tree*),
399 void (*free_tree_pr)(stats_tree*),
400 void (*draw_tree)(stats_tree*),
401 void (*reset_tree)(stats_tree*),
404 static struct _stats_tree_pres_cbs d;
406 d.setup_node_pr = setup_node_pr;
407 d.new_tree_pr = new_tree_pr;
408 d.free_node_pr = free_node_pr;
409 d.free_tree_pr = free_tree_pr;
410 d.draw_node = draw_node;
411 d.draw_tree = draw_tree;
412 d.reset_node = reset_node;
413 d.reset_tree = reset_tree;
415 if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d);
417 if (registry_iterator && registry)
418 g_hash_table_foreach(registry,registry_iterator,data);
423 /* creates a stat_tree node
424 * name: the name of the stats_tree node
425 * parent_name: the name of the ALREADY REGISTERED parent
426 * with_hash: whether or not it should keep a hash with it's children names
427 * as_named_node: whether or not it has to be registered in the root namespace
430 new_stat_node(stats_tree *st, const gchar *name, int parent_id,
431 gboolean with_hash, gboolean as_parent_node)
434 stat_node *node = g_malloc (sizeof(stat_node));
435 stat_node *last_chld = NULL;
438 node->name = g_strdup(name);
439 node->children = NULL;
441 node->st = (stats_tree*) st;
442 node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL;
446 if (as_parent_node) {
447 g_hash_table_insert(st->names,
451 g_ptr_array_add(st->parents,node);
453 node->id = st->parents->len - 1;
458 if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
459 node->parent = g_ptr_array_index(st->parents,parent_id);
461 /* ??? should we set the parent to be root ??? */
462 g_assert_not_reached();
465 if (node->parent->children) {
466 /* insert as last child */
468 for (last_chld = node->parent->children;
470 last_chld = last_chld->next ) ;
472 last_chld->next = node;
475 /* insert as first child */
476 node->parent->children = node;
479 if(node->parent->hash) {
480 g_hash_table_insert(node->parent->hash,node->name,node);
483 if (st->cfg->setup_node_pr) {
484 st->cfg->setup_node_pr(node);
494 stats_tree_create_node(stats_tree *st, const gchar *name, int parent_id, gboolean with_hash)
496 stat_node *node = new_stat_node(st,name,parent_id,with_hash,TRUE);
504 /* XXX: should this be a macro? */
506 stats_tree_create_node_by_pname(stats_tree *st, const gchar *name,
507 const gchar *parent_name, gboolean with_children)
509 return stats_tree_create_node(st,name,stats_tree_parent_id_by_name(st,parent_name),with_children);
515 * Increases by delta the counter of the node whose name is given
516 * if the node does not exist yet it's created (with counter=1)
517 * using parent_name as parent node.
518 * with_hash=TRUE to indicate that the created node will have a parent
521 stats_tree_manip_node(manip_node_mode mode, stats_tree *st, const char *name,
522 int parent_id, gboolean with_hash, gint value)
524 stat_node *node = NULL;
525 stat_node *parent = NULL;
527 g_assert( parent_id >= 0 && parent_id < (int) st->parents->len );
529 parent = g_ptr_array_index(st->parents,parent_id);
532 node = g_hash_table_lookup(parent->hash,name);
534 node = g_hash_table_lookup(st->names,name);
538 node = new_stat_node(st,name,parent_id,with_hash,with_hash);
541 case MN_INCREASE: node->counter += value; break;
542 case MN_SET: node->counter = value; break;
553 stats_tree_get_abbr(const char *optarg)
557 /* XXX: this fails when tshark is given any options
559 g_assert(optarg != NULL);
561 for (i=0; optarg[i] && optarg[i] != ','; i++);
563 if (optarg[i] == ',') {
564 return g_strndup(optarg,i);
572 * This function accepts an input string which should define a long integer range.
573 * The normal result is a struct containing the floor and ceil value of this
576 * It is allowed to define a range string in the following ways :
578 * "0-10" -> { 0, 10 }
579 * "-0" -> { G_MININT, 0 }
580 * "0-" -> { 0, G_MAXINT }
581 * "-" -> { G_MININT, G_MAXINT }
583 * Note that this function is robust to buggy input string. If in some cases it
584 * returns NULL, it but may also return a pair with undefined values.
588 get_range(char *rngstr)
593 split = g_strsplit((gchar*)rngstr,"-",2);
596 if (split[0] == NULL) {
601 /* means we have a non empty string
602 * which does not contain a delimiter */
603 if (split[1] == NULL) {
608 rng = g_malloc(sizeof(range_pair_t));
610 /* string == "X-?" */
611 if (*(split[0]) != '\0') {
612 rng->floor = strtol(split[0],NULL,10);
615 rng->floor = G_MININT;
618 if (*(split[1]) != '\0') {
619 rng->ceil = strtol(split[1],NULL,10);
622 rng->ceil = G_MAXINT;
631 stats_tree_create_range_node(stats_tree *st, const gchar *name, int parent_id, ...)
635 stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
636 stat_node *range_node = NULL;
638 va_start( list, parent_id );
639 while (( curr_range = va_arg(list, gchar*) )) {
640 range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
641 range_node->rng = get_range(curr_range);
650 stats_tree_parent_id_by_name(stats_tree *st, const gchar *parent_name)
652 stat_node *node = g_hash_table_lookup(st->names,parent_name);
657 return 0; /* XXX: this is the root shoud we return -1 instead?*/
662 stats_tree_range_node_with_pname(stats_tree *st, const gchar *name,
663 const gchar *parent_name, ...)
667 stat_node *range_node = NULL;
668 int parent_id = stats_tree_parent_id_by_name(st,parent_name);
669 stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
671 va_start( list, parent_name );
672 while (( curr_range = va_arg(list, gchar*) )) {
673 range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
674 range_node->rng = get_range(curr_range);
683 stats_tree_tick_range(stats_tree *st, const gchar *name, int parent_id,
687 stat_node *node = NULL;
688 stat_node *parent = NULL;
689 stat_node *child = NULL;
692 if (parent_id >= 0 && parent_id < (int) st->parents->len) {
693 parent = g_ptr_array_index(st->parents,parent_id);
695 g_assert_not_reached();
699 node = g_hash_table_lookup(parent->hash,name);
701 node = g_hash_table_lookup(st->names,name);
705 g_assert_not_reached();
707 for ( child = node->children; child; child = child->next) {
708 floor = child->rng->floor;
709 ceil = child->rng->ceil;
711 if ( value_in_range >= floor && value_in_range <= ceil ) {
721 stats_tree_create_pivot(stats_tree *st, const gchar *name, int parent_id)
723 stat_node *node = new_stat_node(st,name,parent_id,TRUE,TRUE);
732 stats_tree_create_pivot_by_pname(stats_tree *st, const gchar *name,
733 const gchar *parent_name)
735 int parent_id = stats_tree_parent_id_by_name(st,parent_name);
738 node = new_stat_node(st,name,parent_id,TRUE,TRUE);
747 stats_tree_tick_pivot(stats_tree *st, int pivot_id, const gchar *pivot_value)
750 stat_node *parent = g_ptr_array_index(st->parents,pivot_id);
753 stats_tree_manip_node( MN_INCREASE, st, pivot_value, pivot_id, FALSE, 1);