replace SPDX identifier GPL-2.0+ with GPL-2.0-or-later.
[metze/wireshark/wip.git] / ui / cli / tap-iostat.c
1 /* tap-iostat.c
2  * iostat   2002 Ronnie Sahlberg
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later*/
9
10 #include "config.h"
11
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include <epan/epan_dissect.h>
16 #include <epan/tap.h>
17 #include <epan/stat_tap_ui.h>
18 #include "globals.h"
19
20 #define CALC_TYPE_FRAMES 0
21 #define CALC_TYPE_BYTES  1
22 #define CALC_TYPE_FRAMES_AND_BYTES 2
23 #define CALC_TYPE_COUNT  3
24 #define CALC_TYPE_SUM    4
25 #define CALC_TYPE_MIN    5
26 #define CALC_TYPE_MAX    6
27 #define CALC_TYPE_AVG    7
28 #define CALC_TYPE_LOAD   8
29
30 void register_tap_listener_iostat(void);
31
32 typedef struct {
33     const char *func_name;
34     int calc_type;
35 } calc_type_ent_t;
36
37 static calc_type_ent_t calc_type_table[] = {
38     { "FRAMES",       CALC_TYPE_FRAMES },
39     { "BYTES",        CALC_TYPE_BYTES },
40     { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES },
41     { "COUNT",        CALC_TYPE_COUNT },
42     { "SUM",          CALC_TYPE_SUM },
43     { "MIN",          CALC_TYPE_MIN },
44     { "MAX",          CALC_TYPE_MAX },
45     { "AVG",          CALC_TYPE_AVG },
46     { "LOAD",         CALC_TYPE_LOAD },
47     { NULL, 0 }
48 };
49
50 typedef struct _io_stat_t {
51     guint64 interval;     /* The user-specified time interval (us) */
52     guint invl_prec;      /* Decimal precision of the time interval (1=10s, 2=100s etc) */
53     int num_cols;         /* The number of columns of stats in the table */
54     struct _io_stat_item_t *items;  /* Each item is a single cell in the table */
55     time_t start_time;    /* Time of first frame matching the filter */
56     const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
57     guint64 *max_vals;    /* The max value sans the decimal or nsecs portion in each stat column */
58     guint32 *max_frame;   /* The max frame number displayed in each stat column */
59 } io_stat_t;
60
61 typedef struct _io_stat_item_t {
62     io_stat_t *parent;
63     struct _io_stat_item_t *next;
64     struct _io_stat_item_t *prev;
65     guint64 start_time;   /* Time since start of capture (us)*/
66     int calc_type;        /* The statistic type */
67     int colnum;           /* Column number of this stat (0 to n) */
68     int hf_index;
69     guint32 frames;
70     guint32 num;          /* The sample size of a given statistic (only needed for AVG) */
71     guint64 counter;      /* The accumulated data for the calculation of that statistic */
72     gfloat float_counter;
73     gdouble double_counter;
74 } io_stat_item_t;
75
76 #define NANOSECS_PER_SEC G_GUINT64_CONSTANT(1000000000)
77
78 static guint64 last_relative_time;
79
80 static int
81 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
82 {
83     io_stat_t *parent;
84     io_stat_item_t *mit;
85     io_stat_item_t *it;
86     guint64 relative_time, rt;
87     nstime_t *new_time;
88     GPtrArray *gp;
89     guint i;
90     int ftype;
91
92     mit = (io_stat_item_t *) arg;
93     parent = mit->parent;
94
95     /* If this frame's relative time is negative, set its relative time to last_relative_time
96        rather than disincluding it from the calculations. */
97     if ((pinfo->rel_ts.secs >= 0) && (pinfo->rel_ts.nsecs >= 0)) {
98         relative_time = ((guint64)pinfo->rel_ts.secs * G_GUINT64_CONSTANT(1000000)) +
99                         ((guint64)((pinfo->rel_ts.nsecs+500)/1000));
100         last_relative_time = relative_time;
101     } else {
102         relative_time = last_relative_time;
103     }
104
105     if (mit->parent->start_time == 0) {
106         mit->parent->start_time = pinfo->abs_ts.secs - pinfo->rel_ts.secs;
107     }
108
109     /* The prev item is always the last interval in which we saw packets. */
110     it = mit->prev;
111
112     /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
113     *  between the last struct and this one. If an item was not found in a previous interval, an empty
114     *  struct will be created for it. */
115     rt = relative_time;
116     while (rt >= it->start_time + parent->interval) {
117         it->next = (io_stat_item_t *)g_malloc(sizeof(io_stat_item_t));
118         it->next->prev = it;
119         it->next->next = NULL;
120         it = it->next;
121         mit->prev = it;
122
123         it->start_time = it->prev->start_time + parent->interval;
124         it->frames = 0;
125         it->counter = 0;
126         it->float_counter = 0;
127         it->double_counter = 0;
128         it->num = 0;
129         it->calc_type = it->prev->calc_type;
130         it->hf_index = it->prev->hf_index;
131         it->colnum = it->prev->colnum;
132     }
133
134     /* Store info in the current structure */
135     it->frames++;
136
137     switch (it->calc_type) {
138     case CALC_TYPE_FRAMES:
139     case CALC_TYPE_BYTES:
140     case CALC_TYPE_FRAMES_AND_BYTES:
141         it->counter += pinfo->fd->pkt_len;
142         break;
143     case CALC_TYPE_COUNT:
144         gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
145         if (gp) {
146             it->counter += gp->len;
147         }
148         break;
149     case CALC_TYPE_SUM:
150         gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
151         if (gp) {
152             guint64 val;
153
154             for (i=0; i<gp->len; i++) {
155                 switch (proto_registrar_get_ftype(it->hf_index)) {
156                 case FT_UINT8:
157                 case FT_UINT16:
158                 case FT_UINT24:
159                 case FT_UINT32:
160                     it->counter += fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
161                     break;
162                 case FT_UINT40:
163                 case FT_UINT48:
164                 case FT_UINT56:
165                 case FT_UINT64:
166                     it->counter += fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
167                     break;
168                 case FT_INT8:
169                 case FT_INT16:
170                 case FT_INT24:
171                 case FT_INT32:
172                     it->counter += fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
173                     break;
174                 case FT_INT40:
175                 case FT_INT48:
176                 case FT_INT56:
177                 case FT_INT64:
178                     it->counter += (gint64)fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
179                     break;
180                 case FT_FLOAT:
181                     it->float_counter +=
182                         (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
183                     break;
184                 case FT_DOUBLE:
185                     it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
186                     break;
187                 case FT_RELATIVE_TIME:
188                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
189                     val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
190                     it->counter  +=  val;
191                     break;
192                 default:
193                     /*
194                      * "Can't happen"; see the checks
195                      * in register_io_tap().
196                      */
197                     g_assert_not_reached();
198                     break;
199                 }
200             }
201         }
202         break;
203     case CALC_TYPE_MIN:
204         gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
205         if (gp) {
206             guint64 val;
207             gfloat float_val;
208             gdouble double_val;
209
210             ftype = proto_registrar_get_ftype(it->hf_index);
211             for (i=0; i<gp->len; i++) {
212                 switch (ftype) {
213                 case FT_UINT8:
214                 case FT_UINT16:
215                 case FT_UINT24:
216                 case FT_UINT32:
217                     val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
218                     if ((it->frames == 1 && i == 0) || (val < it->counter)) {
219                         it->counter = val;
220                     }
221                     break;
222                 case FT_UINT40:
223                 case FT_UINT48:
224                 case FT_UINT56:
225                 case FT_UINT64:
226                     val = fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
227                     if ((it->frames == 1 && i == 0) || (val < it->counter)) {
228                         it->counter = val;
229                     }
230                     break;
231                 case FT_INT8:
232                 case FT_INT16:
233                 case FT_INT24:
234                 case FT_INT32:
235                     val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
236                     if ((it->frames == 1 && i == 0) || ((gint32)val < (gint32)it->counter)) {
237                         it->counter = val;
238                     }
239                     break;
240                 case FT_INT40:
241                 case FT_INT48:
242                 case FT_INT56:
243                 case FT_INT64:
244                     val = fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
245                     if ((it->frames == 1 && i == 0) || ((gint64)val < (gint64)it->counter)) {
246                         it->counter = val;
247                     }
248                     break;
249                 case FT_FLOAT:
250                     float_val = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
251                     if ((it->frames == 1 && i == 0) || (float_val < it->float_counter)) {
252                         it->float_counter = float_val;
253                     }
254                     break;
255                 case FT_DOUBLE:
256                     double_val = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
257                     if ((it->frames == 1 && i == 0) || (double_val < it->double_counter)) {
258                         it->double_counter = double_val;
259                     }
260                     break;
261                 case FT_RELATIVE_TIME:
262                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
263                     val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
264                     if ((it->frames == 1 && i == 0) || (val < it->counter)) {
265                         it->counter = val;
266                     }
267                     break;
268                 default:
269                     /*
270                      * "Can't happen"; see the checks
271                      * in register_io_tap().
272                      */
273                     g_assert_not_reached();
274                     break;
275                 }
276             }
277         }
278         break;
279     case CALC_TYPE_MAX:
280         gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
281         if (gp) {
282             guint64 val;
283             gfloat float_val;
284             gdouble double_val;
285
286             ftype = proto_registrar_get_ftype(it->hf_index);
287             for (i=0; i<gp->len; i++) {
288                 switch (ftype) {
289                 case FT_UINT8:
290                 case FT_UINT16:
291                 case FT_UINT24:
292                 case FT_UINT32:
293                     val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
294                     if (val > it->counter)
295                         it->counter = val;
296                     break;
297                 case FT_UINT40:
298                 case FT_UINT48:
299                 case FT_UINT56:
300                 case FT_UINT64:
301                     val = fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
302                     if (val > it->counter)
303                         it->counter = val;
304                     break;
305                 case FT_INT8:
306                 case FT_INT16:
307                 case FT_INT24:
308                 case FT_INT32:
309                     val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
310                     if ((gint32)val > (gint32)it->counter)
311                         it->counter = val;
312                     break;
313                 case FT_INT40:
314                 case FT_INT48:
315                 case FT_INT56:
316                 case FT_INT64:
317                     val = fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
318                     if ((gint64)val > (gint64)it->counter)
319                         it->counter = val;
320                     break;
321                 case FT_FLOAT:
322                     float_val = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
323                     if (float_val > it->float_counter)
324                         it->float_counter = float_val;
325                     break;
326                 case FT_DOUBLE:
327                     double_val = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
328                     if (double_val > it->double_counter)
329                         it->double_counter = double_val;
330                     break;
331                 case FT_RELATIVE_TIME:
332                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
333                     val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
334                     if (val > it->counter)
335                         it->counter = val;
336                     break;
337                 default:
338                     /*
339                      * "Can't happen"; see the checks
340                      * in register_io_tap().
341                      */
342                     g_assert_not_reached();
343                     break;
344                 }
345             }
346         }
347         break;
348     case CALC_TYPE_AVG:
349         gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
350         if (gp) {
351             guint64 val;
352
353             ftype = proto_registrar_get_ftype(it->hf_index);
354             for (i=0; i<gp->len; i++) {
355                 it->num++;
356                 switch (ftype) {
357                 case FT_UINT8:
358                 case FT_UINT16:
359                 case FT_UINT24:
360                 case FT_UINT32:
361                     val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
362                     it->counter += val;
363                     break;
364                 case FT_UINT40:
365                 case FT_UINT48:
366                 case FT_UINT56:
367                 case FT_UINT64:
368                     val = fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
369                     it->counter += val;
370                     break;
371                 case FT_INT8:
372                 case FT_INT16:
373                 case FT_INT24:
374                 case FT_INT32:
375                     val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
376                     it->counter += val;
377                     break;
378                 case FT_INT40:
379                 case FT_INT48:
380                 case FT_INT56:
381                 case FT_INT64:
382                     val = fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
383                     it->counter += val;
384                     break;
385                 case FT_FLOAT:
386                     it->float_counter += (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
387                     break;
388                 case FT_DOUBLE:
389                     it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
390                     break;
391                 case FT_RELATIVE_TIME:
392                     new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
393                     val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
394                     it->counter += val;
395                     break;
396                 default:
397                     /*
398                      * "Can't happen"; see the checks
399                      * in register_io_tap().
400                      */
401                     g_assert_not_reached();
402                     break;
403                 }
404             }
405         }
406         break;
407     case CALC_TYPE_LOAD:
408         gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
409         if (gp) {
410             ftype = proto_registrar_get_ftype(it->hf_index);
411             if (ftype != FT_RELATIVE_TIME) {
412                 fprintf(stderr,
413                     "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
414                 exit(10);
415             }
416             for (i=0; i<gp->len; i++) {
417                 guint64 val;
418                 int tival;
419                 io_stat_item_t *pit;
420
421                 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
422                 val = ((guint64)new_time->secs*G_GUINT64_CONSTANT(1000000)) + (guint64)(new_time->nsecs/1000);
423                 tival = (int)(val % parent->interval);
424                 it->counter += tival;
425                 val -= tival;
426                 pit = it->prev;
427                 while (val > 0) {
428                     if (val < (guint64)parent->interval) {
429                         pit->counter += val;
430                         break;
431                     }
432                     pit->counter += parent->interval;
433                     val -= parent->interval;
434                     pit = pit->prev;
435                 }
436             }
437         }
438         break;
439     }
440     /* Store the highest value for this item in order to determine the width of each stat column.
441     *  For real numbers we only need to know its magnitude (the value to the left of the decimal point
442     *  so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
443     *  calc the average, round it to the next second and store the seconds. For all other calc types
444     *  of RELATIVE_TIME fields, store the counters without modification.
445     *  fields. */
446     switch (it->calc_type) {
447         case CALC_TYPE_FRAMES:
448         case CALC_TYPE_FRAMES_AND_BYTES:
449             parent->max_frame[it->colnum] =
450                 MAX(parent->max_frame[it->colnum], it->frames);
451             if (it->calc_type == CALC_TYPE_FRAMES_AND_BYTES)
452                 parent->max_vals[it->colnum] =
453                     MAX(parent->max_vals[it->colnum], it->counter);
454             break;
455         case CALC_TYPE_BYTES:
456         case CALC_TYPE_COUNT:
457         case CALC_TYPE_LOAD:
458             parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
459             break;
460         case CALC_TYPE_SUM:
461         case CALC_TYPE_MIN:
462         case CALC_TYPE_MAX:
463             ftype = proto_registrar_get_ftype(it->hf_index);
464             switch (ftype) {
465                 case FT_FLOAT:
466                     parent->max_vals[it->colnum] =
467                         MAX(parent->max_vals[it->colnum], (guint64)(it->float_counter+0.5));
468                     break;
469                 case FT_DOUBLE:
470                     parent->max_vals[it->colnum] =
471                         MAX(parent->max_vals[it->colnum], (guint64)(it->double_counter+0.5));
472                     break;
473                 case FT_RELATIVE_TIME:
474                     parent->max_vals[it->colnum] =
475                         MAX(parent->max_vals[it->colnum], it->counter);
476                     break;
477                 default:
478                     /* UINT16-64 and INT8-64 */
479                     parent->max_vals[it->colnum] =
480                         MAX(parent->max_vals[it->colnum], it->counter);
481                     break;
482             }
483             break;
484         case CALC_TYPE_AVG:
485             if (it->num == 0) /* avoid division by zero */
486                break;
487             ftype = proto_registrar_get_ftype(it->hf_index);
488             switch (ftype) {
489                 case FT_FLOAT:
490                     parent->max_vals[it->colnum] =
491                         MAX(parent->max_vals[it->colnum], (guint64)it->float_counter/it->num);
492                     break;
493                 case FT_DOUBLE:
494                     parent->max_vals[it->colnum] =
495                         MAX(parent->max_vals[it->colnum], (guint64)it->double_counter/it->num);
496                     break;
497                 case FT_RELATIVE_TIME:
498                     parent->max_vals[it->colnum] =
499                         MAX(parent->max_vals[it->colnum], ((it->counter/(guint64)it->num) + G_GUINT64_CONSTANT(500000000)) / NANOSECS_PER_SEC);
500                     break;
501                 default:
502                     /* UINT16-64 and INT8-64 */
503                     parent->max_vals[it->colnum] =
504                         MAX(parent->max_vals[it->colnum], it->counter/it->num);
505                     break;
506             }
507     }
508     return TRUE;
509 }
510
511 static int
512 magnitude (guint64 val, int max_w)
513 {
514     int i, mag = 0;
515
516     for (i=0; i<max_w; i++) {
517         mag++;
518         if ((val /= 10) == 0)
519             break;
520     }
521     return(mag);
522 }
523
524 /*
525 *  Print the calc_type_table[] function label centered in the column header.
526 */
527 static void
528 printcenter (const char *label, int lenval, int numpad)
529 {
530     int lenlab = (int) strlen(label), len;
531     const char spaces[] = "      ", *spaces_ptr;
532
533     len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
534     if (len > 0 && len < 6) {
535         spaces_ptr = &spaces[len];
536         if ((lenval-lenlab)%2 == 0) {
537             printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
538         } else {
539             printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
540         }
541     } else if (len > 0 && len <= 15) {
542         printf("%s|", label);
543     }
544 }
545
546 typedef struct {
547     int fr;  /* Width of this FRAMES column sans padding and border chars */
548     int val; /* Width of this non-FRAMES column sans padding and border chars */
549 } column_width;
550
551 static void
552 iostat_draw(void *arg)
553 {
554     guint32 num;
555     guint64 interval, duration, t, invl_end, dv;
556     int i, j, k, num_cols, num_rows, dur_secs_orig, dur_nsecs_orig, dur_secs, dur_nsecs, dur_mag,
557         invl_mag, invl_prec, tabrow_w, borderlen, invl_col_w, numpad = 1, namelen, len_filt, type,
558         maxfltr_w, ftype;
559     int fr_mag;    /* The magnitude of the max frame number in this column */
560     int val_mag;   /* The magnitude of the max value in this column */
561     char *spaces, *spaces_s, *filler_s = NULL, **fmts, *fmt = NULL;
562     const char *filter;
563     static gchar dur_mag_s[3], invl_prec_s[3], fr_mag_s[3], val_mag_s[3], *invl_fmt, *full_fmt;
564     io_stat_item_t *mit, **stat_cols, *item, **item_in_column;
565     gboolean last_row = FALSE;
566     io_stat_t *iot;
567     column_width *col_w;
568     struct tm *tm_time;
569     time_t the_time;
570
571     mit = (io_stat_item_t *)arg;
572     iot = mit->parent;
573     num_cols = iot->num_cols;
574     col_w = (column_width *)g_malloc(sizeof(column_width) * num_cols);
575     fmts = (char **)g_malloc(sizeof(char *) * num_cols);
576     duration = ((guint64)cfile.elapsed_time.secs * G_GUINT64_CONSTANT(1000000)) +
577                 (guint64)((cfile.elapsed_time.nsecs + 500) / 1000);
578
579     /* Store the pointer to each stat column */
580     stat_cols = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
581     for (j=0; j<num_cols; j++)
582         stat_cols[j] = &iot->items[j];
583
584     /* The following prevents gross inaccuracies when the user specifies an interval that is greater
585     *  than the capture duration. */
586     if (iot->interval > duration || iot->interval == G_MAXUINT64) {
587         interval = duration;
588         iot->interval = G_MAXUINT64;
589     } else {
590         interval = iot->interval;
591     }
592
593     /* Calc the capture duration's magnitude (dur_mag) */
594     dur_secs  = (int)(duration/G_GUINT64_CONSTANT(1000000));
595     dur_secs_orig = dur_secs;
596     dur_nsecs = (int)(duration%G_GUINT64_CONSTANT(1000000));
597     dur_nsecs_orig = dur_nsecs;
598     dur_mag = magnitude((guint64)dur_secs, 5);
599     g_snprintf(dur_mag_s, 3, "%u", dur_mag);
600
601     /* Calc the interval's magnitude */
602     invl_mag = magnitude(interval/G_GUINT64_CONSTANT(1000000), 5);
603
604     /* Set or get the interval precision */
605     if (interval == duration) {
606         /*
607         * An interval arg of 0 or an interval size exceeding the capture duration was specified.
608         * Set the decimal precision of duration based on its magnitude. */
609         if (dur_mag >= 2)
610             invl_prec = 1;
611         else if (dur_mag == 1)
612             invl_prec = 3;
613         else
614             invl_prec = 6;
615
616         borderlen = 30 + dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
617     } else {
618         invl_prec = iot->invl_prec;
619         borderlen = 25 + MAX(invl_mag,dur_mag) + (invl_prec == 0 ? 0 : invl_prec+1);
620     }
621
622     /* Round the duration according to invl_prec */
623     dv = 1000000;
624     for (i=0; i<invl_prec; i++)
625         dv /= 10;
626     if ((duration%dv) > 5*(dv/10)) {
627         duration += 5*(dv/10);
628         duration = (duration/dv) * dv;
629         dur_secs  = (int)(duration/G_GUINT64_CONSTANT(1000000));
630         dur_nsecs = (int)(duration%G_GUINT64_CONSTANT(1000000));
631         /*
632          * Recalc dur_mag in case rounding has increased its magnitude */
633         dur_mag  = magnitude((guint64)dur_secs, 5);
634     }
635     if (iot->interval == G_MAXUINT64)
636         interval = duration;
637
638     /* Calc the width of the time interval column (incl borders and padding). */
639     if (invl_prec == 0)
640         invl_col_w = (2*dur_mag) + 8;
641     else
642         invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
643
644     /* Update the width of the time interval column if date is shown */
645     switch (timestamp_get_type()) {
646     case TS_ABSOLUTE_WITH_YMD:
647     case TS_ABSOLUTE_WITH_YDOY:
648     case TS_UTC_WITH_YMD:
649     case TS_UTC_WITH_YDOY:
650         invl_col_w = MAX(invl_col_w, 23);
651         break;
652
653     default:
654         invl_col_w = MAX(invl_col_w, 12);
655         break;
656     }
657
658     borderlen = MAX(borderlen, invl_col_w);
659
660     /* Calc the total width of each row in the stats table and build the printf format string for each
661     *  column based on its field type, width, and name length.
662     *  NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
663     *        is an *integer*. */
664     tabrow_w = invl_col_w;
665     for (j=0; j<num_cols; j++) {
666         type = iot->items[j].calc_type;
667         if (type == CALC_TYPE_FRAMES_AND_BYTES) {
668             namelen = 5;
669         } else {
670             namelen = (int) strlen(calc_type_table[type].func_name);
671         }
672         if (type == CALC_TYPE_FRAMES
673          || type == CALC_TYPE_FRAMES_AND_BYTES) {
674
675             fr_mag = magnitude(iot->max_frame[j], 15);
676             fr_mag = MAX(6, fr_mag);
677             col_w[j].fr = fr_mag;
678             tabrow_w += col_w[j].fr + 3;
679             g_snprintf(fr_mag_s, 3, "%u", fr_mag);
680
681             if (type == CALC_TYPE_FRAMES) {
682                 fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);
683             } else {
684                 /* CALC_TYPE_FRAMES_AND_BYTES
685                 */
686                 val_mag = magnitude(iot->max_vals[j], 15);
687                 val_mag = MAX(5, val_mag);
688                 col_w[j].val = val_mag;
689                 tabrow_w += (col_w[j].val + 3);
690                 g_snprintf(val_mag_s, 3, "%u", val_mag);
691                 fmt = g_strconcat(" %", fr_mag_s, "u |", " %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
692             }
693             if (fmt)
694                 fmts[j] = fmt;
695             continue;
696         }
697         switch (type) {
698         case CALC_TYPE_BYTES:
699         case CALC_TYPE_COUNT:
700
701             val_mag = magnitude(iot->max_vals[j], 15);
702             val_mag = MAX(5, val_mag);
703             col_w[j].val = val_mag;
704             g_snprintf(val_mag_s, 3, "%u", val_mag);
705             fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
706             break;
707
708         default:
709             ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
710             switch (ftype) {
711                 case FT_FLOAT:
712                 case FT_DOUBLE:
713                     val_mag = magnitude(iot->max_vals[j], 15);
714                     g_snprintf(val_mag_s, 3, "%u", val_mag);
715                     fmt = g_strconcat(" %", val_mag_s, ".6f |", NULL);
716                     col_w[j].val = val_mag + 7;
717                     break;
718                 case FT_RELATIVE_TIME:
719                     /* Convert FT_RELATIVE_TIME field to seconds
720                     *  CALC_TYPE_LOAD was already converted in iostat_packet() ) */
721                     if (type == CALC_TYPE_LOAD) {
722                         iot->max_vals[j] /= interval;
723                     } else if (type != CALC_TYPE_AVG) {
724                         iot->max_vals[j] = (iot->max_vals[j] + G_GUINT64_CONSTANT(500000000)) / NANOSECS_PER_SEC;
725                     }
726                     val_mag = magnitude(iot->max_vals[j], 15);
727                     g_snprintf(val_mag_s, 3, "%u", val_mag);
728                     fmt = g_strconcat(" %", val_mag_s, "u.%06u |", NULL);
729                     col_w[j].val = val_mag + 7;
730                    break;
731
732                 default:
733                     val_mag = magnitude(iot->max_vals[j], 15);
734                     val_mag = MAX(namelen, val_mag);
735                     col_w[j].val = val_mag;
736                     g_snprintf(val_mag_s, 3, "%u", val_mag);
737
738                     switch (ftype) {
739                     case FT_UINT8:
740                     case FT_UINT16:
741                     case FT_UINT24:
742                     case FT_UINT32:
743                     case FT_UINT64:
744                         fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
745                         break;
746                     case FT_INT8:
747                     case FT_INT16:
748                     case FT_INT24:
749                     case FT_INT32:
750                     case FT_INT64:
751                         fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "d |", NULL);
752                         break;
753                     }
754             } /* End of ftype switch */
755         } /* End of calc_type switch */
756         tabrow_w += col_w[j].val + 3;
757         if (fmt)
758             fmts[j] = fmt;
759     } /* End of for loop (columns) */
760
761     borderlen = MAX(borderlen, tabrow_w);
762
763     /* Calc the max width of the list of filters. */
764     maxfltr_w = 0;
765     for (j=0; j<num_cols; j++) {
766         if (iot->filters[j]) {
767             k = (int) (strlen(iot->filters[j]) + 11);
768             maxfltr_w = MAX(maxfltr_w, k);
769         } else {
770             maxfltr_w = MAX(maxfltr_w, 26);
771         }
772     }
773     /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
774     *  (which currently = borderlen); however, if the filter width exceeds the table width and the
775     *  table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
776     *  The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
777     *  NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
778     *  XXX: A pref could be added to change the max width from the default size of 102. */
779     if (maxfltr_w > borderlen && borderlen < 102)
780             borderlen = MIN(maxfltr_w, 102);
781
782     /* Prevent double right border by adding a space */
783     if (borderlen-tabrow_w == 1)
784         borderlen++;
785
786     /* Display the top border */
787     printf("\n");
788     for (i=0; i<borderlen; i++)
789         printf("=");
790
791     spaces = (char *)g_malloc(borderlen+1);
792     for (i=0; i<borderlen; i++)
793         spaces[i] = ' ';
794     spaces[borderlen] = '\0';
795
796     spaces_s = &spaces[16];
797     printf("\n| IO Statistics%s|\n", spaces_s);
798     spaces_s = &spaces[2];
799     printf("|%s|\n", spaces_s);
800
801     if (invl_prec == 0) {
802         invl_fmt = g_strconcat("%", dur_mag_s, "u", NULL);
803         full_fmt = g_strconcat("| Duration: ", invl_fmt, ".%6u secs%s|\n", NULL);
804         spaces_s = &spaces[25 + dur_mag];
805         printf(full_fmt, dur_secs_orig, dur_nsecs_orig, spaces_s);
806         g_free(full_fmt);
807         full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
808         spaces_s = &spaces[18 + dur_mag];
809         printf(full_fmt, (guint32)(interval/G_GUINT64_CONSTANT(1000000)), spaces_s);
810     } else {
811         g_snprintf(invl_prec_s, 3, "%u", invl_prec);
812         invl_fmt = g_strconcat("%", dur_mag_s, "u.%0", invl_prec_s, "u", NULL);
813         full_fmt = g_strconcat("| Duration: ", invl_fmt, " secs%s|\n", NULL);
814         spaces_s = &spaces[19 + dur_mag + invl_prec];
815         printf(full_fmt, dur_secs, dur_nsecs/(int)dv, spaces_s);
816         g_free(full_fmt);
817
818         full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
819         spaces_s = &spaces[19 + dur_mag + invl_prec];
820         printf(full_fmt, (guint32)(interval/G_GUINT64_CONSTANT(1000000)),
821                (guint32)((interval%G_GUINT64_CONSTANT(1000000))/dv), spaces_s);
822     }
823     g_free(full_fmt);
824
825     spaces_s = &spaces[2];
826     printf("|%s|\n", spaces_s);
827
828     /* Display the list of filters and their column numbers vertically */
829     printf("| Col");
830     for (j=0; j<num_cols; j++) {
831         printf((j == 0 ? "%2u: " : "|    %2u: "), j+1);
832         if (!iot->filters[j]) {
833             /*
834             * An empty (no filter) comma field was specified */
835             spaces_s = &spaces[16 + 10];
836             printf("Frames and bytes%s|\n", spaces_s);
837         } else {
838             filter = iot->filters[j];
839             len_filt = (int) strlen(filter);
840
841             /* If the width of the widest filter exceeds the width of the stat table, borderlen has
842             *  been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
843             if (len_filt+11 <= borderlen) {
844                 printf("%s", filter);
845                 if (len_filt+11 <= borderlen) {
846                     spaces_s = &spaces[len_filt + 10];
847                     printf("%s", spaces_s);
848                 }
849                 printf("|\n");
850             } else {
851                 gchar *sfilter1, *sfilter2;
852                 const gchar *pos;
853                 gsize len;
854                 int next_start, max_w = borderlen-11;
855
856                 do {
857                     if (len_filt > max_w) {
858                         sfilter1 = g_strndup(filter, (gsize) max_w);
859                         /*
860                         * Find the pos of the last space in sfilter1. If a space is found, set
861                         * sfilter2 to the string prior to that space and print it; otherwise, wrap
862                         * the filter at max_w. */
863                         pos = g_strrstr(sfilter1, " ");
864                         if (pos) {
865                             len = (gsize)(pos-sfilter1);
866                             next_start = (int) len+1;
867                         } else {
868                             len = (gsize) strlen(sfilter1);
869                             next_start = (int)len;
870                         }
871                         sfilter2 = g_strndup(sfilter1, len);
872                         printf("%s%s|\n", sfilter2, &spaces[len+10]);
873                         g_free(sfilter1);
874                         g_free(sfilter2);
875
876                         printf("|        ");
877                         filter = &filter[next_start];
878                         len_filt = (int) strlen(filter);
879                     } else {
880                         printf("%s%s|\n", filter, &spaces[((int)strlen(filter))+10]);
881                         break;
882                     }
883                 } while (1);
884             }
885         }
886     }
887
888     printf("|-");
889     for (i=0; i<borderlen-3; i++) {
890         printf("-");
891     }
892     printf("|\n");
893
894     /* Display spaces above "Interval (s)" label */
895     spaces_s = &spaces[borderlen-(invl_col_w-2)];
896     printf("|%s|", spaces_s);
897
898     /* Display column number headers */
899     for (j=0; j<num_cols; j++) {
900         item = stat_cols[j];
901         if (item->calc_type == CALC_TYPE_FRAMES_AND_BYTES)
902             spaces_s = &spaces[borderlen - (col_w[j].fr + col_w[j].val)] - 3;
903         else if (item->calc_type == CALC_TYPE_FRAMES)
904             spaces_s = &spaces[borderlen - col_w[j].fr];
905         else
906             spaces_s = &spaces[borderlen - col_w[j].val];
907
908         printf("%-2d%s|", j+1, spaces_s);
909     }
910     if (tabrow_w < borderlen) {
911         filler_s = &spaces[tabrow_w+1];
912         printf("%s|", filler_s);
913     }
914
915     k = 11;
916     switch (timestamp_get_type()) {
917     case TS_ABSOLUTE:
918         printf("\n| Time    ");
919         break;
920     case TS_ABSOLUTE_WITH_YMD:
921     case TS_ABSOLUTE_WITH_YDOY:
922     case TS_UTC_WITH_YMD:
923     case TS_UTC_WITH_YDOY:
924         printf("\n| Date and time");
925         k = 16;
926         break;
927     case TS_RELATIVE:
928     case TS_NOT_SET:
929         printf("\n| Interval");
930         break;
931     default:
932         break;
933     }
934
935     spaces_s = &spaces[borderlen-(invl_col_w-k)];
936     printf("%s|", spaces_s);
937
938     /* Display the stat label in each column */
939     for (j=0; j<num_cols; j++) {
940         type = stat_cols[j]->calc_type;
941         if (type == CALC_TYPE_FRAMES) {
942             printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
943         } else if (type == CALC_TYPE_FRAMES_AND_BYTES) {
944             printcenter ("Frames", col_w[j].fr, numpad);
945             printcenter ("Bytes", col_w[j].val, numpad);
946         } else {
947             printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
948         }
949     }
950     if (filler_s)
951         printf("%s|", filler_s);
952     printf("\n|-");
953
954     for (i=0; i<tabrow_w-3; i++)
955         printf("-");
956     printf("|");
957
958     if (tabrow_w < borderlen)
959         printf("%s|", &spaces[tabrow_w+1]);
960
961     printf("\n");
962     t = 0;
963     if (invl_prec == 0 && dur_mag == 1)
964         full_fmt = g_strconcat("|  ", invl_fmt, " <> ", invl_fmt, "  |", NULL);
965     else
966         full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
967
968     if (interval == 0 || duration == 0) {
969         num_rows = 0;
970     } else {
971         num_rows = (int)(duration/interval) + ((int)(duration%interval) > 0 ? 1 : 0);
972     }
973
974     /* Load item_in_column with the first item in each column */
975     item_in_column = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
976     for (j=0; j<num_cols; j++) {
977         item_in_column[j] = stat_cols[j];
978     }
979
980     /* Display the table values
981     *
982     * The outer loop is for time interval rows and the inner loop is for stat column items.*/
983     for (i=0; i<num_rows; i++) {
984
985         if (i == num_rows-1)
986             last_row = TRUE;
987
988         /* Compute the interval for this row */
989         if (!last_row) {
990             invl_end = t + interval;
991         } else {
992             invl_end = duration;
993         }
994
995         /* Patch for Absolute Time */
996         /* XXX - has a Y2.038K problem with 32-bit time_t */
997         the_time = (time_t)(iot->start_time + (t/G_GUINT64_CONSTANT(1000000)));
998
999         /* Display the interval for this row */
1000         switch (timestamp_get_type()) {
1001         case TS_ABSOLUTE:
1002           tm_time = localtime(&the_time);
1003           if (tm_time != NULL) {
1004             printf("| %02d:%02d:%02d |",
1005                tm_time->tm_hour,
1006                tm_time->tm_min,
1007                tm_time->tm_sec);
1008           } else
1009             printf("| XX:XX:XX |");
1010           break;
1011
1012         case TS_ABSOLUTE_WITH_YMD:
1013           tm_time = localtime(&the_time);
1014           if (tm_time != NULL) {
1015             printf("| %04d-%02d-%02d %02d:%02d:%02d |",
1016                tm_time->tm_year + 1900,
1017                tm_time->tm_mon + 1,
1018                tm_time->tm_mday,
1019                tm_time->tm_hour,
1020                tm_time->tm_min,
1021                tm_time->tm_sec);
1022           } else
1023             printf("| XXXX-XX-XX XX:XX:XX |");
1024           break;
1025
1026         case TS_ABSOLUTE_WITH_YDOY:
1027           tm_time = localtime(&the_time);
1028           if (tm_time != NULL) {
1029             printf("| %04d/%03d %02d:%02d:%02d |",
1030                tm_time->tm_year + 1900,
1031                tm_time->tm_yday + 1,
1032                tm_time->tm_hour,
1033                tm_time->tm_min,
1034                tm_time->tm_sec);
1035           } else
1036             printf("| XXXX/XXX XX:XX:XX |");
1037           break;
1038
1039         case TS_UTC:
1040           tm_time = gmtime(&the_time);
1041           if (tm_time != NULL) {
1042             printf("| %02d:%02d:%02d |",
1043                tm_time->tm_hour,
1044                tm_time->tm_min,
1045                tm_time->tm_sec);
1046           } else
1047             printf("| XX:XX:XX |");
1048           break;
1049
1050         case TS_UTC_WITH_YMD:
1051           tm_time = gmtime(&the_time);
1052           if (tm_time != NULL) {
1053             printf("| %04d-%02d-%02d %02d:%02d:%02d |",
1054                tm_time->tm_year + 1900,
1055                tm_time->tm_mon + 1,
1056                tm_time->tm_mday,
1057                tm_time->tm_hour,
1058                tm_time->tm_min,
1059                tm_time->tm_sec);
1060           } else
1061             printf("| XXXX-XX-XX XX:XX:XX |");
1062           break;
1063
1064         case TS_UTC_WITH_YDOY:
1065           tm_time = gmtime(&the_time);
1066           if (tm_time != NULL) {
1067             printf("| %04d/%03d %02d:%02d:%02d |",
1068                tm_time->tm_year + 1900,
1069                tm_time->tm_yday + 1,
1070                tm_time->tm_hour,
1071                tm_time->tm_min,
1072                tm_time->tm_sec);
1073           } else
1074             printf("| XXXX/XXX XX:XX:XX |");
1075           break;
1076
1077         case TS_RELATIVE:
1078         case TS_NOT_SET:
1079           if (invl_prec == 0) {
1080               if (last_row) {
1081                   int maxw;
1082                   maxw = dur_mag >= 3 ? dur_mag+1 : 3;
1083                   g_free(full_fmt);
1084                   g_snprintf(dur_mag_s, 3, "%u", maxw);
1085                   full_fmt = g_strconcat( dur_mag == 1 ? "|  " : "| ",
1086                                           invl_fmt, " <> ", "%-",
1087                                           dur_mag_s, "s|", NULL);
1088                   printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)), "Dur");
1089               } else {
1090                   printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)),
1091                          (guint32)(invl_end/G_GUINT64_CONSTANT(1000000)));
1092               }
1093           } else {
1094               printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)),
1095                      (guint32)(t%G_GUINT64_CONSTANT(1000000) / dv),
1096                      (guint32)(invl_end/G_GUINT64_CONSTANT(1000000)),
1097                      (guint32)(invl_end%G_GUINT64_CONSTANT(1000000) / dv));
1098           }
1099           break;
1100      /* case TS_DELTA:
1101         case TS_DELTA_DIS:
1102         case TS_EPOCH:
1103             are not implemented */
1104         default:
1105           break;
1106         }
1107
1108         /* Display stat values in each column for this row */
1109         for (j=0; j<num_cols; j++) {
1110             fmt = fmts[j];
1111             item = item_in_column[j];
1112
1113             if (item) {
1114                 switch (item->calc_type) {
1115                 case CALC_TYPE_FRAMES:
1116                     printf(fmt, item->frames);
1117                     break;
1118                 case CALC_TYPE_BYTES:
1119                 case CALC_TYPE_COUNT:
1120                     printf(fmt, item->counter);
1121                     break;
1122                 case CALC_TYPE_FRAMES_AND_BYTES:
1123                     printf(fmt, item->frames, item->counter);
1124                     break;
1125
1126                 case CALC_TYPE_SUM:
1127                 case CALC_TYPE_MIN:
1128                 case CALC_TYPE_MAX:
1129                     ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1130                     switch (ftype) {
1131                     case FT_FLOAT:
1132                         printf(fmt, item->float_counter);
1133                         break;
1134                     case FT_DOUBLE:
1135                         printf(fmt, item->double_counter);
1136                         break;
1137                     case FT_RELATIVE_TIME:
1138                         item->counter = (item->counter + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
1139                         printf(fmt,
1140                                (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
1141                                (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
1142                         break;
1143                     default:
1144                         printf(fmt, item->counter);
1145                         break;
1146                     }
1147                     break;
1148
1149                 case CALC_TYPE_AVG:
1150                     num = item->num;
1151                     if (num == 0)
1152                         num = 1;
1153                     ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1154                     switch (ftype) {
1155                     case FT_FLOAT:
1156                         printf(fmt, item->float_counter/num);
1157                         break;
1158                     case FT_DOUBLE:
1159                         printf(fmt, item->double_counter/num);
1160                         break;
1161                     case FT_RELATIVE_TIME:
1162                         item->counter = ((item->counter / (guint64)num) + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
1163                         printf(fmt,
1164                                (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
1165                                (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
1166                         break;
1167                     default:
1168                         printf(fmt, item->counter / (guint64)num);
1169                         break;
1170                     }
1171                     break;
1172
1173                 case CALC_TYPE_LOAD:
1174                     ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1175                     switch (ftype) {
1176                     case FT_RELATIVE_TIME:
1177                         if (!last_row) {
1178                             printf(fmt,
1179                                 (int) (item->counter/interval),
1180                                    (int)((item->counter%interval)*G_GUINT64_CONSTANT(1000000) / interval));
1181                         } else {
1182                             printf(fmt,
1183                                    (int) (item->counter/(invl_end-t)),
1184                                    (int)((item->counter%(invl_end-t))*G_GUINT64_CONSTANT(1000000) / (invl_end-t)));
1185                         }
1186                         break;
1187                     }
1188                     break;
1189                 }
1190
1191                 if (last_row) {
1192                     g_free(fmt);
1193                 } else {
1194                     item_in_column[j] = item_in_column[j]->next;
1195                 }
1196             } else {
1197                 printf(fmt, (guint64)0, (guint64)0);
1198             }
1199         }
1200         if (filler_s)
1201             printf("%s|", filler_s);
1202         printf("\n");
1203         t += interval;
1204
1205     }
1206     for (i=0; i<borderlen; i++) {
1207         printf("=");
1208     }
1209     printf("\n");
1210     g_free(iot->items);
1211     g_free(iot->max_vals);
1212     g_free(iot->max_frame);
1213     g_free(iot);
1214     g_free(col_w);
1215     g_free(invl_fmt);
1216     g_free(full_fmt);
1217     g_free(fmts);
1218     g_free(spaces);
1219     g_free(stat_cols);
1220     g_free(item_in_column);
1221 }
1222
1223
1224 static void
1225 register_io_tap(io_stat_t *io, int i, const char *filter)
1226 {
1227     GString *error_string;
1228     const char *flt;
1229     int j;
1230     size_t namelen;
1231     const char *p, *parenp;
1232     char *field;
1233     header_field_info *hfi;
1234
1235     io->items[i].prev       = &io->items[i];
1236     io->items[i].next       = NULL;
1237     io->items[i].parent     = io;
1238     io->items[i].start_time = 0;
1239     io->items[i].calc_type  = CALC_TYPE_FRAMES_AND_BYTES;
1240     io->items[i].frames     = 0;
1241     io->items[i].counter    = 0;
1242     io->items[i].num        = 0;
1243
1244     io->filters[i] = filter;
1245     flt = filter;
1246
1247     field = NULL;
1248     hfi = NULL;
1249     for (j=0; calc_type_table[j].func_name; j++) {
1250         namelen = strlen(calc_type_table[j].func_name);
1251         if (filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
1252             io->items[i].calc_type = calc_type_table[j].calc_type;
1253             io->items[i].colnum = i;
1254             if (*(filter+namelen) == '(') {
1255                 p = filter+namelen+1;
1256                 parenp = strchr(p, ')');
1257                 if (!parenp) {
1258                     fprintf(stderr,
1259                         "\ntshark: Closing parenthesis missing from calculated expression.\n");
1260                     exit(10);
1261                 }
1262
1263                 if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES) {
1264                     if (parenp != p) {
1265                         fprintf(stderr,
1266                             "\ntshark: %s does not require or allow a field name within the parens.\n",
1267                             calc_type_table[j].func_name);
1268                         exit(10);
1269                     }
1270                 } else {
1271                     if (parenp == p) {
1272                             /* bail out if a field name was not specified */
1273                             fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n",
1274                                 calc_type_table[j].func_name);
1275                             exit(10);
1276                     }
1277                 }
1278
1279                 field = (char *)g_malloc(parenp-p+1);
1280                 memcpy(field, p, parenp-p);
1281                 field[parenp-p] = '\0';
1282                 flt = parenp + 1;
1283                 if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
1284                     break;
1285                 hfi = proto_registrar_get_byname(field);
1286                 if (!hfi) {
1287                     fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
1288                         field);
1289                     g_free(field);
1290                     exit(10);
1291                 }
1292
1293                 io->items[i].hf_index = hfi->id;
1294                 break;
1295             }
1296         } else {
1297             if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
1298                 flt = "";
1299             io->items[i].colnum = i;
1300         }
1301     }
1302     if (hfi && !(io->items[i].calc_type == CALC_TYPE_BYTES ||
1303                  io->items[i].calc_type == CALC_TYPE_FRAMES ||
1304                  io->items[i].calc_type == CALC_TYPE_FRAMES_AND_BYTES)) {
1305         /* check that the type is compatible */
1306         switch (hfi->type) {
1307         case FT_UINT8:
1308         case FT_UINT16:
1309         case FT_UINT24:
1310         case FT_UINT32:
1311         case FT_UINT64:
1312         case FT_INT8:
1313         case FT_INT16:
1314         case FT_INT24:
1315         case FT_INT32:
1316         case FT_INT64:
1317             /* these types support all calculations */
1318             break;
1319         case FT_FLOAT:
1320         case FT_DOUBLE:
1321             /* these types only support SUM, COUNT, MAX, MIN, AVG */
1322             switch (io->items[i].calc_type) {
1323             case CALC_TYPE_SUM:
1324             case CALC_TYPE_COUNT:
1325             case CALC_TYPE_MAX:
1326             case CALC_TYPE_MIN:
1327             case CALC_TYPE_AVG:
1328                 break;
1329             default:
1330                 fprintf(stderr,
1331                     "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1332                     field,
1333                     calc_type_table[j].func_name);
1334                 exit(10);
1335             }
1336             break;
1337         case FT_RELATIVE_TIME:
1338             /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1339             switch (io->items[i].calc_type) {
1340             case CALC_TYPE_SUM:
1341             case CALC_TYPE_COUNT:
1342             case CALC_TYPE_MAX:
1343             case CALC_TYPE_MIN:
1344             case CALC_TYPE_AVG:
1345             case CALC_TYPE_LOAD:
1346                 break;
1347             default:
1348                 fprintf(stderr,
1349                     "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1350                     field,
1351                     calc_type_table[j].func_name);
1352                 exit(10);
1353             }
1354             break;
1355         default:
1356             /*
1357              * XXX - support all operations on floating-point
1358              * numbers?
1359              */
1360             if (io->items[i].calc_type != CALC_TYPE_COUNT) {
1361                 fprintf(stderr,
1362                     "\ntshark: %s doesn't have integral values, so %s(*) "
1363                     "calculations are not supported on it.\n",
1364                     field,
1365                     calc_type_table[j].func_name);
1366                 exit(10);
1367             }
1368             break;
1369         }
1370         g_free(field);
1371     }
1372
1373     error_string = register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
1374                                        iostat_packet, i ? NULL : iostat_draw);
1375     if (error_string) {
1376         g_free(io->items);
1377         g_free(io);
1378         fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
1379             error_string->str);
1380         g_string_free(error_string, TRUE);
1381         exit(1);
1382     }
1383 }
1384
1385 static void
1386 iostat_init(const char *opt_arg, void *userdata _U_)
1387 {
1388     gdouble interval_float;
1389     guint32 idx = 0;
1390     int i;
1391     io_stat_t *io;
1392     const gchar *filters, *str, *pos;
1393
1394     if ((*(opt_arg+(strlen(opt_arg)-1)) == ',') ||
1395         (sscanf(opt_arg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
1396         (idx < 8)) {
1397         fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1398         exit(1);
1399     }
1400
1401     filters = opt_arg+idx;
1402     if (*filters) {
1403         if (*filters != ',') {
1404             /* For locale's that use ',' instead of '.', the comma might
1405              * have been consumed during the floating point conversion. */
1406             --filters;
1407             if (*filters != ',') {
1408                 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1409                 exit(1);
1410             }
1411         }
1412     } else
1413         filters = NULL;
1414
1415     switch (timestamp_get_type()) {
1416     case TS_DELTA:
1417     case TS_DELTA_DIS:
1418     case TS_EPOCH:
1419         fprintf(stderr, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
1420         exit(1);
1421     default:
1422         break;
1423     }
1424
1425     io = (io_stat_t *)g_malloc(sizeof(io_stat_t));
1426
1427     /* If interval is 0, calculate statistics over the whole file by setting the interval to
1428     *  G_MAXUINT64 */
1429     if (interval_float == 0) {
1430         io->interval = G_MAXUINT64;
1431         io->invl_prec = 0;
1432     } else {
1433         /* Set interval to the number of us rounded to the nearest integer */
1434         io->interval = (guint64)(interval_float * 1000000.0 + 0.5);
1435         /*
1436         * Determine what interval precision the user has specified */
1437         io->invl_prec = 6;
1438         for (i=10; i<10000000; i*=10) {
1439             if (io->interval%i > 0)
1440                 break;
1441             io->invl_prec--;
1442         }
1443         if (io->invl_prec == 0) {
1444             /* The precision is zero but if the user specified one of more zeros after the decimal point,
1445                they want that many decimal places shown in the table for all time intervals except
1446                response time values such as smb.time which always have 6 decimal places of precision.
1447                This feature is useful in cases where for example the duration is 9.1, you specify an
1448                interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to
1449                1.1, the last interval becomes
1450                last interval is rounded up to value that is greater than the duration. */
1451             const gchar *invl_start = opt_arg+8;
1452             gchar *intv_end;
1453             int invl_len;
1454
1455             intv_end = g_strstr_len(invl_start, -1, ",");
1456             invl_len = (int)(intv_end - invl_start);
1457             invl_start = g_strstr_len(invl_start, invl_len, ".");
1458
1459             if (invl_start != NULL) {
1460                 invl_len = (int)(intv_end - invl_start - 1);
1461                 if (invl_len)
1462                     io->invl_prec = MIN(invl_len, 6);
1463             }
1464         }
1465     }
1466     if (io->interval < 1) {
1467         fprintf(stderr,
1468             "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1469         exit(10);
1470     }
1471
1472     /* Find how many ',' separated filters we have */
1473     io->num_cols = 1;
1474     io->start_time = 0;
1475
1476     if (filters && (*filters != '\0')) {
1477         /* Eliminate the first comma. */
1478         filters++;
1479         str = filters;
1480         while ((str = strchr(str, ','))) {
1481             io->num_cols++;
1482             str++;
1483         }
1484     }
1485
1486     io->items     = (io_stat_item_t *)g_malloc(sizeof(io_stat_item_t) * io->num_cols);
1487     io->filters   = (const char **)g_malloc(sizeof(char *) * io->num_cols);
1488     io->max_vals  = (guint64 *)g_malloc(sizeof(guint64) * io->num_cols);
1489     io->max_frame = (guint32 *)g_malloc(sizeof(guint32) * io->num_cols);
1490
1491     for (i=0; i<io->num_cols; i++) {
1492         io->max_vals[i]  = 0;
1493         io->max_frame[i] = 0;
1494     }
1495
1496     /* Register a tap listener for each filter */
1497     if ((!filters) || (filters[0] == 0)) {
1498         register_io_tap(io, 0, NULL);
1499     } else {
1500         gchar *filter;
1501         i = 0;
1502         str = filters;
1503         do {
1504             pos = (gchar*) strchr(str, ',');
1505             if (pos == str) {
1506                 register_io_tap(io, i, NULL);
1507             } else if (pos == NULL) {
1508                 str = (const char*) g_strstrip((gchar*)str);
1509                 filter = g_strdup(str);
1510                 if (*filter)
1511                     register_io_tap(io, i, filter);
1512                 else
1513                     register_io_tap(io, i, NULL);
1514             } else {
1515                 filter = (gchar *)g_malloc((pos-str)+1);
1516                 g_strlcpy( filter, str, (gsize) ((pos-str)+1));
1517                 filter = g_strstrip(filter);
1518                 register_io_tap(io, i, (char *) filter);
1519             }
1520             str = pos+1;
1521             i++;
1522         } while (pos);
1523     }
1524 }
1525
1526 static stat_tap_ui iostat_ui = {
1527     REGISTER_STAT_GROUP_GENERIC,
1528     NULL,
1529     "io,stat",
1530     iostat_init,
1531     0,
1532     NULL
1533 };
1534
1535 void
1536 register_tap_listener_iostat(void)
1537 {
1538     register_stat_tap_ui(&iostat_ui, NULL);
1539 }
1540
1541 /*
1542  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1543  *
1544  * Local variables:
1545  * c-basic-offset: 4
1546  * tab-width: 8
1547  * indent-tabs-mode: nil
1548  * End:
1549  *
1550  * vi: set shiftwidth=4 tabstop=8 expandtab:
1551  * :indentSize=4:tabSize=8:noTabs=true:
1552  */