Alphabetical order, please.
[obnox/wireshark/wip.git] / tap-iostat.c
1 /* tap-iostat.c
2  * iostat   2002 Ronnie Sahlberg
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
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 <epan/stat_cmd_args.h>
40 #include <epan/strutil.h>
41
42
43 typedef struct _io_stat_t {
44         gint64 interval;        /* unit is us */
45         guint32 num_items;
46         struct _io_stat_item_t *items;
47         const char **filters;
48 } io_stat_t;
49
50 #define CALC_TYPE_FRAMES 0
51 #define CALC_TYPE_BYTES  1
52 #define CALC_TYPE_FRAMES_AND_BYTES       2
53 #define CALC_TYPE_COUNT  3
54 #define CALC_TYPE_SUM    4
55 #define CALC_TYPE_MIN    5
56 #define CALC_TYPE_MAX    6
57 #define CALC_TYPE_AVG    7
58 #define CALC_TYPE_LOAD   8
59
60 typedef struct _io_stat_item_t {
61         io_stat_t *parent;
62         struct _io_stat_item_t *next;
63         struct _io_stat_item_t *prev;
64         gint64 time;            /* unit is us since start of capture */
65         int calc_type;
66         int hf_index;
67         guint64 frames;
68         guint64 num;
69         guint64 counter;
70 } io_stat_item_t;
71
72 #define NANOSECS_PER_SEC 1000000000
73
74 static int
75 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
76 {
77         io_stat_item_t *mit = arg;
78         io_stat_item_t *it;
79         gint64 current_time, ct;
80         GPtrArray *gp;
81         guint i;
82
83         current_time = (gint64)(pinfo->fd->rel_ts.secs*1000000) + (pinfo->fd->rel_ts.nsecs+500)/1000;
84
85         /* the prev item before the main one is always the last interval we saw packets for */
86         it=mit->prev;
87
88         /* XXX for the time being, just ignore all frames that are in the past.
89            should be fixed in the future but hopefully it is uncommon */
90         if(current_time<it->time){
91                 return FALSE;
92         }
93
94         /* we have moved into a new interval, we need to create a new struct */
95         ct = current_time;
96         while(ct >= (it->time + mit->parent->interval)){
97                 it->next=g_malloc(sizeof(io_stat_item_t));
98                 it->next->prev=it;
99                 it->next->next=NULL;
100                 it=it->next;
101                 mit->prev=it;
102
103                 it->time    = it->prev->time + mit->parent->interval;
104                 it->frames  = 0;
105                 it->counter = 0;
106                 it->num     = 0;
107                 it->calc_type=it->prev->calc_type;
108                 it->hf_index=it->prev->hf_index;
109         }
110
111         /* it will now give us the current structure to use to store the data in */
112         it->frames++;
113
114         switch(it->calc_type){
115         case CALC_TYPE_BYTES:
116         case CALC_TYPE_FRAMES: 
117         case CALC_TYPE_FRAMES_AND_BYTES:
118                 it->counter+=pinfo->fd->pkt_len;
119                 break;
120         case CALC_TYPE_COUNT:
121                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
122                 if(gp){
123                         it->counter+=gp->len;
124                 }
125                 break;
126         case CALC_TYPE_SUM:
127                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
128                 if(gp){
129                         guint64 val;
130                         nstime_t *new_time;
131
132                         for(i=0;i<gp->len;i++){
133                                 switch(proto_registrar_get_ftype(it->hf_index)){
134                                 case FT_UINT8:
135                                 case FT_UINT16:
136                                 case FT_UINT24:
137                                 case FT_UINT32:
138                                         it->counter+=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
139                                         break;
140                                 case FT_UINT64:
141                                         it->counter+=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
142                                         break;
143                                 case FT_INT8:
144                                 case FT_INT16:
145                                 case FT_INT24:
146                                 case FT_INT32:
147                                         it->counter+=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
148                                         break;
149                                 case FT_INT64:
150                                         it->counter+=(gint64)fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
151                                         break;
152                                 case FT_RELATIVE_TIME:
153                                         new_time = fvalue_get(&((field_info *)gp->pdata[i])->value);
154                                         val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs;
155                                         it->counter += val;
156                                         break;
157                                 }
158                         }
159                 }
160                 break;
161         case CALC_TYPE_MIN:
162                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
163                 if(gp){
164                         int type;
165                         guint64 val;
166                         nstime_t *new_time;
167
168                         type=proto_registrar_get_ftype(it->hf_index);
169                         for(i=0;i<gp->len;i++){
170                                 switch(type){
171                                 case FT_UINT8:
172                                 case FT_UINT16:
173                                 case FT_UINT24:
174                                 case FT_UINT32:
175                                         val=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
176                                         if((it->frames==1)&&(i==0)){
177                                                 it->counter=val;
178                                         } else if(val<it->counter){
179                                                 it->counter=val;
180                                         }
181                                         break;
182                                 case FT_UINT64:
183                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
184                                         if((it->frames==1)&&(i==0)){
185                                                 it->counter=val;
186                                         } else if(val<it->counter){
187                                                 it->counter=val;
188                                         }
189                                         break;
190                                 case FT_INT8:
191                                 case FT_INT16:
192                                 case FT_INT24:
193                                 case FT_INT32:
194                                         val=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
195                                         if((it->frames==1)&&(i==0)){
196                                                 it->counter=val;
197                                         } else if((gint32)val<(gint32)(it->counter)){
198                                                 it->counter=val;
199                                         }
200                                         break;
201                                 case FT_INT64:
202                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
203                                         if((it->frames==1)&&(i==0)){
204                                                 it->counter=val;
205                                         } else if((gint64)val<(gint64)(it->counter)){
206                                                 it->counter=val;
207                                         }
208                                         break;
209                                 case FT_RELATIVE_TIME:
210                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
211                                         val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs;
212                                         if((it->frames==1)&&(i==0)){
213                                                 it->counter=val;
214                                         } else if(val<it->counter){
215                                                 it->counter=val;
216                                         }
217                                         break;
218                                 }
219                         }
220                 }
221                 break;
222         case CALC_TYPE_MAX:
223                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
224                 if(gp){
225                         int type;
226                         guint64 val;
227                         nstime_t *new_time;
228
229                         type=proto_registrar_get_ftype(it->hf_index);
230                         for(i=0;i<gp->len;i++){
231                                 switch(type){
232                                 case FT_UINT8:
233                                 case FT_UINT16:
234                                 case FT_UINT24:
235                                 case FT_UINT32:
236                                         val=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
237                                         if((it->frames==1)&&(i==0)){
238                                                 it->counter=val;
239                                         } else if(val>it->counter){
240                                                 it->counter=val;
241                                         }
242                                         break;
243                                 case FT_UINT64:
244                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
245                                         if((it->frames==1)&&(i==0)){
246                                                 it->counter=val;
247                                         } else if(val>it->counter){
248                                                 it->counter=val;
249                                         }
250                                         break;
251                                 case FT_INT8:
252                                 case FT_INT16:
253                                 case FT_INT24:
254                                 case FT_INT32:
255                                         val=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
256                                         if((it->frames==1)&&(i==0)){
257                                                 it->counter=val;
258                                         } else if((gint32)val>(gint32)(it->counter)){
259                                                 it->counter=val;
260                                         }
261                                         break;
262                                 case FT_INT64:
263                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
264                                         if((it->frames==1)&&(i==0)){
265                                                 it->counter=val;
266                                         } else if((gint64)val>(gint64)(it->counter)){
267                                                 it->counter=val;
268                                         }
269                                         break;
270                                 case FT_RELATIVE_TIME:
271                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
272                                         val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs;
273                                         if((it->frames==1)&&(i==0)){
274                                                 it->counter=val;
275                                         } else if(val>it->counter){
276                                                 it->counter=val;
277                                         }
278                                         break;
279                                 }
280                         }
281                 }
282                 break;
283         case CALC_TYPE_AVG:
284                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
285                 if(gp){
286                         int type;
287                         guint64 val;
288                         nstime_t *new_time;
289
290                         type=proto_registrar_get_ftype(it->hf_index);
291                         for(i=0;i<gp->len;i++){
292                                 it->num++;
293                                 switch(type){
294                                 case FT_UINT8:
295                                 case FT_UINT16:
296                                 case FT_UINT24:
297                                 case FT_UINT32:
298                                         val=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
299                                         it->counter+=val;
300                                         break;
301                                 case FT_UINT64:
302                                 case FT_INT64:
303                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
304                                         it->counter+=val;
305                                         break;
306                                 case FT_INT8:
307                                 case FT_INT16:
308                                 case FT_INT24:
309                                 case FT_INT32:
310                                         val=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
311                                         it->counter+=val;
312                                         break;
313                                 case FT_RELATIVE_TIME:
314                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
315                                         val=(guint64)(new_time->secs) * NANOSECS_PER_SEC + new_time->nsecs;
316                                         it->counter+=val;
317                                         break;
318                                 }
319                         }
320                 }
321                 break;
322         case CALC_TYPE_LOAD:
323                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
324                 if(gp){
325                         int type;
326
327                         type=proto_registrar_get_ftype(it->hf_index);
328                         if (type != FT_RELATIVE_TIME) {
329                                 fprintf(stderr,
330                                         "\ntshark: LOAD() is only supported for relative-time fiels such as smb.time\n"
331                                         );
332                                 exit(10);
333                         }
334                         for(i=0;i<gp->len;i++){
335                                 guint64 val;
336                                 int tival;
337                                 nstime_t *new_time;
338                                 io_stat_item_t *pit;
339
340                                 new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
341                                 val=(guint64)(new_time->secs)*1000000 + new_time->nsecs/1000;
342                                 tival = (int)(val % mit->parent->interval);
343                                 it->counter += tival;
344                                 val -= tival;
345                                 pit = it->prev;
346                                 while (val > 0) {
347                                         if (val < (guint64)mit->parent->interval) {
348                                                 pit->counter += val;
349                                                 val = 0;
350                                                 break;
351                                         }
352
353                                         pit->counter += mit->parent->interval;
354                                         val -= mit->parent->interval;
355                                         pit = pit->prev;
356                                         
357                                 }
358                         }
359                 }
360                 break;
361         }
362
363         return TRUE;
364 }
365
366 static void
367 iostat_draw(void *arg)
368 {
369         io_stat_item_t *mit = arg;
370         io_stat_t *iot;
371         io_stat_item_t **items;
372         guint64 *frames;
373         guint64 *counters;
374         guint64 *num;
375         guint32 i;
376         guint32 borderLen=68;
377         gboolean more_items;
378         gint64 t;
379
380         iot=mit->parent;
381
382         printf("\n");
383         
384         /* Display the table border */
385         for(i=0;i<iot->num_items;i++){
386                 if(iot->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES)
387                         borderLen+=17;
388         }
389         if(iot->interval!=G_MAXINT32)
390                 borderLen+=8;
391         if(iot->num_items>3)
392                 borderLen+=(iot->num_items-3)*17;
393         for(i=0;i<borderLen;i++){
394                 printf("=");
395         }
396         printf("\n");
397         
398         
399         printf("IO Statistics\n");
400         if(iot->interval!=G_MAXINT32)
401                 printf("Interval: %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u secs\n", 
402                                 iot->interval/1000000, iot->interval%1000000);                                          
403
404         for(i=0;i<iot->num_items;i++){
405                 printf("Column #%u: %s\n",i,iot->filters[i]?iot->filters[i]:"");
406         }
407         if(iot->interval==G_MAXINT32){
408                 printf("                |");
409         } else {
410                 printf("                        |");
411         }
412         for(i=0;i<iot->num_items;i++){
413                 if(iot->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES){
414                         printf("            Column #%-2u           |",i);
415                 } else {        
416                         printf("    Column #%-2u  |",i);
417                 }
418         }
419         printf("\n");
420
421         if(iot->interval==G_MAXINT32) {
422                 printf("Time            |");
423         } else {
424                 printf("Time                    |");
425         }
426         for(i=0;i<iot->num_items;i++){
427                 switch(iot->items[i].calc_type){
428                 case CALC_TYPE_FRAMES:
429                         printf("     FRAMES     |");
430                         break;
431                 case CALC_TYPE_BYTES:
432                         printf("     BYTES      |");
433                         break;
434                 case CALC_TYPE_FRAMES_AND_BYTES:
435                         printf("     Frames     |      Bytes     |");
436                         break;
437                 case CALC_TYPE_COUNT:
438                         printf("      COUNT     |");
439                         break;
440                 case CALC_TYPE_SUM:
441                         printf("       SUM      |");
442                         break;
443                 case CALC_TYPE_MIN:
444                         printf("       MIN      |");
445                         break;
446                 case CALC_TYPE_MAX:
447                         printf("       MAX      |");
448                         break;
449                 case CALC_TYPE_AVG:
450                         printf("       AVG      |");
451                         break;
452                 case CALC_TYPE_LOAD:
453                         printf("       LOAD     |");
454                         break;
455                 }
456         }
457         printf("\n");
458
459         items=g_malloc(sizeof(io_stat_item_t *)*iot->num_items);
460         frames=g_malloc(sizeof(guint64)*iot->num_items);
461         counters=g_malloc(sizeof(guint64)*iot->num_items);
462         num=g_malloc(sizeof(guint64)*iot->num_items);
463         /* preset all items at the first interval */
464         for(i=0;i<iot->num_items;i++){
465                 items[i]=&iot->items[i];
466         }
467
468         /* loop the items until we run out of them all */
469         t=0;
470         do {
471                 more_items=FALSE;
472                 for(i=0;i<iot->num_items;i++){
473                         frames[i]=0;
474                         counters[i]=0;
475                         num[i]=0;
476                 }
477                 for(i=0;i<iot->num_items;i++){
478                         if(items[i] && (t>=(items[i]->time+iot->interval))){
479                                 items[i]=items[i]->next;
480                         }
481
482                         if(items[i] && (t<(items[i]->time+iot->interval)) && (t>=items[i]->time) ){
483                                 frames[i]=items[i]->frames;
484                                 counters[i]=items[i]->counter;
485                                 num[i]=items[i]->num;
486                         }
487
488                         if(items[i]){
489                                 more_items=TRUE;
490                         }
491                 }
492
493                 if(more_items){
494                         if(iot->interval==G_MAXINT32) {
495                                 printf("000.000-         ");
496                         } else {
497                                 printf("%04u.%06u-%04u.%06u  ",
498                                                 (int)(t/1000000),(int)(t%1000000),
499                                                 (int)((t+iot->interval)/1000000),
500                                                 (int)((t+iot->interval)%1000000));
501                         }
502                         for(i=0;i<iot->num_items;i++){
503                                 switch(iot->items[i].calc_type){
504                                 case CALC_TYPE_FRAMES:
505                                         printf(" %15" G_GINT64_MODIFIER "u ", frames[i]);
506                                         break;
507                                 case CALC_TYPE_BYTES:
508                                         printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
509                                         break;
510                                 case CALC_TYPE_FRAMES_AND_BYTES:
511                                         printf(" %15" G_GINT64_MODIFIER "u  %15" G_GINT64_MODIFIER "u ", frames[i], counters[i]);
512                                         break;
513                                 case CALC_TYPE_COUNT:
514                                         printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
515                                         break;
516                                 case CALC_TYPE_SUM:
517                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
518                                         case FT_RELATIVE_TIME:
519                                                 counters[i] = (counters[i]+500)/1000;
520                                                 printf(" %8u.%06u ",
521                                                                 (int)(counters[i]/1000000), (int)(counters[i]%1000000));
522                                                 break;
523                                         default:
524                                                 printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
525                                                 break;
526                                         }
527                                         break;
528                                 case CALC_TYPE_MIN:
529                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
530                                         case FT_UINT8:
531                                         case FT_UINT16:
532                                         case FT_UINT24:
533                                         case FT_UINT32:
534                                         case FT_UINT64:
535                                                 printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
536                                                 break;
537                                         case FT_INT8:
538                                         case FT_INT16:
539                                         case FT_INT24:
540                                         case FT_INT32:
541                                         case FT_INT64:
542                                                 printf(" %15" G_GINT64_MODIFIER "d ", counters[i]);
543                                                 break;
544                                         case FT_RELATIVE_TIME:
545                                                 counters[i]=(counters[i]+500)/1000;
546                                                 printf(" %8u.%06u ",
547                                                                 (int)(counters[i]/1000000), (int)(counters[i]%1000000));
548                                                 break;
549                                         }
550                                         break;
551                                 case CALC_TYPE_MAX:
552                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
553                                         case FT_UINT8:
554                                         case FT_UINT16:
555                                         case FT_UINT24:
556                                         case FT_UINT32:
557                                         case FT_UINT64:
558                                                 printf(" %15u ", (int)(counters[i]));
559                                                 break;
560                                         case FT_INT8:
561                                         case FT_INT16:
562                                         case FT_INT24:
563                                         case FT_INT32:
564                                         case FT_INT64:
565                                                 printf(" %15" G_GINT64_MODIFIER "d ", counters[i]);
566                                                 break;
567                                         case FT_RELATIVE_TIME:
568                                                 counters[i]=(counters[i]+500)/1000;
569                                                 printf(" %8u.%06u ",
570                                                                 (int)(counters[i]/1000000), (int)(counters[i]%1000000));
571                                                 break;
572                                         }
573                                         break;
574                                 case CALC_TYPE_AVG:
575                                         if(num[i]==0){
576                                                 num[i]=1;
577                                         }
578                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
579                                         case FT_UINT8:
580                                         case FT_UINT16:
581                                         case FT_UINT24:
582                                         case FT_UINT32:
583                                         case FT_UINT64:
584                                                 printf(" %15" G_GINT64_MODIFIER "u ", counters[i]/num[i]);
585                                                 break;
586                                         case FT_INT8:
587                                         case FT_INT16:
588                                         case FT_INT24:
589                                         case FT_INT32:
590                                         case FT_INT64:
591                                                 printf(" %15" G_GINT64_MODIFIER "d ", counters[i]/num[i]);
592                                                 break;
593                                         case FT_RELATIVE_TIME:
594                                                 counters[i]=((counters[i]/num[i])+500)/1000;
595                                                 printf(" %8u.%06u ",
596                                                                 (int)(counters[i]/1000000), (int)(counters[i]%1000000));
597                                                 break;
598                                         }
599                                         break;
600
601                                 case CALC_TYPE_LOAD:
602                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
603                                         case FT_RELATIVE_TIME:
604                                                 printf("%8u.%06u ",
605                                                                 (int)(counters[i]/iot->interval), (int)((counters[i]%iot->interval)*1000000/iot->interval));
606                                                 break;
607                                         }
608                                         break;
609
610                                 }
611                         }
612                         printf("\n");
613                 }
614
615                 t+=iot->interval;
616         } while(more_items);
617
618         for(i=0;i<borderLen;i++){
619                 printf("=");
620         }
621         printf("\n");
622
623         g_free(items);
624         g_free(frames);
625         g_free(counters);
626         g_free(num);
627 }
628
629
630 typedef struct {
631         const char *func_name;
632         int calc_type;
633 } calc_type_ent_t;
634
635 static calc_type_ent_t calc_type_table[] = {
636         { "FRAMES", CALC_TYPE_FRAMES },
637         { "BYTES", CALC_TYPE_BYTES },
638         { "COUNT", CALC_TYPE_COUNT },
639         { "SUM", CALC_TYPE_SUM },
640         { "MIN", CALC_TYPE_MIN },
641         { "MAX", CALC_TYPE_MAX },
642         { "AVG", CALC_TYPE_AVG },
643         { "LOAD", CALC_TYPE_LOAD },
644         { NULL, 0 }
645 };
646
647 static void
648 register_io_tap(io_stat_t *io, int i, const char *filter)
649 {
650         GString *error_string;
651         const char *flt;
652         int j;
653         size_t namelen;
654         const char *p, *parenp;
655         char *field;
656         header_field_info *hfi;
657
658         io->items[i].prev=&io->items[i];
659         io->items[i].next=NULL;
660         io->items[i].parent=io;
661         io->items[i].time=0;
662         io->items[i].calc_type=CALC_TYPE_FRAMES_AND_BYTES;
663         io->items[i].frames=0;
664         io->items[i].counter=0;
665         io->items[i].num=0;
666         io->filters[i]=filter;
667         flt=filter;
668
669         field=NULL;
670         hfi=NULL;
671         for(j=0; calc_type_table[j].func_name; j++){
672                 namelen=strlen(calc_type_table[j].func_name);
673                 if(filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
674                         io->items[i].calc_type=calc_type_table[j].calc_type;
675                         if(*(filter+namelen)=='(') {
676                                 p=filter+namelen+1;
677                                 parenp=strchr(p, ')');
678                                 if(!parenp){
679                                         fprintf(stderr, "\ntshark: Closing parenthesis missing from calculated expression.\n");
680                                         exit(10);
681                                 }
682                                 
683                                 
684                                 if(io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES){
685                                         if(parenp!=p) { 
686                                                 fprintf(stderr, "\ntshark: %s does require or allow a field name within the parens.\n", 
687                                                         calc_type_table[j].func_name);
688                                                 exit(10);
689                                         }
690                                 } else {
691                                         if(parenp==p) { 
692                                                         /* bail out if a field name was not specified */
693                                                         fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n", 
694                                                                 calc_type_table[j].func_name);
695                                                         exit(10);
696                                         }
697                                 }                               
698
699                                 field=g_malloc(parenp-p+1);
700                                 memcpy(field, p, parenp-p);
701                                 field[parenp-p] = '\0';
702                                 flt=parenp + 1;
703                                 if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
704                                         break;
705                                 hfi=proto_registrar_get_byname(field);
706                                 if(!hfi){
707                                         fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
708                                                 field);
709                                         g_free(field);
710                                         exit(10);
711                                 }
712
713                                 io->items[i].hf_index=hfi->id;
714                                 break;
715                         }
716                 } else {
717                         if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
718                                 flt="";
719                 }
720         }
721         if(hfi && !(io->items[i].calc_type==CALC_TYPE_BYTES || 
722                             io->items[i].calc_type==CALC_TYPE_FRAMES || 
723                             io->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES)){
724                 /* check that the type is compatible */
725                 switch(hfi->type){
726                 case FT_UINT8:
727                 case FT_UINT16:
728                 case FT_UINT24:
729                 case FT_UINT32:
730                 case FT_UINT64:
731                 case FT_INT8:
732                 case FT_INT16:
733                 case FT_INT24:
734                 case FT_INT32:
735                 case FT_INT64:
736                         /* these types support all calculations */
737                         break;
738                 case FT_RELATIVE_TIME:
739                         /* this type only supports SUM, COUNT, MAX, MIN, AVG */
740                         switch(io->items[i].calc_type){
741                         case CALC_TYPE_SUM:
742                         case CALC_TYPE_COUNT:
743                         case CALC_TYPE_MAX:
744                         case CALC_TYPE_MIN:
745                         case CALC_TYPE_AVG:
746                         case CALC_TYPE_LOAD:
747                                 break;
748                         default:
749                                 fprintf(stderr,
750                                         "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
751                                     field,
752                                     calc_type_table[j].func_name);
753                                 exit(10);
754                         }
755                         break;
756                 default:
757                         /*
758                          * XXX - support all operations on floating-point
759                          * numbers?
760                          */
761                         if(io->items[i].calc_type!=CALC_TYPE_COUNT){
762                                 fprintf(stderr,
763                                         "\ntshark: %s doesn't have integral values, so %s(*) calculations are not supported on it.\n",
764                                     field,
765                                     calc_type_table[j].func_name);
766                                 exit(10);
767                         }
768                         break;
769                 }
770                 g_free(field);
771         }
772
773         error_string=register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL, iostat_packet, i?NULL:iostat_draw);
774         if(error_string){
775                 g_free(io->items);
776                 g_free(io);
777                 fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
778                     error_string->str);
779                 g_string_free(error_string, TRUE);
780                 exit(1);
781         }
782 }
783
784 static void
785 iostat_init(const char *optarg, void* userdata _U_)
786 {
787         gdouble interval_float;
788         gint64 interval;
789         int idx=0;
790         io_stat_t *io;
791         const char *filter=NULL;
792
793         if(sscanf(optarg,"io,stat,%lf,%n",&interval_float,&idx)==1){
794                 if(idx){
795                         if(*(optarg+idx)==',')
796                                 filter=optarg+idx+1;
797                         else
798                                 filter=optarg+idx;
799                 } else {
800                         filter=NULL;
801                 }
802         } else {
803                 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>]\" argument\n");
804                 exit(1);
805         }
806
807         /* if interval is 0, calculate statistics over the whole file
808          * by setting the interval to G_MAXINT32
809          */
810         if(interval_float==0) {
811                 interval=G_MAXINT32;
812         } else {
813                 /* make interval be number of us rounded to the nearest integer*/
814                 interval=(gint64)(interval_float*1000000.0+0.5); 
815         }
816
817         if(interval<1){
818                 fprintf(stderr, 
819                         "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
820                 exit(10);
821         }
822
823         io=g_malloc(sizeof(io_stat_t));
824         io->interval=interval;
825         if((!filter)||(filter[0]==0)){
826                 io->num_items=1;
827                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
828                 io->filters=g_malloc(sizeof(char *)*io->num_items);
829
830                 register_io_tap(io, 0, NULL);
831         } else {
832                 const char *str,*pos;
833                 char *tmp;
834                 int i;
835                 /* find how many ',' separated filters we have */
836                 str=filter;
837                 io->num_items=1;
838                 while((str=strchr(str,','))){
839                         io->num_items++;
840                         str++;
841                 }
842
843                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
844                 io->filters=g_malloc(sizeof(char *)*io->num_items);
845
846                 /* for each filter, register a tap listener */
847                 i=0;
848                 str=filter;
849                 do{
850                         pos=strchr(str,',');
851                         if(pos==str){
852                                 register_io_tap(io, i, NULL);
853                         } else if(pos==NULL) {
854                                 tmp=g_strdup(str);
855                                 register_io_tap(io, i, tmp);
856                         } else {
857                                 tmp=g_malloc((pos-str)+1);
858                                 g_strlcpy(tmp,str,(pos-str)+1);
859                                 register_io_tap(io, i, tmp);
860                         }
861                         str=pos+1;
862                         i++;
863                 } while(pos);
864         }
865 }
866
867 void
868 register_tap_listener_iostat(void)
869 {
870         register_stat_cmd_arg("io,stat,", iostat_init, NULL);
871 }