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