Try to make the buildbot happy
[metze/wireshark/wip.git] / ui / cli / 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 "globals.h"
42
43 #define CALC_TYPE_FRAMES 0
44 #define CALC_TYPE_BYTES  1
45 #define CALC_TYPE_FRAMES_AND_BYTES       2
46 #define CALC_TYPE_COUNT  3
47 #define CALC_TYPE_SUM    4
48 #define CALC_TYPE_MIN    5
49 #define CALC_TYPE_MAX    6
50 #define CALC_TYPE_AVG    7
51 #define CALC_TYPE_LOAD   8
52
53 typedef struct {
54         const char *func_name;
55         int calc_type;
56 } calc_type_ent_t;
57
58 static calc_type_ent_t calc_type_table[] = {
59         { "FRAMES", CALC_TYPE_FRAMES },
60         { "BYTES", CALC_TYPE_BYTES },
61         { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES },
62         { "COUNT", CALC_TYPE_COUNT },
63         { "SUM", CALC_TYPE_SUM },
64         { "MIN", CALC_TYPE_MIN },
65         { "MAX", CALC_TYPE_MAX },
66         { "AVG", CALC_TYPE_AVG },
67         { "LOAD", CALC_TYPE_LOAD },
68         { NULL, 0 }
69 };
70
71 typedef struct _io_stat_t {
72         guint64 interval;         /* The user-specified time interval (us) */
73     guint invl_prec;      /* Decimal precision of the time interval (1=10s, 2=100s etc) */
74         guint32 num_cols;     /* The number of columns of statistics in the table */
75         struct _io_stat_item_t 
76         *items;           /* Each item is a single cell in the table */   
77         const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
78     guint64 *max_vals;    /* The max value sans the decimal or nsecs portion in each stat column */
79     guint32 *max_frame;   /* The max frame number displayed in each stat column */
80 } io_stat_t;
81
82 typedef struct _io_stat_item_t {
83         io_stat_t *parent;
84     struct _io_stat_item_t *next;
85         struct _io_stat_item_t *prev;
86         guint64 time;             /* Time since start of capture (us)*/
87         int calc_type;        /* The statistic type */
88     int colnum;           /* Column number of this stat (0 to n) */
89         int hf_index;
90         guint32 frames;
91         guint32 num;          /* The sample size of a given statistic (only needed for AVG) */
92         guint64 counter;      /* The accumulated data for the calculation of that statistic */
93         gfloat float_counter;
94         gdouble double_counter;
95 } io_stat_item_t;
96
97 #define NANOSECS_PER_SEC 1000000000
98
99 static int
100 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
101 {
102         io_stat_t *parent;
103     io_stat_item_t *mit;
104         io_stat_item_t *it;
105         guint64 relative_time, rt;
106     nstime_t *new_time;
107     GPtrArray *gp;
108         guint i;
109     int ftype;
110     
111     mit = (io_stat_item_t *) arg;
112     parent = mit->parent;
113         relative_time = (guint64)((pinfo->fd->rel_ts.secs*1000000) + ((pinfo->fd->rel_ts.nsecs+500)/1000));
114
115         /* The prev item before the main one is always the last interval we saw packets for */
116         it = mit->prev;
117
118         /* XXX for the time being, just ignore all frames that are in the past.
119            should be fixed in the future but hopefully it is uncommon */
120         if(relative_time < it->time){
121                 return FALSE;
122         }
123
124         /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
125     *  between the last struct and this one. If an item was not found in a previous interval, an empty 
126     *  struct will be created for it. */
127         rt = relative_time;
128         while (rt >= it->time + parent->interval) {
129                 it->next = (io_stat_item_t *)g_malloc(sizeof(io_stat_item_t));
130                 it->next->prev = it;
131                 it->next->next = NULL;
132                 it = it->next;
133                 mit->prev = it;
134
135                 it->time = it->prev->time + parent->interval;
136                 it->frames = 0;
137                 it->counter = 0;
138                 it->float_counter = 0;
139                 it->double_counter = 0;
140                 it->num = 0;
141                 it->calc_type = it->prev->calc_type;
142                 it->hf_index = it->prev->hf_index;
143         it->colnum = it->prev->colnum;
144         }
145
146         /* Store info in the current structure */
147         it->frames++;
148
149     switch(it->calc_type) {
150         case CALC_TYPE_FRAMES:
151         case CALC_TYPE_BYTES:
152         case CALC_TYPE_FRAMES_AND_BYTES:
153                 it->counter += pinfo->fd->pkt_len;
154                 break;
155         case CALC_TYPE_COUNT:
156                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
157                 if(gp){
158                         it->counter += gp->len;
159                 }
160                 break;
161         case CALC_TYPE_SUM:
162                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
163                 if(gp){
164                         guint64 val;
165
166                         for(i=0;i<gp->len;i++){
167                                 switch(proto_registrar_get_ftype(it->hf_index)){
168                                 case FT_UINT8:
169                                 case FT_UINT16:
170                                 case FT_UINT24:
171                                 case FT_UINT32:
172                                         it->counter += fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
173                                         break;
174                                 case FT_UINT64:
175                                         it->counter += fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
176                                         break;
177                                 case FT_INT8:
178                                 case FT_INT16:
179                                 case FT_INT24:
180                                 case FT_INT32:
181                                         it->counter += fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
182                                         break;
183                                 case FT_INT64:
184                                         it->counter += (gint64)fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
185                                         break;
186                                 case FT_FLOAT:
187                                         it->float_counter += 
188                         (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
189                                         break;
190                                 case FT_DOUBLE:
191                                         it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
192                                         break;
193                                 case FT_RELATIVE_TIME:
194                                         new_time = fvalue_get(&((field_info *)gp->pdata[i])->value);
195                                         val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
196                                         it->counter  +=  val;
197                                         break;
198                                 }
199                         }
200                 }
201                 break;
202         case CALC_TYPE_MIN:
203                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
204                 if(gp){
205                         guint64 val;
206                         gfloat float_val;
207                         gdouble double_val;
208
209                         ftype=proto_registrar_get_ftype(it->hf_index);
210                         for(i=0;i<gp->len;i++){
211                                 switch(ftype){
212                                 case FT_UINT8:
213                                 case FT_UINT16:
214                                 case FT_UINT24:
215                                 case FT_UINT32:
216                                         val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
217                                         if ((it->frames==1 && i==0) || (val < it->counter)) {
218                                                 it->counter=val;
219                                         }
220                                         break;
221                                 case FT_UINT64:
222                                         val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
223                                         if((it->frames==1 && i==0) || (val < it->counter)){
224                                                 it->counter=val;
225                                         }
226                                         break;
227                                 case FT_INT8:
228                                 case FT_INT16:
229                                 case FT_INT24:
230                                 case FT_INT32:
231                                         val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
232                                         if((it->frames==1 && i==0) || ((gint32)val < (gint32)it->counter)) {
233                                                 it->counter=val;
234                                         }
235                                         break;
236                                 case FT_INT64:
237                                         val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
238                                         if((it->frames==1 && i==0) || ((gint64)val < (gint64)it->counter)) {
239                                                 it->counter=val;
240                                         } 
241                                         break;
242                                 case FT_FLOAT:
243                                         float_val=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
244                                         if((it->frames==1 && i==0) || (float_val < it->float_counter)) {
245                                                 it->float_counter=float_val;
246                                         }
247                                         break;
248                                 case FT_DOUBLE:
249                                         double_val=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
250                                         if((it->frames==1 && i==0) || (double_val < it->double_counter)) {
251                                                 it->double_counter=double_val;
252                                         } 
253                                         break;
254                                 case FT_RELATIVE_TIME:
255                                         new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
256                                         val = (guint64)new_time->secs * NANOSECS_PER_SEC + new_time->nsecs;
257                                         if((it->frames==1 && i==0) || (val < it->counter)) {
258                                                 it->counter=val;
259                                         }
260                                         break;
261                                 }
262                         }
263                 }
264                 break;
265         case CALC_TYPE_MAX:
266                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
267                 if(gp){
268                         guint64 val;
269                         gfloat float_val;
270                         gdouble double_val;
271
272                         ftype=proto_registrar_get_ftype(it->hf_index);
273                         for(i=0;i<gp->len;i++){
274                                 switch(ftype){
275                                 case FT_UINT8:
276                                 case FT_UINT16:
277                                 case FT_UINT24:
278                                 case FT_UINT32:
279                                         val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
280                                         if(val > it->counter)
281                                                 it->counter=val;
282                                         break;
283                                 case FT_UINT64:
284                                         val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
285                                         if(val > it->counter) 
286                                                 it->counter=val;
287                                         break;
288                                 case FT_INT8:
289                                 case FT_INT16:
290                                 case FT_INT24:
291                                 case FT_INT32:
292                                         val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
293                                         if((gint32)val > (gint32)it->counter) 
294                                                 it->counter=val;
295                                         break;
296                                 case FT_INT64:
297                                         val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
298                                         if ((gint64)val > (gint64)it->counter) 
299                                                 it->counter=val;
300                                         break;
301                                 case FT_FLOAT:
302                                         float_val = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
303                                         if(float_val > it->float_counter)
304                         it->float_counter=float_val;
305                                         break;
306                                 case FT_DOUBLE:
307                                         double_val = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
308                                         if(double_val > it->double_counter)
309                         it->double_counter=double_val;
310                                         break;
311                                 case FT_RELATIVE_TIME:
312                                         new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
313                                         val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
314                                         if (val>it->counter)
315                                                 it->counter=val;
316                     break;
317                                 }
318                         }
319                 }
320                 break;
321         case CALC_TYPE_AVG:
322                 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
323                 if(gp){
324                         guint64 val;
325                 
326                         ftype=proto_registrar_get_ftype(it->hf_index);
327                         for(i=0;i<gp->len;i++){
328                                 it->num++;
329                                 switch(ftype) {
330                                 case FT_UINT8:
331                                 case FT_UINT16:
332                                 case FT_UINT24:
333                                 case FT_UINT32:
334                                         val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
335                                         it->counter += val;
336                                         break;
337                                 case FT_UINT64:
338                                 case FT_INT64:
339                                         val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
340                                         it->counter += val;
341                                         break;
342                                 case FT_INT8:
343                                 case FT_INT16:
344                                 case FT_INT24:
345                                 case FT_INT32:
346                                         val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
347                                         it->counter += val;
348                                         break;
349                                 case FT_FLOAT:
350                                         it->float_counter += (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
351                                         break;
352                                 case FT_DOUBLE:
353                                         it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
354                                         break;
355                                 case FT_RELATIVE_TIME:
356                                         new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
357                                         val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
358                                         it->counter += val;
359                                         break;
360                                 }
361                         }
362                 }
363                 break;
364         case CALC_TYPE_LOAD:
365                 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
366                 if (gp) {
367                         ftype = proto_registrar_get_ftype(it->hf_index);
368                         if (ftype != FT_RELATIVE_TIME) {
369                                 fprintf(stderr,
370                                         "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
371                                 exit(10);
372                         }
373                         for(i=0;i<gp->len;i++){
374                                 guint64 val;
375                                 int tival;
376                                 io_stat_item_t *pit;
377
378                                 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
379                                 val = (guint64)((new_time->secs*1000000) + (new_time->nsecs/1000));
380                                 tival = (int)(val % parent->interval);
381                                 it->counter += tival;
382                                 val -= tival;
383                                 pit = it->prev;
384                                 while (val > 0) {
385                                         if (val < (guint64)parent->interval) {
386                                                 pit->counter += val;
387                                                 val = 0;
388                                                 break;
389                                         }
390                                         pit->counter += parent->interval;
391                                         val -= parent->interval;
392                                         pit = pit->prev;
393                                 }
394                         }
395                 }
396                 break;
397         }
398         /* Store the highest value for this item in order to determine the width of each stat column. 
399     *  For real numbers we only need to know its magnitude (the value to the left of the decimal point
400     *  so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
401     *  calc the average, round it to the next second and store the seconds. For all other calc types
402     *  of RELATIVE_TIME fields, store the counters without modification. 
403     *  fields. */
404     switch(it->calc_type) {
405             case CALC_TYPE_FRAMES:
406             case CALC_TYPE_FRAMES_AND_BYTES:
407             parent->max_frame[it->colnum] = 
408                 MAX(parent->max_frame[it->colnum], it->frames);
409             if (it->calc_type==CALC_TYPE_FRAMES_AND_BYTES)
410                 parent->max_vals[it->colnum] = 
411                     MAX(parent->max_vals[it->colnum], it->counter);
412             
413         case CALC_TYPE_BYTES:
414             case CALC_TYPE_COUNT:
415             case CALC_TYPE_LOAD:
416             parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
417             break;
418             case CALC_TYPE_SUM:
419             case CALC_TYPE_MIN:
420             case CALC_TYPE_MAX:
421             ftype=proto_registrar_get_ftype(it->hf_index);
422             switch(ftype) {
423                             case FT_FLOAT:
424                                     parent->max_vals[it->colnum] = 
425                         MAX(parent->max_vals[it->colnum], (guint64)(it->float_counter+0.5));
426                                     break;
427                             case FT_DOUBLE:
428                                     parent->max_vals[it->colnum] = 
429                         MAX(parent->max_vals[it->colnum],(guint64)(it->double_counter+0.5));
430                                     break;
431                             case FT_RELATIVE_TIME:
432                     parent->max_vals[it->colnum] = 
433                         MAX(parent->max_vals[it->colnum], it->counter); 
434                                     break;                          
435                 default:
436                     /* UINT16-64 and INT8-64 */
437                     parent->max_vals[it->colnum] = 
438                         MAX(parent->max_vals[it->colnum], it->counter);
439                     break;
440                 }
441             break;
442             case CALC_TYPE_AVG:
443             ftype=proto_registrar_get_ftype(it->hf_index);
444             switch(ftype) {
445                             case FT_FLOAT:
446                                     parent->max_vals[it->colnum] = 
447                         MAX(parent->max_vals[it->colnum], (guint64)it->float_counter/it->num);
448                                     break;
449                             case FT_DOUBLE:
450                                     parent->max_vals[it->colnum] = 
451                         MAX(parent->max_vals[it->colnum],(guint64)it->double_counter/it->num);
452                                     break;
453                             case FT_RELATIVE_TIME:
454                     parent->max_vals[it->colnum] = 
455                         MAX(parent->max_vals[it->colnum], ((it->counter/it->num) + 500000000) / NANOSECS_PER_SEC); 
456                                     break;
457                 default:
458                     /* UINT16-64 and INT8-64 */
459                     parent->max_vals[it->colnum] = 
460                         MAX(parent->max_vals[it->colnum], it->counter/it->num);
461                     break;
462                 }
463     }
464     return TRUE;
465 }
466
467 static int 
468 magnitude (guint64 val, int max_w)
469 {
470     int i, mag=0;
471
472     for (i=0; i<max_w; i++) {
473         mag++;
474         if ((val /= 10)==0) 
475             break;            
476     }
477     return(mag);
478 }
479
480 /*  
481 *  Print the calc_type_table[] function label centered in the column header. 
482 */
483 static void
484 printcenter (const char *label, int lenval, int numpad)
485 {
486     int lenlab = (int) strlen(label), len;
487     const char spaces[]="      ", *spaces_ptr;
488
489     len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
490     if (len > 0 && len < 6) {
491         spaces_ptr = &spaces[len];       
492         if ((lenval-lenlab)%2==0) {
493             printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
494         } else {
495             printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);             
496         }
497     } else if (len > 0 && len <= 15) {
498         printf("%s|", label);
499     }
500 }
501
502 typedef struct {
503     int fr;  /* Width of this FRAMES column sans padding and border chars */
504     int val; /* Width of this non-FRAMES column sans padding and border chars */
505 } column_width;
506
507 static void
508 iostat_draw(void *arg)
509 {
510         guint32 num;
511     guint64 interval, duration, t, invl_end;
512         int i, j, k, num_cols, num_rows, dv, dur_secs, dur_mag, invl_mag, invl_prec, tabrow_w,
513             borderlen, invl_col_w, numpad=1, namelen, len_filt, type, maxfltr_w, ftype;
514     int fr_mag;    /* The magnitude of the max frame number in this column */
515     int val_mag;   /* The magnitude of the max value in this column */
516     gboolean last_row=FALSE;
517     char *spaces, *spaces_s, *filler_s=NULL, **fmts, *fmt=NULL;
518     const char *filter;
519     static gchar dur_mag_s[3], invl_mag_s[3], invl_prec_s[3], fr_mag_s[3], val_mag_s[3], *invl_fmt, *full_fmt;
520         io_stat_item_t *mit, **stat_cols, *item;
521         io_stat_t *iot;
522     column_width *col_w; 
523
524     mit = (io_stat_item_t *)arg;
525     iot = mit->parent;
526     num_cols = iot->num_cols;
527     col_w = (column_width *)g_malloc(sizeof(column_width) * num_cols);
528     fmts = (char **)g_malloc(sizeof(char *) * num_cols);
529     duration = (guint64)((cfile.elapsed_time.secs*1000000) + ((cfile.elapsed_time.nsecs+500)/1000));
530
531     /* Store the pointer to each stat column */
532     stat_cols = (io_stat_item_t **) g_malloc(sizeof(io_stat_item_t *) * num_cols);
533     for (j=0; j<num_cols; j++)
534         stat_cols[j] = &iot->items[j];
535
536     /* The following prevents gross inaccuracies when the user specifies an interval that is greater
537     *  than the capture duration. */
538     if (iot->interval > duration || iot->interval==G_MAXINT32) {
539         interval = duration;
540         iot->interval = G_MAXINT32;
541     } else {
542         interval = iot->interval;
543     }
544
545     /* Calc the capture duration's magnitude (dur_mag) */
546     dur_secs = (int)duration/1000000;
547     dur_mag = magnitude((guint64)dur_secs, 5);
548     g_snprintf(dur_mag_s, 3, "%u", dur_mag);  
549
550     /* Calc the interval's magnitude */
551     invl_mag = magnitude((guint64)interval/1000000, 5); 
552
553     /* Set or get the interval precision */
554     if (interval==duration) {
555         /* 
556         * An interval arg of 0 or an interval size exceeding the capture duration was specified.
557         * Set the decimal precision of duration based on its magnitude. */
558         if (dur_mag >= 2)
559             invl_prec = 1;
560         else if (dur_mag==1) 
561             invl_prec = 3;
562         else
563             invl_prec = 6;
564
565         borderlen = 30 + dur_mag + (invl_prec==0 ? 0 : invl_prec+1);
566     } else {
567         invl_prec = iot->invl_prec;
568         borderlen = 24 + invl_mag + (invl_prec==0 ? 0 : invl_prec+1);
569     }
570
571     /* Round the duration according to invl_prec */
572     dv=1000000;
573     for (i=0; i<invl_prec; i++)
574         dv /= 10;
575     duration = duration + (5*(dv/10));
576     if (iot->interval==G_MAXINT32) 
577         interval = duration;
578
579     /* Recalc the dur_mag in case rounding has increased its magnitude */
580     dur_secs = (int)duration/1000000;
581     dur_mag = magnitude((guint64)dur_secs, 5);
582
583     /* Calc the width of the time interval column (incl borders and padding). */
584     if (invl_prec==0) 
585         invl_col_w = (2*dur_mag) + 8;
586     else
587         invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
588     invl_col_w = MAX(invl_col_w, 12);
589     borderlen = MAX(borderlen, invl_col_w);
590
591     /* Calc the total width of each row in the stats table and build the printf format string for each
592     *  column based on its field type, width, and name length.
593     *  NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
594     *        is an *integer*. */
595     tabrow_w = invl_col_w;
596     for (j=0; j<num_cols; j++) {
597         type = iot->items[j].calc_type;
598         if (type==CALC_TYPE_FRAMES_AND_BYTES) {
599             namelen = 5;
600         } else {
601             namelen = (int) strlen(calc_type_table[type].func_name);
602         }
603         if(type==CALC_TYPE_FRAMES
604             || type==CALC_TYPE_FRAMES_AND_BYTES) {
605
606             fr_mag = magnitude(iot->max_frame[j], 15);
607             fr_mag = MAX(6, fr_mag);
608             col_w[j].fr = fr_mag;
609             tabrow_w += col_w[j].fr + 3;
610             g_snprintf(fr_mag_s, 3, "%u", fr_mag);
611
612             if (type==CALC_TYPE_FRAMES) {
613                 fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);                    
614             } else { 
615                 /* CALC_TYPE_FRAMES_AND_BYTES 
616                 */
617                 val_mag = magnitude(iot->max_vals[j], 15);
618                 val_mag = MAX(5, val_mag);
619                 col_w[j].val = val_mag;
620                 tabrow_w += (col_w[j].val + 3);
621                 g_snprintf(val_mag_s, 3, "%u", val_mag);
622                 fmt = g_strconcat(" %", fr_mag_s, "u |", " %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
623             }
624             if (fmt)
625                 fmts[j] = fmt;
626             continue;
627         }
628         switch(type) {
629         case CALC_TYPE_BYTES:
630         case CALC_TYPE_COUNT:
631
632             val_mag = magnitude(iot->max_vals[j], 15);
633             val_mag = MAX(5, val_mag);
634             col_w[j].val = val_mag;
635             g_snprintf(val_mag_s, 3, "%u", val_mag);
636             fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
637             break;
638             
639         default:
640             ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
641             switch (ftype) {    
642                 case FT_FLOAT:
643                 case FT_DOUBLE:
644                     val_mag = magnitude(iot->max_vals[j], 15);
645                     g_snprintf(val_mag_s, 3, "%u", val_mag);
646                     fmt = g_strconcat(" %", val_mag_s, ".6f |", NULL);
647                     col_w[j].val = val_mag + 7;
648                     break;
649                 case FT_RELATIVE_TIME:
650                     /* Convert FT_RELATIVE_TIME field to seconds 
651                     *  CALC_TYPE_LOAD was already converted in iostat_packet() ) */
652                     if (type==CALC_TYPE_LOAD) {
653                         iot->max_vals[j] /= interval;
654                     } else {
655                         iot->max_vals[j] = (iot->max_vals[j] + 500000000) / NANOSECS_PER_SEC;
656                     }
657                     val_mag = magnitude(iot->max_vals[j], 15);
658                     g_snprintf(val_mag_s, 3, "%u", val_mag);
659                     fmt = g_strconcat(" %", val_mag_s, "u.%06u |", NULL);
660                     col_w[j].val = val_mag + 7;
661                    break;       
662                 
663                 default:
664                     val_mag = magnitude(iot->max_vals[j], 15);
665                     val_mag = MAX(namelen, val_mag);
666                     col_w[j].val = val_mag;
667                     g_snprintf(val_mag_s, 3, "%u", val_mag);
668                     
669                     switch (ftype) {
670                     case FT_UINT8:
671                     case FT_UINT16:
672                             case FT_UINT24:
673                             case FT_UINT32:
674                             case FT_UINT64:
675                         fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
676                         break;            
677                     case FT_INT8:
678                             case FT_INT16:
679                             case FT_INT24:
680                             case FT_INT32:
681                             case FT_INT64:
682                         fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "d |", NULL);
683                         break;
684                     }
685             } /* End of ftype switch */
686         } /* End of calc_type switch */
687         tabrow_w += col_w[j].val + 3;
688         if (fmt) 
689             fmts[j] = fmt;
690     } /* End of for loop (columns) */
691
692     borderlen = MAX(borderlen, tabrow_w); 
693
694     /* Calc the max width of the list of filters. */
695     maxfltr_w = 0;
696     for(j=0; j<num_cols; j++) {
697         if (iot->filters[j]) {
698             k = (int) (strlen(iot->filters[j]) + 11);
699             maxfltr_w = MAX(maxfltr_w, k);
700         } else {
701             maxfltr_w = MAX(maxfltr_w, 26);
702         }
703     }
704     /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
705     *  (which currently = borderlen); however, if the filter width exceeds the table width and the
706     *  table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
707     *  The filters will wrap at the lesser of borderlen-2 and the last space in the filter. 
708     *  NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
709     *  XXX: A pref could be added to change the max width from the default size of 102. */
710     if (maxfltr_w > borderlen && borderlen < 102)
711             borderlen = MIN(maxfltr_w, 102);
712
713     /* Prevent double right border by adding a space */
714     if (borderlen-tabrow_w==1)
715         borderlen++; 
716
717     /* Display the top border */
718     printf("\n");
719     for (i=0; i<borderlen; i++)
720             printf("=");
721     
722     spaces = (char*) g_malloc(borderlen+1);
723     for (i=0; i<borderlen; i++) 
724         spaces[i] = ' ';
725     spaces[borderlen] = '\0';
726     
727     spaces_s = &spaces[16];
728     printf("\n| IO Statistics%s|\n", spaces_s);
729     spaces_s = &spaces[2];
730     printf("|%s|\n", spaces_s);
731    
732     g_snprintf(invl_mag_s, 3, "%u", invl_mag);
733     if (invl_prec > 0) {
734         g_snprintf(invl_prec_s, 3, "%u", invl_prec);  
735         invl_fmt = g_strconcat("%", invl_mag_s, "u.%0", invl_prec_s, "u", NULL);            
736         if (interval==duration) {
737             full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs (dur)%s", NULL);
738             spaces_s = &spaces[30+invl_mag+invl_prec];
739         } else {
740             full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs%s", NULL);
741             spaces_s = &spaces[24+invl_mag+invl_prec];
742         }
743         printf(full_fmt, (guint32)interval/1000000,
744                             (guint32)((interval%1000000)/dv), spaces_s);
745     } else {
746         invl_fmt = g_strconcat("%", invl_mag_s, "u", NULL);
747         full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs%s", NULL);
748         spaces_s = &spaces[23 + invl_mag];
749         printf(full_fmt, (guint32)interval/1000000, spaces_s);
750     }
751     g_free(invl_fmt);
752     g_free(full_fmt);
753
754     if (invl_prec > 0) 
755         invl_fmt = g_strconcat("%", dur_mag_s, "u.%0", invl_prec_s, "u", NULL);
756     else 
757         invl_fmt = g_strconcat("%", dur_mag_s, "u", NULL);
758     
759     /* Display the list of filters and their column numbers vertically */
760     printf("|\n| Col");
761     for(j=0; j<num_cols; j++){
762         printf((j==0 ? "%2u: " : "|    %2u: "), j+1);
763         if (!iot->filters[j] || (iot->filters[j]==0)) { 
764             /* 
765             * An empty (no filter) comma field was specified */
766             spaces_s = &spaces[16 + 10];
767             printf("Frames and bytes%s|\n", spaces_s);
768         } else {
769             filter = iot->filters[j];
770             len_filt = (int) strlen(filter);
771             
772             /* If the width of the widest filter exceeds the width of the stat table, borderlen has
773             *  been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
774             if (len_filt+11 <= borderlen) {
775                 printf("%s", filter);
776                 if (len_filt+11 <= borderlen) {
777                     spaces_s = &spaces[len_filt + 10];
778                     printf("%s", spaces_s);
779                 }
780                 printf("|\n");
781             } else {
782                 gchar *sfilter1, *sfilter2;
783                 const gchar *pos;
784                 gsize len;
785                 int next_start, max_w=borderlen-11;
786                 
787                 do {
788                     if (len_filt > max_w) {
789                         sfilter1 = g_strndup( (gchar *) filter, (gsize) max_w);
790                         /* 
791                         * Find the pos of the last space in sfilter1. If a space is found, set
792                         * sfilter2 to the string prior to that space and print it; otherwise, wrap
793                         * the filter at max_w. */
794                         pos = g_strrstr(sfilter1, " ");
795                         if (pos) {
796                             len = (gsize)(pos-sfilter1);
797                             next_start = (int) len+1;
798                         } else {
799                             len = (gsize) strlen(sfilter1);
800                             next_start = (int)len;
801                         }
802                         sfilter2 = g_strndup(sfilter1, len);
803                         printf("%s%s|\n", sfilter2, &spaces[len+10]);
804                         g_free(sfilter1);
805                         g_free(sfilter2);
806
807                         printf("|        ");
808                         filter = &filter[next_start];
809                         len_filt = (int) strlen(filter);
810                     } else {
811                         printf("%s%s|\n", filter, &spaces[((int)strlen(filter))+10]);
812                         break;
813                     }
814                 } while (1); 
815             }
816         }
817         }
818
819     printf("|-");
820         for(i=0;i<borderlen-3;i++){
821                 printf("-");
822         }
823         printf("|\n");
824
825     /* Display spaces above "Interval (s)" label */
826     spaces_s = &spaces[borderlen-(invl_col_w-2)];
827     printf("|%s|", spaces_s);
828
829     /* Display column number headers */
830         for(j=0; j<num_cols; j++) {
831         item = stat_cols[j];
832                 if(item->calc_type==CALC_TYPE_FRAMES_AND_BYTES)
833             spaces_s = &spaces[borderlen - (col_w[j].fr + col_w[j].val)] - 3; 
834         else if (item->calc_type==CALC_TYPE_FRAMES)
835             spaces_s = &spaces[borderlen - col_w[j].fr]; 
836         else
837             spaces_s = &spaces[borderlen - col_w[j].val]; 
838
839         printf("%-2u%s|", j+1, spaces_s);
840         }
841     if (tabrow_w < borderlen) {
842         filler_s = &spaces[tabrow_w+1];
843         printf("%s|", filler_s);
844     }
845     
846         printf("\n| Interval");
847     spaces_s = &spaces[borderlen-(invl_col_w-11)];
848     printf("%s|", spaces_s);
849
850     /* Display the stat label in each column */
851     for(j=0; j<num_cols; j++) {
852         type = stat_cols[j]->calc_type;
853         if(type==CALC_TYPE_FRAMES) {
854             printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
855         } else if (type==CALC_TYPE_FRAMES_AND_BYTES) {
856             printcenter ("Frames", col_w[j].fr, numpad);
857             printcenter ("Bytes", col_w[j].val, numpad);
858         } else {
859             printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
860             }
861         }
862     if (filler_s)
863         printf("%s|", filler_s);
864     printf("\n|-");
865
866         for(i=0; i<tabrow_w-3; i++)
867                 printf("-");
868     printf("|");
869
870     if (tabrow_w < borderlen)
871         printf("%s|", &spaces[tabrow_w+1]);
872
873         printf("\n");
874         t=0;
875     full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
876     num_rows = (int)(duration/interval) + (((duration%interval+500000)/1000000) > 0 ? 1 : 0);
877
878     /* Display the table values.
879     * The outer loop is for time interval rows and the inner loop is for stat column items.*/
880     for (i=0; i<num_rows; i++) {
881
882         if (i==num_rows-1)
883             last_row = TRUE;
884
885         /* Display the interval for this row */
886         if (!last_row) {
887             invl_end = t + interval;
888         } else {
889             invl_end = duration;
890         }
891         if (invl_prec==0) {
892             printf(full_fmt, (guint32)(t/1000000),
893                              (guint32)(invl_end/1000000));
894         } else {
895             printf(full_fmt, (guint32)(t/1000000),
896                              (guint32)(t%1000000) / dv,
897                              (guint32) (invl_end/1000000),
898                              (guint32)((invl_end%1000000) / dv));
899         }
900
901         /* Display all the stat values in this row */
902         for (j=0; j<num_cols; j++) {
903             /*
904             * Point to the list for this stat (column). */
905             item = stat_cols[j];
906             /* 
907             * Point to the item in the current row (time interval i) within this list. */            
908             for (k=0; k<i; k++)
909                 if (item && item->next)
910                     item = item->next;
911                 else
912                     item = NULL;
913             fmt = fmts[j];
914
915             if (item) {
916                 switch(item->calc_type) {
917                     case CALC_TYPE_FRAMES:
918                         printf(fmt, item->frames);
919                         break;
920                     case CALC_TYPE_BYTES:
921                                 case CALC_TYPE_COUNT:
922                         printf(fmt, item->counter);
923                         break;
924                     case CALC_TYPE_FRAMES_AND_BYTES:
925                         printf(fmt, item->frames, item->counter);
926                         break;
927
928                     case CALC_TYPE_SUM:
929                                 case CALC_TYPE_MIN:
930                                 case CALC_TYPE_MAX:
931                         ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
932                                         switch(ftype){
933                                         case FT_FLOAT:
934                                                 printf(fmt, item->float_counter);
935                                                 break;
936                                         case FT_DOUBLE:
937                                                 printf(fmt, item->double_counter);
938                                                 break;
939                                         case FT_RELATIVE_TIME:
940                                                 item->counter = (item->counter + 500) / 1000;
941                                                 printf(fmt, (int)(item->counter/1000000), (int)(item->counter%1000000));
942                                                 break;
943                         default:
944                             printf(fmt, item->counter);
945                                                 break;
946                                         }
947                                         break;
948
949                                 case CALC_TYPE_AVG:
950                         num = item->num;
951                                         if(num==0)
952                                                 num=1;
953                         ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
954                                         switch(ftype){
955                                         case FT_FLOAT:
956                                                 printf(fmt, item->float_counter/num);
957                                                 break;
958                                         case FT_DOUBLE:
959                                                 printf(fmt, item->double_counter/num);
960                                                 break;
961                                         case FT_RELATIVE_TIME:
962                                                 item->counter = ((item->counter/num) + 500) / 1000;
963                                                 printf(fmt,
964                                                         (int)(item->counter/1000000), (int)(item->counter%1000000));
965                                                 break;
966                         default:
967                             printf(fmt, item->counter/num);
968                                                 break;
969                                         }
970                                         break;
971
972                                 case CALC_TYPE_LOAD:
973                         ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
974                                         switch(ftype){
975                                         case FT_RELATIVE_TIME:
976                             if (!last_row) {
977                                 printf(fmt,
978                                     (int) (item->counter/interval), 
979                                     (int)((item->counter%interval)*1000000 / interval));                        
980                             } else {
981                                 printf(fmt,
982                                     (int) (item->counter/(invl_end-t)),
983                                     (int)((item->counter%(invl_end-t))*1000000 / (invl_end-t)));
984                             }
985                                                 break;
986                                         }
987                                         break;
988                             }
989                 if (last_row)
990                     if (fmt)
991                         g_free(fmt);
992
993                     } else {
994                 printf(fmt, (guint64)0);
995             }
996         }
997         if (filler_s)
998             printf("%s|", filler_s);
999         printf("\n");
1000         t += interval;
1001        
1002     }
1003         for(i=0;i<borderlen;i++){
1004                 printf("=");
1005         }
1006     printf("\n");
1007     g_free(invl_fmt);
1008     g_free(col_w);
1009     g_free(full_fmt);
1010     g_free(spaces);
1011 }
1012
1013
1014 static void
1015 register_io_tap(io_stat_t *io, int i, const char *filter)
1016 {
1017         GString *error_string;
1018         const char *flt;
1019         int j;
1020         size_t namelen;
1021         const char *p, *parenp;
1022         char *field;
1023         header_field_info *hfi;
1024
1025         io->items[i].prev=&io->items[i];
1026         io->items[i].next=NULL;
1027         io->items[i].parent=io;
1028         io->items[i].time=0;
1029         io->items[i].calc_type=CALC_TYPE_FRAMES_AND_BYTES;
1030         io->items[i].frames=0;
1031         io->items[i].counter=0;
1032         io->items[i].num=0;
1033
1034         io->filters[i]=filter;
1035         flt=filter;
1036
1037         field=NULL;
1038         hfi=NULL;
1039         for(j=0; calc_type_table[j].func_name; j++){
1040                 namelen=strlen(calc_type_table[j].func_name);
1041                 if(filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
1042                         io->items[i].calc_type=calc_type_table[j].calc_type;
1043             io->items[i].colnum = i;
1044                         if(*(filter+namelen)=='(') {
1045                                 p=filter+namelen+1;
1046                                 parenp=strchr(p, ')');
1047                                 if(!parenp){
1048                                         fprintf(stderr, 
1049                         "\ntshark: Closing parenthesis missing from calculated expression.\n");
1050                                         exit(10);
1051                                 }
1052
1053                                 if(io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES){
1054                                         if(parenp!=p) {
1055                                                 fprintf(stderr, 
1056                             "\ntshark: %s does not require or allow a field name within the parens.\n",
1057                                                         calc_type_table[j].func_name);
1058                                                 exit(10);
1059                                         }
1060                                 } else {
1061                                         if(parenp==p) {
1062                                                         /* bail out if a field name was not specified */
1063                                                         fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n",
1064                                                                 calc_type_table[j].func_name);
1065                                                         exit(10);
1066                                         }
1067                                 }
1068
1069                                 field = (char *) g_malloc(parenp-p+1);
1070                                 memcpy(field, p, parenp-p);
1071                                 field[parenp-p] = '\0';
1072                                 flt=parenp + 1;
1073                                 if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
1074                                         break;
1075                                 hfi=proto_registrar_get_byname(field);
1076                                 if(!hfi){
1077                                         fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
1078                                                 field);
1079                                         g_free(field);
1080                                         exit(10);
1081                                 }
1082
1083                                 io->items[i].hf_index=hfi->id;
1084                                 break;
1085                         }
1086                 } else {
1087                         if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
1088                                 flt="";
1089             io->items[i].colnum = i;
1090                 }
1091         }
1092         if(hfi && !(io->items[i].calc_type==CALC_TYPE_BYTES ||
1093                             io->items[i].calc_type==CALC_TYPE_FRAMES ||
1094                             io->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES)){
1095                 /* check that the type is compatible */
1096                 switch(hfi->type){
1097                 case FT_UINT8:
1098                 case FT_UINT16:
1099                 case FT_UINT24:
1100                 case FT_UINT32:
1101                 case FT_UINT64:
1102                 case FT_INT8:
1103                 case FT_INT16:
1104                 case FT_INT24:
1105                 case FT_INT32:
1106                 case FT_INT64:
1107                         /* these types support all calculations */
1108                         break;
1109                 case FT_FLOAT:
1110                 case FT_DOUBLE:
1111                         /* these types only support SUM, COUNT, MAX, MIN, AVG */
1112                         switch(io->items[i].calc_type){
1113                         case CALC_TYPE_SUM:
1114                         case CALC_TYPE_COUNT:
1115                         case CALC_TYPE_MAX:
1116                         case CALC_TYPE_MIN:
1117                         case CALC_TYPE_AVG:
1118                                 break;
1119                         default:
1120                                 fprintf(stderr,
1121                                         "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1122                                         field,
1123                                         calc_type_table[j].func_name);
1124                                 exit(10);
1125                         }
1126                         break;
1127                 case FT_RELATIVE_TIME:
1128                         /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1129                         switch(io->items[i].calc_type){
1130                         case CALC_TYPE_SUM:
1131                         case CALC_TYPE_COUNT:
1132                         case CALC_TYPE_MAX:
1133                         case CALC_TYPE_MIN:
1134                         case CALC_TYPE_AVG:
1135                         case CALC_TYPE_LOAD:
1136                                 break;
1137                         default:
1138                                 fprintf(stderr,
1139                                         "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1140                                     field,
1141                                     calc_type_table[j].func_name);
1142                                 exit(10);
1143                         }
1144                         break;
1145                 default:
1146                         /*
1147                          * XXX - support all operations on floating-point
1148                          * numbers?
1149                          */
1150                         if(io->items[i].calc_type!=CALC_TYPE_COUNT){
1151                                 fprintf(stderr,
1152                                         "\ntshark: %s doesn't have integral values, so %s(*) "
1153                     "calculations are not supported on it.\n",
1154                                     field,
1155                                     calc_type_table[j].func_name);
1156                                 exit(10);
1157                         }
1158                         break;
1159                 }
1160                 g_free(field);
1161         }
1162
1163         error_string=register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
1164                                        iostat_packet, i?NULL:iostat_draw);
1165         if(error_string){
1166                 g_free(io->items);
1167                 g_free(io);
1168                 fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
1169                     error_string->str);
1170                 g_string_free(error_string, TRUE);
1171                 exit(1);
1172         }
1173 }
1174
1175 static void
1176 iostat_init(const char *optarg, void* userdata _U_)
1177 {
1178         gdouble interval_float;
1179         guint32 idx=0, i;
1180         io_stat_t *io;
1181     const gchar *filters=NULL, *str, *pos;
1182
1183         if (sscanf(optarg, "io,stat,%lf,%n", &interval_float, (int *)&idx)==0) {
1184                 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>]\" argument\n");
1185                 exit(1);
1186         }
1187         
1188     io = (io_stat_t *) g_malloc(sizeof(io_stat_t));
1189
1190         /* If interval is 0, calculate statistics over the whole file by setting the interval to
1191     *  G_MAXINT32 */
1192         if (interval_float==0) {
1193                 io->interval = G_MAXINT32;
1194         io->invl_prec = 0;
1195         } else {
1196                 /* Set interval to the number of us rounded to the nearest integer */
1197                 io->interval = (gint64)(interval_float*1000000.0+0.5);
1198         /*
1199         * Determine what interval precision the user has specified */
1200         io->invl_prec = 6;
1201         for (i=10; i<10000000; i*=10) { 
1202             if (io->interval%i > 0)
1203                 break;
1204             io->invl_prec--;
1205         }
1206         }
1207     if (io->interval < 1){
1208                 fprintf(stderr,
1209                         "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1210                 exit(10);
1211         }
1212
1213     /* Find how many ',' separated filters we have */
1214     io->num_cols = 1;
1215
1216     if (idx) {
1217         filters = optarg + idx;
1218         if (strlen(filters) > 0 ) {
1219             str = filters;
1220                 while((str = strchr(str, ','))) {
1221                         io->num_cols++;
1222                         str++;
1223             }
1224         }
1225         } else {
1226                 filters=NULL;
1227         }
1228
1229         io->items = (io_stat_item_t *) g_malloc(sizeof(io_stat_item_t) * io->num_cols);
1230         io->filters = g_malloc(sizeof(char *) * io->num_cols);
1231     io->max_vals = (guint64 *) g_malloc(sizeof(guint64) * io->num_cols);
1232     io->max_frame = (guint32 *) g_malloc(sizeof(guint32) * io->num_cols);
1233
1234     for (i=0; i<io->num_cols; i++) {
1235         io->max_vals[i] = 0;
1236         io->max_frame[i] = 0;
1237     } 
1238
1239     /* Register a tap listener for each filter */
1240     if((!filters) || (filters[0]==0)) {
1241         register_io_tap(io, 0, NULL);
1242     } else {
1243         gchar *filter;
1244                 i = 0;
1245                 str = filters;
1246                 do {
1247                         pos = (gchar*) strchr(str, ',');
1248                         if(pos==str){
1249                                 register_io_tap(io, i, NULL);
1250                         } else if (pos==NULL) {
1251                                 str = (char*) g_strstrip((gchar*)str);
1252                 filter = g_strdup((gchar*) str);
1253                 if (*filter)
1254                                     register_io_tap(io, i, filter);
1255                 else
1256                     register_io_tap(io, i, NULL);
1257                         } else {
1258                                 filter = g_malloc((pos-str)+1);
1259                                 g_strlcpy( filter, str, (gsize) ((pos-str)+1));
1260                 filter = g_strstrip(filter);
1261                 register_io_tap(io, i, (char *) filter);
1262                         }
1263                         str = pos+1;
1264                         i++;
1265                 } while(pos);
1266         }
1267 }
1268
1269 void
1270 register_tap_listener_iostat(void)
1271 {
1272         register_stat_cmd_arg("io,stat,", iostat_init, NULL);
1273 }