wireshark/epan/stats_tree.c
Guy Harris 8ed7a73e22 Fix a bunch of warnings.
Cast away some implicit 64-bit-to-32-bit conversion errors due to use of
sizeof.

Cast away some implicit 64-bit-to-32-bit conversion errors due to use of
strtol() and strtoul().

Change some data types to avoid those implicit conversion warnings.

When assigning a constant to a float, make sure the constant isn't a
double, by appending "f" to the constant.

Constify a bunch of variables, parameters, and return values to
eliminate warnings due to strings being given const qualifiers.  Cast
away those warnings in some cases where an API we don't control forces
us to do so.

Enable a bunch of additional warnings by default.  Note why at least
some of the other warnings aren't enabled.

randpkt.c and text2pcap.c are used to build programs, so they don't need
to be in EXTRA_DIST.

If the user specifies --enable-warnings-as-errors, add -Werror *even if
the user specified --enable-extra-gcc-flags; assume they know what
they're doing and are willing to have the compile fail due to the extra
GCC warnings being treated as errors.

svn path=/trunk/; revision=46748
2012-12-26 05:57:06 +00:00

798 lines
18 KiB
C

/* stats_tree.c
* API for a counter tree for Wireshark
* 2004, Luis E. G. Ontanon
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* 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
* of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <glib.h>
#include <epan/stats_tree_priv.h>
#include <string.h>
#include "stats_tree.h"
/*
TODO:
- sort out the sorting issue
*/
/* used to contain the registered stat trees */
static GHashTable *registry = NULL;
/* writes into the buffers pointed by value, rate and percent
the string representations of a node*/
extern void
stats_tree_get_strs_from_node(const stat_node *node, gchar *value, gchar *rate, gchar *percent)
{
float f;
if (value) g_snprintf(value,NUM_BUF_SIZE,"%u",node->counter);
if (rate) {
*rate = '\0';
if (node->st->elapsed > 0.0) {
f = ((float)node->counter) / (float)node->st->elapsed;
g_snprintf(rate,NUM_BUF_SIZE,"%f",f);
}
}
if (percent) {
*percent = '\0';
if (node->parent->counter > 0) {
f = (float)(((float)node->counter * 100.0) / node->parent->counter);
g_snprintf(percent,NUM_BUF_SIZE,"%.2f%%",f);
}
}
}
/* a text representation of a node
if buffer is NULL returns a newly allocated string */
extern gchar*
stats_tree_node_to_str(const stat_node *node, gchar *buffer, guint len)
{
if (buffer) {
g_snprintf(buffer,len,"%s: %i",node->name, node->counter);
return buffer;
} else {
return g_strdup_printf("%s: %i",node->name, node->counter);
}
}
extern guint
stats_tree_branch_max_namelen(const stat_node *node, guint indent)
{
stat_node *child;
guint maxlen = 0;
guint len;
indent = indent > INDENT_MAX ? INDENT_MAX : indent;
if (node->children) {
for (child = node->children; child; child = child->next ) {
len = stats_tree_branch_max_namelen(child,indent+1);
maxlen = len > maxlen ? len : maxlen;
}
}
len = (guint) strlen(node->name) + indent;
maxlen = len > maxlen ? len : maxlen;
return maxlen;
}
static gchar *format;
/* populates the given GString with a tree representation of a branch given by node,
using indent spaces as initial indentation */
extern void
stats_tree_branch_to_str(const stat_node *node, GString *s, guint indent)
{
stat_node *child;
static gchar indentation[INDENT_MAX+1];
static gchar value[NUM_BUF_SIZE];
static gchar rate[NUM_BUF_SIZE];
static gchar percent[NUM_BUF_SIZE];
guint i = 0;
if (indent == 0) {
format = g_strdup_printf(" %%s%%-%us%%12s %%12s %%12s\n",stats_tree_branch_max_namelen(node,0));
}
stats_tree_get_strs_from_node(node, value, rate, percent);
indent = indent > INDENT_MAX ? INDENT_MAX : indent;
/* fill indentation with indent spaces */
if (indent > 0) {
while(i<indent)
indentation[i++] = ' ';
}
indentation[i] = '\0';
g_string_append_printf(s,format,
indentation,node->name,value,rate,percent);
if (node->children) {
for (child = node->children; child; child = child->next ) {
stats_tree_branch_to_str(child,s,indent+1);
}
}
if (indent == 0) {
g_free(format);
}
}
/* frees the resources allocated by a stat_tree node */
static void
free_stat_node(stat_node *node)
{
stat_node *child;
stat_node *next;
if (node->children) {
for (child = node->children; child; child = next ) {
/* child->next will be gone after free_stat_node, so cache it here */
next = child->next;
free_stat_node(child);
}
}
if(node->st->cfg->free_node_pr) node->st->cfg->free_node_pr(node);
if (node->hash) g_hash_table_destroy(node->hash);
g_free(node->rng);
g_free(node->name);
g_free(node);
}
/* destroys the whole tree instance */
extern void
stats_tree_free(stats_tree *st)
{
stat_node *child;
stat_node *next;
g_free(st->filter);
g_hash_table_destroy(st->names);
g_ptr_array_free(st->parents,TRUE);
for (child = st->root.children; child; child = next ) {
/* child->next will be gone after free_stat_node, so cache it here */
next = child->next;
free_stat_node(child);
}
if (st->cfg->free_tree_pr)
st->cfg->free_tree_pr(st);
if (st->cfg->cleanup)
st->cfg->cleanup(st);
g_free(st);
}
/* reset a node to its original state */
static void
reset_stat_node(stat_node *node)
{
stat_node *child;
if (node->children) {
for (child = node->children; child; child = child->next )
reset_stat_node(child);
}
node->counter = 0;
if(node->st->cfg->reset_node) {
node->st->cfg->reset_node(node);
}
}
/* reset the whole stats_tree */
extern void
stats_tree_reset(void *p)
{
stats_tree *st = p;
st->start = -1.0;
st->elapsed = 0.0;
reset_stat_node(&st->root);
if (st->cfg->reset_tree) {
st->cfg->reset_tree(st);
}
}
extern void
stats_tree_reinit(void *p)
{
stats_tree *st = p;
stat_node *child;
stat_node *next;
for (child = st->root.children; child; child = next) {
/* child->next will be gone after free_stat_node, so cache it here */
next = child->next;
free_stat_node(child);
}
st->root.children = NULL;
st->root.counter = 0;
if (st->cfg->init) {
st->cfg->init(st);
}
}
/* register a new stats_tree */
extern void
stats_tree_register_with_group(const char *tapname, const char *abbr, const char *name,
guint flags,
stat_tree_packet_cb packet, stat_tree_init_cb init,
stat_tree_cleanup_cb cleanup, register_stat_group_t stat_group)
{
stats_tree_cfg *cfg = g_malloc( sizeof(stats_tree_cfg) );
/* at the very least the abbrev and the packet function should be given */
g_assert( tapname && abbr && packet );
cfg->plugin = FALSE;
cfg->tapname = g_strdup(tapname);
cfg->abbr = g_strdup(abbr);
cfg->name = name ? g_strdup(name) : g_strdup(abbr);
cfg->stat_group = stat_group;
cfg->packet = packet;
cfg->init = init;
cfg->cleanup = cleanup;
cfg->flags = flags;
/* these have to be filled in by implementations */
cfg->setup_node_pr = NULL;
cfg->new_tree_pr = NULL;
cfg->free_node_pr = NULL;
cfg->free_tree_pr = NULL;
cfg->draw_node = NULL;
cfg->draw_tree = NULL;
cfg->reset_node = NULL;
cfg->reset_tree = NULL;
if (!registry) registry = g_hash_table_new(g_str_hash,g_str_equal);
g_hash_table_insert(registry,cfg->abbr,cfg);
}
/* register a new stats_tree with default group REGISTER_STAT_GROUP_UNSORTED */
extern void
stats_tree_register(const char *tapname, const char *abbr, const char *name,
guint flags,
stat_tree_packet_cb packet, stat_tree_init_cb init,
stat_tree_cleanup_cb cleanup)
{
stats_tree_register_with_group(tapname, abbr, name,
flags,
packet, init,
cleanup, REGISTER_STAT_GROUP_UNSORTED);
}
/* register a new stat_tree with default group REGISTER_STAT_GROUP_UNSORTED from a plugin */
extern void
stats_tree_register_plugin(const char *tapname, const char *abbr, const char *name,
guint flags,
stat_tree_packet_cb packet, stat_tree_init_cb init,
stat_tree_cleanup_cb cleanup)
{
stats_tree_cfg *cfg;
stats_tree_register(tapname, abbr, name,
flags,
packet, init,
cleanup);
cfg = stats_tree_get_cfg_by_abbr((char*)abbr);
cfg->plugin = TRUE;
}
extern stats_tree*
stats_tree_new(stats_tree_cfg *cfg, tree_pres *pr, const char *filter)
{
stats_tree *st = g_malloc(sizeof(stats_tree));
st->cfg = cfg;
st->pr = pr;
st->names = g_hash_table_new(g_str_hash,g_str_equal);
st->parents = g_ptr_array_new();
st->filter = g_strdup(filter);
st->start = -1.0;
st->elapsed = 0.0;
st->root.counter = 0;
st->root.name = g_strdup(cfg->name);
st->root.st = st;
st->root.parent = NULL;
st->root.children = NULL;
st->root.next = NULL;
st->root.hash = NULL;
st->root.pr = NULL;
g_ptr_array_add(st->parents,&st->root);
return st;
}
/* will be the tap packet cb */
extern int
stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *pri)
{
stats_tree *st = p;
double now = nstime_to_msec(&pinfo->fd->rel_ts);
if (st->start < 0.0) st->start = now;
st->elapsed = now - st->start;
if (st->cfg->packet)
return st->cfg->packet(st,pinfo,edt,pri);
else
return 0;
}
extern stats_tree_cfg*
stats_tree_get_cfg_by_abbr(char *abbr)
{
return g_hash_table_lookup(registry,abbr);
}
extern GList*
stats_tree_get_cfg_list(void)
{
return g_hash_table_get_values(registry);
}
struct _stats_tree_pres_cbs {
void (*setup_node_pr)(stat_node*);
void (*free_node_pr)(stat_node*);
void (*draw_node)(stat_node*);
void (*reset_node)(stat_node*);
tree_pres *(*new_tree_pr)(stats_tree*);
void (*free_tree_pr)(stats_tree*);
void (*draw_tree)(stats_tree*);
void (*reset_tree)(stats_tree*);
};
static void
setup_tree_presentation(gpointer k _U_, gpointer v, gpointer p)
{
stats_tree_cfg *cfg = v;
struct _stats_tree_pres_cbs *d = p;
cfg->in_use = FALSE;
cfg->setup_node_pr = d->setup_node_pr;
cfg->new_tree_pr = d->new_tree_pr;
cfg->free_node_pr = d->free_node_pr;
cfg->free_tree_pr = d->free_tree_pr;
cfg->draw_node = d->draw_node;
cfg->draw_tree = d->draw_tree;
cfg->reset_node = d->reset_node;
cfg->reset_tree = d->reset_tree;
}
extern void
stats_tree_presentation(void (*registry_iterator)(gpointer,gpointer,gpointer),
void (*setup_node_pr)(stat_node*),
void (*free_node_pr)(stat_node*),
void (*draw_node)(stat_node*),
void (*reset_node)(stat_node*),
tree_pres *(*new_tree_pr)(stats_tree*),
void (*free_tree_pr)(stats_tree*),
void (*draw_tree)(stats_tree*),
void (*reset_tree)(stats_tree*),
void *data)
{
static struct _stats_tree_pres_cbs d;
d.setup_node_pr = setup_node_pr;
d.new_tree_pr = new_tree_pr;
d.free_node_pr = free_node_pr;
d.free_tree_pr = free_tree_pr;
d.draw_node = draw_node;
d.draw_tree = draw_tree;
d.reset_node = reset_node;
d.reset_tree = reset_tree;
if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d);
if (registry_iterator && registry)
g_hash_table_foreach(registry,registry_iterator,data);
}
/* creates a stat_tree node
* name: the name of the stats_tree node
* parent_name: the name of the ALREADY REGISTERED parent
* with_hash: whether or not it should keep a hash with it's children names
* as_named_node: whether or not it has to be registered in the root namespace
*/
static stat_node*
new_stat_node(stats_tree *st, const gchar *name, int parent_id,
gboolean with_hash, gboolean as_parent_node)
{
stat_node *node = g_malloc (sizeof(stat_node));
stat_node *last_chld = NULL;
node->counter = 0;
node->name = g_strdup(name);
node->children = NULL;
node->next = NULL;
node->st = (stats_tree*) st;
node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL;
node->parent = NULL;
node->rng = NULL;
if (as_parent_node) {
g_hash_table_insert(st->names,
node->name,
node);
g_ptr_array_add(st->parents,node);
node->id = st->parents->len - 1;
} else {
node->id = -1;
}
if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
node->parent = g_ptr_array_index(st->parents,parent_id);
} else {
/* ??? should we set the parent to be root ??? */
g_assert_not_reached();
}
if (node->parent->children) {
/* insert as last child */
for (last_chld = node->parent->children;
last_chld->next;
last_chld = last_chld->next ) ;
last_chld->next = node;
} else {
/* insert as first child */
node->parent->children = node;
}
if(node->parent->hash) {
g_hash_table_insert(node->parent->hash,node->name,node);
}
if (st->cfg->setup_node_pr) {
st->cfg->setup_node_pr(node);
} else {
node->pr = NULL;
}
return node;
}
/***/
extern int
stats_tree_create_node(stats_tree *st, const gchar *name, int parent_id, gboolean with_hash)
{
stat_node *node = new_stat_node(st,name,parent_id,with_hash,TRUE);
if (node)
return node->id;
else
return 0;
}
/* XXX: should this be a macro? */
extern int
stats_tree_create_node_by_pname(stats_tree *st, const gchar *name,
const gchar *parent_name, gboolean with_children)
{
return stats_tree_create_node(st,name,stats_tree_parent_id_by_name(st,parent_name),with_children);
}
/*
* Increases by delta the counter of the node whose name is given
* if the node does not exist yet it's created (with counter=1)
* using parent_name as parent node.
* with_hash=TRUE to indicate that the created node will have a parent
*/
extern int
stats_tree_manip_node(manip_node_mode mode, stats_tree *st, const char *name,
int parent_id, gboolean with_hash, gint value)
{
stat_node *node = NULL;
stat_node *parent = NULL;
g_assert( parent_id >= 0 && parent_id < (int) st->parents->len );
parent = g_ptr_array_index(st->parents,parent_id);
if( parent->hash ) {
node = g_hash_table_lookup(parent->hash,name);
} else {
node = g_hash_table_lookup(st->names,name);
}
if ( node == NULL )
node = new_stat_node(st,name,parent_id,with_hash,with_hash);
switch (mode) {
case MN_INCREASE: node->counter += value; break;
case MN_SET: node->counter = value; break;
}
if (node)
return node->id;
else
return -1;
}
extern char*
stats_tree_get_abbr(const char *optarg)
{
guint i;
/* XXX: this fails when tshark is given any options
after the -z */
g_assert(optarg != NULL);
for (i=0; optarg[i] && optarg[i] != ','; i++);
if (optarg[i] == ',') {
return g_strndup(optarg,i);
} else {
return NULL;
}
}
/*
* This function accepts an input string which should define a long integer range.
* The normal result is a struct containing the floor and ceil value of this
* range.
*
* It is allowed to define a range string in the following ways :
*
* "0-10" -> { 0, 10 }
* "-0" -> { G_MININT, 0 }
* "0-" -> { 0, G_MAXINT }
* "-" -> { G_MININT, G_MAXINT }
*
* Note that this function is robust to buggy input string. If in some cases it
* returns NULL, it but may also return a pair with undefined values.
*
*/
static range_pair_t*
get_range(char *rngstr)
{
gchar **split;
range_pair_t *rng;
split = g_strsplit((gchar*)rngstr,"-",2);
/* empty string */
if (split[0] == NULL) {
g_strfreev(split);
return NULL;
}
/* means we have a non empty string
* which does not contain a delimiter */
if (split[1] == NULL) {
g_strfreev(split);
return NULL;
}
rng = g_malloc(sizeof(range_pair_t));
/* string == "X-?" */
if (*(split[0]) != '\0') {
rng->floor = (gint)strtol(split[0],NULL,10);
} else
/* string == "-?" */
rng->floor = G_MININT;
/* string != "?-" */
if (*(split[1]) != '\0') {
rng->ceil = (gint)strtol(split[1],NULL,10);
} else
/* string == "?-" */
rng->ceil = G_MAXINT;
g_strfreev(split);
return rng;
}
extern int
stats_tree_create_range_node(stats_tree *st, const gchar *name, int parent_id, ...)
{
va_list list;
gchar *curr_range;
stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
stat_node *range_node = NULL;
va_start( list, parent_id );
while (( curr_range = va_arg(list, gchar*) )) {
range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
range_node->rng = get_range(curr_range);
}
va_end( list );
return rng_root->id;
}
extern int
stats_tree_create_range_node_string(stats_tree *st, const gchar *name,
int parent_id, int num_str_ranges,
gchar** str_ranges)
{
int i;
stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
stat_node *range_node = NULL;
for (i = 0; i < num_str_ranges; i++) {
range_node = new_stat_node(st, str_ranges[i], rng_root->id, FALSE, FALSE);
range_node->rng = get_range(str_ranges[i]);
}
return rng_root->id;
}
/****/
extern int
stats_tree_parent_id_by_name(stats_tree *st, const gchar *parent_name)
{
stat_node *node = g_hash_table_lookup(st->names,parent_name);
if (node)
return node->id;
else
return 0; /* XXX: this is the root shoud we return -1 instead?*/
}
extern int
stats_tree_range_node_with_pname(stats_tree *st, const gchar *name,
const gchar *parent_name, ...)
{
va_list list;
gchar *curr_range;
stat_node *range_node = NULL;
int parent_id = stats_tree_parent_id_by_name(st,parent_name);
stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
va_start( list, parent_name );
while (( curr_range = va_arg(list, gchar*) )) {
range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
range_node->rng = get_range(curr_range);
}
va_end( list );
return rng_root->id;
}
extern int
stats_tree_tick_range(stats_tree *st, const gchar *name, int parent_id,
int value_in_range)
{
stat_node *node = NULL;
stat_node *parent = NULL;
stat_node *child = NULL;
gint floor, ceil;
if (parent_id >= 0 && parent_id < (int) st->parents->len) {
parent = g_ptr_array_index(st->parents,parent_id);
} else {
g_assert_not_reached();
}
if( parent->hash ) {
node = g_hash_table_lookup(parent->hash,name);
} else {
node = g_hash_table_lookup(st->names,name);
}
if ( node == NULL )
g_assert_not_reached();
for ( child = node->children; child; child = child->next) {
floor = child->rng->floor;
ceil = child->rng->ceil;
if ( value_in_range >= floor && value_in_range <= ceil ) {
child->counter++;
return node->id;
}
}
return node->id;
}
extern int
stats_tree_create_pivot(stats_tree *st, const gchar *name, int parent_id)
{
stat_node *node = new_stat_node(st,name,parent_id,TRUE,TRUE);
if (node)
return node->id;
else
return 0;
}
extern int
stats_tree_create_pivot_by_pname(stats_tree *st, const gchar *name,
const gchar *parent_name)
{
int parent_id = stats_tree_parent_id_by_name(st,parent_name);
stat_node *node;
node = new_stat_node(st,name,parent_id,TRUE,TRUE);
if (node)
return node->id;
else
return 0;
}
extern int
stats_tree_tick_pivot(stats_tree *st, int pivot_id, const gchar *pivot_value)
{
stat_node *parent = g_ptr_array_index(st->parents,pivot_id);
parent->counter++;
stats_tree_manip_node( MN_INCREASE, st, pivot_value, pivot_id, FALSE, 1);
return pivot_id;
}