Update to io-stat for tethereal.

Make it able to calculate COUNT() SUM() MIN() MAX() and AVG() for integers and
relative time fields.

See tethereal manpage for examples.

svn path=/trunk/; revision=7550
This commit is contained in:
Ronnie Sahlberg 2003-04-24 12:25:33 +00:00
parent eb8c0fb210
commit c8e3705196
2 changed files with 426 additions and 57 deletions

View File

@ -363,6 +363,44 @@ all traffic to/from host 1.2.3.4
Example: -z "io,stat,0.001,smb&&ip.addr==1.2.3.4" to generate 1ms statistics for all SMB frames to/from host 1.2.3.4.
The examples above all use the standard syntax for generating statistics
which only calculates the number of frames and bytes in each interval.
io,stat can also do much more statistics and calculate COUNT() SUM() MIN()
MAX() and AVG() using a slightly filter syntax:
[COUNT|SUM|MIN|MAX|AVG](<field>)<filter>
One important thing to note here is that the field that the calculation is
based on MUST also be part of the filter string or else the calculation will
fail.
So: -z io,stat,0.010,AVG(smb.time) does not work. Use -z io,stat,0.010,AVG(smb.time)smb.time instead.
Also be aware that a field can exist multiple times inside the same packet
and will then be counted multiple times in those packets.
COUNT(<field>) can be used on any type which has a display filter name. It will
count how many times this particular field is encountered in the filtered packet list.
Example: -z io,stat,0.010,COUNT(smb.sid)smb.sid
This will count the total number of SIDs seen in each 10ms interval.
SUM(<field>) can only be used on named fields of integer type.
This will sum together every occurence of this fields value for each interval.
Example: -z io,stat,0.010,SUM(frame.pkt_len)frame.pkt_len
This will report the total number of bytes seen in all the frames within an interval.
MIN/MAX/AVG(<field>) can only be used on named fields that are either integers or relative time fields.
This will calculate maximum/minimum or average seen in each interval.
If the field is a relative time field the output will be presented in seconds
and three digits after the decimal point. The resolution for time calculations is 1ms and anything smaller will be truncated.
Example: -z "io,stat,0.010,smb.time&&ip.addr==1.1.1.1,MIN(smb.time)smb.time&&ip.addr==1.1.1.1,MAX(smb.time)smb.time&&ip.addr==1.1.1.1,MAX(smb.time)smb.time&&ip.addr==1.1.1.1"
This will calculate statistics for all smb response times we see to/from host 1.1.1.1 in 10ms intervals.
The output will be displayed in 4 columns; number of frames/bytes, minimum response time, maximum response time and average response time.
B<-z> io,users,I<type>[,I<filter>]

View File

@ -1,7 +1,7 @@
/* tap-iostat.c
* iostat 2002 Ronnie Sahlberg
*
* $Id: tap-iostat.c,v 1.6 2003/04/23 08:20:01 guy Exp $
* $Id: tap-iostat.c,v 1.7 2003/04/24 12:25:31 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -33,6 +33,7 @@
#endif
#include <string.h>
#include "epan/epan_dissect.h"
#include "epan/packet_info.h"
#include "tap.h"
#include "register.h"
@ -45,64 +46,33 @@ typedef struct _io_stat_t {
char **filters;
} io_stat_t;
#define CALC_TYPE_BYTES 0
#define CALC_TYPE_COUNT 1
#define CALC_TYPE_SUM 2
#define CALC_TYPE_MIN 3
#define CALC_TYPE_MAX 4
#define CALC_TYPE_AVG 5
typedef struct _io_stat_item_t {
io_stat_t *parent;
struct _io_stat_item_t *next;
struct _io_stat_item_t *prev;
gint32 time; /* unit is ms since start of capture */
int calc_type;
int hf_index;
guint32 frames;
guint32 bytes;
guint32 num;
guint32 counter;
} io_stat_item_t;
#ifdef REMOVED
/* Tethereal does not use the reset callback.
But if someone ports this feature to Gtk with a nice gui, this is what
reset should look like.
*/
static void
iostat_reset(io_stat_item_t *mit)
{
io_stat_item_t *it;
mit->prev=mit;
mit->time=0;
mit->frames=0;
mit->bytes=0;
while(mit->next){
it=mit->next;
mit=mit->next->next;
g_free(it);
}
}
/* function to remove and clean up an io stat. would be used by Gtk/Gtk2 version
io iostat when the iostat window is closed.
*/
static void
iostat_cleanup(io_stat_t *io)
{
int i;
for(i=0;i<io->num_items;i++){
if(filters[i]){
g_free(filters[i]);
filters[i]=NULL;
}
iostat_reset(&io->items[i]);
remove_tap_listener(&io->items[i]);
}
g_free(io->items);
g_free(io->filters);
g_free(io);
}
#endif
static int
iostat_packet(io_stat_item_t *mit, packet_info *pinfo, epan_dissect_t *edt _U_, void *dummy _U_)
{
io_stat_item_t *it;
gint32 current_time;
GPtrArray *gp;
guint i;
current_time=((pinfo->fd->rel_secs*1000)+(pinfo->fd->rel_usecs/1000));
@ -125,13 +95,156 @@ iostat_packet(io_stat_item_t *mit, packet_info *pinfo, epan_dissect_t *edt _U_,
it->time=(current_time / mit->parent->interval) * mit->parent->interval;
it->frames=0;
it->bytes=0;
it->counter=0;
it->num=0;
it->calc_type=it->prev->calc_type;
it->hf_index=it->prev->hf_index;
}
/* it will now give us the current structure to use to store the data in */
it->frames++;
it->bytes+=pinfo->fd->pkt_len;
switch(it->calc_type){
case CALC_TYPE_BYTES:
it->counter+=pinfo->fd->pkt_len;
break;
case CALC_TYPE_COUNT:
gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
if(gp){
it->counter+=gp->len;
}
break;
case CALC_TYPE_SUM:
gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
if(gp){
for(i=0;i<gp->len;i++){
it->counter+=fvalue_get_integer(((field_info *)gp->pdata[i])->value);
}
}
break;
case CALC_TYPE_MIN:
gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
if(gp){
int type;
guint32 val;
nstime_t *new_time;
type=proto_registrar_get_ftype(it->hf_index);
for(i=0;i<gp->len;i++){
switch(type){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
val=fvalue_get_integer(((field_info *)gp->pdata[i])->value);
if((it->frames==1)&&(i==0)){
it->counter=val;
} else if(val<it->counter){
it->counter=val;
}
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
val=fvalue_get_integer(((field_info *)gp->pdata[i])->value);
if((it->frames==1)&&(i==0)){
it->counter=val;
} else if((gint32)val<(gint32)(it->counter)){
it->counter=val;
}
break;
case FT_RELATIVE_TIME:
new_time=fvalue_get(((field_info *)gp->pdata[i])->value);
val=new_time->secs*1000+new_time->nsecs/1000000;
if((it->frames==1)&&(i==0)){
it->counter=val;
} else if(val<it->counter){
it->counter=val;
}
break;
}
}
}
break;
case CALC_TYPE_MAX:
gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
if(gp){
int type;
guint32 val;
nstime_t *new_time;
type=proto_registrar_get_ftype(it->hf_index);
for(i=0;i<gp->len;i++){
switch(type){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
val=fvalue_get_integer(((field_info *)gp->pdata[i])->value);
if((it->frames==1)&&(i==0)){
it->counter=val;
} else if(val>it->counter){
it->counter=val;
}
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
val=fvalue_get_integer(((field_info *)gp->pdata[i])->value);
if((it->frames==1)&&(i==0)){
it->counter=val;
} else if((gint32)val>(gint32)(it->counter)){
it->counter=val;
}
break;
case FT_RELATIVE_TIME:
new_time=fvalue_get(((field_info *)gp->pdata[i])->value);
val=new_time->secs*1000+new_time->nsecs/1000000;
if((it->frames==1)&&(i==0)){
it->counter=val;
} else if(val>it->counter){
it->counter=val;
}
break;
}
}
}
break;
case CALC_TYPE_AVG:
gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
if(gp){
int type;
guint32 val;
nstime_t *new_time;
type=proto_registrar_get_ftype(it->hf_index);
for(i=0;i<gp->len;i++){
it->num++;
switch(type){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
val=fvalue_get_integer(((field_info *)gp->pdata[i])->value);
it->counter+=val;
break;
case FT_RELATIVE_TIME:
new_time=fvalue_get(((field_info *)gp->pdata[i])->value);
val=new_time->secs*1000+new_time->nsecs/1000000;
it->counter+=val;
break;
}
}
}
break;
}
return TRUE;
}
@ -141,7 +254,8 @@ iostat_draw(io_stat_item_t *mit)
io_stat_t *iot;
io_stat_item_t **items;
guint32 *frames;
guint32 *bytes;
guint32 *counters;
guint32 *num;
guint32 i,more_items;
gint t;
@ -161,13 +275,33 @@ iostat_draw(io_stat_item_t *mit)
printf("\n");
printf("Time ");
for(i=0;i<iot->num_items;i++){
printf("|frames| bytes ");
switch(iot->items[i].calc_type){
case CALC_TYPE_BYTES:
printf("|frames| bytes ");
break;
case CALC_TYPE_COUNT:
printf("| COUNT ");
break;
case CALC_TYPE_SUM:
printf("| SUM ");
break;
case CALC_TYPE_MIN:
printf("| MIN ");
break;
case CALC_TYPE_MAX:
printf("| MAX ");
break;
case CALC_TYPE_AVG:
printf("| AVG ");
break;
}
}
printf("\n");
items=g_malloc(sizeof(io_stat_item_t *)*iot->num_items);
frames=g_malloc(sizeof(guint32)*iot->num_items);
bytes=g_malloc(sizeof(guint32)*iot->num_items);
counters=g_malloc(sizeof(guint32)*iot->num_items);
num=g_malloc(sizeof(guint32)*iot->num_items);
/* preset all items at the first interval */
for(i=0;i<iot->num_items;i++){
items[i]=&iot->items[i];
@ -179,7 +313,8 @@ iostat_draw(io_stat_item_t *mit)
more_items=0;
for(i=0;i<iot->num_items;i++){
frames[i]=0;
bytes[i]=0;
counters[i]=0;
num[i]=0;
}
for(i=0;i<iot->num_items;i++){
if(items[i] && (t>=(items[i]->time+iot->interval))){
@ -188,7 +323,8 @@ iostat_draw(io_stat_item_t *mit)
if(items[i] && (t<(items[i]->time+iot->interval)) && (t>=items[i]->time) ){
frames[i]=items[i]->frames;
bytes[i]=items[i]->bytes;
counters[i]=items[i]->counter;
num[i]=items[i]->num;
}
if(items[i]){
@ -201,7 +337,79 @@ iostat_draw(io_stat_item_t *mit)
t/1000,t%1000,
(t+iot->interval)/1000,(t+iot->interval)%1000);
for(i=0;i<iot->num_items;i++){
printf("%6d %9d ",frames[i],bytes[i]);
switch(iot->items[i].calc_type){
case CALC_TYPE_BYTES:
printf("%6d %9d ",frames[i],counters[i]);
break;
case CALC_TYPE_COUNT:
printf(" %8d ", counters[i]);
break;
case CALC_TYPE_SUM:
printf(" %8d ", counters[i]);
break;
case CALC_TYPE_MIN:
switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
printf(" %8u ", counters[i]);
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
printf(" %8d ", counters[i]);
break;
case FT_RELATIVE_TIME:
printf(" %6d.%03d ", counters[i]/1000, counters[i]%1000);
break;
}
break;
case CALC_TYPE_MAX:
switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
printf(" %8u ", counters[i]);
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
printf(" %8d ", counters[i]);
break;
case FT_RELATIVE_TIME:
printf(" %6d.%03d ", counters[i]/1000, counters[i]%1000);
break;
}
break;
case CALC_TYPE_AVG:
if(num[i]==0){
num[i]=1;
}
switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
printf(" %8u ", counters[i]/num[i]);
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
printf(" %8d ", counters[i]/num[i]);
break;
case FT_RELATIVE_TIME:
counters[i]/=num[i];
printf(" %6d.%03d ", counters[i]/1000, counters[i]%1000);
break;
}
break;
}
}
printf("\n");
}
@ -213,24 +421,147 @@ iostat_draw(io_stat_item_t *mit)
g_free(items);
g_free(frames);
g_free(bytes);
g_free(counters);
g_free(num);
}
static int
get_calc_field(char *filter, char **flt)
{
char field[256];
int i;
header_field_info *hfi;
*flt="";
for(i=0;filter[i];i++){
if(i>=255){
fprintf(stderr,"get_calc_field(): Too long field name: %s\n", filter);
exit(10);
}
if(filter[i]==')'){
break;
}
field[i]=filter[i];
field[i+1]=0;
}
if(filter[i]==')'){
*flt=&filter[i+1];
}
hfi=proto_registrar_get_byname(field);
if(!hfi){
fprintf(stderr, "get_calc_field(): No such field %s\n", field);
exit(10);
}
return hfi->id;
}
static void
register_io_tap(io_stat_t *io, int i, char *filter)
{
GString *error_string;
char *flt;
io->items[i].prev=&io->items[i];
io->items[i].next=NULL;
io->items[i].parent=io;
io->items[i].time=0;
io->items[i].calc_type=CALC_TYPE_BYTES;
io->items[i].frames=0;
io->items[i].bytes=0;
io->items[i].counter=0;
io->items[i].num=0;
io->filters[i]=filter;
flt=filter;
error_string=register_tap_listener("frame", &io->items[i], filter, NULL, (void*)iostat_packet, i?NULL:(void*)iostat_draw);
if(!filter){
filter="";
}
if(!strncmp("COUNT(", filter, 6)){
io->items[i].calc_type=CALC_TYPE_COUNT;
io->items[i].hf_index=get_calc_field(filter+6, &flt);
} else if (!strncmp("SUM(", filter, 4)){
io->items[i].calc_type=CALC_TYPE_SUM;
io->items[i].hf_index=get_calc_field(filter+4, &flt);
switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
break;
default:
fprintf(stderr, "register_io_tap(): Invalid field type. SUM(x) only supports 8,16,24 and 32 byte integer fields\n");
exit(10);
}
} else if (!strncmp("MIN(", filter, 4)){
io->items[i].calc_type=CALC_TYPE_MIN;
io->items[i].hf_index=get_calc_field(filter+4, &flt);
switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
case FT_RELATIVE_TIME:
break;
default:
fprintf(stderr, "register_io_tap(): Invalid field type. MIN(x) only supports 8,16,24 and 32 byte integer fields and relative time fields\n");
exit(10);
}
} else if (!strncmp("MAX(", filter, 4)){
io->items[i].calc_type=CALC_TYPE_MAX;
io->items[i].hf_index=get_calc_field(filter+4, &flt);
switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
case FT_RELATIVE_TIME:
break;
default:
fprintf(stderr, "register_io_tap(): Invalid field type. MAX(x) only supports 8,16,24 and 32 byte integer fields and relative time fields\n");
exit(10);
}
} else if (!strncmp("AVG(", filter, 4)){
io->items[i].calc_type=CALC_TYPE_AVG;
io->items[i].hf_index=get_calc_field(filter+4, &flt);
switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
case FT_RELATIVE_TIME:
break;
default:
fprintf(stderr, "register_io_tap(): Invalid field type. AVG(x) only supports 8,16,24 and 32 byte integer fields and relative time fields\n");
exit(10);
}
}
/*
CALC_TYPE_SUM 2
CALC_TYPE_MIN 3
CALC_TYPE_MAX 4
CALC_TYPE_AVG 5
*/
error_string=register_tap_listener("frame", &io->items[i], flt, NULL, (void*)iostat_packet, i?NULL:(void*)iostat_draw);
if(error_string){
g_free(io->items);
g_free(io);