1.3.6 -> 1.5.0
[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         guint32 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         guint32 i;
323         gboolean more_items;
324         gint t;
325
326         iot=mit->parent;
327
328         printf("\n");
329         printf("===================================================================\n");
330         printf("IO Statistics\n");
331         if(iot->interval!=G_MAXINT32)
332                 printf("Interval: %d.%03d secs\n", iot->interval/1000, iot->interval%1000);
333         for(i=0;i<iot->num_items;i++){
334                 printf("Column #%u: %s\n",i,iot->filters[i]?iot->filters[i]:"");
335         }
336         printf("                ");
337         for(i=0;i<iot->num_items;i++){
338                 printf("|   Column #%-2u   ",i);
339         }
340         printf("\n");
341         printf("Time            ");
342         for(i=0;i<iot->num_items;i++){
343                 switch(iot->items[i].calc_type){
344                 case CALC_TYPE_BYTES:
345                         printf("|frames|  bytes  ");
346                         break;
347                 case CALC_TYPE_COUNT:
348                         printf("|          COUNT ");
349                         break;
350                 case CALC_TYPE_SUM:
351                         printf("|            SUM ");
352                         break;
353                 case CALC_TYPE_MIN:
354                         printf("|            MIN ");
355                         break;
356                 case CALC_TYPE_MAX:
357                         printf("|            MAX ");
358                         break;
359                 case CALC_TYPE_AVG:
360                         printf("|            AVG ");
361                         break;
362                 }
363         }
364         printf("\n");
365
366         items=g_malloc(sizeof(io_stat_item_t *)*iot->num_items);
367         frames=g_malloc(sizeof(guint64)*iot->num_items);
368         counters=g_malloc(sizeof(guint64)*iot->num_items);
369         num=g_malloc(sizeof(guint64)*iot->num_items);
370         /* preset all items at the first interval */
371         for(i=0;i<iot->num_items;i++){
372                 items[i]=&iot->items[i];
373         }
374
375         /* loop the items until we run out of them all */
376         t=0;
377         do {
378                 more_items=FALSE;
379                 for(i=0;i<iot->num_items;i++){
380                         frames[i]=0;
381                         counters[i]=0;
382                         num[i]=0;
383                 }
384                 for(i=0;i<iot->num_items;i++){
385                         if(items[i] && (t>=(items[i]->time+iot->interval))){
386                                 items[i]=items[i]->next;
387                         }
388
389                         if(items[i] && (t<(items[i]->time+iot->interval)) && (t>=items[i]->time) ){
390                                 frames[i]=items[i]->frames;
391                                 counters[i]=items[i]->counter;
392                                 num[i]=items[i]->num;
393                         }
394
395                         if(items[i]){
396                                 more_items=TRUE;
397                         }
398                 }
399
400                 if(more_items){
401                         if(iot->interval==G_MAXINT32) {
402                                 printf("000.000-         ");
403                         } else {
404                                 printf("%03d.%03d-%03d.%03d  ",
405                                         t/1000,t%1000,
406                                         (t+iot->interval)/1000,
407                                         (t+iot->interval)%1000);
408                         }
409                         for(i=0;i<iot->num_items;i++){
410                                 switch(iot->items[i].calc_type){
411                                 case CALC_TYPE_BYTES:
412                                         printf("%6" G_GINT64_MODIFIER "u %9" G_GINT64_MODIFIER "u ",frames[i], counters[i]);
413                                         break;
414                                 case CALC_TYPE_COUNT:
415                                         printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
416                                         break;
417                                 case CALC_TYPE_SUM:
418                                         printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
419                                         break;
420                                 case CALC_TYPE_MIN:
421                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
422                                         case FT_UINT8:
423                                         case FT_UINT16:
424                                         case FT_UINT24:
425                                         case FT_UINT32:
426                                         case FT_UINT64:
427                                                 printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
428                                                 break;
429                                         case FT_INT8:
430                                         case FT_INT16:
431                                         case FT_INT24:
432                                         case FT_INT32:
433                                         case FT_INT64:
434                                                 printf(" %15" G_GINT64_MODIFIER "d ", counters[i]);
435                                                 break;
436                                         case FT_RELATIVE_TIME:
437                                                 printf(" %11" G_GINT64_MODIFIER "d.%03d ", counters[i]/1000, (gint)counters[i]%1000);
438                                                 break;
439                                         }
440                                         break;
441                                 case CALC_TYPE_MAX:
442                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
443                                         case FT_UINT8:
444                                         case FT_UINT16:
445                                         case FT_UINT24:
446                                         case FT_UINT32:
447                                         case FT_UINT64:
448                                                 printf(" %15" G_GINT64_MODIFIER "u ", counters[i]);
449                                                 break;
450                                         case FT_INT8:
451                                         case FT_INT16:
452                                         case FT_INT24:
453                                         case FT_INT32:
454                                         case FT_INT64:
455                                                 printf(" %15" G_GINT64_MODIFIER "d ", counters[i]);
456                                                 break;
457                                         case FT_RELATIVE_TIME:
458                                                 printf(" %11" G_GINT64_MODIFIER "d.%03d ", counters[i]/1000, (gint)counters[i]%1000);
459                                                 break;
460                                         }
461                                         break;
462                                 case CALC_TYPE_AVG:
463                                         if(num[i]==0){
464                                                 num[i]=1;
465                                         }
466                                         switch(proto_registrar_get_ftype(iot->items[i].hf_index)){
467                                         case FT_UINT8:
468                                         case FT_UINT16:
469                                         case FT_UINT24:
470                                         case FT_UINT32:
471                                         case FT_UINT64:
472                                                 printf(" %15" G_GINT64_MODIFIER "u ", counters[i]/num[i]);
473                                                 break;
474                                         case FT_INT8:
475                                         case FT_INT16:
476                                         case FT_INT24:
477                                         case FT_INT32:
478                                         case FT_INT64:
479                                                 printf(" %15" G_GINT64_MODIFIER "d ", counters[i]/num[i]);
480                                                 break;
481                                         case FT_RELATIVE_TIME:
482                                                 counters[i]/=num[i];
483                                                 printf(" %11" G_GINT64_MODIFIER "d.%03d ", counters[i]/1000, (gint)counters[i]%1000);
484                                                 break;
485                                         }
486                                         break;
487
488                                 }
489                         }
490                         printf("\n");
491                 }
492
493                 t+=iot->interval;
494         } while(more_items);
495
496         printf("===================================================================\n");
497
498         g_free(items);
499         g_free(frames);
500         g_free(counters);
501         g_free(num);
502 }
503
504
505 typedef struct {
506         const char *func_name;
507         int calc_type;
508 } calc_type_ent_t;
509
510 static calc_type_ent_t calc_type_table[] = {
511         { "COUNT", CALC_TYPE_COUNT },
512         { "SUM", CALC_TYPE_SUM },
513         { "MIN", CALC_TYPE_MIN },
514         { "MAX", CALC_TYPE_MAX },
515         { "AVG", CALC_TYPE_AVG },
516         { NULL, 0 }
517 };
518
519 static void
520 register_io_tap(io_stat_t *io, int i, const char *filter)
521 {
522         GString *error_string;
523         const char *flt;
524         int j;
525         size_t namelen;
526         const char *p, *parenp;
527         char *field;
528         header_field_info *hfi;
529
530         io->items[i].prev=&io->items[i];
531         io->items[i].next=NULL;
532         io->items[i].parent=io;
533         io->items[i].time=0;
534         io->items[i].calc_type=CALC_TYPE_BYTES;
535         io->items[i].frames=0;
536         io->items[i].counter=0;
537         io->items[i].num=0;
538         io->filters[i]=filter;
539         flt=filter;
540
541         field=NULL;
542         hfi=NULL;
543         for(j=0; calc_type_table[j].func_name; j++){
544                 namelen=strlen(calc_type_table[j].func_name);
545                 if(filter
546                     && strncmp(filter, calc_type_table[j].func_name, namelen) == 0
547                     && *(filter+namelen)=='('){
548                         io->items[i].calc_type=calc_type_table[j].calc_type;
549
550                         p=filter+namelen+1;
551                         parenp=strchr(p, ')');
552                         if(!parenp){
553                                 fprintf(stderr, "tshark: Closing parenthesis missing from calculated expression.\n");
554                                 exit(10);
555                         }
556                         /* bail out if there was no field specified */
557                         if(parenp==p){
558                                 fprintf(stderr, "tshark: You didn't specify a field name for %s(*).\n",
559                                     calc_type_table[j].func_name);
560                                 exit(10);
561                         }
562                         field=g_malloc(parenp-p+1);
563                         if(!field){
564                                 fprintf(stderr, "tshark: Out of memory.\n");
565                                 exit(10);
566                         }
567                         memcpy(field, p, parenp-p);
568                         field[parenp-p] = '\0';
569                         flt=parenp + 1;
570
571                         hfi=proto_registrar_get_byname(field);
572                         if(!hfi){
573                                 fprintf(stderr, "tshark: There is no field named '%s'.\n",
574                                     field);
575                                 g_free(field);
576                                 exit(10);
577                         }
578
579                         io->items[i].hf_index=hfi->id;
580                         break;
581                 }
582         }
583         if(hfi && io->items[i].calc_type!=CALC_TYPE_BYTES){
584                 /* check that the type is compatible */
585                 switch(hfi->type){
586                 case FT_UINT8:
587                 case FT_UINT16:
588                 case FT_UINT24:
589                 case FT_UINT32:
590                 case FT_UINT64:
591                 case FT_INT8:
592                 case FT_INT16:
593                 case FT_INT24:
594                 case FT_INT32:
595                 case FT_INT64:
596                         /* these types support all calculations */
597                         break;
598                 case FT_RELATIVE_TIME:
599                         /* this type only supports SUM, COUNT, MAX, MIN, AVG */
600                         switch(io->items[i].calc_type){
601                         case CALC_TYPE_SUM:
602                         case CALC_TYPE_COUNT:
603                         case CALC_TYPE_MAX:
604                         case CALC_TYPE_MIN:
605                         case CALC_TYPE_AVG:
606                                 break;
607                         default:
608                                 fprintf(stderr,
609                                     "tshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
610                                     field,
611                                     calc_type_table[j].func_name);
612                                 exit(10);
613                         }
614                         break;
615                 default:
616                         /*
617                          * XXX - support all operations on floating-point
618                          * numbers?
619                          */
620                         if(io->items[i].calc_type!=CALC_TYPE_COUNT){
621                                 fprintf(stderr,
622                                     "tshark: %s doesn't have integral values, so %s(*) calculations are not supported on it.\n",
623                                     field,
624                                     calc_type_table[j].func_name);
625                                 exit(10);
626                         }
627                         break;
628                 }
629                 g_free(field);
630         }
631
632 /*
633 CALC_TYPE_SUM   2
634 CALC_TYPE_MIN   3
635 CALC_TYPE_MAX   4
636 CALC_TYPE_AVG   5
637 */
638
639         error_string=register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL, iostat_packet, i?NULL:iostat_draw);
640         if(error_string){
641                 g_free(io->items);
642                 g_free(io);
643                 fprintf(stderr, "tshark: Couldn't register io,stat tap: %s\n",
644                     error_string->str);
645                 g_string_free(error_string, TRUE);
646                 exit(1);
647         }
648 }
649
650 static void
651 iostat_init(const char *optarg, void* userdata _U_)
652 {
653         float interval_float;
654         gint32 interval;
655         int idx=0;
656         io_stat_t *io;
657         const char *filter=NULL;
658
659         if(sscanf(optarg,"io,stat,%f,%n",&interval_float,&idx)==1){
660                 if(idx){
661                         filter=optarg+idx;
662                 } else {
663                         filter=NULL;
664                 }
665         } else {
666                 fprintf(stderr, "tshark: invalid \"-z io,stat,<interval>[,<filter>]\" argument\n");
667                 exit(1);
668         }
669
670         /* if interval is 0, calculate statistics over the whole file
671          * by setting the interval to G_MAXINT32
672          */
673         if(interval_float==0) {
674                 interval=G_MAXINT32;
675         } else {
676                 /* make interval be number of ms */
677                 interval=(gint32)(interval_float*1000.0+0.9);
678         }
679
680         if(interval<1){
681                 fprintf(stderr, "tshark: \"-z\" interval must be >=0.001 seconds or 0.\n");
682                 exit(10);
683         }
684
685         io=g_malloc(sizeof(io_stat_t));
686         io->interval=interval;
687         if((!filter)||(filter[0]==0)){
688                 io->num_items=1;
689                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
690                 io->filters=g_malloc(sizeof(char *)*io->num_items);
691
692                 register_io_tap(io, 0, NULL);
693         } else {
694                 const char *str,*pos;
695                 char *tmp;
696                 int i;
697                 /* find how many ',' separated filters we have */
698                 str=filter;
699                 io->num_items=1;
700                 while((str=strchr(str,','))){
701                         io->num_items++;
702                         str++;
703                 }
704
705                 io->items=g_malloc(sizeof(io_stat_item_t)*io->num_items);
706                 io->filters=g_malloc(sizeof(char *)*io->num_items);
707
708                 /* for each filter, register a tap listener */
709                 i=0;
710                 str=filter;
711                 do{
712                         pos=strchr(str,',');
713                         if(pos==str){
714                                 register_io_tap(io, i, NULL);
715                         } else if(pos==NULL) {
716                                 tmp=g_strdup(str);
717                                 register_io_tap(io, i, tmp);
718                         } else {
719                                 tmp=g_malloc((pos-str)+1);
720                                 g_strlcpy(tmp,str,(pos-str)+1);
721                                 register_io_tap(io, i, tmp);
722                         }
723                         str=pos+1;
724                         i++;
725                 } while(pos);
726         }
727 }
728
729 void
730 register_tap_listener_iostat(void)
731 {
732         register_stat_cmd_arg("io,stat,", iostat_init, NULL);
733 }