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