The Styleguide section has been moved to the Wireshark Developer's Guide.
[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         gint32 interval;        /* unit is ms */
45         guint32 num_items;
46         struct _io_stat_item_t *items;
47         const char **filters;
48 } io_stat_t;
49
50 #define CALC_TYPE_BYTES 0
51 #define CALC_TYPE_COUNT 1
52 #define CALC_TYPE_SUM   2
53 #define CALC_TYPE_MIN   3
54 #define CALC_TYPE_MAX   4
55 #define CALC_TYPE_AVG   5
56
57 typedef struct _io_stat_item_t {
58         io_stat_t *parent;
59         struct _io_stat_item_t *next;
60         struct _io_stat_item_t *prev;
61         gint32 time;            /* unit is ms since start of capture */
62         int calc_type;
63         int hf_index;
64         guint64 frames;
65         guint64 num;
66         guint64 counter;
67 } io_stat_item_t;
68
69
70 static int
71 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
72 {
73         io_stat_item_t *mit = arg;
74         io_stat_item_t *it;
75         gint32 current_time;
76         GPtrArray *gp;
77         guint i;
78
79         current_time=(gint32) ((pinfo->fd->rel_ts.secs*1000)+(pinfo->fd->rel_ts.nsecs/1000000));
80
81         /* the prev item before the main one is always the last interval we saw packets for */
82         it=mit->prev;
83
84         /* XXX for the time being, just ignore all frames that are in the past.
85            should be fixed in the future but hopefully it is uncommon */
86         if(current_time<it->time){
87                 return FALSE;
88         }
89
90         /* we have moved into a new interval, we need to create a new struct */
91         if(current_time>=(it->time+mit->parent->interval)){
92                 it->next=g_malloc(sizeof(io_stat_item_t));
93                 it->next->prev=it;
94                 it->next->next=NULL;
95                 it=it->next;
96                 mit->prev=it;
97
98                 it->time=(current_time / mit->parent->interval) * mit->parent->interval;
99                 it->frames=0;
100                 it->counter=0;
101                 it->num=0;
102                 it->calc_type=it->prev->calc_type;
103                 it->hf_index=it->prev->hf_index;
104         }
105
106         /* it will now give us the current structure to use to store the data in */
107         it->frames++;
108
109         switch(it->calc_type){
110         case CALC_TYPE_BYTES:
111                 it->counter+=pinfo->fd->pkt_len;
112                 break;
113         case CALC_TYPE_COUNT:
114                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
115                 if(gp){
116                         it->counter+=gp->len;
117                 }
118                 break;
119         case CALC_TYPE_SUM:
120                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
121                 if(gp){
122                         for(i=0;i<gp->len;i++){
123                                 switch(proto_registrar_get_ftype(it->hf_index)){
124                                 case FT_UINT8:
125                                 case FT_UINT16:
126                                 case FT_UINT24:
127                                 case FT_UINT32:
128                                         it->counter+=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
129                                         break;
130                                 case FT_UINT64:
131                                         it->counter+=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
132                                         break;
133                                 case FT_INT8:
134                                 case FT_INT16:
135                                 case FT_INT24:
136                                 case FT_INT32:
137                                         it->counter+=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
138                                         break;
139                                 case FT_INT64:
140                                         it->counter+=(gint64)fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
141                                         break;
142                                 }
143                         }
144                 }
145                 break;
146         case CALC_TYPE_MIN:
147                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
148                 if(gp){
149                         int type;
150                         guint64 val;
151                         nstime_t *new_time;
152
153                         type=proto_registrar_get_ftype(it->hf_index);
154                         for(i=0;i<gp->len;i++){
155                                 switch(type){
156                                 case FT_UINT8:
157                                 case FT_UINT16:
158                                 case FT_UINT24:
159                                 case FT_UINT32:
160                                         val=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
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                                 case FT_UINT64:
168                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
169                                         if((it->frames==1)&&(i==0)){
170                                                 it->counter=val;
171                                         } else if(val<it->counter){
172                                                 it->counter=val;
173                                         }
174                                         break;
175                                 case FT_INT8:
176                                 case FT_INT16:
177                                 case FT_INT24:
178                                 case FT_INT32:
179                                         val=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
180                                         if((it->frames==1)&&(i==0)){
181                                                 it->counter=val;
182                                         } else if((gint32)val<(gint32)(it->counter)){
183                                                 it->counter=val;
184                                         }
185                                         break;
186                                 case FT_INT64:
187                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
188                                         if((it->frames==1)&&(i==0)){
189                                                 it->counter=val;
190                                         } else if((gint64)val<(gint64)(it->counter)){
191                                                 it->counter=val;
192                                         }
193                                         break;
194                                 case FT_RELATIVE_TIME:
195                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
196                                         val=(guint64) (new_time->secs*1000+new_time->nsecs/1000000);
197                                         if((it->frames==1)&&(i==0)){
198                                                 it->counter=val;
199                                         } else if(val<it->counter){
200                                                 it->counter=val;
201                                         }
202                                         break;
203                                 }
204                         }
205                 }
206                 break;
207         case CALC_TYPE_MAX:
208                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
209                 if(gp){
210                         int type;
211                         guint64 val;
212                         nstime_t *new_time;
213
214                         type=proto_registrar_get_ftype(it->hf_index);
215                         for(i=0;i<gp->len;i++){
216                                 switch(type){
217                                 case FT_UINT8:
218                                 case FT_UINT16:
219                                 case FT_UINT24:
220                                 case FT_UINT32:
221                                         val=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
222                                         if((it->frames==1)&&(i==0)){
223                                                 it->counter=val;
224                                         } else if(val>it->counter){
225                                                 it->counter=val;
226                                         }
227                                         break;
228                                 case FT_UINT64:
229                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
230                                         if((it->frames==1)&&(i==0)){
231                                                 it->counter=val;
232                                         } else if(val>it->counter){
233                                                 it->counter=val;
234                                         }
235                                         break;
236                                 case FT_INT8:
237                                 case FT_INT16:
238                                 case FT_INT24:
239                                 case FT_INT32:
240                                         val=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
241                                         if((it->frames==1)&&(i==0)){
242                                                 it->counter=val;
243                                         } else if((gint32)val>(gint32)(it->counter)){
244                                                 it->counter=val;
245                                         }
246                                         break;
247                                 case FT_INT64:
248                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
249                                         if((it->frames==1)&&(i==0)){
250                                                 it->counter=val;
251                                         } else if((gint64)val>(gint64)(it->counter)){
252                                                 it->counter=val;
253                                         }
254                                         break;
255                                 case FT_RELATIVE_TIME:
256                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
257                                         val=(guint64) (new_time->secs*1000+new_time->nsecs/1000000);
258                                         if((it->frames==1)&&(i==0)){
259                                                 it->counter=val;
260                                         } else if(val>it->counter){
261                                                 it->counter=val;
262                                         }
263                                         break;
264                                 }
265                         }
266                 }
267                 break;
268         case CALC_TYPE_AVG:
269                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
270                 if(gp){
271                         int type;
272                         guint64 val;
273                         nstime_t *new_time;
274
275                         type=proto_registrar_get_ftype(it->hf_index);
276                         for(i=0;i<gp->len;i++){
277                                 it->num++;
278                                 switch(type){
279                                 case FT_UINT8:
280                                 case FT_UINT16:
281                                 case FT_UINT24:
282                                 case FT_UINT32:
283                                         val=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
284                                         it->counter+=val;
285                                         break;
286                                 case FT_UINT64:
287                                 case FT_INT64:
288                                         val=fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
289                                         it->counter+=val;
290                                         break;
291                                 case FT_INT8:
292                                 case FT_INT16:
293                                 case FT_INT24:
294                                 case FT_INT32:
295                                         val=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
296                                         it->counter+=val;
297                                         break;
298                                 case FT_RELATIVE_TIME:
299                                         new_time=fvalue_get(&((field_info *)gp->pdata[i])->value);
300                                         val=(guint64) (new_time->secs*1000+new_time->nsecs/1000000);
301                                         it->counter+=val;
302                                         break;
303                                 }
304                         }
305                 }
306                 break;
307         }
308
309         return TRUE;
310 }
311
312 static void
313 iostat_draw(void *arg)
314 {
315         io_stat_item_t *mit = arg;
316         io_stat_t *iot;
317         io_stat_item_t **items;
318         guint64 *frames;
319         guint64 *counters;
320         guint64 *num;
321         guint32 i;
322         gboolean 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 #%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 #%-2u   ",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=FALSE;
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=TRUE;
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(" %15" G_GINT64_MODIFIER "u ", counters[i]);
415                                         break;
416                                 case CALC_TYPE_SUM:
417                                         printf(" %15" 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(" %15" 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(" %15" G_GINT64_MODIFIER "d ", counters[i]);
434                                                 break;
435                                         case FT_RELATIVE_TIME:
436                                                 printf(" %11" 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(" %15" 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(" %15" G_GINT64_MODIFIER "d ", counters[i]);
455                                                 break;
456                                         case FT_RELATIVE_TIME:
457                                                 printf(" %11" 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(" %15" 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(" %15" G_GINT64_MODIFIER "d ", counters[i]/num[i]);
479                                                 break;
480                                         case FT_RELATIVE_TIME:
481                                                 counters[i]/=num[i];
482                                                 printf(" %11" 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                         if(*(optarg+idx)==',')
661                                 filter=optarg+idx+1;
662                         else
663                                 filter=optarg+idx;
664                 } else {
665                         filter=NULL;
666                 }
667         } else {
668                 fprintf(stderr, "tshark: invalid \"-z io,stat,<interval>[,<filter>]\" argument\n");
669                 exit(1);
670         }
671
672         /* if interval is 0, calculate statistics over the whole file
673          * by setting the interval to G_MAXINT32
674          */
675         if(interval_float==0) {
676                 interval=G_MAXINT32;
677         } else {
678                 /* make interval be number of ms */
679                 interval=(gint32)(interval_float*1000.0+0.9);
680         }
681
682         if(interval<1){
683                 fprintf(stderr, "tshark: \"-z\" interval must be >=0.001 seconds or 0.\n");
684                 exit(10);
685         }
686
687         io=g_malloc(sizeof(io_stat_t));
688         io->interval=interval;
689         if((!filter)||(filter[0]==0)){
690                 io->num_items=1;
691                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
692                 io->filters=g_malloc(sizeof(char *)*io->num_items);
693
694                 register_io_tap(io, 0, NULL);
695         } else {
696                 const char *str,*pos;
697                 char *tmp;
698                 int i;
699                 /* find how many ',' separated filters we have */
700                 str=filter;
701                 io->num_items=1;
702                 while((str=strchr(str,','))){
703                         io->num_items++;
704                         str++;
705                 }
706
707                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
708                 io->filters=g_malloc(sizeof(char *)*io->num_items);
709
710                 /* for each filter, register a tap listener */
711                 i=0;
712                 str=filter;
713                 do{
714                         pos=strchr(str,',');
715                         if(pos==str){
716                                 register_io_tap(io, i, NULL);
717                         } else if(pos==NULL) {
718                                 tmp=g_strdup(str);
719                                 register_io_tap(io, i, tmp);
720                         } else {
721                                 tmp=g_malloc((pos-str)+1);
722                                 g_strlcpy(tmp,str,(pos-str)+1);
723                                 register_io_tap(io, i, tmp);
724                         }
725                         str=pos+1;
726                         i++;
727                 } while(pos);
728         }
729 }
730
731 void
732 register_tap_listener_iostat(void)
733 {
734         register_stat_cmd_arg("io,stat,", iostat_init, NULL);
735 }