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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <epan/stats_tree_priv.h>
35 #include "stats_tree.h"
39 - sort out the sorting issue
43 /* used to contain the registered stat trees */
44 static GHashTable
*registry
= NULL
;
46 /* writes into the buffers pointed by value, rate and percent
47 the string representations of a node*/
49 stats_tree_get_strs_from_node(const stat_node
*node
, gchar
*value
, gchar
*rate
, gchar
*percent
)
53 if (value
) g_snprintf(value
,NUM_BUF_SIZE
,"%u",node
->counter
);
57 if (node
->st
->elapsed
> 0.0) {
58 f
= ((float)node
->counter
) / (float)node
->st
->elapsed
;
59 g_snprintf(rate
,NUM_BUF_SIZE
,"%f",f
);
65 if (node
->parent
->counter
> 0) {
66 f
= (float)(((float)node
->counter
* 100.0) / node
->parent
->counter
);
67 g_snprintf(percent
,NUM_BUF_SIZE
,"%.2f%%",f
);
73 /* a text representation of a node
74 if buffer is NULL returns a newly allocated string */
76 stats_tree_node_to_str(const stat_node
*node
, gchar
*buffer
, guint len
)
79 g_snprintf(buffer
,len
,"%s: %i",node
->name
, node
->counter
);
82 return g_strdup_printf("%s: %i",node
->name
, node
->counter
);
87 stats_tree_branch_max_namelen(const stat_node
*node
, guint indent
)
93 indent
= indent
> INDENT_MAX
? INDENT_MAX
: indent
;
96 for (child
= node
->children
; child
; child
= child
->next
) {
97 len
= stats_tree_branch_max_namelen(child
,indent
+1);
98 maxlen
= len
> maxlen
? len
: maxlen
;
102 len
= (guint
) strlen(node
->name
) + indent
;
103 maxlen
= len
> maxlen
? len
: maxlen
;
108 static gchar
*format
;
110 /* populates the given GString with a tree representation of a branch given by node,
111 using indent spaces as initial indentation */
113 stats_tree_branch_to_str(const stat_node
*node
, GString
*s
, guint indent
)
116 static gchar indentation
[INDENT_MAX
+1];
117 static gchar value
[NUM_BUF_SIZE
];
118 static gchar rate
[NUM_BUF_SIZE
];
119 static gchar percent
[NUM_BUF_SIZE
];
124 format
= g_strdup_printf(" %%s%%-%us%%12s %%12s %%12s\n",stats_tree_branch_max_namelen(node
,0));
127 stats_tree_get_strs_from_node(node
, value
, rate
, percent
);
129 indent
= indent
> INDENT_MAX
? INDENT_MAX
: indent
;
131 /* fill indentation with indent spaces */
134 indentation
[i
++] = ' ';
137 indentation
[i
] = '\0';
139 g_string_append_printf(s
,format
,
140 indentation
,node
->name
,value
,rate
,percent
);
142 if (node
->children
) {
143 for (child
= node
->children
; child
; child
= child
->next
) {
144 stats_tree_branch_to_str(child
,s
,indent
+1);
154 /* frees the resources allocated by a stat_tree node */
156 free_stat_node(stat_node
*node
)
161 if (node
->children
) {
162 for (child
= node
->children
; child
; child
= next
) {
163 /* child->next will be gone after free_stat_node, so cache it here */
165 free_stat_node(child
);
169 if(node
->st
->cfg
->free_node_pr
) node
->st
->cfg
->free_node_pr(node
);
171 if (node
->hash
) g_hash_table_destroy(node
->hash
);
178 /* destroys the whole tree instance */
180 stats_tree_free(stats_tree
*st
)
186 g_hash_table_destroy(st
->names
);
187 g_ptr_array_free(st
->parents
,TRUE
);
189 for (child
= st
->root
.children
; child
; child
= next
) {
190 /* child->next will be gone after free_stat_node, so cache it here */
192 free_stat_node(child
);
195 if (st
->cfg
->free_tree_pr
)
196 st
->cfg
->free_tree_pr(st
);
198 if (st
->cfg
->cleanup
)
199 st
->cfg
->cleanup(st
);
205 /* reset a node to its original state */
207 reset_stat_node(stat_node
*node
)
211 if (node
->children
) {
212 for (child
= node
->children
; child
; child
= child
->next
)
213 reset_stat_node(child
);
218 if(node
->st
->cfg
->reset_node
) {
219 node
->st
->cfg
->reset_node(node
);
224 /* reset the whole stats_tree */
226 stats_tree_reset(void *p
)
228 stats_tree
*st
= (stats_tree
*)p
;
233 reset_stat_node(&st
->root
);
235 if (st
->cfg
->reset_tree
) {
236 st
->cfg
->reset_tree(st
);
241 stats_tree_reinit(void *p
)
243 stats_tree
*st
= (stats_tree
*)p
;
247 for (child
= st
->root
.children
; child
; child
= next
) {
248 /* child->next will be gone after free_stat_node, so cache it here */
250 free_stat_node(child
);
253 st
->root
.children
= NULL
;
254 st
->root
.counter
= 0;
261 /* register a new stats_tree */
263 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
= (stats_tree_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
);
275 cfg
->tapname
= g_strdup(tapname
);
276 cfg
->abbr
= g_strdup(abbr
);
277 cfg
->name
= name
? g_strdup(name
) : g_strdup(abbr
);
278 cfg
->stat_group
= stat_group
;
280 cfg
->packet
= packet
;
282 cfg
->cleanup
= cleanup
;
286 /* these have to be filled in by implementations */
287 cfg
->setup_node_pr
= NULL
;
288 cfg
->new_tree_pr
= NULL
;
289 cfg
->free_node_pr
= NULL
;
290 cfg
->free_tree_pr
= NULL
;
291 cfg
->draw_node
= NULL
;
292 cfg
->draw_tree
= NULL
;
293 cfg
->reset_node
= NULL
;
294 cfg
->reset_tree
= NULL
;
296 if (!registry
) registry
= g_hash_table_new(g_str_hash
,g_str_equal
);
298 g_hash_table_insert(registry
,cfg
->abbr
,cfg
);
301 /* register a new stats_tree with default group REGISTER_STAT_GROUP_UNSORTED */
303 stats_tree_register(const char *tapname
, const char *abbr
, const char *name
,
305 stat_tree_packet_cb packet
, stat_tree_init_cb init
,
306 stat_tree_cleanup_cb cleanup
)
308 stats_tree_register_with_group(tapname
, abbr
, name
,
311 cleanup
, REGISTER_STAT_GROUP_UNSORTED
);
314 /* register a new stat_tree with default group REGISTER_STAT_GROUP_UNSORTED from a plugin */
316 stats_tree_register_plugin(const char *tapname
, const char *abbr
, const char *name
,
318 stat_tree_packet_cb packet
, stat_tree_init_cb init
,
319 stat_tree_cleanup_cb cleanup
)
323 stats_tree_register(tapname
, abbr
, name
,
327 cfg
= stats_tree_get_cfg_by_abbr((char*)abbr
);
332 stats_tree_new(stats_tree_cfg
*cfg
, tree_pres
*pr
, const char *filter
)
334 stats_tree
*st
= (stats_tree
*)g_malloc(sizeof(stats_tree
));
339 st
->names
= g_hash_table_new(g_str_hash
,g_str_equal
);
340 st
->parents
= g_ptr_array_new();
341 st
->filter
= g_strdup(filter
);
346 st
->root
.counter
= 0;
347 st
->root
.name
= g_strdup(cfg
->name
);
349 st
->root
.parent
= NULL
;
350 st
->root
.children
= NULL
;
351 st
->root
.next
= NULL
;
352 st
->root
.hash
= NULL
;
355 g_ptr_array_add(st
->parents
,&st
->root
);
360 /* will be the tap packet cb */
362 stats_tree_packet(void *p
, packet_info
*pinfo
, epan_dissect_t
*edt
, const void *pri
)
364 stats_tree
*st
= (stats_tree
*)p
;
365 double now
= nstime_to_msec(&pinfo
->rel_ts
);
367 if (st
->start
< 0.0) st
->start
= now
;
369 st
->elapsed
= now
- st
->start
;
372 return st
->cfg
->packet(st
,pinfo
,edt
,pri
);
377 extern stats_tree_cfg
*
378 stats_tree_get_cfg_by_abbr(char *abbr
)
380 return (stats_tree_cfg
*)g_hash_table_lookup(registry
,abbr
);
384 stats_tree_get_cfg_list(void)
386 return g_hash_table_get_values(registry
);
389 struct _stats_tree_pres_cbs
{
390 void (*setup_node_pr
)(stat_node
*);
391 void (*free_node_pr
)(stat_node
*);
392 void (*draw_node
)(stat_node
*);
393 void (*reset_node
)(stat_node
*);
394 tree_pres
*(*new_tree_pr
)(stats_tree
*);
395 void (*free_tree_pr
)(stats_tree
*);
396 void (*draw_tree
)(stats_tree
*);
397 void (*reset_tree
)(stats_tree
*);
401 setup_tree_presentation(gpointer k _U_
, gpointer v
, gpointer p
)
403 stats_tree_cfg
*cfg
= (stats_tree_cfg
*)v
;
404 struct _stats_tree_pres_cbs
*d
= (struct _stats_tree_pres_cbs
*)p
;
407 cfg
->setup_node_pr
= d
->setup_node_pr
;
408 cfg
->new_tree_pr
= d
->new_tree_pr
;
409 cfg
->free_node_pr
= d
->free_node_pr
;
410 cfg
->free_tree_pr
= d
->free_tree_pr
;
411 cfg
->draw_node
= d
->draw_node
;
412 cfg
->draw_tree
= d
->draw_tree
;
413 cfg
->reset_node
= d
->reset_node
;
414 cfg
->reset_tree
= d
->reset_tree
;
419 stats_tree_presentation(void (*registry_iterator
)(gpointer
,gpointer
,gpointer
),
420 void (*setup_node_pr
)(stat_node
*),
421 void (*free_node_pr
)(stat_node
*),
422 void (*draw_node
)(stat_node
*),
423 void (*reset_node
)(stat_node
*),
424 tree_pres
*(*new_tree_pr
)(stats_tree
*),
425 void (*free_tree_pr
)(stats_tree
*),
426 void (*draw_tree
)(stats_tree
*),
427 void (*reset_tree
)(stats_tree
*),
430 static struct _stats_tree_pres_cbs d
;
432 d
.setup_node_pr
= setup_node_pr
;
433 d
.new_tree_pr
= new_tree_pr
;
434 d
.free_node_pr
= free_node_pr
;
435 d
.free_tree_pr
= free_tree_pr
;
436 d
.draw_node
= draw_node
;
437 d
.draw_tree
= draw_tree
;
438 d
.reset_node
= reset_node
;
439 d
.reset_tree
= reset_tree
;
441 if (registry
) g_hash_table_foreach(registry
,setup_tree_presentation
,&d
);
443 if (registry_iterator
&& registry
)
444 g_hash_table_foreach(registry
,registry_iterator
,data
);
449 /* creates a stat_tree node
450 * name: the name of the stats_tree node
451 * parent_name: the name of the ALREADY REGISTERED parent
452 * with_hash: whether or not it should keep a hash with its children names
453 * as_named_node: whether or not it has to be registered in the root namespace
456 new_stat_node(stats_tree
*st
, const gchar
*name
, int parent_id
,
457 gboolean with_hash
, gboolean as_parent_node
)
460 stat_node
*node
= (stat_node
*)g_malloc (sizeof(stat_node
));
461 stat_node
*last_chld
= NULL
;
464 node
->name
= g_strdup(name
);
465 node
->children
= NULL
;
467 node
->st
= (stats_tree
*) st
;
468 node
->hash
= with_hash
? g_hash_table_new(g_str_hash
,g_str_equal
) : NULL
;
472 if (as_parent_node
) {
473 g_hash_table_insert(st
->names
,
477 g_ptr_array_add(st
->parents
,node
);
479 node
->id
= st
->parents
->len
- 1;
484 if (parent_id
>= 0 && parent_id
< (int) st
->parents
->len
) {
485 node
->parent
= (stat_node
*)g_ptr_array_index(st
->parents
,parent_id
);
487 /* ??? should we set the parent to be root ??? */
488 g_assert_not_reached();
491 if (node
->parent
->children
) {
492 /* insert as last child */
494 for (last_chld
= node
->parent
->children
;
496 last_chld
= last_chld
->next
) ;
498 last_chld
->next
= node
;
501 /* insert as first child */
502 node
->parent
->children
= node
;
505 if(node
->parent
->hash
) {
506 g_hash_table_insert(node
->parent
->hash
,node
->name
,node
);
509 if (st
->cfg
->setup_node_pr
) {
510 st
->cfg
->setup_node_pr(node
);
520 stats_tree_create_node(stats_tree
*st
, const gchar
*name
, int parent_id
, gboolean with_hash
)
522 stat_node
*node
= new_stat_node(st
,name
,parent_id
,with_hash
,TRUE
);
530 /* XXX: should this be a macro? */
532 stats_tree_create_node_by_pname(stats_tree
*st
, const gchar
*name
,
533 const gchar
*parent_name
, gboolean with_children
)
535 return stats_tree_create_node(st
,name
,stats_tree_parent_id_by_name(st
,parent_name
),with_children
);
541 * Increases by delta the counter of the node whose name is given
542 * if the node does not exist yet it's created (with counter=1)
543 * using parent_name as parent node.
544 * with_hash=TRUE to indicate that the created node will have a parent
547 stats_tree_manip_node(manip_node_mode mode
, stats_tree
*st
, const char *name
,
548 int parent_id
, gboolean with_hash
, gint value
)
550 stat_node
*node
= NULL
;
551 stat_node
*parent
= NULL
;
553 g_assert( parent_id
>= 0 && parent_id
< (int) st
->parents
->len
);
555 parent
= (stat_node
*)g_ptr_array_index(st
->parents
,parent_id
);
558 node
= (stat_node
*)g_hash_table_lookup(parent
->hash
,name
);
560 node
= (stat_node
*)g_hash_table_lookup(st
->names
,name
);
564 node
= new_stat_node(st
,name
,parent_id
,with_hash
,with_hash
);
567 case MN_INCREASE
: node
->counter
+= value
; break;
568 case MN_SET
: node
->counter
= value
; break;
579 stats_tree_get_abbr(const char *opt_arg
)
583 /* XXX: this fails when tshark is given any options
585 g_assert(opt_arg
!= NULL
);
587 for (i
=0; opt_arg
[i
] && opt_arg
[i
] != ','; i
++);
589 if (opt_arg
[i
] == ',') {
590 return g_strndup(opt_arg
,i
);
598 * This function accepts an input string which should define a long integer range.
599 * The normal result is a struct containing the floor and ceil value of this
602 * It is allowed to define a range string in the following ways :
604 * "0-10" -> { 0, 10 }
605 * "-0" -> { G_MININT, 0 }
606 * "0-" -> { 0, G_MAXINT }
607 * "-" -> { G_MININT, G_MAXINT }
609 * Note that this function is robust to buggy input string. If in some cases it
610 * returns NULL, it but may also return a pair with undefined values.
614 get_range(char *rngstr
)
619 split
= g_strsplit((gchar
*)rngstr
,"-",2);
622 if (split
[0] == NULL
) {
627 rng
= (range_pair_t
*)g_malloc(sizeof(range_pair_t
));
629 if (split
[1] == NULL
) {
630 /* means we have a non empty string with no delimiter
631 * so it must be a single number */
632 rng
->floor
= (gint
)strtol(split
[0],NULL
,10);
633 rng
->ceil
= rng
->floor
;
635 /* string == "X-?" */
636 if (*(split
[0]) != '\0') {
637 rng
->floor
= (gint
)strtol(split
[0],NULL
,10);
640 rng
->floor
= G_MININT
;
643 if (*(split
[1]) != '\0') {
644 rng
->ceil
= (gint
)strtol(split
[1],NULL
,10);
647 rng
->ceil
= G_MAXINT
;
656 stats_tree_create_range_node(stats_tree
*st
, const gchar
*name
, int parent_id
, ...)
660 stat_node
*rng_root
= new_stat_node(st
, name
, parent_id
, FALSE
, TRUE
);
661 stat_node
*range_node
= NULL
;
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
);
674 stats_tree_create_range_node_string(stats_tree
*st
, const gchar
*name
,
675 int parent_id
, int num_str_ranges
,
679 stat_node
*rng_root
= new_stat_node(st
, name
, parent_id
, FALSE
, TRUE
);
680 stat_node
*range_node
= NULL
;
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
]);
692 stats_tree_parent_id_by_name(stats_tree
*st
, const gchar
*parent_name
)
694 stat_node
*node
= (stat_node
*)g_hash_table_lookup(st
->names
,parent_name
);
699 return 0; /* XXX: this is the root shoud we return -1 instead?*/
704 stats_tree_range_node_with_pname(stats_tree
*st
, const gchar
*name
,
705 const gchar
*parent_name
, ...)
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
);
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
);
725 stats_tree_tick_range(stats_tree
*st
, const gchar
*name
, int parent_id
,
729 stat_node
*node
= NULL
;
730 stat_node
*parent
= NULL
;
731 stat_node
*child
= NULL
;
734 if (parent_id
>= 0 && parent_id
< (int) st
->parents
->len
) {
735 parent
= (stat_node
*)g_ptr_array_index(st
->parents
,parent_id
);
737 g_assert_not_reached();
741 node
= (stat_node
*)g_hash_table_lookup(parent
->hash
,name
);
743 node
= (stat_node
*)g_hash_table_lookup(st
->names
,name
);
747 g_assert_not_reached();
749 for ( child
= node
->children
; child
; child
= child
->next
) {
750 floor
= child
->rng
->floor
;
751 ceil
= child
->rng
->ceil
;
753 if ( value_in_range
>= floor
&& value_in_range
<= ceil
) {
763 stats_tree_create_pivot(stats_tree
*st
, const gchar
*name
, int parent_id
)
765 stat_node
*node
= new_stat_node(st
,name
,parent_id
,TRUE
,TRUE
);
774 stats_tree_create_pivot_by_pname(stats_tree
*st
, const gchar
*name
,
775 const gchar
*parent_name
)
777 int parent_id
= stats_tree_parent_id_by_name(st
,parent_name
);
780 node
= new_stat_node(st
,name
,parent_id
,TRUE
,TRUE
);
789 stats_tree_tick_pivot(stats_tree
*st
, int pivot_id
, const gchar
*pivot_value
)
792 stat_node
*parent
= (stat_node
*)g_ptr_array_index(st
->parents
,pivot_id
);
795 stats_tree_manip_node( MN_INCREASE
, st
, pivot_value
, pivot_id
, FALSE
, 1);