Ethereal-dev: [Ethereal-dev] A new counters tree window, and some implementations (IP, HTTP, I

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

Date: Wed, 5 Jan 2005 14:03:42 +0100
Hi,

As a byproduct of some work I'm doing on HTTP I've written a counters
tree window interface, it is very straightforward to implement, as a
matter of fact as once I was done with HTTP, it took me just minutes
to write the ISUP and the IP implementations .

Attached you will find:
- gtk/gtk_stats_tree.[ch] the code for the stats_tree tap-window
- gtk/ip_stat.c the stats window for IP
- a patch to gtk/isup_stat.c (which replaces thee old window with the newer one)
- a patch to gtk/http_stat.c and packet-http.[ch]
- three snapshots to the stat windows I've made

There are still some things to do:
I don't know if it runs under GTK1. 
Being this my first GTK widget I do not know If I've got all straight.
Do I need to destroy myself the tree and other GTK "items" that belong
to the window or they are freed as the window is destroyed?

However being so simple to use (take a look at ip_stat.c and see it
yourselves) I think is worth a try.

Cheers,
Luis

Attachment: ip_stat.png
Description: PNG image

Attachment: http_stat.png
Description: PNG image

Attachment: isup_stat.png
Description: PNG image

Attachment: http-statistics.patch
Description: Binary data

Attachment: isup_stat.c.patch
Description: Binary data

/* gtk_stat_tree.c
 * a gtk based conter tree for ethereal
 * 2004, Luis E. G. Ontanon
 * based on http_stat.c by Jean-Michel FAYARD
 *
 * $Id: $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */


#include "gtk_stats_tree.h"

typedef struct _stat_node stat_node;

struct _stat_node {
	guint			counter;
	gchar*			name;
	GtkTreeIter*	iter;
	stats_tree*		st;
	GHashTable*		hash;

	stat_node*	parent;
	stat_node*	children;
	stat_node*	next;
};

struct _stats_tree {
	guint8*			abbr;
	guint8*			name;
	char*			filter;
	
	GHashTable*		names;
	GString*		text;
	float			start;
	float			elapsed;
	stat_node		root;	
	guint			indent;
	
	GtkWidget*		win;
	GtkTreeStore*   store;
	GtkWidget*		tree;
	
};

enum {
	COUNT_COLUMN,
	RATE_COLUMN,
	TITLE_COLUMN,
	PERCENT_COLUMN,
	N_COLUMNS
};

#define NUM_BUF_SIZE  32

static stat_node*  setup_stat_node(const stats_tree* st,
								   const gchar* name,
								   const gchar* parent_name,
								   gboolean with_hash,
								   gboolean as_named_node) {
	stat_node *node = g_malloc (sizeof(stat_node));
	stat_node* last_chld = NULL;
	GtkTreeIter* parent = NULL;
	
	node->counter = 0;
	node->name = g_strdup(name);
	node->iter = g_malloc(sizeof(GtkTreeIter));
	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;
	
	if (as_named_node)
		g_hash_table_insert(st->names,node->name,node);

	if (parent_name == NULL) 
		node->parent = (stat_node*) &st->root;
	else 
		node->parent = g_hash_table_lookup(st->names,(gchar*) parent_name);
	
	
	if ( node->parent )  {
		parent =  node->parent->iter;
				
		if (node->parent->children) {
			
			for (last_chld = node->parent->children;
				 last_chld->next;
				 last_chld = last_chld->next ) ;
			
			last_chld->next = node;
			
		} else {
			node->parent->children = node;
		}
		
		if(node->parent->hash) {
			g_hash_table_insert(node->parent->hash,node->name,node);
		}
		
	} else {
		g_warning("No parent named: %s",parent_name);
	}
	
	if (st->store) {
		gtk_tree_store_append (st->store, node->iter, parent);
		gtk_tree_store_set(st->store, node->iter, TITLE_COLUMN, node->name, RATE_COLUMN, "", COUNT_COLUMN, "", -1);
	}
	
	return node;
}


#define INDENT_MAX 32

static void draw_stat_node(stat_node* node) {
	stat_node* child;
	static gchar indentation[INDENT_MAX];
	static gchar value[NUM_BUF_SIZE];
	static gchar rate[NUM_BUF_SIZE];
	static gchar percent[NUM_BUF_SIZE];
	float num;
	guint i;
	
	g_snprintf(value,NUM_BUF_SIZE,"%u",node->counter);
	rate[0] = '-';
	rate[1] = '\0';
	percent[0] = '-';
	percent[1] = '\0';
	
	if (node->st->elapsed > 0.0) {
		num = ((float)node->counter) / node->st->elapsed;
		g_snprintf(rate,NUM_BUF_SIZE,"%f",num);
	}
	
	if (node->parent->counter > 0) {
		num = ((float)node->counter * 100.0) / node->parent->counter;
		g_snprintf(percent,NUM_BUF_SIZE,"%.2f%%",num);
	}
	
	if(node->st->text) {
		for ( i = 0 ; i<(INDENT_MAX-1) && i< node->st->indent ; i++) indentation[i] = '\t';
		indentation[i++] = '\0';
		g_string_sprintfa(node->st->text,"%s%s = %s (%s/s) (%s)\n",
						  indentation,node->name,value,rate,percent);
	}
	
	if (node->st->store)
		gtk_tree_store_set(node->st->store, node->iter,
						   RATE_COLUMN, rate,
						   COUNT_COLUMN, value,
						   PERCENT_COLUMN, percent,
						   -1);

	if (node->children) {
		for (child = node->children; child; child = child->next ) {
			node->st->indent++;
			draw_stat_node(child);
			node->st->indent--;
		}
	}
}

	
static void free_stat_node( stat_node* node ) {
	stat_node* child;
	
	if (node->iter) g_free(node->iter);
	
	if (node->name) g_free(node->name);
	
	if (node->children) {
		for (child = node->children; child; child = child->next ) 
			free_stat_node(child);
	}
	
	g_free(node);
}

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;
}

static void reset_stat_tree(void *psp  ) {
 	stats_tree *st=psp;
	if (st) {
		reset_stat_node(&st->root);
	}
}

extern guint8* tick_node(const stats_tree* st, const guint8* name, const guint8* parent_name, gboolean with_hash) {
	stat_node* node = NULL;
	stat_node* parent = NULL;
	
	if ( parent_name == NULL ) 
		parent = (stat_node*)&st->root;
	else 
		parent = g_hash_table_lookup(st->names,parent_name);
	
	if ( parent ) {

		if( parent->hash ) 
			node = g_hash_table_lookup(parent->hash,name);
		else 
			node = g_hash_table_lookup(st->names,name);
		
		if ( node == NULL ) 
			node = setup_stat_node(st,name,parent->name,with_hash,with_hash);
		
		node->counter++;
		
	}
	
	if (node) 
		return node->name;
	else
		return NULL;
}

static void
draw_stat_tree(void *psp  )
{
	stats_tree *st = psp;
	stat_node* child;
	
	st->text = g_string_new("");

	if (st->filter)
		g_string_sprintfa(st->text,"%s with filter: %s\n",st->name,st->filter);
	else
		g_string_sprintfa(st->text,"%s\n",st->name);

	for (child = st->root.children; child; child = child->next )
		draw_stat_node(child);
	
	/* g_message(st->text->str); */
	
	gtk_tree_view_set_model(GTK_TREE_VIEW(st->tree),GTK_TREE_MODEL(st->store));

	g_string_free(st->text,TRUE);
	
	st->text = NULL;
	st->indent = 0;
	
}


static void destroy_stat_tree_window(stats_tree* st) {
	stat_node* child;
	g_free(st->abbr);
	g_free(st->filter);
	
	for (child = st->root.children; child; child = child->next ) 
		free_stat_node(child);

	window_destroy(st->win);
	g_free(st);
}

void protect_thread_critical_region(void);
void unprotect_thread_critical_region(void);

static void win_destroy_cb(GtkWindow *win _U_, gpointer data)
{
	stats_tree *st=(stats_tree *)data;
	
	protect_thread_critical_region();
	remove_tap_listener(st);
	unprotect_thread_critical_region();
	
	destroy_stat_tree_window(st);
}

extern stats_tree* new_stat_tree(const guint8* abbr, const guint8* name, const guint8* optarg) {
	stats_tree* st;
	guint8* title = NULL;	
	GtkTreeViewColumn* column;
	GtkCellRenderer* renderer;
	GtkWidget *scr_win;
	guint8* window_name = NULL;
	guint8* base_string = NULL;
	
	/* top level window */
    st = g_malloc( sizeof(stats_tree) );
	
	st->abbr = g_strdup(abbr);
	
	st->root.counter = 0;
	st->root.name = STAT_TREE_ROOT;
	st->root.iter = NULL;
	st->root.st = st;
	st->root.parent = NULL;
	st->root.children = NULL;
	st->root.next = NULL;
	st->root.hash = NULL;
	
	st->names = g_hash_table_new(g_str_hash,g_str_equal);
	st->text = NULL;
	st->name = g_strdup(name);
	st->indent = 0;
	st->start = -1.0;
	st->elapsed = 0.0;
	
	base_string = g_strdup_printf("%s,stat,",abbr);
	
	if (strncmp (optarg, base_string, strlen(base_string)) == 0){
		st->filter=((guint8*)optarg)+10;
	} else {
		st->filter=NULL;
	}
	
	g_free(base_string);
	
	window_name = g_strdup_printf("%s_stat", abbr);
	
	st->win = window_new_with_geom(GTK_WINDOW_TOPLEVEL,window_name,window_name);
	g_free(window_name);
	
	if(st->filter){
		title=g_strdup_printf("%s with filter: %s",st->name,st->filter);
	} else {
		st->filter=NULL;
		title=g_strdup_printf("%s", st->name);
	}
	
    gtk_window_set_title(GTK_WINDOW(st->win), title);
	g_free(title);
	
	st->store = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
									G_TYPE_STRING, G_TYPE_STRING);
	
	st->tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (st->store));
	
	scr_win = scrolled_window_new(NULL, NULL);
	gtk_container_add( GTK_CONTAINER(scr_win), st->tree);
	gtk_container_add( GTK_CONTAINER(st->win), scr_win);
	
	
	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ("What", renderer,
													   "text", TITLE_COLUMN,
													   NULL);
	gtk_tree_view_column_set_resizable (column,TRUE);
	gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
	gtk_tree_view_append_column (GTK_TREE_VIEW (st->tree), column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ("count", renderer,
													   "text", COUNT_COLUMN,
													   NULL);
	gtk_tree_view_column_set_resizable (column,TRUE);
	gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
	gtk_tree_view_append_column (GTK_TREE_VIEW (st->tree), column);
	
	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ("rate", renderer,
													   "text", RATE_COLUMN,
													   NULL);
	gtk_tree_view_column_set_resizable (column,TRUE);
	gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
	gtk_tree_view_append_column (GTK_TREE_VIEW (st->tree), column);
	
	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ("percent", renderer,
													   "text", PERCENT_COLUMN,
													   NULL);
	gtk_tree_view_column_set_resizable (column,TRUE);
	gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
	gtk_tree_view_append_column (GTK_TREE_VIEW (st->tree), column);
	
	return st;
}

extern void init_stat_tree_window(stats_tree* st, tap_packet_cb packet_cb) {
	GString	*error_string;

	error_string = register_tap_listener( st->abbr,
										  st,
										  st->filter,
										  reset_stat_tree,
										  packet_cb,
										  draw_stat_tree);
	if (error_string) {
		/* error, we failed to attach to the tap. clean up */
		simple_dialog( ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str );
		destroy_stat_tree_window(st);
		g_string_free(error_string, TRUE);
		return ;
	}
	
    SIGNAL_CONNECT(GTK_WINDOW(st->win), "delete_event", window_delete_event_cb, NULL);
	SIGNAL_CONNECT(GTK_WINDOW(st->win), "destroy", win_destroy_cb, st);
	
    gtk_widget_show_all(st->win);
    window_present(st->win);
	
	retap_packets(&cfile);
}

extern void stat_tree_update(stats_tree* st, packet_info* pinfo) {
	float now = (((float)pinfo->fd->rel_secs) + (((float)pinfo->fd->rel_usecs)/1000000) );
	if (st->start < 0.0) st->start = now;
	st->elapsed = now - st->start;
}

extern void create_node(const stats_tree* st, const gchar* name, const gchar* parent_name, gboolean with_hash) {
	setup_stat_node(st,name,parent_name,with_hash,TRUE);
}

/* gtk_stat_tree.h
 * a gtk based conter tree for ethereal
 * 2004, Luis E. G. Ontanon
 * somehow based on http_stat.c by Jean-Michel FAYARD
 *
 * $Id: $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
#ifndef _GTK_STAT_TREE
#define _GTK_STAT_TREE

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <string.h>
#include <gtk/gtk.h>

#include <epan/epan.h>
#include "simple_dialog.h"
#include "globals.h"
#include <epan/packet_info.h>
#include <epan/tap.h>
#include "tap_menu.h"
#include "ui_util.h"
#include "dlg_utils.h"
#include "compat_macros.h"
#include "register.h"
#include "tap_dfilter_dlg.h"
#include "../tap_dfilter_dlg.h"

#define STAT_TREE_ROOT "root"

typedef struct _stats_tree stats_tree;

/* creates a new stats tree */
extern stats_tree* new_stat_tree(const guint8* abbr,
								 const guint8* name,
								 const guint8* optarg);

/* creates a node in the tree */
extern void create_node(const stats_tree* st,
						const gchar* name,
						const gchar* parent_name,
						gboolean with_children);

/* increases the counter of the node
 * if the node does not exist yet it's created (with counter=1). */
extern guint8* tick_node(const stats_tree* st,
						 const guint8* name,
						 const guint8* parent_name,
						 gboolean with_children);

/* initializes the tree the window and then registers the tap */
extern void init_stat_tree_window(stats_tree* st, tap_packet_cb packet_cb);

/* to be called before anything else in the tap_packet_cb  */
extern void stat_tree_update(stats_tree* sp, packet_info* pinfo);

#endif
/* http_stat.c
 * http_stat   2004 Luis E. G. Ontanon
 *
 * $Id: $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "gtk_stats_tree.h"

#include <epan/dissectors/packet-ip.h>

static const guint8* packets = "Total IP Packets";
static const guint8* packets_by_addr = "IP Packets by address";
static const guint8* packets_by_src = "IP Packets by sender";
static const guint8* packets_by_dst = "IP Packets by receiver";
static const guint8* packets_by_tos = "IP Packets by TOS";
static const guint8* packets_by_len = "IP Packets by length";

static const guint8* packets_sizes[] = {
	"<100","100-199","200,299","300-399","400-499",
	"500-599","600-699","700-799","800-899","900-999","1000-1099",
	"1100-1199","1200-1299","1300-1399","1400-1499","1500-1599",
	"1600-1699","1700-1799","1800-1899","1900-1999",">2000"};

static int ipstat_packet(void *psp , packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri) {
	static guint8 src[128];
	static guint8 dst[128];
	static guint8 addr_a[128];
	static guint8 addr_b[128];
	static guint8 tos[128];
	static guint8 proto[128];
	const e_ip *value = pri;
	stats_tree *sp = (stats_tree *) psp;
	guint len;
	
	stat_tree_update(sp,pinfo);
	
	tick_node(sp, packets, NULL, FALSE);
	tick_node(sp, packets_by_src, packets, FALSE);
	tick_node(sp, packets_by_dst, packets, FALSE);
	tick_node(sp, packets_by_addr, packets, FALSE);
	tick_node(sp, packets_by_addr, packets, FALSE);
	tick_node(sp, packets_by_tos, packets, FALSE);
	tick_node(sp, packets_by_len, packets, FALSE);
	
	g_snprintf(src, sizeof(src),"Src: %s",address_to_str(&pinfo->src));
	tick_node(sp, src, packets_by_src,FALSE);
	
	g_snprintf(dst, sizeof(dst),"Dst: %s",address_to_str(&pinfo->dst));
	tick_node(sp, dst, packets_by_dst,FALSE);
	
	g_snprintf(addr_a, sizeof(addr_a),"Addr: %s",address_to_str(&pinfo->src));
	tick_node(sp, addr_a, packets_by_addr,FALSE);
	
	g_snprintf(addr_b, sizeof(addr_b),"Addr: %s",address_to_str(&pinfo->dst));
	tick_node(sp, addr_b, packets_by_addr,FALSE);
	
	g_snprintf(tos, sizeof(tos),"TOS: %u", (guint)value->ip_tos);
	tick_node(sp, tos, packets_by_tos,FALSE);
	
	len = value->ip_len/100;
	if (len > 20) len = 20;
	tick_node(sp, packets_sizes[len],packets_by_len,FALSE);
	
	return 1;
}

static void
gtk_ipstat_init(char *optarg)
{
	guint i;
	
	stats_tree *sp = new_stat_tree("ip","IP Statistics",optarg);
	
	create_node(sp, packets, NULL, FALSE);
	create_node(sp, packets_by_addr, packets, TRUE);
	create_node(sp, packets_by_src, packets, TRUE);
	create_node(sp, packets_by_dst, packets, TRUE);
	create_node(sp, packets_by_tos, packets, TRUE);
	create_node(sp, packets_by_len, packets, TRUE);
	
	for (i=0; i < 20; i++) 
		create_node(sp, packets_sizes[i], packets_by_len, TRUE);
	
	init_stat_tree_window(sp,ipstat_packet);
}

static tap_dfilter_dlg ip_stat_dlg = {
	"IP Packet Counter",
	"ip,stat",
	gtk_ipstat_init,
	-1
};

void
register_tap_listener_gtkipstat(void)
{
	register_ethereal_tap("ip,stat", gtk_ipstat_init);

	register_tap_menu_item("IP", REGISTER_TAP_GROUP_NONE,
	    gtk_tap_dfilter_dlg_cb, NULL, NULL, &(ip_stat_dlg));
}

Attachment: gtk_Makefile.common.patch
Description: Binary data