Updates to reflect revision 4 of ethereal-win32-libs.
[obnox/wireshark/wip.git] / tap-iostat.c
1 /* tap-iostat.c
2  * iostat   2002 Ronnie Sahlberg
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #include <string.h>
36 #include "epan/epan_dissect.h"
37 #include "epan/packet_info.h"
38 #include <epan/tap.h>
39 #include "register.h"
40
41
42 typedef struct _io_stat_t {
43         gint32 interval;        /* unit is ms */
44         guint32 num_items;
45         struct _io_stat_item_t *items;
46         char **filters;
47 } io_stat_t;    
48
49 #define CALC_TYPE_BYTES 0
50 #define CALC_TYPE_COUNT 1
51 #define CALC_TYPE_SUM   2
52 #define CALC_TYPE_MIN   3
53 #define CALC_TYPE_MAX   4
54 #define CALC_TYPE_AVG   5
55
56 typedef struct _io_stat_item_t {
57         io_stat_t *parent;
58         struct _io_stat_item_t *next;
59         struct _io_stat_item_t *prev;
60         gint32 time;            /* unit is ms since start of capture */
61         int calc_type;
62         int hf_index;
63         guint32 frames;
64         guint32 num;
65         guint32 counter;
66 } io_stat_item_t;
67
68
69 static int
70 iostat_packet(io_stat_item_t *mit, packet_info *pinfo, epan_dissect_t *edt _U_, void *dummy _U_)
71 {
72         io_stat_item_t *it;
73         gint32 current_time;
74         GPtrArray *gp;
75         guint i;
76
77         current_time=((pinfo->fd->rel_secs*1000)+(pinfo->fd->rel_usecs/1000));
78
79         /* the prev item before the main one is always the last interval we saw packets for */
80         it=mit->prev;
81
82         /* XXX for the time being, just ignore all frames that are in the past.
83            should be fixed in the future but hopefully it is uncommon */
84         if(current_time<it->time){
85                 return FALSE;
86         }
87
88         /* we have moved into a new interval, we need to create a new struct */
89         if(current_time>=(it->time+mit->parent->interval)){
90                 it->next=g_malloc(sizeof(io_stat_item_t));
91                 it->next->prev=it;
92                 it->next->next=NULL;
93                 it=it->next;
94                 mit->prev=it;
95
96                 it->time=(current_time / mit->parent->interval) * mit->parent->interval;
97                 it->frames=0;
98                 it->counter=0;
99                 it->num=0;
100                 it->calc_type=it->prev->calc_type;
101                 it->hf_index=it->prev->hf_index;
102         }
103
104         /* it will now give us the current structure to use to store the data in */
105         it->frames++;
106
107         switch(it->calc_type){
108         case CALC_TYPE_BYTES:
109                 it->counter+=pinfo->fd->pkt_len;
110                 break;
111         case CALC_TYPE_COUNT:
112                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
113                 if(gp){
114                         it->counter+=gp->len;
115                 }
116                 break;
117         case CALC_TYPE_SUM:
118                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
119                 if(gp){
120                         for(i=0;i<gp->len;i++){
121                                 it->counter+=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
122                         }
123                 }
124                 break;
125         case CALC_TYPE_MIN:
126                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
127                 if(gp){
128                         int type;
129                         guint32 val;
130                         nstime_t *new_time;
131
132                         type=proto_registrar_get_ftype(it->hf_index);
133                         for(i=0;i<gp->len;i++){
134                                 switch(type){
135                                 case FT_UINT8:
136                                 case FT_UINT16:
137                                 case FT_UINT24:
138                                 case FT_UINT32:
139                                         val=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
140                                         if((it->frames==1)&&(i==0)){
141                                                 it->counter=val;
142                                         } else if(val<it->counter){
143                                                 it->counter=val;
144                                         }                               
145                                         break;
146                                 case FT_INT8:
147                                 case FT_INT16:
148                                 case FT_INT24:
149                                 case FT_INT32:
150                                         val=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
151                                         if((it->frames==1)&&(i==0)){
152                                                 it->counter=val;
153                                         } else if((gint32)val<(gint32)(it->counter)){
154                                                 it->counter=val;
155                                         }                               
156                                         break;
157                                 case FT_RELATIVE_TIME:
158                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
159                                         val=new_time->secs*1000+new_time->nsecs/1000000;
160                                         if((it->frames==1)&&(i==0)){
161                                                 it->counter=val;
162                                         } else if(val<it->counter){
163                                                 it->counter=val;
164                                         }                               
165                                         break;
166                                 }
167                         }
168                 }
169                 break;
170         case CALC_TYPE_MAX:
171                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
172                 if(gp){
173                         int type;
174                         guint32 val;
175                         nstime_t *new_time;
176
177                         type=proto_registrar_get_ftype(it->hf_index);
178                         for(i=0;i<gp->len;i++){
179                                 switch(type){
180                                 case FT_UINT8:
181                                 case FT_UINT16:
182                                 case FT_UINT24:
183                                 case FT_UINT32:
184                                         val=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
185                                         if((it->frames==1)&&(i==0)){
186                                                 it->counter=val;
187                                         } else if(val>it->counter){
188                                                 it->counter=val;
189                                         }                               
190                                         break;
191                                 case FT_INT8:
192                                 case FT_INT16:
193                                 case FT_INT24:
194                                 case FT_INT32:
195                                         val=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
196                                         if((it->frames==1)&&(i==0)){
197                                                 it->counter=val;
198                                         } else if((gint32)val>(gint32)(it->counter)){
199                                                 it->counter=val;
200                                         }                               
201                                         break;
202                                 case FT_RELATIVE_TIME:
203                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
204                                         val=new_time->secs*1000+new_time->nsecs/1000000;
205                                         if((it->frames==1)&&(i==0)){
206                                                 it->counter=val;
207                                         } else if(val>it->counter){
208                                                 it->counter=val;
209                                         }                               
210                                         break;
211                                 }
212                         }
213                 }
214                 break;
215         case CALC_TYPE_AVG:
216                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
217                 if(gp){
218                         int type;
219                         guint32 val;
220                         nstime_t *new_time;
221
222                         type=proto_registrar_get_ftype(it->hf_index);
223                         for(i=0;i<gp->len;i++){
224                                 it->num++;
225                                 switch(type){
226                                 case FT_UINT8:
227                                 case FT_UINT16:
228                                 case FT_UINT24:
229                                 case FT_UINT32:
230                                 case FT_INT8:
231                                 case FT_INT16:
232                                 case FT_INT24:
233                                 case FT_INT32:
234                                         val=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
235                                         it->counter+=val;
236                                         break;
237                                 case FT_RELATIVE_TIME:
238                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
239                                         val=new_time->secs*1000+new_time->nsecs/1000000;
240                                         it->counter+=val;
241                                         break;
242                                 }
243                         }
244                 }
245                 break;
246         }
247
248         return TRUE;
249 }
250
251 static void
252 iostat_draw(io_stat_item_t *mit)
253 {
254         io_stat_t *iot;
255         io_stat_item_t **items;
256         guint32 *frames;
257         guint32 *counters;
258         guint32 *num;
259         guint32 i,more_items;
260         gint t;
261
262         iot=mit->parent;
263
264         printf("\n");
265         printf("===================================================================\n");
266         printf("IO Statistics\n");
267         printf("Interval: %d.%03d secs\n", iot->interval/1000, iot->interval%1000);
268         for(i=0;i<iot->num_items;i++){
269                 printf("Column #%d: %s\n",i,iot->filters[i]?iot->filters[i]:"");
270         }
271         printf("                ");
272         for(i=0;i<iot->num_items;i++){
273                 printf("|   Column #%-2d   ",i);
274         }
275         printf("\n");
276         printf("Time            ");
277         for(i=0;i<iot->num_items;i++){
278                 switch(iot->items[i].calc_type){
279                 case CALC_TYPE_BYTES:
280                         printf("|frames|  bytes  ");
281                         break;
282                 case CALC_TYPE_COUNT:
283                         printf("|          COUNT ");
284                         break;
285                 case CALC_TYPE_SUM:
286                         printf("|            SUM ");
287                         break;
288                 case CALC_TYPE_MIN:
289                         printf("|            MIN ");
290                         break;
291                 case CALC_TYPE_MAX:
292                         printf("|            MAX ");
293                         break;
294                 case CALC_TYPE_AVG:
295                         printf("|            AVG ");
296                         break;
297                 }
298         }
299         printf("\n");
300
301         items=g_malloc(sizeof(io_stat_item_t *)*iot->num_items);
302         frames=g_malloc(sizeof(guint32)*iot->num_items);
303         counters=g_malloc(sizeof(guint32)*iot->num_items);
304         num=g_malloc(sizeof(guint32)*iot->num_items);
305         /* preset all items at the first interval */
306         for(i=0;i<iot->num_items;i++){
307                 items[i]=&iot->items[i];
308         }
309
310         /* loop the items until we run out of them all */
311         t=0;
312         do {
313                 more_items=0;
314                 for(i=0;i<iot->num_items;i++){
315                         frames[i]=0;
316                         counters[i]=0;
317                         num[i]=0;
318                 }
319                 for(i=0;i<iot->num_items;i++){
320                         if(items[i] && (t>=(items[i]->time+iot->interval))){
321                                 items[i]=items[i]->next;
322                         }
323
324                         if(items[i] && (t<(items[i]->time+iot->interval)) && (t>=items[i]->time) ){
325                                 frames[i]=items[i]->frames;
326                                 counters[i]=items[i]->counter;
327                                 num[i]=items[i]->num;
328                         }
329
330                         if(items[i]){
331                                 more_items=1;
332                         }
333                 }
334
335                 if(more_items){
336                         printf("%03d.%03d-%03d.%03d  ",
337                                 t/1000,t%1000,
338                                 (t+iot->interval)/1000,(t+iot->interval)%1000);
339                         for(i=0;i<iot->num_items;i++){
340                                 switch(iot->items[i].calc_type){
341                                 case CALC_TYPE_BYTES:
342                                         printf("%6d %9d ",frames[i],counters[i]);
343                                         break;
344                                 case CALC_TYPE_COUNT:
345                                         printf("        %8d ", counters[i]);
346                                         break;
347                                 case CALC_TYPE_SUM:
348                                         printf("        %8d ", counters[i]);
349                                         break;
350                                 case CALC_TYPE_MIN:
351                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
352                                         case FT_UINT8:
353                                         case FT_UINT16:
354                                         case FT_UINT24:
355                                         case FT_UINT32:
356                                                 printf("        %8u ", counters[i]);
357                                                 break;
358                                         case FT_INT8:
359                                         case FT_INT16:
360                                         case FT_INT24:
361                                         case FT_INT32:
362                                                 printf("        %8d ", counters[i]);
363                                                 break;
364                                         case FT_RELATIVE_TIME:
365                                                 printf("      %6d.%03d ", counters[i]/1000, counters[i]%1000);
366                                                 break;
367                                         }
368                                         break;
369                                 case CALC_TYPE_MAX:
370                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
371                                         case FT_UINT8:
372                                         case FT_UINT16:
373                                         case FT_UINT24:
374                                         case FT_UINT32:
375                                                 printf("        %8u ", counters[i]);
376                                                 break;
377                                         case FT_INT8:
378                                         case FT_INT16:
379                                         case FT_INT24:
380                                         case FT_INT32:
381                                                 printf("        %8d ", counters[i]);
382                                                 break;
383                                         case FT_RELATIVE_TIME:
384                                                 printf("      %6d.%03d ", counters[i]/1000, counters[i]%1000);
385                                                 break;
386                                         }
387                                         break;
388                                 case CALC_TYPE_AVG:
389                                         if(num[i]==0){
390                                                 num[i]=1;
391                                         }
392                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
393                                         case FT_UINT8:
394                                         case FT_UINT16:
395                                         case FT_UINT24:
396                                         case FT_UINT32:
397                                                 printf("        %8u ", counters[i]/num[i]);
398                                                 break;
399                                         case FT_INT8:
400                                         case FT_INT16:
401                                         case FT_INT24:
402                                         case FT_INT32:
403                                                 printf("        %8d ", counters[i]/num[i]);
404                                                 break;
405                                         case FT_RELATIVE_TIME:
406                                                 counters[i]/=num[i];
407                                                 printf("      %6d.%03d ", counters[i]/1000, counters[i]%1000);
408                                                 break;
409                                         }
410                                         break;
411
412                                 }
413                         }
414                         printf("\n");
415                 }
416
417                 t+=iot->interval;
418         } while(more_items);
419
420         printf("===================================================================\n");
421
422         g_free(items);
423         g_free(frames);
424         g_free(counters);
425         g_free(num);
426 }
427
428
429 static int
430 get_calc_field(char *filter, char **flt)
431 {
432         char field[256];
433         int i;
434         header_field_info *hfi;
435
436         *flt="";
437         for(i=0;filter[i];i++){
438                 if(i>=255){
439                         fprintf(stderr,"get_calc_field(): Too long field name: %s\n", filter);
440                         exit(10);
441                 }
442                 if(filter[i]==')'){
443                         break;
444                 }
445                 field[i]=filter[i];
446                 field[i+1]=0;
447         }
448         if(filter[i]==')'){
449                 *flt=&filter[i+1];
450         }
451
452         hfi=proto_registrar_get_byname(field);
453         if(!hfi){
454                 fprintf(stderr, "get_calc_field(): No such field %s\n", field);
455                 exit(10);
456         }
457         
458         return hfi->id;
459 }
460
461 static void
462 register_io_tap(io_stat_t *io, int i, char *filter)
463 {
464         GString *error_string;
465         char *flt;
466
467         io->items[i].prev=&io->items[i];
468         io->items[i].next=NULL;
469         io->items[i].parent=io;
470         io->items[i].time=0;
471         io->items[i].calc_type=CALC_TYPE_BYTES;
472         io->items[i].frames=0;
473         io->items[i].counter=0;
474         io->items[i].num=0;
475         io->filters[i]=filter;
476         flt=filter;
477
478         if(!filter){
479                 filter="";
480         }
481         if(!strncmp("COUNT(", filter, 6)){
482                 io->items[i].calc_type=CALC_TYPE_COUNT;
483                 io->items[i].hf_index=get_calc_field(filter+6, &flt);
484         } else if (!strncmp("SUM(", filter, 4)){
485                 io->items[i].calc_type=CALC_TYPE_SUM;
486                 io->items[i].hf_index=get_calc_field(filter+4, &flt);
487                 switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
488                 case FT_UINT8:
489                 case FT_UINT16:
490                 case FT_UINT24:
491                 case FT_UINT32:
492                 case FT_INT8:
493                 case FT_INT16:
494                 case FT_INT24:
495                 case FT_INT32:
496                         break;
497                 default:
498                         fprintf(stderr, "register_io_tap(): Invalid field type. SUM(x) only supports 8,16,24 and 32 byte integer fields\n");
499                         exit(10);
500                 }
501         } else if (!strncmp("MIN(", filter, 4)){
502                 io->items[i].calc_type=CALC_TYPE_MIN;
503                 io->items[i].hf_index=get_calc_field(filter+4, &flt);
504                 switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
505                 case FT_UINT8:
506                 case FT_UINT16:
507                 case FT_UINT24:
508                 case FT_UINT32:
509                 case FT_INT8:
510                 case FT_INT16:
511                 case FT_INT24:
512                 case FT_INT32:
513                 case FT_RELATIVE_TIME:
514                         break;
515                 default:
516                         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");
517                         exit(10);
518                 }
519         } else if (!strncmp("MAX(", filter, 4)){
520                 io->items[i].calc_type=CALC_TYPE_MAX;
521                 io->items[i].hf_index=get_calc_field(filter+4, &flt);
522                 switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
523                 case FT_UINT8:
524                 case FT_UINT16:
525                 case FT_UINT24:
526                 case FT_UINT32:
527                 case FT_INT8:
528                 case FT_INT16:
529                 case FT_INT24:
530                 case FT_INT32:
531                 case FT_RELATIVE_TIME:
532                         break;
533                 default:
534                         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");
535                         exit(10);
536                 }
537         } else if (!strncmp("AVG(", filter, 4)){
538                 io->items[i].calc_type=CALC_TYPE_AVG;
539                 io->items[i].hf_index=get_calc_field(filter+4, &flt);
540                 switch(proto_registrar_get_nth(io->items[i].hf_index)->type){
541                 case FT_UINT8:
542                 case FT_UINT16:
543                 case FT_UINT24:
544                 case FT_UINT32:
545                 case FT_INT8:
546                 case FT_INT16:
547                 case FT_INT24:
548                 case FT_INT32:
549                 case FT_RELATIVE_TIME:
550                         break;
551                 default:
552                         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");
553                         exit(10);
554                 }
555         }
556
557 /*
558 CALC_TYPE_SUM   2
559 CALC_TYPE_MIN   3
560 CALC_TYPE_MAX   4
561 CALC_TYPE_AVG   5
562 */
563
564         error_string=register_tap_listener("frame", &io->items[i], flt, NULL, (void*)iostat_packet, i?NULL:(void*)iostat_draw);
565         if(error_string){
566                 g_free(io->items);
567                 g_free(io);
568                 fprintf(stderr, "tethereal: Couldn't register io,stat tap: %s\n",
569                     error_string->str);
570                 g_string_free(error_string, TRUE);
571                 exit(1);
572         }
573 }
574
575 void
576 iostat_init(char *optarg)
577 {
578         float interval_float;
579         gint32 interval; 
580         int pos=0;
581         io_stat_t *io;
582         char *filter=NULL;
583
584         if(sscanf(optarg,"io,stat,%f,%n",&interval_float,&pos)==1){
585                 if(pos){
586                         filter=optarg+pos;
587                 } else {
588                         filter=NULL;
589                 }
590         } else {
591                 fprintf(stderr, "tethereal: invalid \"-z io,stat,<interval>[,<filter>]\" argument\n");
592                 exit(1);
593         }
594
595
596         /* make interval be number of ms */
597         interval=(gint32)(interval_float*1000.0+0.9);   
598         if(interval<1){
599                 fprintf(stderr, "tethereal:iostat_init()  interval must be >=0.001 seconds\n");
600                 exit(10);
601         }
602         
603         io=g_malloc(sizeof(io_stat_t));
604         io->interval=interval;
605         if((!filter)||(filter[0]==0)){
606                 io->num_items=1;
607                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
608                 io->filters=g_malloc(sizeof(char *)*io->num_items);
609
610                 register_io_tap(io, 0, NULL);
611         } else {
612                 char *str,*pos,*tmp;
613                 int i;
614                 /* find how many ',' separated filters we have */
615                 str=filter;
616                 io->num_items=1;
617                 while((str=strchr(str,','))){
618                         io->num_items++;
619                         str++;
620                 }
621
622                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
623                 io->filters=g_malloc(sizeof(char *)*io->num_items);
624
625                 /* for each filter, register a tap listener */          
626                 i=0;
627                 str=filter;
628                 do{
629                         pos=strchr(str,',');
630                         if(pos==str){
631                                 register_io_tap(io, i, NULL);
632                         } else if(pos==NULL) {
633                                 tmp=g_malloc(strlen(str)+1);
634                                 strcpy(tmp,str);
635                                 register_io_tap(io, i, tmp);
636                         } else {
637                                 tmp=g_malloc((pos-str)+1);
638                                 strncpy(tmp,str,(pos-str));
639                                 tmp[pos-str]=0;
640                                 register_io_tap(io, i, tmp);
641                         }
642                         str=pos+1;
643                         i++;                    
644                 } while(pos);
645         }                       
646 }
647
648 void
649 register_tap_listener_iostat(void)
650 {
651         register_ethereal_tap("io,stat,", iostat_init);
652 }
653